A track written 0.236fr is not 0.236 wide. That is the kind of detail golden-grid rests on, the layout component I have just published in eva-css-fluid 2.6.0.

A 12-column grid is a system of division. This one is a system of proportion, and the difference shows up in what you write.

Dividing or proportioning

In a grid of 12 equal columns, you take 4, 6 or 8 columns depending on what has to fit. The proportions are a result, never a decision. And since every column is worth the same, there are only two ways left to build a hierarchy: centre, or count.

golden-grid has four tracks of different, non-interchangeable widths, each with a function. The rail (φ⁻³, 0.236) carries metadata and aligns right. The shoulder (φ⁻⁴, 0.146) carries nothing: it is what throws the page off axis. The text track (φ⁻², 0.382) is the reading measure. The spill (φ⁻³, 0.236) is where images run off to the right. An element never declares a width: it declares the role it plays, and the track gives it its measure.

The geometry is one construction: the page is worth 1, you apply the golden cut, then you cut each of the two parts again by the same cut. The four tracks are not four chosen numbers, they are the four leaves of a two-level tree. The sum is exactly 1 through the golden ratio recurrence φⁿ = φⁿ⁺¹ + φⁿ⁺², and it still is after rounding to three decimals: 0.236 + 0.146 + 0.382 + 0.236 = 1.000. CSS does not require it, fr normalises. Summing to 1 is a reading discipline: every number is directly the share of page it takes, and a mistake shows up in the addition.

The six tracks of golden-grid and their fractions A horizontal band cut into six segments of unequal widths. At both ends, the margins, each 0.056 of the page. Between them, four tracks: the rail at 0.236, the shoulder at 0.146 marked more strongly because it stays empty, the text track at 0.382 which is the widest segment, and the spill at 0.236. A rule above measures the whole page, a rule below measures only the four central tracks, called the composition. THE PAGE = 1 0.056 0.236 0.146 0.382 0.236 0.056 margin rail shoulder text spill margin THE COMPOSITION = 1
Two units: the margins are shares of the page, the tracks are shares of what they leave.

Three consequences, and they are why the component exists. The page has no axis, it has a progression: three left edges instead of one middle, and the eye goes down following edges. Empty space is a value, not a remainder: the shoulder is a declared track, drawn and measurable, and no class targets it, so no content can take it back. The reading measure, finally, is not set but deduced. Not a single max-width: 65ch in the stylesheet: it is 0.382 of the composition.

The margin is a share of the page

The margins are golden too, but on another unit, and it is the point that took me longest to settle.

--gg-gutter: max(5.6%, calc((100% - 1440px) / 2));   /* 5.6% = φ⁻⁶ */

One declaration, two regimes. Below the maximum width, the percentage wins: the margin is φ⁻⁶ of the page and grows with the window, instead of being a scale step placed by hand. Above it, centring wins: the margin absorbs half the surplus and the composition settles. The switch happens at 1622 px.

Two units, and that is the key to the system: the margin is a share of the page, the tracks are shares of the composition. The “1” of the golden ratio is not the window, it is what the margins and the gaps leave behind. At 1440 px, two margins of 80.6 and five gaps of 24.1 leave 1158.2 of composition: rail 273, shoulder 169, text 442, spill 273.

What fixed the exponent at φ⁻⁶ is not a matter of taste, it is a ratio. The margin is then 0.47 of the shoulder: wide enough to read as a drawn margin, narrow enough for the shoulder to stay the dominant void. At φ⁻⁵ the ratio climbs to 0.82, the two voids compete, and the page loses what throws it off axis.

Five gaps, and the series stops there

The temptation was to extend it: margin at φ⁻⁵, gap at φ⁻⁶. It does not survive the arithmetic. There are only two margins, but five gaps. At the next term of the series they would take 403 px out of 1440 and the composition would fall to 778 px. The four tracks would have nothing left to exist in.

The gap is not a division of the page, it is the air between the tracks: it belongs to the scale, not to the series. I would rather have a formal inconsistency I own than a consistency that empties the composition.

It has no consequence anyway, and that can be checked. column-gap is taken out before the fr are distributed: the four tracks share what is left, their ratios do not move. Pushing the gap from 4 px to 103 px in the browser, the composition goes from 1413 to 920 px and the fractions stay 0.2360 / 0.1460 / 0.3820 / 0.2360. Not one decimal. That is why the gap is the only setting exposed at runtime: it tightens the geometry, it cannot distort it.

The composition shrinks, the four shares do not move Two horizontal bands, one above the other, each cut into four segments. The top band, measured with a 4 pixel gap, has a composition of 1413 pixels. The bottom band, measured with a 103 pixel gap, is down to 920 and is visibly much shorter. Under each segment of both bands, the same fraction is written: 0.2360 for the rail, 0.1460 for the shoulder, 0.3820 for the text track, 0.2360 for the spill. GAP 4 PX COMPOSITION 1413 PX 0.2360 0.1460 0.3820 0.2360 GAP 103 PX COMPOSITION 920 PX 0.2360 0.1460 0.3820 0.2360
493 px less composition, and the four shares identical to the fourth decimal.

subgrid does the real work

It is the piece without which none of this would hold. grid-template-columns: subgrid inherits the tracks and their names: a block writes grid-column: rail without knowing anything about the page containing it. Without it, you would have to recompute the tracks as percentages in every container, which means reintroducing exactly the drift you are trying to remove. So it is also the component’s real compatibility floor: Chrome 117, Safari 16, Firefox 71, everywhere since September 2023.

Two silent failures

The first one fits in two characters. A track written 0.236fr is really written minmax(auto, 0.236fr), and that automatic minimum is the min-content contribution of the content: an unbreakable URL in the rail widens the track and breaks the golden proportion. No error, no warning, nothing in the console, just a page that is no longer golden. So the component writes minmax(0, …) on its four tracks.

The second one was in the reference implementation, and only showed up at the first real layout.

.blocks > * { grid-column: main; }   // the flow default
.rail       { grid-column: rail; }   // the placement

These two selectors have the same specificity, 0,1,0. In that order, all is well. In the other one, the default overrides every placement class, and rail, wide blocks and spills fall back silently into the text column. The CSS is still valid, the page is still valid, the grid is not.

Those two convinced me of one thing: a layout rule is only worth something if it can be checked. The component’s rule fits in a sentence, every container starts and ends on a grid line, and it is verified at runtime. You read grid-template-columns off the render, deduce the line positions, measure every placed container, and the report names the deviations instead of counting them. The only accepted deviation is the page title and its margin-left: -0.055em, an optical shift so that the drawing of the letter touches the line rather than its box.

Same reasoning for grid-auto-flow: row dense, which pulls the text up onto the row of a title placed on the rail. It can visually reorder content, and that costs accessibility. The constraint that makes it acceptable: any block that leaves the text column must come before, in the DOM, whatever it refers to.

What it costs

Making the margins golden costs reading measure. It goes from about 60 to about 53 characters at 1440 px. That is still inside the comfortable range, between 45 and 75, but it is a trade-off, not a free lunch. $golden-grid-gutter-phi: 7 brings it back to 56 characters with a 49 px margin, and false restores the 60 characters by putting the margin back on the EVA scale. One line, either way.

The other limit is one of scope. This is a page grid: cards, tables and forms belong to something else and the component has nothing to teach them. It does not provide an intermediate state either. Below 54 rem the four tracks become one, all the named lines fold onto its two edges, and no placement rule is rewritten. Adding a block never forces you to write its mobile counterpart. That is the real maintenance gain, more than the geometry.

The component is opt-in and emits no rule at all until you turn it on.

@use 'eva-css-fluid' with ($golden-grid: true);

Without Sass, dist/golden-grid.css weighs 2.1 kB and sits after eva.css, whose variables it consumes.