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

column-span-8main

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.

Unit width shrinks fast

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

4
4
4
Why the default is a span, not 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

column-span-10 column-start-2columns 2–11
column-start-3 column-span-4columns 3–6

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.

Pitfall — exceeding the canvas is not a local mistake

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

3
6
3

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

main8 → 6 → 12

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.

The useful diagnostic: if your local CSS mostly makes one primitive behave like another, change the primitive rather than adding more CSS.