Design Tokens
read as.mdEvery @ggui-ai/design primitive — and by extension every gadget that ships with the OSS renderer — reads its visual state from DTCG 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 an app’s own theme is stored as its full projection for both modes. Every write door refuses a theme that leaves a variable the components read unset, and the iframe-runtime merges the theme into the scoped token block the components read. A partial change is still fine where you author it: a preset plus a few overrides is projected in full, both modes, before it is stored. Agents can enumerate presets with ggui_list_themes and pick one per render via ggui_render({themeId}) — see Custom Theming.
/* Every primitive uses var(--ggui-…) with a hardcoded fallback */color: var(--ggui-color-primary-600, #1f1f1f);padding: var(--ggui-spacing-md, 16px);border-radius: var(--ggui-shape-radius-md, 2px);Colors
Section titled “Colors”Color Palettes
Section titled “Color Palettes”Primary
Neutral
Success
Warning
Error
Info
Semantic Colors
Section titled “Semantic Colors”These tokens map to specific UI roles and adapt between light and dark themes.
Material Role Pairs
Section titled “Material Role Pairs”The canonical theme states its layering as role pairs — a background and the ink that sits on it. A theme.json authors three of the four (ground, container, sunken, each with its on*); elevated is derived. Primitives use these to keep contrast right across nested backgrounds.
| CSS variable | Role |
|---|---|
--ggui-color-ground |
The canvas — page / sheet background |
--ggui-color-onGround |
Primary text and icons on ground |
--ggui-color-container |
Cards, panels, filled containers |
--ggui-color-onContainer |
Text/icons on container |
--ggui-color-elevated |
Raised surfaces (menus, popovers) — derived, not authored |
--ggui-color-onElevated |
Text/icons on elevated — derived |
--ggui-color-sunken |
Wells, inputs at rest, code blocks — recessed inside container |
--ggui-color-onSunken |
Secondary text/icons on sunken |
--ggui-color-outline |
Standard borders + dividers |
--ggui-color-outlineVariant |
Faint borders, disabled outlines |
--ggui-color-onPrimary |
Text/icons on a primary fill (CTA labels) |
--ggui-color-primaryContainer |
Tinted primary container (soft CTA, highlight) |
--ggui-color-onPrimaryContainer |
Text/icons on primaryContainer |
--ggui-color-onError |
Text/icons on an error fill |
--ggui-color-errorContainer |
Softer error container (inline error banners) |
--ggui-color-onErrorContainer |
Text/icons on errorContainer |
--ggui-color-tertiaryContainer |
Tinted tertiary container |
--ggui-color-onTertiaryContainer |
Text/icons on tertiaryContainer |
Spacing
Section titled “Spacing”A consistent spacing scale ensures visual rhythm across all components.
xs0.25rem--ggui-spacing-xssm0.5rem--ggui-spacing-smmd1rem--ggui-spacing-mdlg1.5rem--ggui-spacing-lgxl2rem--ggui-spacing-xl2xl3rem--ggui-spacing-2xlTypography
Section titled “Typography”Canonical theme path: font.{family,weight,ramp,letterSpacing} — a theme authors the families, the weights, one font.ramp and optional letter-spacing; the size and line-height scales are derived from the ramp. Emitted as --ggui-font-family-*, --ggui-font-weight-*, and the derived --ggui-font-size-* / --ggui-font-lineHeight-* shown below. Web fonts are declared as typography.faces (https: sources only); ggui deploy names any family the hosted path does not deliver yet. A face’s origin is admitted to the card’s font-src when the card mounts, so a face added on a new origin while a card is already open loads on that card’s next mount. Until then the runtime reports the blocked face to the embedding host as a font-face-blocked event, the same event it posts for a blocked host-announced font. Authored font.size or font.lineHeight is refused.
Font Families
Font Sizes
xs12px--ggui-font-size-xssm14px--ggui-font-size-smbase16px--ggui-font-size-baselg18px--ggui-font-size-lgxl20px--ggui-font-size-xl2xl24px--ggui-font-size-2xl3xl30px--ggui-font-size-3xl4xl36px--ggui-font-size-4xlFont Weights
NormalThe quick brown fox jumps over the lazy dog400MediumThe quick brown fox jumps over the lazy dog500SemiboldThe quick brown fox jumps over the lazy dog600BoldThe quick brown fox jumps over the lazy dog700HeadingThe quick brown fox jumps over the lazy dog700Line Heights
NormalBorder Radius
Section titled “Border Radius”Canonical theme path: shape.radius.*. Emitted as --ggui-shape-radius-{sm,md,lg,xl,full,control}.
--ggui-shape-radius-sm--ggui-shape-radius-md--ggui-shape-radius-lg--ggui-shape-radius-xl--ggui-shape-radius-full--ggui-shape-radius-controlShadows
Section titled “Shadows”Canonical theme path: shape.shadow.*. Emitted as --ggui-shape-shadow-{sm,md,lg,xl}.
Motion
Section titled “Motion”Every design-system transition is composed from six variables: three duration steps and three easing roles. A theme may state its own tempo (motion.duration.{fast,base,slow}, motion.easing.{standard,emphasized,exit}), and since v0.21.0 the card animates at the stated tempo; a theme that states none gets the shipped defaults below.
| Variable | Shipped default | Role |
|---|---|---|
--ggui-motion-duration-fast |
100ms |
Hover, focus, small swaps |
--ggui-motion-duration-base |
200ms |
The default for colour, opacity and transforms |
--ggui-motion-duration-slow |
300ms |
Larger changes |
--ggui-motion-easing-standard |
cubic-bezier(0.4, 0, 0.2, 1) |
The ease-in-out for colour, border and size |
--ggui-motion-easing-emphasized |
cubic-bezier(0, 0, 0.2, 1) |
The decelerating arrival — transforms, entrances |
--ggui-motion-easing-exit |
cubic-bezier(0.4, 0, 1, 1) |
The accelerating departure |
There are no pre-composed transition variables: write the shorthand yourself from a duration and an easing, naming the properties you move. Keyframe animations (spinners, pulses, shimmers) are not on this scale. A theme’s motion.keyframes become @keyframes ggui-<name> rules rather than variables. Its other motion members (transition, reduce) and its accessibility and zIndex groups are accepted in the document, but they emit no --ggui-* variable, so no component reads them.
Using Tokens
Section titled “Using Tokens”In primitives
Section titled “In primitives”Nothing to wire — every primitive already consumes the tokens:
import { Button, Card, Input } from '@ggui-ai/design';
<Card> <Input placeholder="Enter your name" /> <Button variant="primary">Submit</Button></Card>;In your own components
Section titled “In your own components”Reference tokens by their CSS variable, with a hardcoded fallback for environments where the theme provider hasn’t loaded yet:
.my-component { background: var(--ggui-color-ground, #f4f3ed); padding: var(--ggui-spacing-md, 16px); border-radius: var(--ggui-shape-radius-lg, 4px); box-shadow: var(--ggui-shape-shadow-md, 0 1px 2px rgba(41, 41, 41, 0.06)); 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-onGround, #292929); transition: color var(--ggui-motion-duration-base, 200ms) var(--ggui-motion-easing-standard, ease);}Theming
Section titled “Theming”Override on any element — :root for global, a wrapper for per-subtree:
:root { --ggui-color-primary-600: #7c3aed; /* Purple instead of the default ink */ --ggui-color-ground: #fefce8; /* Warm paper canvas */ --ggui-color-onGround: #1f1410; /* Ink for the new ground */ --ggui-shape-radius-md: 16px; /* Pillier corners */ --ggui-motion-duration-base: 240ms; /* Slightly more deliberate */}See Custom Theming for the full recipe — global overrides, dark-mode pairs, and scoped subtrees.