I have a wrapper that can contain 2, 3 or 4 elements (I don't know it in advance because every element renders itself, depending on API response).
If there are 3 (or less) I want them to stack like:
No big deal. But when there are 4 of them, I need this other layout:
So far, I thought CSS Grid would be the way to go and I tried:
/* Just to add some interaction to the demo */
const w = document.getElementById("wrapper");
let x;
function toggle(event) {
const d = document.getElementById("D");
return d
?
x = d.cloneNode() && w.removeChild(d)
: w.appendChild(x);
}
#wrapper {
width: 100%;
max-width: 500px;
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 10px;
}
.item {
grid-column-start: span 2
}
.item:nth-last-child(1),
.item:nth-last-child(2) {
grid-column-start: auto;
}
/* Non-relevant CSS here: */
button {
margin: 20px auto;
font-size: 16px;
padding: 3px 6px;
border-radius: 6px;
}
#A { background: #7984f7 }
#B { background: #cb8af8 }
#C { background: #8cd4fb }
#D { background: #97f8d8 }
.item {
border-radius: 6px;
padding: 10px 0;
font-family: sans-serif;
font-weight: bold;
color: white;
text-align: center;
}
<div id="wrapper">
<div id="A" class="item">A</div>
<div id="B" class="item">B</div>
<div id="C" class="item">C</div>
<div id="D" class="item">D</div>
</div>
<button onclick="toggle()">Click me!</button>
But it doesn't work for 3 elements... In fact, I've tried many things (all CSS Grid related) and there's probably an easier solution I can't see right now... Any help will be much appreciated!
I would suggest using flexbox. For the given three possibilities use this simple css in the snippet below.
Basically you make the items appear in a row but with wrapping if they don't fit. You make the first two elements take all width so that there is only one per row. And for the 3rd and 4th you set their flex-basis (initial width) to something smaller and make them grow (flex-grow: 1) if there is space.
.examples{
display: flex;
flex-direction: row;
align-items: flex-start;
}
.container{
width: 200px;
border: 1px solid red;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: flex-start;
}
.box{
height: 50px;
background-color: blue;
margin: 5px;
border-radius: 5px;
flex-basis: 200px;
}
.box:nth-child(3), .box:nth-child(4){
flex-basis: 50px;
flex-grow: 1;
}
<div class="examples">
<div class="container">
<div class="box"></div>
<div class="box"></div>
</div>
<div class="container">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
<div class="container">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
</div>
Solution also here: JSFiddle
Using CSS grid:
One side effect is that if there are ever more than 4 items, the additional ones will display in two columns.
/* Just to add some interaction to the demo */
const w = document.getElementById("wrapper");
let x;
function toggle(event) {
const d = document.getElementById("D");
return d
?
x = d.cloneNode() && w.removeChild(d)
: w.appendChild(x);
}
#wrapper {
width: 100%;
max-width: 500px;
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 10px;
}
.item {
grid-column-start: auto;
}
.item:nth-child(1),
.item:nth-child(2),
.item:nth-child(3):last-child {
grid-column-start: span 2;
}
/* Non-relevant CSS here: */
button {
margin: 20px auto;
font-size: 16px;
padding: 3px 6px;
border-radius: 6px;
}
#A { background: #7984f7 }
#B { background: #cb8af8 }
#C { background: #8cd4fb }
#D { background: #97f8d8 }
.item {
border-radius: 6px;
padding: 10px 0;
font-family: sans-serif;
font-weight: bold;
color: white;
text-align: center;
}
<div id="wrapper">
<div id="A" class="item">A</div>
<div id="B" class="item">B</div>
<div id="C" class="item">C</div>
<div id="D" class="item">D</div>
</div>
<button onclick="toggle()">Click me!</button>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With