Skip to main content

Crate makeover_geometry

Crate makeover_geometry 

Source
Expand description

The invariant half of the make-family design system.

[makeover] resolves colour, which varies by theme. This crate carries everything that does not: spacing, radius, border width and the type scale. The split is the same one Balanced Breakfast’s theme contract has always drawn — a theme overrides colour tokens only — moved out of two app stylesheets so the three consumers stop maintaining three copies of it.

§Spacing is relational, not numeric

The Mac OS 8 Human Interface Guidelines specify white space by what two things are being separated, never by a size name, and define no base grid unit. A control and its satellite pop-up are set 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.

That vocabulary is the primary interface here. Gap names the relationship and the size follows from it, exactly as surface-raised names an intent and the hex follows from it. The raw Step scale exists underneath for distances a relationship does not describe, but reaching for it is a smell worth a second look.

Naming the relationship is what makes the rule reviewable. Whether a gap should be 6px or 8px is unanswerable in isolation; whether two things are peers is not.

§Ratios, not pixel counts

This is the deliberate departure from the HIG, which is specified in hard device pixels because in 1997 there was one pixel density and one text size. Every Step here is a Ratio of a single base unit, so the whole system scales from one knob: --geometry-base, 1rem by default.

At the default base the ratios land exactly on the HIG’s numbers — Snug is three eighths of 16px, which is 6px — so nothing is lost in the translation. What is gained is that the layout tracks the user’s text size instead of fighting it, an accessibility setting becomes one value rather than a sweep, and the scale means the same thing at any display density.

§Density presets

Naming relationships instead of sizes is what makes a density preset possible at all. Density changes what each relationship resolves to without touching a single call site, because no call site names a size. The mobile and desktop builds of a Tauri app should differ mostly by which preset they emit, not by a parallel set of hand-written mode-scoped rules.

§What Touch is a claim about

Touch is a claim about the contact patch and nothing else. A fingertip is coarse where a cursor hotspot is a point, and the only consequence of that is mis-tap cost: when two adjacent things do different things, an imprecise contact needs more room between them to land on the intended one.

Shells are not tap targets. Panel padding and the outer page margin separate a region from the edge of the screen, and no amount of coarseness in the pointing device makes that separation riskier. So Touch opens the gaps that separate targets and leaves the shells exactly where Pointer put them:

GapPointerTouchwhy
bound44not a separation at all
peer610adjacent distinct targets, the whole point
group1012holds the peer/group distinction open
section1216a deliberate break stays legible as one
pane2424a shell is not a target
page3232a shell is not a target

The previous Touch preset was thrown out on 2026-07-29 because it also tightened Pane and Page, on the argument that outer margin is screen you do not get. That is a claim about screen budget, not about the input device, and smuggling it into this axis is what broke: opening Section to 16 while tightening Pane below it made any Pointer Pane at or under 16 an inversion, so a derived preset silently set a floor under the one quoted from the HIG. A phone is small and touch; a tablet and a touchscreen laptop are big and touch. Screen budget is a separate axis and does not belong here.

§The one cross-density rule

Touch never resolves tighter than Pointer, at any gap. Stated as a deliberate claim rather than inherited, and chosen for its direction: it constrains the derived preset by the quoted one, never the reverse. A Pointer retune downward moves freely and cannot be blocked by Touch, which is the exact failure this replaces. Only a Pointer move upward can push Touch, and that is the correct direction of authority.

§Size class: the axis Density kept being asked to carry

SizeClass answers how much screen there is, which is not the same question as what is pointing at it. A phone is small and touch; a tablet and a touchscreen laptop are big and touch; a half-width desktop window is small and pointer. Four real combinations, and one axis cannot name them.

This is the home for the claim the old Touch preset was thrown out for making: outer margin is screen you do not get. That claim was never wrong, it was on the wrong axis, and putting it on the input device is what let a derived preset set a floor under a quoted one.

Boundaries are quoted (Material 3 window size classes: 600 and 840) rather than derived, for the same reason the Gap values are. This crate carries the boundaries only; what appears or disappears at each is a product decision and belongs to makeover-touch.

Deliberately absent: size class does not feed Gap::step_at yet. Whether shells tighten on a compact window is a look call, and deriving it a second time is how the last one went wrong.

§Surfaces, and why a TUI is not a third density

Surface is the fourth axis and the one that carries this to alloy_tui. A terminal is not a density preset; it is a surface whose smallest representable step is one cell rather than one pixel. Give Ratio::quanta a quantum and it answers in whole units of it, so the relational vocabulary crosses to 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.

On Surface::terminal the pointer preset resolves to 0, 0, 1, 1, 2, 2 cells. Bound and Peer collapsing to nothing 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 distinct.

So the three axes are: Gap is what is being separated, Density is who is operating it, Surface is what it is drawn on.

Structs§

Ratio
A fraction of the base unit.
Surface
What the layout is being drawn on: a base unit, and the smallest step the surface can actually represent.

Enums§

Density
Which input the layout is being sized for.
Gap
A named separation between two things.
SizeClass
How much screen there is, independent of what is pointing at it.
Step
A raw step on the underlying scale.

Constants§

BASE_TOKEN
The CSS custom property every ratio scales from.
DEFAULT_BASE_PX
The default base unit in CSS pixels, at a 16px root font size.

Functions§

density_css
The whole spacing layer with the canonical density selection, as CSS.
gap_css_declarations
Emit the relational layer for one density as CSS declarations, no selector.
gap_css_overrides
Emit a density preset as a scoped override block.
geometry_css_vars
Emit the whole geometry layer as a :root { … } block at one density.
scale_css_declarations
Emit the base unit and the raw scale as CSS declarations, no selector.