Type is the system
One number per element — --type — sets its size, and from that same number the engine derives its leading, tracking, and weight. Spacing is a fraction of the line, so everything scales together. There are no pixel values to keep in sync.
One step, four outputs
--cfg-ratio for the whole scale.The live scale
measured from the DOM · try S/M/L aboveEach row is a real element at that --type. The readout is its computed size / line-height / weight — so it reflects the shipped engine and the current data-ui-size, not a formula copy.
h1h2h3h4h5bodysmallmicroSpacing is a fraction of the line
Layout gaps are calc(0.25 · 1lh) and control padding is in em — both relative to the type around them. Bump a region's --type (or the header's S/M/L) and its rhythm grows with it. No spacing scale to maintain.
:where(.row, .column) { gap: calc(0.25 * 1lh); }
:where(button) { padding: 0 0.8em; } /* em → scales with --type */
Set a step, or zoom a region
--type is per-element (non-inheriting) — set it on one thing. --scale inherits, so it zooms a whole region (handy for a mini preview). data-ui-size reconfigures the base + ratio for the whole app.
<h2 style="--type:4">Heading</h2>
<small style="--type:-2">Micro caption</small>
<div style="--scale:0.6">…mini version…</div>
--type — see Control heights for the live proof, and the Demos for the scale dressed up as three different products.