Skip to main content

Design Tokens

Semantic CSS variables, color contrast roles, border radius multipliers, and z-index scales.

View as Markdown

Use semantic CSS variables to customize colors, typography, spacing, radiuses, and shadows. A variable applies to components that use it within its CSS scope.

CSS Variables#

With UnoCSS, presetMoraine() supplies a neutral light and dark palette and accepts named override entries. Use presetMoraine({ wind3: true }) with Wind3. When defining the palette in CSS, set themeDefaults: false and supply the colors and semantic shadows yourself. Tailwind users also define these values in CSS.

The following example defines the default colors and scale values. Add the semantic shadows and font families described below when using an external theme. color-scheme makes native browser controls follow the light or dark theme:

src/theme.css
:root {
  color-scheme: light;
  --background: rgb(255, 255, 255);
  --foreground: rgb(10, 10, 10);
  --card: rgb(255, 255, 255);
  --card-foreground: rgb(10, 10, 10);
  --popover: rgb(255, 255, 255);
  --popover-foreground: rgb(10, 10, 10);
  --primary: rgb(23, 23, 23);
  --primary-foreground: rgb(250, 250, 250);
  --secondary: rgb(245, 245, 245);
  --secondary-foreground: rgb(23, 23, 23);
  --muted: rgb(245, 245, 245);
  --muted-foreground: rgb(115, 115, 115);
  --accent: rgb(245, 245, 245);
  --accent-foreground: rgb(23, 23, 23);
  --destructive: rgb(231, 0, 11);
  --border: rgb(229, 229, 229);
  --input: rgb(229, 229, 229);
  --control: rgb(255, 255, 255);
  --backdrop: rgb(0 0 0 / 0.1);
  --ring: rgb(161, 161, 161);
  --radius: 0.625rem;
  --spacing: 0.25rem;
  --font-size: 1rem;
}

.dark {
  color-scheme: dark;
  --background: rgb(10, 10, 10);
  --foreground: rgb(250, 250, 250);
  --card: rgb(23, 23, 23);
  --card-foreground: rgb(250, 250, 250);
  --popover: rgb(23, 23, 23);
  --popover-foreground: rgb(250, 250, 250);
  --primary: rgb(229, 229, 229);
  --primary-foreground: rgb(23, 23, 23);
  --secondary: rgb(38, 38, 38);
  --secondary-foreground: rgb(250, 250, 250);
  --muted: rgb(38, 38, 38);
  --muted-foreground: rgb(161, 161, 161);
  --accent: rgb(38, 38, 38);
  --accent-foreground: rgb(250, 250, 250);
  --destructive: rgb(255, 100, 103);
  --border: rgb(35, 35, 35);
  --input: rgb(47, 47, 47);
  --control: rgb(23, 23, 23);
  --backdrop: rgb(0 0 0 / 0.1);
  --ring: rgb(115, 115, 115);
}

Moraine-specific Variables#

A shadcn/ui palette supplies the shared color roles and --radius. Also configure these variables when adapting an external theme for Moraine:

Variable Purpose Default or fallback
--control Surface for outline fields, unchecked choices, and upload controls; independent of --input boundaries. UnoCSS: white in light mode, rgb(23, 23, 23) in dark mode. No utility fallback; external themes must define it.
--backdrop Scrim behind modal overlays, used by bg-backdrop. Black at 10% opacity.
--shadow-surface, --shadow-overlay, --shadow-input Elevation by component role, used by shadow-surface, shadow-overlay, and shadow-input. UnoCSS: Tailwind sm, md, and xs, respectively. External themes must define them; none removes a role’s shadow.
--<color>-hover, --<color>-active Explicit interaction colors, such as --primary-hover and --accent-active. Generated state color, then the documented state cascade.
--destructive-foreground Optional text and icon color for destructive surfaces. --background.
--font-size Local base for Moraine’s text-xs through text-9xl scale in Tailwind 4 and Wind4. 1rem.
--sidebar-width Desktop sidebar width; also used by w-sidebar. No variable value is set by default; the width falls back to clamp(14rem, 25%, 20rem).
--mo-anim-duration Overrides all Moraine CSS animation durations in its scope. Type-specific durations when unset.
--mo-anim-duration-enter, --mo-anim-duration-exit, --mo-anim-duration-loop, --mo-anim-duration-spin Duration per animation family when the global duration is unset. 250ms, 150ms, 2s, and 1s, respectively.
--mo-anim-ease Overrides the easing of animate-mo-enter and animate-mo-exit in its scope. Direction-specific easing when unset.
--mo-anim-ease-enter, --mo-anim-ease-exit Easing for entrance and exit when the shared easing is unset. cubic-bezier(0.16, 1, 0.3, 1) and cubic-bezier(0.7, 0, 0.84, 0), respectively.

UnoCSS emits default control, backdrop, and semantic shadows only when themeDefaults is enabled or the values are explicitly configured. The backdrop utility retains its fallback when the variable is absent. --font-size, --spacing, and --radius keep their root defaults when themeDefaults: false. Animation duration variables use CSS fallbacks unless configured. The preset emits --sidebar-width only when configured; SidebarFrame uses clamp(14rem, 25%, 20rem) when unset. With Tailwind, supply the palette and shadow values in your stylesheet.

Use override.<theme>.colors for control, backdrop, and explicit interaction colors; use override.<theme>.shadows for semantic shadows. The preset also accepts fontSize, spacing, radius, and sidebarWidth at the top level or inside a theme override. Set animation controls directly in CSS. For example, customize a container using Wind4 or Tailwind v4:

.workspace {
  --font-size: 0.875rem;
  --spacing: 0.2rem;
  --sidebar-width: 18rem;
  --mo-anim-duration-enter: 180ms;
  --mo-anim-duration-exit: 120ms;
}

Variables inherit through DOM ancestors. Portaled overlays need these values on an ancestor of the portal container, such as the document root, or a portal mounted inside the themed container.

Use the Animations guide to customize entrance and exit motion.

Surface Colors and Contrast Pairing#

Grouped surface colors use corresponding -foreground variables for text, icons, and indicators. control is a single-value form surface: ordinary field text uses foreground and placeholders use muted-foreground. Check both when replacing a palette:

Role Surface token Foreground token Primary use
Control --control --foreground Default outline fields, unchecked choices, upload controls.
Page --background --foreground Default viewport canvas and readable body text.
Primary --primary --primary-foreground High-emphasis actions and active selections.
Secondary --secondary --secondary-foreground Secondary action buttons, subtle badges.
Card --card --card-foreground Cards and grouped surfaces.
Popover --popover --popover-foreground Floating overlays, menus, tooltips, dialogs.
Muted --muted --muted-foreground Subtle backgrounds, secondary descriptions, disabled state.
Accent --accent --accent-foreground Hover states, tab indicators, highlighted list items.
Destructive --destructive --destructive-foreground (optional) Irreversible actions, error banners, validation errors.

Form surface migration#

Define --control in both light and dark external themes. It controls outline fields, unchecked choices, and upload surfaces independently of --input boundaries. Subtle fields use --muted; field text uses --foreground and placeholders use --muted-foreground.

Use the values above as a starting point, or supply your own CSS colors or variable references. bg-control/50 supports a translucent control surface. Check text and boundary contrast against the resulting background. control has no dedicated foreground, hover, or active tokens.

Interactive State Cascade#

For the grouped colors background, primary, secondary, card, popover, muted, accent, and destructive, Moraine supports dedicated --*-hover and --*-active variables. Explicit values take priority. When omitted, supported browsers mix the current color toward its foreground in the scope where the utility is used:

  • Hover utilities resolve --<color>-hover → generated hover → --<color>.
  • Active utilities resolve --<color>-active → generated active → --<color>-hover → generated hover → --<color>.

By default, automatic hover and active colors mix 8% and 12% toward the corresponding foreground. For background, the foreground is --foreground. Other roles use their own --<color>-foreground, falling back to --foreground; destructive colors fall back to --background.

Without color-mix(in oklch, …) support, set explicit state colors when hover or active must differ from the base color. See the UnoCSS guide for configuring automatic state colors and older-browser support.

Border Radius Scale#

Set --radius on a container to scale its default named radius utilities. Explicit CSS engine theme overrides can replace these values. The pixel examples below assume 1rem = 16px:

Utility Formula Computed value (with --radius: 0.625rem)
rounded-xs calc(var(--radius) * 0.5) 0.3125rem (5px)
rounded-sm calc(var(--radius) * 0.6) 0.375rem (6px)
rounded-md calc(var(--radius) * 0.8) 0.5rem (8px)
rounded-lg var(--radius) 0.625rem (10px)
rounded-xl calc(var(--radius) * 1.4) 0.875rem (14px)
rounded-2xl calc(var(--radius) * 1.8) 1.125rem (18px)
rounded-3xl calc(var(--radius) * 2.2) 1.375rem (22px)
rounded-4xl calc(var(--radius) * 2.6) 1.625rem (26px)

Backdrop and Semantic Shadows#

Use these tokens to customize component elevation by role. --backdrop controls the overlay behind modals, dialogs, and sheets. Backdrop blur remains a separate class on the overlay slot.

Utility Token UnoCSS default Component use
bg-backdrop --backdrop Black at 10% Modal backdrops
shadow-surface --shadow-surface Tailwind sm Cards, floating sidebar surfaces, badges, indicators
shadow-overlay --shadow-overlay Tailwind md Menus, selection panels, popovers, dialogs, sheets, tooltips, command palettes
shadow-input --shadow-input Tailwind xs Outline and subtle fields, checkbox controls, switch tracks and thumbs, slider thumbs, upload controls

Set a shadow token to none to remove elevation for that role. UnoCSS emits default values for the three semantic shadows, matching Tailwind’s sm, md, and xs values used by shadcn/ui’s neutral theme. With Tailwind or themeDefaults: false, define the semantic shadows in your stylesheet or explicit overrides. Changing a role token leaves explicit size utilities such as shadow-lg unchanged.

src/theme.css
:root {
  --shadow-surface: 0 1px 3px rgb(0 0 0 / 0.05);
  --shadow-overlay: 0 8px 24px rgb(0 0 0 / 0.12);
  --shadow-input: none;
}

.dark {
  --backdrop: rgb(0 0 0 / 0.4);
  --shadow-surface: none;
  --shadow-overlay: 0 8px 24px rgb(0 0 0 / 0.3);
}

These variables can be scoped to a container. Portaled overlays need the variables on an ancestor of their portal container. UnoCSS accepts colors.backdrop and shadows.surface, shadows.overlay, and shadows.input in each named override entry; Tailwind reads the same CSS variables directly.

CSS Engine Shadows#

Size utilities such as shadow-xs, shadow-md, and shadow-lg use the CSS engine’s native shadow scale. Moraine does not register or configure these sizes. Use UnoCSS’s theme options or Tailwind’s @theme to customize them separately from Moraine’s semantic shadows.

Typography and Spacing#

Token Description Fallback
--font-sans Primary UI typeface None in Moraine
--font-mono Code, numbers, and Kbd None in Moraine
--font-serif Editorial serif typeface None in Moraine
--font-size Base for text-xs–text-9xl in Tailwind 4 and Wind4 1rem
--spacing Base spacing unit 0.25rem

With Wind4 and Tailwind v4, set --font-size on a container to scale the default text-xs through text-9xl utilities. Wind3 uses its configured font sizes and does not scale them through --font-size. Explicit CSS engine typography overrides follow the values you configure.

Moraine’s default font-sans, font-mono, and font-serif utilities use the corresponding CSS variables, but Moraine does not supply family values. With UnoCSS, configure theme.font (Wind4) or theme.fontFamily (Wind3) to use literal families or your own variable references; see Font families. To use the default variable mapping, define the families in your stylesheet:

src/theme.css
:root {
  --font-sans: Inter, system-ui, sans-serif;
  --font-mono: 'Maple Mono', ui-monospace, monospace;
  --font-serif: Georgia, serif;
}

Default numeric spacing utilities use the local --spacing in Tailwind v4 and Wind4. For example, p-4 is 2rem under --spacing: 0.5rem. Wind3 keeps its native numeric spacing and size values, including explicit theme overrides, even with wind3: true. Use p-[var(--app-padding)] for variable-based padding in Wind3.

Use leading-* to customize line height. With Wind3, configure typography through UnoCSS’s theme.fontSize and theme.lineHeight.

Semantic Z-Index Utilities#

Moraine avoids magic numeric z-indices in favor of a 7-tier semantic scale:

Utility Value Scope
z-base 1 Base elevated content
z-raised 2 Raised content and ranges
z-control 3 Controls above local ranges
z-sticky 10 Sticky headers and rails
z-resize 20 Splitter and resize handles
z-overlay 40 Backdrop masks and overlay scrims
z-floating 50 Dialogs, popovers, menus, tooltips