The numeric resolution core under theme_validate.ts and theme_check.ts:
it turns the CSS strings themes author back into numbers so the lint and
the gates can run. create_theme_resolver exposes it for UI lookups; the
rest of the module's exports are the internals the two modules share.
Its contract:
- Knob-tier defaults come from the numeric-twin constants in ramps.ts, not
from parsing default_variables; intent and neutral hues default to their
palette-letter binding (
hue_accent → hue_a, hue_neutral → hue_f,
and so on) so they follow an overridden letter. - Theme-authored values parse as a CSS
<number> literal, an exact
var(--x) reference (recursed through the same effective-value merge,
with a visited-set cycle guard), a scaled reference
calc(var(--x) * <number>), or the machine-emitted compiled-cap
min(calc(...), <number>) form. Anything else is unresolvable and is
recorded with its variable, value, and reason. A derived color is
clamped as the browser clamps the oklch() it renders. - Derived ramp stops (
palette_lightness_NN and its shade/text twins,
palette_chroma_NN, chroma_shape_NN) use a pinned numeric value when
the theme pins one, fall back to the ramps.ts formulas with the
resolved knobs otherwise, and mark the touching gates unchecked when a
pin is unresolvable. - The color variables the gates read (the palette, intent, shade, and text
stops,
text_max, and border_color_30) derive from those numbers
unless the theme authors one directly. An authored color is measured as
written when it is an oklch(L C H) literal with plain numeric
components (an optional / alpha composites where the gate measures a
color over its ground) or an exact var() reference to another color
the gates evaluate; any other form is recorded in unchecked and its
gates are skipped. The gates look every color up by variable name, so
this covers exactly the variables a gate reads and no others. - The gates measure what the default styles paint, so they follow the
roles those styles paint through:
text_color, text_disabled,
link_color, link_color_selected, border_color, and outline_color
(theme_gate_role_names). Each aliases a stop or another role by
default, read from the shipped declarations, and a theme that repoints
one is gated at the color it points to - same rule, same unchecked
fallback. One table of
pairing gates names each role beside its grounds and threshold, so the
gate definitions and the following can't drift. - Outside the gates: the micro-surface colors no pairing models
(
caret_color, selection_color, scrollbar_thumb_color,
backdrop_color), the input_fill behind field text, a
background_image painted over the shade_00 ground, and the stronger
button tints of the hover, focus, and pressed states.
The effective-value merge mirrors the renderer's cascade-layer semantics
(theme.ts render_theme_style): light = theme.light; dark =
theme.dark ?? theme.light, falling back to the numeric-twin default for
the scheme. A single-scheme
stance (Theme.scheme) resolves through the same scheme_stance_variables
mirror resolve_theme_stance computes, so the gates evaluate the stanced
reality in both schemes whether or not the theme arrives resolved.