makeover-geometry
The invariant half of the make-family design system. makeover
resolves colour, which varies by theme; this crate carries everything that does
not.
Spacing is a relationship, not a size
The Mac OS 8 Human Interface Guidelines specify white space by what two things are being separated, and define no grid unit. A control and its satellite pop-up sit 4 pixels apart; peers stacked in a list get 6; a group box's inner margin is 10; separated groups and rows of push buttons get 12.
So the vocabulary here names the relationship and lets the size follow, the same
way surface-raised names an intent and lets the hex follow:
| Relationship | What it separates |
|---|---|
gap-bound |
a control and the thing it belongs to |
gap-peer |
items of the same kind in a list |
gap-group |
a container's inner margin, sibling groups |
gap-section |
separated groups, rows of actions |
gap-pane |
panel padding, content shells |
gap-page |
the outermost shell margin |
Whether a gap should be 6px or 8px is unanswerable on its own. Whether two things are peers is not. That is the whole argument for the layer.
A raw step-* scale sits underneath for distances no relationship describes,
such as an optical nudge inside a badge. Reaching for it is a smell worth a
second look.
Ratios, not pixel counts
The deliberate departure from the HIG, which is written in hard device pixels because in 1997 there was one pixel density and one text size.
Every step is a ratio of one base unit, --geometry-base, which defaults to
1rem. At that base the ratios land exactly on the HIG's numbers, so nothing
is lost in translation. What is gained: the layout tracks the user's text size
instead of fighting it, an accessibility setting is one value rather than a
sweep, and the scale means the same thing at any display density.
--) / 8); /* 6px at the default base */
--);
Density presets
Because no call site names a size, a preset can change what every relationship resolves to without touching one of them. Mobile and desktop builds should differ mostly by which preset they emit.
The presets are not scaled copies of each other, which is also why a single
base scalar could not express this. Touch wants more room between things
you tap and less around the edges, since a fingertip is coarse and outer
margin is screen you do not get to use. gap-peer and gap-section open up;
gap-pane and gap-page tighten. gap-bound never moves — it is the one
relationship that says "these are one object".
use ;
let mut css = geometry_css_vars;
css.push_str;
The override block emits only the relational layer. The base and the scale are declared once.
Surfaces, and why a terminal is not a third preset
A terminal is not a density. It is a surface whose smallest representable step
is one cell rather than one pixel, and that single difference is the whole of
it. Ratio::quanta takes a quantum and answers in whole units of it, so the
relational vocabulary reaches a character grid with nothing added.
Quantising is not a terminal special case either. A display quantises to the pixel; it is only that rounding 6.0 to the nearest pixel is uninteresting, while rounding three eighths of a cell to the nearest cell decides the layout.
use ;
let t = terminal;
assert_eq!; // cells
assert_eq!;
assert_eq!;
bound and peer collapsing to zero cells is correct rather than lossy: in a
grid that dense, both relationships are expressed by adjacency. A coarse surface
genuinely has fewer distinctions available, and the model should say so instead
of inventing a gap to keep six names apart. What it must never do is invert
two relationships, and there is a test across several quanta pinning that.
So three orthogonal axes: Gap is what is being separated, Density is who is
operating it, Surface is what it is drawn on.
Consumers
Web surfaces bake the CSS in at build time; unlike colour, none of this changes
at runtime, so there is nothing for JS to apply on load. egui and ratatui
surfaces resolve through Surface instead, which is the reason this is a crate
rather than a stylesheet.
Licence
MIT.