Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Responsive two to three column layout in css

I'm trying to achieve a particular flex/grid responsive behavior that grows from two to three columns, expanding only two containers. I illustrated the desired results below. I was able to manage the two-column stage. However, I don't know how to expand this to three columns using css flex-box.

I chose flex-box as this got me closest to where I need to get. However, the solution must by no means be limited to flex-box.

enter image description here

Situation:

  • 5 containers in total.
  • 3 have a fixed but individual height, 2 should grow in their hight dimension.
  • The containers are organized in 2 columns for small screens and 3 columns for large screens as shown on the sketch.
  • Between the 2 and 3 column stages and beyond the three column stage, the width of all 5 containers should equal 50% (33% respectively) of the parent element's width.

let wf = 0.5;
let wm = 500;

function simulateWidthUpdate() {
  const g = document.getElementById('grid')
  wf = wf == 1 ? 0.5 : 1;
  g.style.width = parseInt(wf * wm) + 'px';
}
button {
 margin: 10px 0;
}

#grid {
  border: 1px solid red;
  width: 250px;
  height: 400px;
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  gap: 10px;
}

.item {
  border-radius: 10px;
  background: #233C3C;
  color: #fff;
  padding: 10px;
  text-align: center;
  flex-grow: 0;
  max-width: 100px;
  width: 100%;
}

#item-c,
#item-d {
  flex-grow: 1;
  min-height: 200px;
}

#item-a {
   height: 20px;
}


#item-b {
   height: 40px;
}

#item-e {
   height: 50px;
}
<button onclick="simulateWidthUpdate()">Alternate Width</button>
<div id="grid">
  <div class="item" id="item-a">A</div>
  <div class="item" id="item-c">B</div>
  <div class="item" id="item-e">C</div>
  <div class="item" id="item-b">D</div>
  <div class="item" id="item-d">E</div>
</div>
like image 433
lenny Avatar asked Oct 17 '25 20:10

lenny


1 Answers

You could consider explicitly organizing the columns as separate elements. Then use JavaScript to organize the elements as per your wireframes when simulateWidthUpdate() runs.

let wf = 0.5;
let wm = 500;

const a = document.getElementById('item-a');
const b = document.getElementById('item-b');
const c = document.getElementById('item-c');
const d = document.getElementById('item-d');

function simulateWidthUpdate() {
  const g = document.getElementById('grid')
  wf = wf == 1 ? 0.5 : 1;
  g.style.width = parseInt(wf * wm) + 'px';
  
  if (wf === 1) {
    a.insertAdjacentElement('afterend', b);
    
    const newColumn = document.createElement('div');
    newColumn.className = 'column';
    newColumn.appendChild(c);
    document.querySelector('.column').insertAdjacentElement('afterend', newColumn);
  } else {
    d.insertAdjacentElement('beforebegin', b);
    a.insertAdjacentElement('afterend', c);
    document.querySelectorAll('.column')[1].remove();
  }
}
button {
 margin: 10px 0;
}

#grid {
  border: 1px solid red;
  width: 250px;
  height: 400px;
  display: flex;
  gap: 10px;
}

.column {
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex-grow: 1;
}  

.item {
  border-radius: 10px;
  background: #233C3C;
  color: #fff;
  padding: 10px;
  text-align: center;
  flex-grow: 0;
  max-width: 100px;
  width: 100%;
}

#item-c,
#item-d {
  flex-grow: 1;
  min-height: 200px;
}

#item-a {
   height: 20px;
}


#item-b {
   height: 40px;
}

#item-e {
   height: 50px;
}
<button onclick="simulateWidthUpdate()">Alternate Width</button>
<div id="grid">
  <div class="column">
    <div class="item" id="item-a">A</div>
    <div class="item" id="item-c">C</div>
    <div class="item" id="item-e">E</div>
  </div>
  <div class="column">
    <div class="item" id="item-b">B</div>
    <div class="item" id="item-d">D</div>
  </div>
</div>

Pure CSS

You could use container queries and CSS grid:

let wf = 0.5;
let wm = 500;

function simulateWidthUpdate() {
  const g = document.getElementById('grid')
  wf = wf == 1 ? 0.5 : 1;
  g.style.width = parseInt(wf * wm) + 'px';
}
button {
 margin: 10px 0;
}

#grid {
  border: 1px solid red;
  width: 250px;
  height: 400px;
  container-type: inline-size;
}

.inner {
  display: grid;
  grid-template:
    "a b" 40px
    "c b" 10px
    "c d" minmax(200px, 1fr)
    "e d" 70px;
  gap: 10px;
  width: 100%;
  height: 100%;
}

@container (width > 400px) {
  .inner {
    grid-template:
      "a c d" max-content
      "b c d" max-content
      "e c d" max-content
      "x c d" 1fr;
  }
}

.item {
  border-radius: 10px;
  background: #233C3C;
  color: #fff;
  padding: 10px;
  text-align: center;
  flex-grow: 0;
  max-width: 100px;
  width: 100%;
}

#item-c,
#item-d {
  min-height: 200px;
}

#item-a {
   height: 20px;
   grid-area: a;
}


#item-b {
   height: 40px;
   grid-area: b;
}

#item-c {
   grid-area: c;
}

#item-d {
   grid-area: d;
}

#item-e {
   height: 50px;
   grid-area: e;
}
<button onclick="simulateWidthUpdate()">Alternate Width</button>
<div id="grid">
  <div class="inner">
    <div class="item" id="item-a">A</div>
    <div class="item" id="item-b">B</div>
    <div class="item" id="item-c">C</div>
    <div class="item" id="item-d">D</div>
    <div class="item" id="item-e">E</div>
  </div>
</div>

You could use a style attribute selector instead of container queries:

let wf = 0.5;
let wm = 500;

function simulateWidthUpdate() {
  const g = document.getElementById('grid')
  wf = wf == 1 ? 0.5 : 1;
  g.style.width = parseInt(wf * wm) + 'px';
}
button {
 margin: 10px 0;
}

#grid {
  border: 1px solid red;
  width: 250px;
  height: 400px;
  display: grid;
  grid-template:
    "a b" 40px
    "c b" 10px
    "c d" minmax(200px, 1fr)
    "e d" 70px;
  gap: 10px;
}

#grid[style*="500px"] {
  grid-template:
    "a c d" max-content
    "b c d" max-content
    "e c d" max-content
    "x c d" 1fr;
}

.item {
  border-radius: 10px;
  background: #233C3C;
  color: #fff;
  padding: 10px;
  text-align: center;
  flex-grow: 0;
  max-width: 100px;
  width: 100%;
}

#item-c,
#item-d {
  min-height: 200px;
}

#item-a {
   height: 20px;
   grid-area: a;
}


#item-b {
   height: 40px;
   grid-area: b;
}

#item-c {
   grid-area: c;
}

#item-d {
   grid-area: d;
}

#item-e {
   height: 50px;
   grid-area: e;
}
<button onclick="simulateWidthUpdate()">Alternate Width</button>
<div id="grid">
  <div class="item" id="item-a">A</div>
  <div class="item" id="item-b">B</div>
  <div class="item" id="item-c">C</div>
  <div class="item" id="item-d">D</div>
  <div class="item" id="item-e">E</div>
</div>

Or a "brute-force" approach similar to the first JavaScript solution, where we duplicate the DOM for both instances and switch between them using a style attribute selector or container query:

let wf = 0.5;
let wm = 500;

function simulateWidthUpdate() {
  const g = document.getElementById('grid')
  wf = wf == 1 ? 0.5 : 1;
  g.style.width = parseInt(wf * wm) + 'px';
}
button {
 margin: 10px 0;
}

#grid {
  border: 1px solid red;
  width: 250px;
  height: 400px;
  display: flex;
  gap: 10px;
  container-type: inline-size;
}

.column {
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex-grow: 1;
}  

.item {
  border-radius: 10px;
  background: #233C3C;
  color: #fff;
  padding: 10px;
  text-align: center;
  flex-grow: 0;
  max-width: 100px;
  width: 100%;
}

#item-c,
#item-d {
  flex-grow: 1;
  min-height: 200px;
}

#item-a {
   height: 20px;
}


#item-b {
   height: 40px;
}

#item-e {
   height: 50px;
}

@container (width >= 500px) {
  .wide-hide {
    display: none;
  }
}

@container (width < 500px) {
  .wide-show {
    display: none;
  }
}
<button onclick="simulateWidthUpdate()">Alternate Width</button>
<div id="grid">
  <div class="column">
    <div class="item" id="item-a">A</div>
    <div class="item wide-show" id="item-b">B</div>
    <div class="item wide-hide" id="item-c">C</div>
    <div class="item" id="item-e">E</div>
  </div>
  <div class="column wide-show">
    <div class="item" id="item-c">C</div>
  </div>
  <div class="column">
    <div class="item wide-hide" id="item-b">B</div>
    <div class="item" id="item-d">D</div>
  </div>
</div>
like image 198
Wongjn Avatar answered Oct 19 '25 10:10

Wongjn



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!