---
title: Design Tokens
description: Visual reference for the DTCG design tokens that ggui primitives consume — colors, spacing, typography, radius, and shadows.
---

import TokenSwatch from '../../../components/TokenSwatch.astro';
import SpacingScale from '../../../components/SpacingScale.astro';
import TypographySample from '../../../components/TypographySample.astro';
import ShadowPreview from '../../../components/ShadowPreview.astro';
import {
  colorGroups,
  semanticColors,
  spacingTokens,
  fontFamilies,
  fontSizeTokens,
  fontWeightTokens,
  lineHeightTokens,
  radiusTokens,
  shadowTokens,
} from '../../../data/tokens';

Every `@ggui-ai/design` primitive — and by extension every gadget that ships with the OSS renderer — reads its visual state from [DTCG](https://design-tokens.github.io/community-group/format/) tokens exposed as CSS custom properties. Tokens themselves need no runtime: override the variables on any ancestor and the cascade does the rest on the next paint.

Operator theming builds on these same variables: `ggui.json#theme` selects one of the shipped presets (or a DTCG file), and per-app overlays are validated `--ggui-*` maps injected after the base token block, so a partial overlay keeps token defaults. Agents can enumerate presets with `ggui_list_themes` and pick one per render via `ggui_render({themeId})` — see [Custom Theming](/cookbook/custom-theming/).

```css
/* Every primitive uses var(--ggui-…) with a hardcoded fallback */
color: var(--ggui-color-primary-600, #0284c7);
padding: var(--ggui-spacing-md, 16px);
border-radius: var(--ggui-shape-radius-md, 8px);
```

:::tip
The values below mirror the OSS default light theme (`packages/design/src/themes/defaults/light.ts`), emitted by the DTCG parser as `--ggui-color-…`, `--ggui-spacing-…`, `--ggui-shape-radius-…`, `--ggui-shape-shadow-…` (path = group chain, hyphenated). The canonical `DtcgTheme` nests radius and shadow under `shape.*` and transitions under `motion.*` — primitives reference `--ggui-shape-radius-md`, `--ggui-shape-shadow-md`, `--ggui-motion-duration-normal`, etc.
:::

:::caution
The swatch/scale tables below are rendered from a hand-maintained snapshot at `apps/docs/src/data/tokens.ts` (Astro can't import the React design package directly). If a value here disagrees with `packages/design/src/themes/defaults/light.ts`, the design package is authoritative.
:::

---

## Colors

### Color Palettes

{colorGroups.map((group) => (
  <div style="margin-bottom: 32px;">
    <h4 style="margin-bottom: 12px;">{group.name}</h4>
    <div style="display: flex; flex-wrap: wrap; gap: 8px;">
      {group.swatches.map((swatch) => (
        <TokenSwatch hex={swatch.hex} shade={swatch.shade} cssVar={swatch.cssVar} />
      ))}
    </div>
  </div>
))}

### Semantic Colors

These tokens map to specific UI roles and adapt between light and dark themes.

<div class="semantic-grid">
  {semanticColors.map((color) => (
    <div class="semantic-row">
      <div class="semantic-swatch" style={`background-color: ${color.hex}; border: 1px solid rgba(0,0,0,0.1);`} />
      <div class="semantic-meta">
        <strong>{color.name}</strong>
        <code>{color.hex}</code>
        <code class="semantic-var">{color.cssVar}</code>
      </div>
    </div>
  ))}
</div>

:::note
**Status colors are scales, not singletons.** `success`, `warning`, `error`, and `info` each expose the full 50/100/200/500/600/700/800 stops (see the swatch panels above). Reach for `--ggui-color-success-500` for fills, `-600/-700` for hover/pressed states, and `-50/-100` for subtle tints — the singletons that used to live here are retired.
:::

### Material Role Pairs

The canonical theme adds eight Material 3-inspired role pairs for surface/content layering. They sit alongside (and extend) the legacy text tokens — primitives use these to keep contrast right across nested surfaces.

| CSS variable                       | Role                                            |
| ---------------------------------- | ----------------------------------------------- |
| `--ggui-color-surface`             | Default page / sheet background                 |
| `--ggui-color-onSurface`           | Primary text and icons on `surface`             |
| `--ggui-color-surfaceVariant`      | Subtle alternate background (cards, rails)      |
| `--ggui-color-onSurfaceVariant`    | Secondary text/icons on `surfaceVariant`        |
| `--ggui-color-container`           | Filled container (chips, banners, soft buttons) |
| `--ggui-color-onContainer`         | Text/icons on `container`                       |
| `--ggui-color-outline`             | Standard borders + dividers                     |
| `--ggui-color-outlineVariant`      | Faint borders, disabled outlines                |

<style>{`
  .semantic-grid {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin: 16px 0;
  }
  .semantic-row {
    display: flex;
    align-items: center;
    gap: 12px;
  }
  .semantic-swatch {
    width: 40px;
    height: 40px;
    border-radius: 8px;
    flex-shrink: 0;
  }
  .semantic-meta {
    display: flex;
    align-items: baseline;
    gap: 8px;
  }
  .semantic-meta strong {
    font-size: 14px;
    min-width: 120px;
  }
  .semantic-meta code {
    font-size: 12px;
    background: none !important;
    border: none !important;
    padding: 0 !important;
    color: var(--sl-color-gray-4);
  }
  .semantic-var {
    font-size: 11px !important;
    color: var(--sl-color-gray-3) !important;
  }
`}</style>

---

## Spacing

A consistent spacing scale ensures visual rhythm across all components.

<SpacingScale tokens={spacingTokens} />

---

## Typography

Canonical theme path: `font.{family,size,weight,lineHeight}.*`. Emitted as `--ggui-font-family-*`, `--ggui-font-size-*`, `--ggui-font-weight-*`, `--ggui-font-lineHeight-*`.

<TypographySample
  fontSizes={fontSizeTokens}
  fontWeights={fontWeightTokens}
  fontFamilies={fontFamilies}
  lineHeights={lineHeightTokens}
/>

---

## Border Radius

Canonical theme path: `shape.radius.*`. Emitted as `--ggui-shape-radius-{none,sm,md,lg,xl,2xl,full}`.

<div class="radius-grid">
  {radiusTokens.map((token) => (
    <div class="radius-card">
      <div
        class="radius-preview"
        style={`border-radius: ${token.value};`}
      />
      <div class="radius-meta">
        <code class="radius-name">{token.name}</code>
        <span class="radius-value">{token.value}</span>
      </div>
      <code class="radius-var">{token.cssVar}</code>
    </div>
  ))}
</div>

<style>{`
  .radius-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    margin: 16px 0;
  }
  .radius-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    min-width: 80px;
  }
  .radius-preview {
    width: 64px;
    height: 64px;
    background: var(--sl-color-accent);
    opacity: 0.2;
    border: 2px solid var(--sl-color-accent);
  }
  .radius-meta {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
  }
  .radius-name {
    font-size: 13px;
    font-weight: 600;
    background: none !important;
    border: none !important;
    padding: 0 !important;
  }
  .radius-value {
    font-size: 11px;
    color: var(--sl-color-gray-3);
  }
  .radius-var {
    font-size: 10px;
    color: var(--sl-color-gray-3);
    background: none !important;
    border: none !important;
    padding: 0 !important;
  }
`}</style>

---

## Shadows

Canonical theme path: `shape.shadow.*`. Emitted as `--ggui-shape-shadow-{none,xs,sm,md,lg,xl,2xl}`.

<ShadowPreview shadows={shadowTokens} />

---

## Motion

The canonical `motion` group covers durations, easings, keyframes, and ready-made `transition` shorthands. Durations + transitions both surface as CSS custom properties; transitions are pre-composed so primitives can drop in a single `var(--ggui-motion-transition-…)` without rewriting the curve.

| Variable                              | Typical value                                  | When to use                              |
| ------------------------------------- | ---------------------------------------------- | ---------------------------------------- |
| `--ggui-motion-duration-instant`      | `0ms`                                          | Immediate state flips (no animation)     |
| `--ggui-motion-duration-fast`         | `100ms`                                        | Hover, focus, small icon swaps           |
| `--ggui-motion-duration-normal`       | `200ms`                                        | Default for color/opacity transitions    |
| `--ggui-motion-duration-slow`         | `300ms`                                        | Layout shifts, large fades               |
| `--ggui-motion-transition-colors`     | `color/background-color/border-color 200ms`    | Theme-aware color changes                |
| `--ggui-motion-transition-opacity`    | `opacity 200ms`                                | Fades, show/hide                         |
| `--ggui-motion-transition-transform` | `transform 200ms`                              | Translate/scale interactions             |
| `--ggui-motion-transition-all`        | `all 200ms`                                    | Catch-all when several properties move   |

Respect `accessibility.reducedMotion` — set durations to `0ms` (or override the transitions to `none`) when it's `'reduce'`.

---

## Accessibility

Top-level `accessibility` tokens make a11y intent explicit instead of leaving it implicit in component styles. They emit as `--ggui-accessibility-*` and pair with the standard media queries.

| Variable                            | Type                          | Notes                                              |
| ----------------------------------- | ----------------------------- | -------------------------------------------------- |
| `--ggui-accessibility-focusRing`    | shadow / outline shorthand    | The focus indicator used by every primitive        |
| `--ggui-accessibility-reducedMotion`| `'no-preference' \| 'reduce'` | Mirror of `prefers-reduced-motion`; drives motion  |
| `--ggui-accessibility-highContrast` | `'standard' \| 'increased'`   | Mirror of `prefers-contrast`; thickens borders     |

Override `focusRing` at the theme level to brand the focus indicator across every primitive at once.

---

## Z-Index

A canonical layering scale prevents floating UIs from fighting each other. Values are unitless integers and increase with elevation.

| Variable                       | Layer             | Typical occupant                           |
| ------------------------------ | ----------------- | ------------------------------------------ |
| `--ggui-zIndex-hide`           | `-1`              | Off-screen / underlay                      |
| `--ggui-zIndex-base`           | `0`               | Page content                               |
| `--ggui-zIndex-docked`         | `10`              | Docked sidebars, sticky toolbars           |
| `--ggui-zIndex-dropdown`       | `1000`            | Menus, select popovers                     |
| `--ggui-zIndex-sticky`         | `1100`            | Sticky table headers                       |
| `--ggui-zIndex-banner`         | `1200`            | Announcement / cookie banners              |
| `--ggui-zIndex-overlay`        | `1300`            | Backdrop scrims                            |
| `--ggui-zIndex-modal`          | `1400`            | Modal dialogs                              |
| `--ggui-zIndex-popover`        | `1500`            | Floating popovers anchored to content      |
| `--ggui-zIndex-skipLink`       | `1600`            | Keyboard skip-link (must beat modals)      |
| `--ggui-zIndex-toast`          | `1700`            | Toast notifications                        |
| `--ggui-zIndex-tooltip`        | `1800`            | Tooltips (topmost interactive element)     |

---

## Using Tokens

### In primitives

Nothing to wire — every primitive already consumes the tokens:

```tsx
import { Button, Card, Input } from '@ggui-ai/design';

<Card>
  <Input placeholder="Enter your name" />
  <Button variant="primary">Submit</Button>
</Card>;
```

### In your own components

Reference tokens by their CSS variable, with a hardcoded fallback for environments where the theme provider hasn't loaded yet:

```css
.my-component {
  background: var(--ggui-color-surface, #ffffff);
  padding: var(--ggui-spacing-md, 16px);
  border-radius: var(--ggui-shape-radius-lg, 12px);
  box-shadow: var(--ggui-shape-shadow-md, 0 8px 16px -4px rgba(15, 23, 42, 0.10));
  font-family: var(--ggui-font-family-sans, system-ui, -apple-system, sans-serif);
  font-size: var(--ggui-font-size-sm, 14px);
  color: var(--ggui-color-onSurface, #111827);
  transition: var(--ggui-motion-transition-colors, color 200ms ease);
}
```

### Theming

Override on any element — `:root` for global, a wrapper for per-subtree:

```css
:root {
  --ggui-color-primary-600: #7c3aed;     /* Purple instead of sky blue */
  --ggui-color-surface: #fefce8;         /* Warm paper background */
  --ggui-color-onSurface: #1f1410;       /* Ink for the new surface */
  --ggui-shape-radius-md: 16px;          /* Pillier corners */
  --ggui-motion-duration-normal: 240ms;  /* Slightly more deliberate */
}
```

See [Custom Theming](/cookbook/custom-theming/) for the full recipe — global overrides, dark-mode pairs, and scoped subtrees.