ADR-0101 A voice per theme family
Amends: ADR-0099, ADR-0100
#Context
ADR-0099 sets condensed display caps for section headings in the field themes only. ADR-0100 left that undone, because it needs styling that changes with the theme family, and ADR-0098 says the two voices are values, not machinery. A component stylesheet may not name a theme, so the switch has to arrive through the token tiers like every other value.
Two facts shape the mechanism. First, a size aliased on the element that carries data-wr-theme resolves in that element's density, which is wrong inside a Scope that declares its own. Storybook's Region set story is that case: a comfortable region on a compact page. Second, the budget's fixtures load no family stylesheet. Whatever a voice sets must fall back to what those fixtures already measure.
#Decision
A voice is a family-tier value set. family.config.json declares voices. Each voice names, per theme, the role whose face it takes, its case, its weight, and which of its two declared sizes it uses. family.css emits five properties per theme: --wr-voice-<name>-family, -case, -weight, -lift and -floor.
The size is a switch, built on the component. lift picks the base or the lifted step, and floor is the least the size may resolve to. The component tier builds the size on the component's own element from both steps, so it resolves in the density that applies there.
The display floor is enforced. A voice speaking in the display role takes displayFloorRem (1.5rem, ADR-0099's 24px) as its floor, and the verifier fails any theme and density where it would resolve below it. Field night at compact density is the case the floor holds up: its lead step is 1.25rem, and the heading renders at 24px.
One voice ships: heading.
| Theme | Face | Case | Weight | Size |
|---|---|---|---|---|
| interior-light, interior-dark | body | none | 400 | text-body |
| field-day, field-night | display | uppercase | 800 | text-lead, floor 1.5rem |
Components read a voice through their own tier. A voice bucket in component.config.json maps a part to a voice and emits --wr-<component>-<part>-family, -case, -weight and -size. A part sized by a voice may not also be sized under text. Every property except the face falls back to the voice's fallback theme (interior-light). The face follows ADR-0100 and has no fallback. Region's name is the first consumer.
#Consequences
- No scene value moves. The fixtures regenerate with the new stylesheet and
render the interior voice through the fallbacks. On darwin-arm64, all 214 measured scope values and every verdict are identical to HEAD measured on the same machine. Field scenes therefore do not measure the field voice, the same gap ADR-0100 recorded for faces.
- Four more parts speak in the voice, in a follow-up unit: the data table
caption, the accordion label, and both shell panel names. The data table's empty line read the caption's size, and now has its own empty-size at body size, because that line is a sentence, not a heading. On darwin-arm64 the base set (214 values) and the themed set (513 values) measure identically before and after.
#Rejected
- Theme selectors in component stylesheets. A nested theme would match
both an outer and an inner selector, and source order, not nearness, would decide which applies. Inherited custom properties respect nearness.
- Aliasing the size in
family.css. It resolves in the wrong density
inside a nested Scope.
- Loading the family stylesheet in fixtures. It inlines font URLs no
fixture resolves, which ADR-0100 already refused.