Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Layout depending on number of elements

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:

3 elements layout

No big deal. But when there are 4 of them, I need this other layout:

4 elements 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!

like image 951
Jordi Nebot Avatar asked Jan 02 '23 06:01

Jordi Nebot


2 Answers

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

like image 75
Jakub Rusilko Avatar answered Jan 05 '23 16:01

Jakub Rusilko


Using CSS grid:

  1. display all items in two columns by default
  2. display items 1 and 2 in a single column
  3. display item 3 in a single column only if it's the last item

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>
like image 26
Maciek Lewkowicz Avatar answered Jan 05 '23 18:01

Maciek Lewkowicz