Lyse UI GitHub

Design Tokens

Lyse UI uses a 3-layer token architecture built with CSS custom properties. Tokens handle colors, typography, spacing, and layout across light and dark modes.

Architecture

Tokens flow through 3 layers. Each layer adds meaning and abstraction:

text
Layer 1: Primitives (root-*.css)
  --root-color-brand-500, --root-space-4, --root-font-size-base
         ↓
Layer 2: Semantics (semantic-*.css)
  --background-brand-strong-default, --text-base-strong
         ↓
Layer 3: Bridge (shadcn-bridge.css)
  --primary, --foreground, --ring

Components consume Layer 2 tokens in their .css files and Layer 3 tokens via Tailwind utilities.

Layer 1: Primitives

Raw values with no semantic meaning. These are the foundation that everything builds on.

Colorsroot-colors.css — 5 scales (brand, danger, neutral, success, warning) with 50–950 steps each.
Typographyroot-typography.css — Font sizes, weights, and line heights.
Layoutroot-layout.css — Spacing, radius, borders, opacity, and sizing.
root-colors.css
/* Examples */
--root-color-brand-500: #6366f1;
--root-space-4: 1rem;
--root-font-size-base: 0.875rem;
--root-radius-md: 8px;

Layer 2: Semantics

Named by purpose, not value. Semantic tokens reference primitives and remap between light and dark mode.

Colorssemantic-colors.css — Background, text, border, and icon tokens with light/dark values.
Globalsemantic-global.css — Mode-independent aliases for spacing, sizing, and layout.
Typographytypography.css — Composite utility classes like .text-content-body and .text-heading-small.
semantic-colors.css
/* Light mode (:root) */
--background-brand-strong-default: var(--root-color-brand-500);
--text-base-strong: var(--root-color-neutral-900);

/* Dark mode (.dark) */
--background-brand-strong-default: var(--root-color-brand-400);
--text-base-strong: var(--root-color-neutral-50);

Layer 3: Bridge

Maps Lyse semantic tokens to shadcn variable names. This is what makes Lyse components compatible with the shadcn ecosystem and Tailwind utilities.

shadcn-bridge.css
/* shadcn-bridge.css */
--primary: var(--background-brand-strong-default);
--foreground: var(--text-base-strong);
--ring: var(--border-brand-default);
--destructive: var(--background-danger-strong-default);

These bridge variables are registered in globals.css via @theme inline, making them available as Tailwind utilities like bg-primary and text-foreground.

Usage

Components use Layer 2 tokens in their CSS files for theming, and Layer 3 tokens via Tailwind utilities for layout:

button.css
/* button.css — Layer 2 tokens for theming */
.button-primary {
  background: var(--background-brand-strong-default);
  color: var(--text-inverse);
}
.button-primary:hover {
  background: var(--background-brand-strong-hover);
}
button.tsx
{/* button.tsx — Layer 3 via Tailwind */}
<button className="rounded-md bg-primary text-primary-foreground">
  Click me
</button>

For spacing and layout, use the layout tokens: var(--layout-padding-*), var(--layout-gap-*), var(--layout-radius-*).

Dark mode

Semantic tokens automatically remap when the dark class is present on any ancestor element. No component changes needed — the token layer handles everything.

semantic-colors.css
/* :root = light mode (default) */
:root {
  --text-base-strong: var(--root-color-neutral-900);
}

/* .dark = dark mode */
.dark {
  --text-base-strong: var(--root-color-neutral-50);
}