# RFC 0001 — Migrate `@bytelyst/design-tokens` to DTCG v3 | Status | Draft | | --------- | ---------------- | | Author | Cascade | | Created | 2026-05-27 | | Tracks | ROADMAP TODO #4 | | Reviewers | _to be assigned_ | ## Summary Migrate `packages/design-tokens/tokens/bytelyst.tokens.json` from the current bespoke schema to the **W3C Design Tokens Community Group (DTCG) Format Module v3**. Re-emit all generated outputs (CSS / TS / Kotlin / Swift / per-product CSS) from the new schema with **no observable change to runtime CSS variable names**. ## Motivation 1. **Designer tooling.** Figma-Tokens (now "Tokens Studio") and Specify both speak DTCG natively. Today designers cannot round-trip tokens with engineering — the JSON schema is bespoke. 2. **Long-term portability.** DTCG is becoming the lingua franca of design systems (Adobe Spectrum, Salesforce Lightning, GitHub Primer are all migrating). 3. **Three-tier semantics.** DTCG `$type` + reference syntax (`{path}`) gives us a clean way to express the **reference → semantic → component** layering documented in `learning_ai_uxui_web/docs/ROADMAP_2026.md` §3. ## Current schema (excerpt) ```json { "meta": { "name": "ByteLyst Design Tokens", "version": "1.1.0" }, "color": { "palette": { "neutral": { "0": "#FFFFFF", "50": "#F6F8FC", ... } }, "semantic": { "dark": { "bgCanvas": "#06070A", ... }, "light": { "bgCanvas": "#F8F9FC", ... } } }, "spacing": { "0": 0, "1": 4, "2": 8, ... }, "radius": { "xs": 4, "sm": 6, ... }, "typography": { "fontSize": { "xs": 12, ... } }, ... } ``` ## Proposed DTCG v3 schema (excerpt) ```json { "$description": "ByteLyst design tokens — DTCG v3", "$schema": "https://design-tokens.github.io/community-group/format/", "color": { "neutral": { "0": { "$value": "#FFFFFF", "$type": "color" }, "50": { "$value": "#F6F8FC", "$type": "color" }, "950": { "$value": "#06070A", "$type": "color" } }, "brand": { "blue": { "$value": "#5A8CFF", "$type": "color" }, "coral": { "$value": "#FF6E6E", "$type": "color" } }, "semantic": { "bgCanvas": { "$value": "{color.neutral.950}", "$type": "color", "$description": "Page background — dark" } } }, "spacing": { "1": { "$value": "4px", "$type": "dimension" }, "2": { "$value": "8px", "$type": "dimension" } }, "radius": { "xs": { "$value": "4px", "$type": "dimension" } }, "component": { "button": { "padding-x": { "$value": "{spacing.4}", "$type": "dimension" }, "bg": { "$value": "{color.semantic.accent}", "$type": "color" } } } } ``` ### Key changes | Aspect | Today | DTCG v3 | | -------------- | ------------------------ | ------------------------------------- | | Value wrapper | bare value | `{ "$value": ..., "$type": ... }` | | Type metadata | inferred from JS path | explicit `$type` | | References | manual JS lookup | `{path.to.token}` syntax | | Light/dark | parallel sibling objects | DTCG **token sets** (per-theme files) | | Documentation | none | `$description` per token | | Component tier | doesn't exist | new top-level `component` group | ## Implementation plan The migration is split into **4 PRs** to keep each one reviewable. ### PR 1 — Add converter + dual-emit (no behaviour change) - Add `scripts/convert-legacy-to-dtcg.ts` that reads the bespoke JSON and emits `bytelyst.tokens.dtcg.json`. - Modify `scripts/generate.ts` to accept either schema and produce byte-identical output. - Add a vitest case asserting the two emitters produce identical CSS byte-for-byte. ### PR 2 — Flip source of truth to DTCG JSON - Delete the legacy JSON. - Update Figma-Tokens plugin documentation + commit a `.tokens.config.json` for round-trip. ### PR 3 — Introduce the component tier - New `component.*` group in the DTCG JSON. - Generator emits new `--bl-{component}-{slot}` variables. - Audit existing hardcoded values in `@bytelyst/ui` and replace with the new component tokens. ### PR 4 — Multi-theme via DTCG token sets - Split the single JSON into: - `tokens/global.tokens.json` (reference + semantic core) - `tokens/themes/dark.tokens.json` (light/dark overrides) - `tokens/themes/light.tokens.json` - `tokens/brands/lysnrai.tokens.json` (brand-layer overrides — Wave 7) - Generator composes per-output set. ## Backward compatibility - All existing `--ml-*` and `--bl-*` CSS variable names are preserved. - All TS/Kotlin/Swift exported identifiers preserved. - Consumer apps require zero changes through PRs 1–3. - PR 4 introduces multi-set composition but the **default** still produces the same `tokens.css`. ## Risks | Risk | Severity | Mitigation | | ------------------------------------------------ | -------- | ------------------------------------- | | Generator divergence between schemas during PR 1 | High | Byte-identical assertion test | | Figma-Tokens plugin version churn | Med | Pin plugin version in docs/THEMING.md | | Designer learning curve | Med | One-pager + paired migration session | | Reference cycles in DTCG (`{a}` → `{b}` → `{a}`) | Low | Generator validates DAG before emit | ## Alternatives considered - **Style Dictionary** — Amazon's tool. Heavier, more opinionated. Rejected: we already have a working generator; switching tools is a larger change than switching schemas. - **Stay on bespoke JSON** — Rejected: blocks designer round-trip and locks us out of the ecosystem. ## Estimate ~1 person-week for PRs 1–3, +1 pw for PR 4 = **2 pw total**. ## Open questions - Do we adopt the DTCG **2024 candidate** or wait for the **2026 final**? Candidate is stable enough; recommend candidate now, re-verify when final. - Where do per-product brand layers live — separate packages (`@bytelyst/brand-lysnrai`) or subpaths (`@bytelyst/design-tokens/brands/lysnrai`)? Recommend subpaths pre-Wave 7, separate packages from Wave 7.