Responsive flexbox layout: reordering elements - css

I'm trying to achieve a responsive layout as outlined in the image, left side would be mobile, right side desktop. This would be relatively easy using flexbox if I could set a fixed height for the wrapper, but because the content is dynamic this is not possible.
Another solution would be to use position absolute on element C, but this seems very hacky, I'm hoping to find a more elegant solution.
Here is a framework for the code:
.wrapper {
display: flex;
flex-direction: column;
}
#media(min-width: 800px) {
.wrapper {
flex-direction: row;
flex-wrap: wrap;
}
}
.section {
display: flex;
justify-content: center;
align-items: center;
font-size: 36px;
}
.section-a {
background: green;
height: 200px;
}
#media(min-width: 800px) {
.section-a {
flex-basis: 33%;
}
}
.section-b {
background: yellow;
height: 400px;
}
#media(min-width: 800px) {
.section-b {
flex-basis: 66%;
}
}
.section-c {
background: blue;
height: 200px;
}
#media(min-width: 800px) {
.section-c {
flex-basis: 33%;
}
}
<div class="wrapper">
<div class="section section-a">A</div>
<div class="section section-b">B</div>
<div class="section section-c">C</div>
</div>
Thanks for the help!

You can achieve this by using grid. I have simplified your code and removed unwanted css
.wrapper {
display: grid;
grid-gap: 10px;
grid-template-columns: 1fr 1fr;
grid-template-rows: [row1-start] auto [row2-start] auto [row2-end];
}
.section {
padding: 20px;
display: flex;
align-items: center;
}
.section-a {
height: 50px;
background-color: green;
}
.section-b {
grid-row: row1-start / row2-end;
grid-column: 2/-1;
background-color: yellow;
}
.section-c {
background-color: blue;
height: 180px;
}
<div class="wrapper">
<div class="section section-a">A</div>
<div class="section section-b">B</div>
<div class="section section-c">C</div>
</div>
Working fiddle here

Edit: it's an attempt, as I misunderstood OP's question. My answer has a given .wrapper height.
A solution using flex with the following properties:
order
flex-basis
This code applies for your desktop layout:
.wrapper {
width: 100%;
height: 100vh;
display: flex;
flex-flow: column wrap;
}
.section {
width: 50%;
border: 1px solid black;
box-sizing: border-box;
}
.section-a {
height: 25vh; // for example
}
.section-b {
order: 3;
flex-basis: 100%;
}
.section-c {
flex-grow: 1;
}
<div class="wrapper">
<div class="section section-a">A</div>
<div class="section section-b">B</div>
<div class="section section-c">C</div>
</div>

Related

Sizing a grid column based on a child's aspect ratio

My question is similar to this one: I'm trying to contain an aspect-ratio element within its parent element. One difference though, this aspect-ratio element has siblings—a header and a footer—and all this nice family should be center-aligned and share a common width.
Images are worth a thousand words:
GIFs are worth a thousand images:
I'm close to that result, but I'm not quite there yet:
body {
height: 100%;
margin: 0;
}
html {
background-color: lightgrey;
height: 100%;
}
#footer,
#header {
background-color: blue;
height: 50px;
}
#paper {
aspect-ratio: 1;
background-color: red;
margin: auto;
max-height: 100%;
overflow: hidden;
width: 100%;
}
#wrapper {
align-content: center;
display: grid;
height: 100%;
}
<div id="wrapper">
<div id="header"></div>
<div id="paper"></div>
<div id="footer"></div>
</div>
Any CSS wizard to help me out?
Not sure if you can get all the requirements but here is the best I could do (seems to work on chrome only)
body {
background-color: lightgrey;
margin: 0;
}
#footer,
#header {
background-color: blue;
height: 50px;
}
#paper {
aspect-ratio: 1;
background-color: red;
max-height: 100%;
max-width: 100vw;
}
#wrapper {
place-content: center;
display: grid;
height: 100vmin;
margin-block: max(0px,50vh - 50vmin);
grid-template-rows: auto minmax(0, 1fr) auto;
}
<div id="wrapper">
<div id="header"></div>
<div id="paper"></div>
<div id="footer"></div>
</div>
If the 50px is known you can do like below:
body {
background-color: lightgrey;
margin: 0;
--h: 50px; /* the fixed height */
}
#footer,
#header {
background-color: blue;
height: var(--h);
}
#paper {
aspect-ratio: 1;
background-color: red;
width: min(100vw,100vh - 2*var(--h));
}
#wrapper {
place-content: center;
display: grid;
height: min(100vh, 100vw + 2*var(--h));
margin-block: max(0px, (100vh - 100vw - 2*var(--h))/2);
grid-template-rows: auto minmax(0, 1fr) auto;
}
<div id="wrapper">
<div id="header"></div>
<div id="paper"></div>
<div id="footer"></div>
</div>
Try this solution, all the magic happens in grid-template-columns and grid-template-rows.
html {
background-color: lightgrey;
height: 100%;
}
body {
height: 100%;
margin: 0;
}
#wrapper {
--footer-header-height: 50px;
align-content: center;
display: grid;
height: 100vh;
grid-template-columns: 1fr minmax(auto, calc(100vh - var(--footer-header-height) * 2)) 1fr;
grid-template-rows: auto minmax(auto, 100vw) auto;
}
#footer,
#header {
grid-column: 2;
background-color: blue;
height: var(--footer-header-height);
}
#paper {
grid-column: 2;
aspect-ratio: 1 / 1;
background-color: red;
}
<div id="wrapper">
<div id="header"></div>
<div id="paper"></div>
<div id="footer"></div>
</div>
Actually Container Queries enable us to solve this kind of problems elegantly.
Support for this feature is currently very bad (see here), but it's part of Interop 2022 so I guess it'll look different by the end of the year.
I post this as an answer as it might help someone in the future 👽👋
Note that you currently need to turn on a flag on Chrome to be able to test it.
body {
container-type: size;
height: 100%;
margin: 0;
}
html {
background-color: lightgrey;
height: 100%;
}
#footer,
#header {
background-color: blue;
}
#paper {
background-color: red;
}
#wrapper {
align-content: center;
display: grid;
grid-template-columns: min(100cqi, (100cqb - 100px));
grid-template-rows: 50px min(100cqb - 100px, 100cqi) 50px;
justify-content: center;
}
<div id="wrapper">
<div id="header"></div>
<div id="paper"></div>
<div id="footer"></div>
</div>
Here's the same code but relying on viewport units (works in all browsers):
body {
height: 100%;
margin: 0;
}
html {
background-color: lightgrey;
height: 100%;
}
#footer,
#header {
background-color: blue;
}
#paper {
background-color: red;
}
#wrapper {
align-content: center;
display: grid;
grid-template-columns: min(100vw, (100vh - 100px));
grid-template-rows: 50px min(100vh - 100px, 100vw) 50px;
height: 100%;
justify-content: center;
}
<div id="wrapper">
<div id="header"></div>
<div id="paper"></div>
<div id="footer"></div>
</div>

Can this be achieved with a flexbox or grid?

I am trying to achieve this effect using flexbox and without media query. Can i get this effect with the grid?
desktop -> tablet -> mobile:
Sure, You can achieve something like this using flexbox. I believe the easiest way to do this would be having a main container for all three blocks, and a sub-container for the first two blocks that'll move. Here's a working example:
.container, .subcontainer {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.container {
width: 100%;
}
.subcontainer {
width: 50%;
min-width: 125px;
}
#block1, #block2, #block3 {
margin-bottom: 50px;
width: 100px;
height: 100px;
}
#block1 {
background-color: peru;
margin-right: 25px;
}
#block2 {
background-color: darkorange;
margin-right: -50px;
}
#block3 {
background-color: orange;
height: unset;
min-height: 100px;
display: flex;
align-items: stretch;
}
<div class="container">
<div class="subcontainer">
<div id="block1"></div>
<div id="block2"></div>
</div>
<div id="block3"></div>
</div>
Here's an alternate solution, using flexbox, grid, and #media queries. Easier to use flexbox on the 1st and 3rd picture because they have one dimension/direction. With the 2nd picture it's easier to use grid as it's more of a 2D layout that requires you to mind the horizontal and vertical of elements to set up properly.
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
display: flex;
justify-content: space-evenly;
height: 80vh;
margin: 3rem;
}
.container>div {
border: 1px solid black;
display: grid;
width: 10rem;
place-content: center;
}
#media screen and (max-width: 992px) {
.container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
gap: 1rem;
}
.b {
grid-column: 1/2;
grid-row: 2/3;
}
.c {
grid-column: 2/3;
grid-row: span 2;
}
}
#media screen and (max-width: 600px) {
.container {
display: flex;
flex-direction: column;
}
.container>div {
flex: 1;
}
}
<div class="container">
<div class="a">A</div>
<div class="b">B</div>
<div class="c">C</div>
</div>

100% total width for flex items in flex container, with gap inbetween

I am trying to position elements in a flexbox with flex-wrap with gap in between
Ideally the way this should be displayed is:
On the first row the blue box taking the full width no gaps anywhere
Second row Red box taking the first 33%, Green box taking the remaining 66%
There should be 12px gap between the 2 rows
There should be 12px gap between the Red and Green item without them going on the next row, so their widths should actually become 33% - 6px and 66% - 6px so that there is space left for the gap.
End result should look something like this:
.container {
max-width: 200px;
width: 200px;
display: flex;
flex-wrap: wrap;
gap: 12px;
padding: 0 12px 0 12px;
}
.item1 {
width:33%;
height: 200px;
background-color: red;
}
.item2 {
width:66%;
height: 200px;
background-color: green;
}
.item3 {
width: 100%;
height: 200px;
background-color: blue;
}
<div class="container">
<div class="item3"></div>
<div class="item1"></div>
<div class="item2"></div>
</div>
This a CSS grid use case where you don't need a lot of code and headaches:
.container {
max-width: 200px;
display: grid;
grid-template-columns: 1fr 2fr;
grid-auto-rows:200px;
gap: 12px;
}
.item1 {
background-color: red;
}
.item2 {
background-color: green;
}
.item3 {
grid-column: 1/-1;
background-color: blue;
}
<div class="container">
<div class="item3"></div>
<div class="item1"></div>
<div class="item2"></div>
</div>
I have added a flex-direction: row, Removed the padding and added a calc function to the widths as well as upped the 66% to 67% to create 100% total.
.container {
max-width: 200px;
width: 200px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 12px;
}
.item1 {
width: calc(33% - 6px);
height: 200px;
background-color: red;
}
.item2 {
width:calc(67% - 6px);
height: 200px;
background-color: green;
}
.item3 {
width: 100%;
height: 200px;
background-color: blue;
}
<div class="container">
<div class="item3"></div>
<div class="item1"></div>
<div class="item2"></div>
</div>
You can use flex-basis and flex-grow instead of width. See the example below...
.container {
max-width: 200px;
width: 200px;
display: flex;
flex-wrap: wrap;
gap: 12px;
padding: 0 12px 0 12px;
}
.item1 {
flex-basis: 33%;
height: 200px;
background-color: red;
}
.item2 {
flex-basis: 60%; /* Reduce this value abit and allow to auto grow */
flex-grow: 1; /* Last item will fill up the remaining space */
height: 200px;
background-color: green;
}
.item3 {
flex-basis: 100%;
height: 200px;
background-color: blue;
}
<div class="container">
<div class="item3"></div>
<div class="item1"></div>
<div class="item2"></div>
</div>
This is #Ben variant but improve calculations
.container {
--gap: 12px;
max-width: 200px;
width: 200px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: var(--gap); // better use like veriable, you can edit it one time in all plays together
}
.item {
height: 200px; // we have pattern, we don't need repeat it
}
.item1 {
width: calc(33.33% - var(--gap) / 2); // improve calculation
background-color: red;
}
.item2 {
width: calc(66.66% - var(--gap) / 2);// improve calculation
background-color: green;
}
.item3 {
width: 100%;
background-color: blue;
}
<div class="container">
<div class="item item3"></div>
<div class="item item1"></div>
<div class="item item2"></div>
</div>

How to create a 4 section flex layout which collapses only on screen resize

I am attempting to create a flex layout as illustrated in the screenshots below.
This is what the page will look like on normal screen size (before resize).
Page resize will look like this, with sidebar over main content.
The code I have written appears to work. However, how can I make the sidebar fall under the main content when screen size is reduced?
CSS:
div#container{
display: flex;
flex-direction: column;
}
main{
display: flex;
background-color: #ccc;
flex-wrap: wrap;
height: 80vh;
}
header {
background-color: #F2F2F2;
height: 100px;
}
footer {
background-color: #F2F2F2;
height: 100px;
}
section#content {
background-color: #D62121;
width: auto;
flex-grow: 1;
}
section#sidebar {
background-color: green;
width: 350px;
}
/* For demo purposes */
#media (max-width: 815px) {
section#sidebar {
width: 100%;
}
main{
flex-wrap: wrap;
}
}
HTML
<div id="container">
<header>
<p>HEADER</p>
</header>
<main>
<section id="content">
<div>
<!-- Add long text here and main content falls below sidebar.-->
</div>
</section>
<section id="sidebar">
<h1>Sidebar</h1>
</section>
</main>
<footer>
<p>FOOTER</p>
</footer>
</div>
div#container{
display: flex;
flex-wrap: wrap;
}
main{
display: flex;
flex-wrap: wrap;
height: 80vh;
margin-bottom: 30px;
}
header {
background-color: coral;
height: 100px;
flex: 0 0 100%;
max-width: 100%;
margin-bottom: 30px;
}
footer {
background-color: coral;
height: 100px;
}
main {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
width: 100%;
}
section#content {
background-color: #D62121;
flex: 0 0 calc(100% - 380px);
max-width: calc(100% - 380px);
}
section#sidebar {
background-color: green;
width: 350px;
flex: 0 0 350px;
}
footer {
flex: 0 0 100%;
max-width: 100%;
}
/* For demo purposes */
#media (max-width: 815px) {
section#content,
section#sidebar{
flex: 0 0 100%;
max-width: 100%;
width: 100%;
}
section#content {
order: 2;
}
section#sidebar{
order: 1;
margin-bottom: 30px;
}
}
The layout breakage was because of the flex-grow:1. After setting its flex-grow to the initial value and setting its maximum width flex: 0 0 calc(100% - 380px) in it. The layout will stop expanding to its max-width and text will break accordingly.
I hope this will help. Please review in js fiddle.
https://jsfiddle.net/chhiring90/8xmvy4ur/

How create proper flexbox to make one column inside of content scrollable and another one static?

I want to create a layout where we have a header (responsive, with any height), footer (responsive, with any height), and content that fills the rest of space. Content should have two columns - first(right) that fits the content and second(left) that is very long and should have overflow with y-axis scrolling.
Detailed snipped with HTML and CSS attached.
I have problem with making them work inside of flexible height. I know how to make it work when I have only two columns, but with a responsive footer and header, I have problems.
I also have a code snippet inside of codepen
Thanks in advance for any help :)
html,
body {
height: 100%;
width: 100%;
}
.container {
height: 100%;
display: flex;
flex-direction: column;
}
.column {
background-color: white;
display: flex;
flex-direction: column;
flex-basis: 100%;
flex: 1;
color: black;
border: 5px purple solid;
}
.column-with-overflow {
overflow: auto;
max-height: 100%;
flex-grow: 1;
}
.column-with-overflow-content {
height: 1000px;
background-color: blue;
display: flex;
}
.columns {
display: flex;
flex-direction: row;
background-color: yellow;
width: 100%;
max-height: 100%;
}
.content {
background-color: blue;
flex-grow: 1;
}
.box {
text-align: center;
color: white;
display: flex;
justify-content: center;
}
.header {
background-color: green;
height: 60px; // assume that It's not known - any height
}
.footer {
background-color: red;
height: 60px; // assume that It's not known - any height
}
<div class="container">
<div class="box header">Header</div>
<div class="box content">
<div class="columns">
<div class='column column-with-overflow'>
<div class='column-with-overflow-content'>
box 1
</div>
</div>
<div class='column'>box 2</div>
</div>
</div>
<div class="box footer">Footer</div>
</div>
Set a fixed height to your .box.content div element.
And use min-height in .column-with-overflow-content insted of height
html,
body {
height: 100%;
width: 100%;
}
.container {
height: 100%;
display: flex;
flex-direction: column;
}
.column {
background-color: white;
display: flex;
flex-direction: column;
flex-basis: 100%;
flex: 1;
color: black;
border: 5px purple solid;
}
.column-with-overflow {
overflow: auto;
max-height: 100%;
flex-grow: 1;
}
.column-with-overflow-content {
min-height: 1000px; /* use min-height */
background-color: blue;
display: flex;
}
.columns {
display: flex;
flex-direction: row;
background-color: yellow;
width: 100%;
max-height: 100%;
}
.content {
background-color: blue;
flex-grow: 1;
}
.box.content { height: 100px; } /* set height to columns container */
.box {
text-align: center;
color: white;
display: flex;
justify-content: center;
}
.header {
background-color: green;
height: 60px; // assume that It's not known - any height
}
.footer {
background-color: red;
height: 60px; // assume that It's not known - any height
}
<div class="container">
<div class="box header">Header</div>
<div class="box content">
<div class="columns">
<div class='column column-with-overflow'>
<div class='column-with-overflow-content'>
box 1
</div>
</div>
<div class='column'>box 2</div>
</div>
</div>
<div class="box footer">Footer</div>
</div>

Resources