---
title: Design Tokens
description: Semantic CSS variables, color contrast roles, border radius
  multipliers, and z-index scales.
package: moraine
version: 0.6.0
repository: https://github.com/subframe7536/moraine
---

# Design Tokens

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

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](https://moraine.subf.dev/docs/unocss.md#theme-tokens). 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](#backdrop-and-semantic-shadows) and [font families](#typography-and-spacing) described below when using an external theme. `color-scheme` makes native browser controls follow the light or dark theme:

```css title="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](https://ui.shadcn.com/docs/theming#default-theme-css) 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](#interactive-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:

```css
.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](https://moraine.subf.dev/docs/animations.md) 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](https://moraine.subf.dev/docs/unocss.md#hover-and-active-colors) 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.

```css title="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](https://moraine.subf.dev/docs/unocss.md#font-families). To use the default variable mapping, define the families in your stylesheet:

```css title="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 |

