Skip to main content

Design tokens

Every colour, size, weight, radius, shadow, and duration that ties the system together. Tokens are CSS variables — override any of them in @theme and every component picks up the change.

Naming convention
Every token is prefixed with its category — --color-*, --font-*, --radius-*, --shadow-*, --duration-*. Semantic tokens (background, foreground, muted) reference the primitives below.

Brand palette

Aidash blue. The 500 step is the default; 600 for hover, 400 for dark mode.

TokenValueUsage
--color-brand-50oklch(0.97 0.01 250)Backdrop tint
--color-brand-100oklch(0.93 0.04 250)Ghost button hover
--color-brand-200oklch(0.87 0.08 250)
--color-brand-300oklch(0.78 0.13 250)Disabled primary
--color-brand-400oklch(0.68 0.17 252)Dark-mode primary
--color-brand-500oklch(0.62 0.20 255)Default primary
--color-brand-600oklch(0.54 0.20 255)Primary hover
--color-brand-700oklch(0.46 0.18 255)Primary active
--color-brand-800oklch(0.38 0.15 255)
--color-brand-900oklch(0.30 0.12 255)Deep backgrounds

Semantic colours

Names components reference at runtime. Each has a light-mode and a dark-mode value.

TokenValueUsage
--backgroundlight: oklch(1 0 0) · dark: oklch(0.145 0 0)Page background
--foregroundlight: oklch(0.145 0 0) · dark: oklch(0.985 0 0)Primary text
--cardlight: oklch(1 0 0) · dark: oklch(0.18 0 0)Card surface
--card-foregroundlight: oklch(0.145 0 0) · dark: oklch(0.985 0 0)Text on cards
--mutedlight: oklch(0.97 0 0) · dark: oklch(0.22 0 0)Subtle surfaces / hover
--muted-foregroundlight: oklch(0.556 0 0) · dark: oklch(0.68 0 0)Secondary text
--borderlight: oklch(0.92 0 0) · dark: oklch(0.26 0 0)Hairline borders
--inputlight: oklch(0.925 0 0) · dark: oklch(0.28 0 0)Form input borders
--ringvar(--color-brand-500)Focus ring
--primaryvar(--color-brand-500)Primary CTA background
--primary-foregroundoklch(0.985 0 0)Text on primary
--destructivevar(--color-danger-500)Destructive actions

Typography

Font families.

TokenValueUsage
--font-bodyInter, ui-sans-serif, system-uiDefault UI font
--font-displayInter, ui-sans-serif, system-uiHeadings
--font-monoJetBrains Mono, ui-monospaceCode, kbd, terminals

Radius

Corner rounding steps. Fully round shapes like chips and avatars use rounded-full and ignore these tokens.

TokenValueUsage
--radius-sm4pxUnused by components — free for your own UI
--radius-md6pxKbd, menu items, table cells, small controls
--radius-lg8pxButtons, inputs, tooltips, alerts — the default step
--radius-xl12pxCards, modals, popovers, panels

Shadows

Elevation levels.

TokenValueUsage
--shadow-card0 1px 2px rgba(0,0,0,0.04)Card resting
--shadow-card-hover0 2px 6px rgba(0,0,0,0.06)Card hover
--shadow-glow0 0 12px oklch(0.62 0.14 255 / 0.12)Brand halo

Motion

Easing and duration tokens.

TokenValueUsage
--ease-standardcubic-bezier(0.2, 0, 0, 1)Standard transitions
--ease-springcubic-bezier(0.25, 1.2, 0.4, 1)Spring/bounce effects
--duration-fast120msMicro-interactions
--duration-base180msDefault transitions
--duration-slow280msComplex transitions

Complete source

The full token block. Drop it into app/globals.css and you're done.

app/globals.css
css
@import "tailwindcss";

@custom-variant dark (&:where(.dark, .dark *));

@theme {
  --color-brand-50:  oklch(0.97 0.01 250);
  --color-brand-100: oklch(0.93 0.04 250);
  --color-brand-200: oklch(0.87 0.08 250);
  --color-brand-300: oklch(0.78 0.13 250);
  --color-brand-400: oklch(0.68 0.17 252);
  --color-brand-500: oklch(0.62 0.20 255);
  --color-brand-600: oklch(0.54 0.20 255);
  --color-brand-700: oklch(0.46 0.18 255);
  --color-brand-800: oklch(0.38 0.15 255);
  --color-brand-900: oklch(0.30 0.12 255);

  --color-success: oklch(0.70 0.17 150);
  --color-warn:    oklch(0.78 0.16 80);
  --color-danger:  oklch(0.65 0.22 25);

  --font-body:    "Inter", ui-sans-serif, system-ui, sans-serif;
  --font-display: "Inter", ui-sans-serif, system-ui, sans-serif;
  --font-mono:    "JetBrains Mono", ui-monospace, monospace;

  --radius-sm: 4px;
  --radius-md: 6px;
  --radius-lg: 8px;
  --radius-xl: 12px;

  --shadow-card:       0 1px 2px rgba(0, 0, 0, 0.04);
  --shadow-card-hover: 0 2px 6px rgba(0, 0, 0, 0.06);
  --shadow-glow:       0 0 12px oklch(0.62 0.14 255 / 0.12);

  --ease-standard: cubic-bezier(0.2, 0, 0, 1);
  --ease-spring:   cubic-bezier(0.25, 1.2, 0.4, 1);
  --duration-fast: 120ms;
  --duration-base: 180ms;
  --duration-slow: 280ms;
}

:root {
  --background:         oklch(1 0 0);
  --foreground:         oklch(0.145 0 0);
  --card:               oklch(1 0 0);
  --card-foreground:    oklch(0.145 0 0);
  --muted:              oklch(0.97 0 0);
  --muted-foreground:   oklch(0.556 0 0);
  --border:             oklch(0.92 0 0);
  --input:              oklch(0.925 0 0);
  --primary:            var(--color-brand-500);
  --primary-foreground: oklch(0.985 0 0);
  --destructive:        var(--color-danger);
  --ring:               var(--color-brand-500);
}

.dark {
  --background:         oklch(0.145 0 0);
  --foreground:         oklch(0.985 0 0);
  --card:               oklch(0.18 0 0);
  --card-foreground:    oklch(0.985 0 0);
  --muted:              oklch(0.22 0 0);
  --muted-foreground:   oklch(0.68 0 0);
  --border:             oklch(0.26 0 0);
  --input:              oklch(0.28 0 0);
  --primary:            var(--color-brand-400);
  --primary-foreground: oklch(0.145 0 0);
  --destructive:        var(--color-danger);
  --ring:               var(--color-brand-400);
}

Also see

Deeper dives into individual token families.