# Styling Token Contract (T02) Date: 2026-03-27 Scope: `recipe-manager/frontend` ## Canonical token source **Single source of truth:** - `src/styles/tokens.css` All design-token values (color, typography scales, spacing, radius, elevation/focus) must be authored in `tokens.css` first. ## Allowed token consumption paths 1. **Preferred in UI markup:** tokenized classes and `ui-*` primitives from `src/index.css` - Examples: `ui-card`, `ui-btn`, `text-[var(--text)]`, `border-[var(--border)]` 2. **Tailwind theme keys** mapped to CSS vars in `tailwind.config.js` - Examples: `bg-surface`, `text-primary`, `shadow-card` 3. **TS access layer:** `src/theme.ts` - Must expose `var(--...)` references only. - Must not hardcode competing token values. ## Explicit non-contract patterns - Do **not** introduce new hardcoded design-token values in `theme.ts`. - Do **not** define duplicate token constants in TS that can drift from `tokens.css`. - Avoid ad-hoc palette classes (`bg-blue-*`, `text-slate-*`, etc.) in shared shell/feature UI when tokenized equivalents exist. ## `theme.ts` role after T02 `theme.ts` is now a **typed token accessor/compatibility layer**, not an independent token definition file. - ✅ Allowed: `colors.primary = 'var(--color-primary)'` - ❌ Not allowed: `colors.primary = '#ea580c'` If a token does not exist yet: 1. Add it to `tokens.css` 2. (Optional) Map it in `tailwind.config.js` if utility-class access is needed 3. Expose accessor in `theme.ts` only as `var(--token-name)` ## Practical authoring guide (for future tasks) - Use `ui-*` classes first for common controls/layout shells. - Use tokenized Tailwind utilities or `var(--...)` references for one-off styling. - Keep inline `style={{ ... }}` for runtime/dynamic values only (e.g., tag color from DB), not for static design tokens. ## Current known exceptions (post-T02) - Some components/pages still import `radius`/`colors` from `theme.ts` for inline styling; values are now token references, so no hardcoded drift remains. - `recipeAccentPalette` still includes a few non-tokenized accent hexes pending a future semantic palette pass.