Css grid, column size defined by row height

hey, i know this is topical because of the Css grid event later today. However i have run into a small wall with a project of mine Layout -# i will use pseudocode as example of my layout
<media-section>
<image-row>
<card>
<img>
</card>
</image-row>
</media-section>
<media-section>
<image-row>
<card>
<img>
</card>
</image-row>
</media-section>
/* Base layout */
.image-row {
display: grid;
gap: 16px; /* tweak as needed */
}

/* Ensure images fill their boxes nicely */
.image {
width: 100%;
overflow: hidden;
}
.image > img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}

/* ---------- 2 cards: 50% each, 16:9 ---------- */
/* Exactly 2 cards = has 2nd child but no 3rd */
.image-row:has(> .image-card:nth-child(2)):not(:has(> .image-card:nth-child(3))) {
grid-template-columns: repeat(2, 1fr);
}
.image-row:has(> .image-card:nth-child(2)):not(:has(> .image-card:nth-child(3)))
.image {
aspect-ratio: 16 / 9;
}

/* ---------- 3 cards: alternate featured 16:9, others 1:1 ---------- */
/* Exactly 3 cards = has 3rd child but no 4th */
.image-row:has(> .image-card:nth-child(3)):not(:has(> .image-card:nth-child(4))) {
grid-template-columns: repeat(3, 1fr);
}

/* Default all 1:1 on 3-card rows */
.image-row:has(> .image-card:nth-child(3)):not(:has(> .image-card:nth-child(4)))
.image {
aspect-ratio: 1 / 1;
}

/* Odd 3-card rows: first card is 16:9 */
.media-container
> .image-row:nth-child(odd):has(> .image-card:nth-child(3)):not(:has(> .image-card:nth-child(4)))
> .image-card:first-child
.image {
aspect-ratio: 16 / 9;
}

/* Even 3-card rows: last card is 16:9 */
.media-container
> .image-row:nth-child(even):has(> .image-card:nth-child(3)):not(:has(> .image-card:nth-child(4)))
> .image-card:last-child
.image {
aspect-ratio: 16 / 9;
}

/* ---------- 4 cards: all 1:1 ---------- */
/* Exactly 4 cards = has 4th child */
.image-row:has(> .image-card:nth-child(4)) {
grid-template-columns: repeat(4, 1fr);
}
.image-row:has(> .image-card:nth-child(4))
.image {
aspect-ratio: 1 / 1;
}
/* Base layout */
.image-row {
display: grid;
gap: 16px; /* tweak as needed */
}

/* Ensure images fill their boxes nicely */
.image {
width: 100%;
overflow: hidden;
}
.image > img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}

/* ---------- 2 cards: 50% each, 16:9 ---------- */
/* Exactly 2 cards = has 2nd child but no 3rd */
.image-row:has(> .image-card:nth-child(2)):not(:has(> .image-card:nth-child(3))) {
grid-template-columns: repeat(2, 1fr);
}
.image-row:has(> .image-card:nth-child(2)):not(:has(> .image-card:nth-child(3)))
.image {
aspect-ratio: 16 / 9;
}

/* ---------- 3 cards: alternate featured 16:9, others 1:1 ---------- */
/* Exactly 3 cards = has 3rd child but no 4th */
.image-row:has(> .image-card:nth-child(3)):not(:has(> .image-card:nth-child(4))) {
grid-template-columns: repeat(3, 1fr);
}

/* Default all 1:1 on 3-card rows */
.image-row:has(> .image-card:nth-child(3)):not(:has(> .image-card:nth-child(4)))
.image {
aspect-ratio: 1 / 1;
}

/* Odd 3-card rows: first card is 16:9 */
.media-container
> .image-row:nth-child(odd):has(> .image-card:nth-child(3)):not(:has(> .image-card:nth-child(4)))
> .image-card:first-child
.image {
aspect-ratio: 16 / 9;
}

/* Even 3-card rows: last card is 16:9 */
.media-container
> .image-row:nth-child(even):has(> .image-card:nth-child(3)):not(:has(> .image-card:nth-child(4)))
> .image-card:last-child
.image {
aspect-ratio: 16 / 9;
}

/* ---------- 4 cards: all 1:1 ---------- */
/* Exactly 4 cards = has 4th child */
.image-row:has(> .image-card:nth-child(4)) {
grid-template-columns: repeat(4, 1fr);
}
.image-row:has(> .image-card:nth-child(4))
.image {
aspect-ratio: 1 / 1;
}
Though the issue currently is the repeat(n, 1fr), i want the 1/1 images to be the height of the 16/9, is there a smarter way to do this?
4 Replies
Chris Bolson
Chris Bolson3mo ago
Whilst not related to your question, I am pretty sure that you can simplify those :has selectors to detect where the "n" element is the last-child, something like this:
/* only has 2 children*/
.image-row:has(> .image-card:nth-child(2):last-child) {

}
/* only has 3 children */
.image-row:has(> .image-card:nth-child(3):last-child) {

}
/* only has 2 children*/
.image-row:has(> .image-card:nth-child(2):last-child) {

}
/* only has 3 children */
.image-row:has(> .image-card:nth-child(3):last-child) {

}
As to your question, I wonder if you might be better off having 4 columns (in all cases) and in the 3 items rows have the first (or last) element spanning 2 cells 🤔 ? This wouldn't give you an exactly 16:9 ratio but might create the result that you are after.
Odinn 🐸
Odinn 🐸OP3mo ago
The last-child suggestion is a good find! thanks, As to the 4 col setup, i think that might work but will be different than what i'd expect. the goal is to be a bit more "dynamic" in the design. I have now set up a static setup, where the evens are 16fr 9fr 9fr and the odds 9fr 9fr 16fr for the columns, this is not scalable i think but it works with what i have rn. Again thanks for the selector optimisation!
Martin Bavio
Martin Bavio3mo ago
@Odinn 🐸 you might wanna explore flex for the layout you are looking for, since you have specific aspect-ratios on mind for each card member, which typically doesn't align quite well with the concept of defined grid columns
Martin Bavio
Martin Bavio3mo ago
here are two articles that might help you.
Exploiting flex-grow for an Image Gallery
How to build a full-bleed Image Gallery with flexbox.
9elements
Building a combined CSS-aspect-ratio-grid - 9elements
Combining finest craftsmanship with elegant design to ship innovative digital experiences.

Did you find this page helpful?