Actual CSS
Column layout
Twelve units, and nothing else. .column-layout is the one
layout primitive that makes no responsive decision — you
place regions on the canvas, and you own what happens when it gets
narrow.
Every stage below is resizable — drag its bottom-right corner. Sections 1 through 3 never change shape, however far you drag: that is the contract, not a missing feature. Sections 4 and 5 do change, because this page's own stylesheet says so.
1. The canvas
Twelve minmax(0, 1fr) tracks. The ruler and the regions
below share the same canvas, so a span can be counted by eye.
container — unit —
Neither region names a start line. Grid auto-placement flows the aside
into columns 9 through 12 on its own — the same way
col-8 and col-4 compose in any twelve-column
system you have used before.
Watch the unit readout as you drag. Eleven gaps are a fixed cost, so
they take a growing share of a shrinking canvas. Around 280px a unit
is roughly 12px and a column-span-4 region is 84px.
Twelve columns is a wide-context tool; the readout is here to make
that concrete rather than theoretical.
2. Unplaced children span the canvas
A child with no placement class is a full-width region. A heading, an intro, or a footer shares the canvas with placed regions without carrying a class of its own — and a forgotten class stays readable instead of collapsing to one twelfth.
container — unit —
No class — full canvas
1 / -1
The default is grid-column-end: span 12. Writing it as
grid-column: 1 / -1 would look equivalent but sets a
definite start line on every child, removing them all from
column auto-placement. Section 1 would then stack — both
regions would resolve to column 1 — and every composition
would need an explicit column-start-*.
3. Starts
A start names the line a region begins on. This is why there are no
offset utilities: grid-column-start is what an offset was
always approximating, and it flips in a right-to-left context for free.
container — unit —
A start with no span runs to the end of the canvas, so a single class is always a valid placement. Combining the two is where you have to keep the arithmetic inside the canvas.
Keep S + N - 1 ≤ 12.
column-start-9 column-span-4 is valid;
column-start-10 column-span-4 is not.
Exceeding it adds a thirteenth, content-sized implicit column, and
the twelve 1fr tracks then divide whatever is left. So
every region in the canvas shifts, not just the one
you got wrong. Actual CSS does not guard against this: a coordinate
system that silently corrected your coordinates would be worse.
4. You own the narrow behavior
This stage collapses, and none of that behavior comes from Actual. It comes from four lines in this page's own stylesheet:
@container (width < 40rem) {
.stack-all > * {
grid-column: 1 / -1;
}
}
container — stacks below 40rem — because this page says so
The .container-query wrapper is here because
this page wrote a @container rule. Delete the rule
and the wrapper stops mattering. That is the difference from a primitive
whose responsive contract depends on a wrapper someone else has to
remember: here, the person who needs the wrapper is the person who wrote
the rule that needs it.
Placement classes carry zero specificity, so
.stack-all > * wins on its own. No
!important, and nothing to undo first.
5. Recomposition is the point
Stacking everything is the least interesting thing a coordinate system can do. This stage keeps the canvas through an intermediate state:
8 + 4 → 6 + 6 → stacked
container — 8+4, then 6+6 below 48rem, then stacked below 32rem
Two thresholds, chosen for this composition. A single framework threshold could not have produced the middle state, and a hidden framework default would have had to be undone before writing it. That asymmetry — additive versus corrective — is why the primitive ships no responsive behavior at all.
6. When this is the wrong primitive
.column-layout is easy to reach for and easy to overuse. It
is the right answer only when the placement is part of the design. If
another primitive already names the relationship, that primitive is
better, because it brings intrinsic responsive behavior this one does
not have.
-
Independent repeated items —
.grid. -
A known peer density —
.grid-N, not threecolumn-span-4regions. -
Peers that must switch together —
.switcher. -
Main content plus an aside —
.sidebar-layout, which already collapses on its own. -
Tracks whose sizes carry meaning —
--grid-columnson.grid, for exampleminmax(12rem, 18rem) 1fr. Twelve interchangeable units are not that.
The useful diagnostic: if your local CSS mostly makes one primitive behave like another, change the primitive rather than adding more CSS.