Issue
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.
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>
Solution
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>
Answered By - Wongjn
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.