WORK-187
ID:WORK-187Status:ready

Config-driven token stylesheet generation

Make refrakt.config.jsontheme.tokens the canonical authoring surface for token overrides. The build pipeline validates the config against the typed contract, generates a :root { --rf-* } stylesheet, and injects it after the theme's base CSS but before any user CSS. Power users can still drop a stylesheet for things JSON can't express (color-mix(), scoped overrides), but the common case is config sugar.

Priority:highComplexity:mediumMilestone:v0.14.0Source:SPEC-048
claude/spec-053-tint-authoring-notes View source

Criteria completion

Criteria completion: 0 of 7 (0%) checked; tracking started on May 18, no incremental history yet0%25%50%75%100%May 18Jun 15

Tracking started May 18 — check back for trends.

Branches 3
History 1
  1. 8df92a3
    Created (ready)by bjornolofandersson

Acceptance Criteria

  • theme.tokens field in refrakt.config.json validated against ThemeTokensConfig at build time
  • Validation errors surface clear messages (path + invalid value + valid options where applicable), not opaque schema errors
  • Build pipeline emits a generated :root { --rf-* } stylesheet matching the validated config
  • Generated stylesheet injected into the rendered page after the theme package's CSS and before any user CSS files
  • extra: Record<string, string> escape hatch passes through to the generated stylesheet as :root { --<key>: <value>; } declarations
  • A site with theme.tokens.color.text = "#ff0000" in config renders body text as red without any custom CSS
  • Unit tests cover: validation passes for valid configs, validation fails with clear messages for invalid configs, generated stylesheet matches expected output

Approach

The generation pipeline lives in @refrakt-md/transform or @refrakt-md/content (decide during implementation — likely transform since it's where the merge logic lives).

Validation: use a runtime schema validator (Zod is already in use elsewhere in the project — check first; otherwise valibot or hand-rolled given the shape's stability). Validate at config-load time, fail fast.

Stylesheet generation: walk the ThemeTokensConfig tree, emit one CSS custom property per leaf, generate the dot-to-dash mapping (color.surface.base--rf-color-surface-base).

Injection: the SvelteKit Vite plugin and any other adapter integrates the generated stylesheet after the theme package's CSS in the document head. Order matters — theme provides defaults, generated CSS overrides them.

Out of scope here: the actual token values for the neutral default (that's WORK-200), preset merge (that's WORK-190), mode overlays (that's WORK-188).

Dependencies

  • WORK-185ThemeTokensConfig shape must exist.

References

  • SPEC-048 — "Config is sugar over CSS, not a replacement" design principle
  • Existing refrakt.config.json validation (if any) — extend rather than parallel-implement
  • packages/sveltekit — Vite plugin where CSS injection order is currently controlled