Expand a 2-Column Grid to 3 Columns
Here's how to expand a CSS grids into 1, 2, then 3 columns.
The Markup
<div class="cols-container">
<div class="cols">
<div class="col1">
<div>widget one</div>
</div>
<div class="col2">
<div>widget two</div>
<div>widget three</div>
</div>
</div>
</div>
Kinda interesting, eh. What's up with col2
?
1-Column State
In 1 column, all widgets are stacked:
+--------------------------------+
| |
| +----------------------------+ |
| | | |
| | 1 | |
| | | |
| +----------------------------+ |
| |
| +----------------------------+ |
| | | |
| | 2 | |
| | | |
| +----------------------------+ |
| |
| +----------------------------+ |
| | | |
| | 3 | |
| | | |
| +----------------------------+ |
| |
| |
| |
+--------------------------------+
We'll start with a grid
, but we'll have just one implied column:
.cols {
display: grid;
gap: 1.25rem;
}
2-Column State
In the 2 column state, we have multiple widgets in the second column:
+--------------------------------+ +------------------------+
| | | |
| +----------------------------+ | | +--------------------+ |
| | | | | | | |
| | | | | | 2 | |
| | 1 | | | | | |
| | | | | +--------------------+ |
| | | | | |
| | | | | +--------------------+ |
| | | | | | 3 | |
| +----------------------------+ | | | | |
| | | +--------------------+ |
| | | |
| | | |
| | | |
+--------------------------------+ +------------------------+
In order to get 2 columns, we'll detect sufficient width with a @container
query:
.cols-container {
container-type: inline-size;
container-name: cols;
}
@container cols (min-width: 38rem) {
.cols {
grid-template-columns: 60fr 40fr;
}
}
3-Column State
In a sufficiently-wide screen, we want to put one of these widgets each in their own column.
+-------------------------+ +-------------------------+ +-------------------------+
| | | | | |
| +---------------------+ | | +---------------------+ | | +---------------------+ |
| | | | | | | | | | | |
| | | | | | | | | | | |
| | | | | | | | | | | |
| | 1 | | | | 2 | | | | 3 | |
| | | | | | | | | | | |
| | | | | | | | | | | |
| | | | | | | | | | | |
| | | | | | | | | | | |
| +---------------------+ | | +---------------------+ | | | | |
| | | | | | | |
| | | | | +---------------------+ |
| | | | | |
| | | | | |
+-------------------------+ +-------------------------+ +-------------------------+
The strategy is the same on the conversion from 2 to 3 columns: use a @container
query. In addition, make .col2
have columns also:
@container cols (min-width: 75rem) {
.cols {
grid-template-columns: 1fr 2fr;
}
.col2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.25rem;
}
}
This works. This is the current final solution.
Failed Attempt: grid-area
Here are a couple reasons why I ended up with the solution I did.
What I really wanted was to have infinite widgets as siblings and be able control their column placement with CSS only.
<div class="cols-container">
<div class="cols">
<div>widget one</div>
<div class="widget2">widget two</div>
<div class="widget3">widget three</div>
</div>
</div>
Maybe I could set each widget using a grid-area
:
.cols {
grid-template-areas: "one two";
}
.widget2,
.widget3 {
grid-area: two;
}
The problem is that when you set multiple children as the same grid-area
, they overlay each other. They don't flow within the column. Agh.
+--------------------------------+ +------------------------+
| | | |
| +----------------------------+ | | +--------------------+ |
| | | | | | 3 | |
| | | | | | 2 | |
| | 1 | | | +--------------------+ |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| +----------------------------+ | | |
| | | |
| | | |
| | | |
| | | |
+--------------------------------+ +------------------------+
Failed Attempt: grid-column
When grid-area didn't work, I tried something similar and simpler. I assigned the widgets to the grid lines, like I had with area names:
.widget2,
.widget3 {
grid-column: 2;
}
The problem with that is that the next widget in the column will be place on the next row. It doesn't flow vertically within the column like a masonry layout. It's a grid, with dilineated rows as well as columns. Agh.
+--------------------------------+ +------------------------+
| | | |
| +----------------------------+ | | +--------------------+ |
| | | | | | | |
| | | | | | 2 | |
| | 1 | | | | | |
| | | | | +--------------------+ |
| | | | | |
| | | | | |
| | | | | |
| +----------------------------+ | | |
| | | |
- -+- (cell line)- - - - - - - - - -+ -+- - - - - - - - - - - - +- -
| | | |
| | | +--------------------+ |
+--------------------------------+ | | 3 | |
| | | |
| +--------------------+ |
| |
| |
| |
+------------------------+
All the CSS
So that's why I ended up with column2 as a separate child grid in order to get column 3 and the flow I wanted.
What other ways have you solved this?
The CSS in whole:
.cols-container {
container-type: inline-size;
container-name: cols;
}
.cols {
display: grid;
gap: 1.25rem;
}
@container cols (min-width: 38rem) {
.cols {
grid-template-columns: 60fr 40fr;
}
}
@container cols (min-width: 75rem) {
.cols {
grid-template-columns: 1fr 2fr;
}
.col2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.25rem;
}
}