Skip to content
Transtyle v0.1.0-alpha.5
ok: build 10:17 UTC31 pages main · 7ed21c5

Alpha — breaking changes ship without a deprecation cycle. What is built vs. planned

Radix Colors / Themes exporter

See it live

Acme · Cathode · GOV.UK · Carbon: one page, four design systems, compiled to Radix Themes. All 44 demos →

Radix’s 12-step-per-color model is, cell for cell, the role grid (numbered instead of named). That makes this exporter the grid’s own acceptance test. Only 2 of 12 steps need a fresh mix (steps 2 and 6). Everything else is a direct grid cell.

On Acme that’s 510 declarations (12 steps plus a 12-step alpha ramp per role, in both modes) over 205 classified rows.

"targets": { "radix": { "output": "dist/radix" } }

Mapping#

Radix step Meaning Comes from
1 App background elevation.0.surface
2 Subtle background mix toward the page, 96%, no direct cell
3 / 4 / 5 UI background · hover · active tint / tint-hover / tint-active
6 Subtle border mix toward the card surface, 78%, no direct cell; it fills the gap between tint and outline
7 / 8 Border · hover border outline / outline-hover
9 / 10 Solid · hover solid solid / solid-hover
11 / 12 Low-contrast text · high-contrast text text / text-strong
contrast Text on the solid fill on-solid
a1–a12 Alpha variants the same 12 colors, alpha from a fixed ramp, not Radix’s real per-color alpha algorithm

neutral also ships as --gray-*, Radix’s conventional paired-gray name.

A gap found by building this, and fixed#

Any step that falls outside the sRGB gamut gets flagged approximated too: browsers gamut-map oklch() on render, which can look different from what was intended. Building this exporter surfaced a case: text-strong (step 12) re-anchors a role at the content text lightness while keeping the role’s full chroma. For a vivid brand color in dark mode, that combination could leave the gamut (very light and very saturated has little headroom). It’s fixed at the derivation source (clampChromaToGamut, role grid F20) rather than only flagged downstream. It now reduces chroma at the same lightness/hue until the color is representable, instead of leaving it to be clipped per-channel on render.

Using it with @radix-ui/themes#

Radix’s <Theme accentColor="..."> only accepts its own preset names, so to drive real Radix components from your brand, override an existing preset rather than invent a new one:

:root {
  --violet-1: var(--primary-1); /* ...through 12, plus -a1..a12 and -contrast */
}
<Theme accentColor="violet" grayColor="gray">

See it running on real @radix-ui/themes components: npm run dev -w acme-demo-radix (or cathode-demo-radix) in the examples.

Descriptions and deprecations#

Radix is a CSS target, so it takes /* … */ comments like shadcn. A variable that’s exactly one grid cell (steps 3 to 5 and 7 to 12 of each role, and --<role>-contrast) carries that cell’s description and deprecated above its declaration in the :root block (entryNotes() and blockComment() from @transtyle/ir). The .dark and other blocks don’t repeat them. Step 1 is the page surface every role shares, steps 2 and 6 are mixes, and the alpha steps are derived, so they get none.

Contrast, motion and brand#

A design system that declares contrast or brand gets extra blocks after .dark, with every scale step (alpha steps included) that changes: [data-contrast="more"], [data-brand="globex"], and compound blocks like .dark[data-brand="globex"] where the separate ones would be wrong on a dark page. contrast: more also follows prefers-contrast: more until data-contrast is set. motion is reported dropped, because Radix Colors are color scales only.