refract v0.1 · beta
Core concepts / Length units

Length units

A length's unit is a property of the value, not a global switch. You author magnitudes; a single build-time units config resolves each length to a concrete { value, unit } — once, format-neutrally — so every adapter (CSS, SCSS, JSON, DTCG) emits the same unit.

A length can be written three ways. A bare number is deferred — its unit is resolved from units. A <number><unit> string is pinned — trusted verbatim, never converted. Anything else (calc(), clamp(), var(), a keyword) is a raw-string escape and passes through untouched.

theme.ts
createTheme(raw, {
  adapter: createCssAdapter(),
  units: {
    default: "px",        // global fallback
    typography: "rem",     // fontSize → rem
    layout: "rem",        // layout lengths — spacing / gutters / sizes
    borders: "px",         // hairlines stay crisp
  },
  baseFontSize: 16,        // divisor when a deferred length → rem
});
// fontSize 24 → "1.5rem"   ·   borders.width 1 → "1px"   ·   letterSpacing "-0.02em" → verbatim

Keys are token-path prefixes; the most-specific wins, over a built-in seed. So units.typography = "rem" converts fontSize, but the seed keeps lineHeight unit-less and letterSpacing in em unless you override them at the property grain.

The same units and baseFontSize are accepted at run-time on createTheme (above) and at build-time on defineConfig — so refract build resolves lengths identically and emits the chosen unit straight to disk.

Resolution order (a length leaf's unit)Example key
① value-level unit (pinned)"1.5rem"
② user property grainunits["typography.letterSpacing"]
③ built-in seed (property grain)lineHeight → none · letterSpacing → em
④ user subsystem grainunits["typography"]
⑤ user globalunits.default
⑥ built-in fallbackpx

Length leaves covered: typography fontSize / letterSpacing / lineHeight, layout spacing / gutters / sizes, borders width / offset / radius, effects blur and shadow geometry. Non-lengths (opacity, z-index, aspect-ratio, transition/animation ms) are never touched.

rem math is scoped. The ÷ baseFontSize conversion runs only when a deferred magnitude resolves to rem; a pinned "1.5rem" is never divided, and a pinned "24px" stays px even in a rem theme. This is not the media / container-query threshold unit — those live in the media config, which additionally supports the query-evaluation em.
Technical documentation for @theme-registry/refract · all output is compiled client-side by the real library (CSS adapter). The chrome is theme-aware; the render panes carry each preset's own world. · MIT licensed.