Design Tokens
Runtime semantic token overrides for non-dashboard pages.
Overview
Yayaw supports runtime design token overrides without rebuilding the app.
Scope for this feature:
- targets pages outside
/dashboard/*and/auth/* - supports global overrides and organization-scoped overrides for public organization hosts
- supports an optional immutable per-page layer for one global public page
- supports semantic colors (including chart colors), public layout tokens
(
page-section-padding-y,page-section-margin-y,radius,spacing,tracking-normal), typography tokens (font-sans,font-serif,font-mono), and shadow tokens (shadow-*)
CMS Role
Design tokens are the public presentation layer for CMS-rendered pages and previews. They let operators adjust semantic color, radius, spacing, typography, tracking, and shadow values without editing page documents, section definitions, or component source.
Use tokens for theme-level visual decisions. Use page documents and sections for content structure, and keep dashboard/auth styling on their dedicated app-shell scope.
Data Model
The payload is versioned and stored as semantic token maps:
interface DesignTokensV1 {
version: 1;
light: Partial<Record<DesignToken, string>>;
dark: Partial<Record<DesignToken, string>>;
}Storage:
- global scope:
system_settingsusing keydesign-tokens.non-dashboard.v1 - organization scope:
organization_settings.designTokens - global page scope:
ui_page_revisions.definition.data.root.props.designTokens
Runtime Resolution
Resolution pipeline:
- load the global token payload
- load the active organization token payload when the request resolves to an organization public host or organization-scoped preview
- merge organization values over global values for each light/dark token
- when a global page revision declares a page layer, apply its semantic color and approved sans/serif overrides inside that page only
- generate scoped CSS variables:
.scope-nondashboard:not(.dark) { ... }.scope-nondashboard.dark { ... }
The scope class is applied only on pages outside dashboard/auth routes. Verified custom public hosts apply the owning organization's effective token payload after the global baseline, so one organization can theme its public pages without changing the canonical Yayaw site or another organization's host.
The page layer uses a revision-derived scope class and the same wrapper for
published pages and immutable draft previews. Its selectors also cover
body-level portals while that page is mounted. A page with no layer receives no
extra class or style element and inherits the effective global theme unchanged.
The first release intentionally limits contextual page layers to
global_public; organization pages continue to use their organization token
layer so draft and custom-domain rendering share one inherited baseline.
The dashboard Puck authoring canvas preserves this field but does not inject
the page layer in version 1. The immutable preview is the visual authority for
this layer and must be used for design evidence and publication review.
The public page renderer applies page-section-padding-y and
page-section-margin-y to reusable sections and generated body sections. Header
and footer chrome stay outside that rhythm frame. The page editor uses the same
frame, so changing either token updates authoring previews and public output
without adding spacing nodes or route-specific styles to page documents.
The repository baseline for page-section-padding-y is 1.5rem; operators can
override it globally or for an organization public host.
Dashboard routes intentionally keep their app-shell styling independent from
public-site token overrides. If a future dashboard theming feature is needed,
add a separate scope and storage key instead of reusing
design-tokens.non-dashboard.v1.
Token Families
Token families include:
- semantic colors such as background, foreground, primary, secondary, accent, muted, destructive, border, input, ring, card, popover, and chart colors
- public layout tokens such as page section padding, page section margin, radius, and spacing
- typography tokens such as sans, serif, and mono font stacks
- tracking tokens such as
tracking-normal - shadow tokens such as
shadow-sm,shadow-md, and related depth values
Only declared Yayaw tokens are accepted. Unknown CSS variables from imported themes are ignored.
Revision-owned page layers are narrower than theme-level payloads. They accept semantic color tokens and these loaded font families only:
- system, Geist, or Alegreya Sans for
font-sans - system, Georgia, or Alegreya for
font-serif
font-mono, custom stacks, remote font URLs, layout tokens, and shadow tokens
are not accepted in a page layer. Alegreya and Alegreya Sans are registered as
root CSS variables through next/font; they do not affect another page unless
that page explicitly selects the corresponding semantic font role.
Editing Flow
Admin UI route:
/dashboard/content/design-tokens
Behavior:
- immediate save (no draft/publish split)
- token fields are initialized from repository baseline values in
theme-vars.css - color tokens use a picker with HEX input
- the brush icon on each color input copies the current HEX value with inline success feedback
- layout tokens use a numeric input with selectable unit
- typography tokens use a preset select with optional custom font stack input and are shared across light/dark
- shadow tokens use direct text inputs
- font tokens currently store CSS font-family stacks (for example
Inter, sans-serif); font file upload and font catalog selection are not part of this UI yet - dark values can be left unset to inherit light first, then baseline dark (or baseline light when no dark baseline exists)
- color values are canonicalized and stored in OKLCH format
- import by URL is available directly in the editor (current provider support: TweakCN URLs like
https://tweakcn.com/editor/theme?theme=neo-brutalism) - import by shadcn CLI command is available directly in the editor (for example
bunx --bun shadcn@latest add @ss-themes/ghibli-studioorpnpm dlx shadcn@latest add https://shadcnthemer.com/r/themes/<id>.json; package-manager agnostic) - import by pasted CSS is available directly in the editor (
:root { ... }and optional.dark { ... }) - theme import uses a single "Import Theme" dropdown that opens a method-specific modal (URL, command, or CSS) with contextual guidance
- importing fills the current scope editor state; you still explicitly save to publish
Contextual MCP page plans can set page.designTokens to a non-empty version-1
payload for a global page. Create plans omit or use null to inherit. Rework
plans omit the field to preserve the target revision's layer, use null to
clear it explicitly, or provide a payload to replace it. This operation does
not mutate the global or organization token records. Publication accepts a
revision-owned layer only when strict contextual persistence issued its design
proof and visual review approved that exact runtime; low-level page writes
cannot publish an unreviewed page theme.
Theme Import Parsing
The parser is provider-based and currently includes one implementation:
tweakcn: accepts editor/theme URLs, resolves the slug, then fetcheshttps://tweakcn.com/r/themes/{slug}.json- if the registry JSON endpoint fails for a valid theme page URL (for example
https://tweakcn.com/themes/{id}), Yayaw falls back to parsing the server-rendered theme payload from the theme page
- if the registry JSON endpoint fails for a valid theme page URL (for example
shadcn-registry: accepts a shadcn CLIaddcommand (or direct@scope/itemreference), resolves the registry alias, then fetches the registry JSON item endpointcss: parses pasted CSS with:rootand optional.darkvariable blocks
Normalization rules:
- only known Yayaw tokens are imported
- color tokens are converted to canonical OKLCH
- text/layout/typography/shadow tokens are validated with the same token value constraints as manual editing
- unsupported or invalid token values are ignored
Runtime Consumers
Effective tokens are consumed by:
- public generated pages
- docs/public layout wrappers outside dashboard/auth
- component catalog previews when validating token-aware recipes
- generated section runtime previews
The component catalog applies effective runtime tokens to preview surfaces so authors can validate imported or local components against the current public theme.
Permissions
Editing requires:
global-variables:manage
Organization-scoped token saves also require the caller to operate inside the active organization context that owns the override.
Read access still allows opening the page in read-only mode.
Validation
Useful checks after design token changes:
bun test src/lib/server/services/design-tokens/design-tokens.test.ts
bun run check
bunx tsc --noEmit