Theme documentation
Theme
Every component uses the light/dark tokens in registry/ninaero/theme.mjs. The workshop and installable registry use this same source.
Shared surfaces
Background
Card
Primary
Secondary
Muted
Accent
Use the theme selector in the header to compare light and dark surfaces.
Accent presets
Choose an accent with the Accent selector. Accent and color mode are independent. Primary actions, selected controls, links, and focus rings follow the palette. Paper, ink, structural borders, and status colors stay unchanged. The site remembers your choice when browser storage is available.
Installed themes support the same presets without a React provider. Set data-accent on the root element and use the dark class for dark mode. Edit preset colors in registry/ninaero/accents.mjs, then regenerate the registry.
<html data-accent="lavender" class="dark">Site-wide overlays
Site-wide overlays add an optional static texture behind page content and controls, including menus and dialogs. See Add a site-wide overlay for setup and tuning.
Typography
Google Sans Flex handles interface text and headings, with its ROND axis set to 100. Google Sans Code handles code and other monospaced text. Pair a font size with a type-* role to set weight, line height, and letter spacing. Headings use tighter spacing; body text, controls, and captions keep normal spacing.
<h2 className="text-xl type-heading">Account settings</h2><p className="text-sm type-body">Manage your profile and preferences.</p>Compact controls keep a 16px line height. Dialog titles and ruled prose keep a 24px line height. Role utilities leave font sizes unchanged, including responsive input sizing.
View the typography specimenElevation
Choose a shadow by the element's distance from the paper beneath it. Elevation does not change stacking order. Use z-index separately for overlays.
Groups made entirely of raised buttons share one control shadow; their buttons keep their pressed states. The header uses the paper shadow-color context so the cast blends with its neutral background.
<Card className="shadow-floating">...</Card>Update the theme
- Edit the tokens and typography in
registry/ninaero/theme.mjs. - Regenerate the CSS, registry, and documentation with the command below.
- Review the updated theme across the component documentation pages before publishing.
pnpm registry:buildDo not edit app/globals.css or registry.json directly. They are generated.
Light and dark tokens
{ "light": { "background": "oklch(1 0 0)", "neutral-border": "#e8e8e8", "neutral-input": "#b8b8b8", "input-edge": "#c8c8c8", "input-fill": "oklch(0.975 0 0)", "input-placeholder": "oklch(0.5 0 0)", "input-disabled-fill": "var(--muted)", "tabs-track-fill": "#ededed", "tabs-active-fill": "#f6f6f6", "tabs-active-top": "#ffffff", "tabs-active-bottom": "#e1e1e1", "tabs-active-edge": "#777777", "tabs-active-highlight": "#ffffff80", "tabs-hover-fill": "#e3e3e3", "tabs-ink": "#575757", "recess-shade-weight": "6%", "recess-highlight-weight": "35%", "foreground": "oklch(0.145 0 0)", "display-ink-top": "oklch(0.32 0 0)", "display-ink-bottom": "oklch(0.22 0 0)", "display-ink-shadow": "#00000014", "card": "oklch(1 0 0)", "card-foreground": "oklch(0.145 0 0)", "popover": "var(--card)", "dialog-fill-shade": "color-mix(in oklab, var(--popover) 98%, black)", "dialog-highlight": "#ffffffb3", "dialog-shade": "#0000000d", "tooltip-scanline-ink": "color-mix(in oklab, var(--background) 7%, transparent)", "popover-foreground": "var(--card-foreground)", "secondary": "oklch(0.97 0 0)", "secondary-foreground": "oklch(0.205 0 0)", "muted": "oklch(0.97 0 0)", "muted-foreground": "oklch(0.556 0 0)", "destructive": "#b02f1e", "chart-1": "oklch(0.87 0 0)", "chart-2": "oklch(0.556 0 0)", "chart-3": "oklch(0.439 0 0)", "chart-4": "oklch(0.371 0 0)", "chart-5": "oklch(0.269 0 0)", "radius": "0.625rem", "field-height": "2.25rem", "field-padding-inline": "1rem", "sidebar": "oklch(0.985 0 0)", "sidebar-foreground": "oklch(0.145 0 0)", "button-primary-fill": "#b9e9f5", "button-primary-hover": "#a2dfef", "button-primary-pressed": "#8dd4e6", "button-primary-ink": "#123846", "button-primary-edge": "#3a8197", "button-link-ink": "#17677d", "button-focus": "#006d89", "accent-soft": "#e6f4f8", "glass-mark-rim-0": "#3a8197", "glass-mark-rim-50": "#367a8f", "glass-mark-rim-100": "#337387", "glass-mark-face-0": "#a2dfef", "glass-mark-face-50": "#98d9ea", "glass-mark-face-100": "#8dd4e6", "primary": "var(--button-primary-fill)", "primary-foreground": "var(--button-primary-ink)", "accent": "var(--accent-soft)", "accent-foreground": "var(--button-link-ink)", "ring": "var(--button-focus)", "border": "var(--neutral-border)", "input": "var(--neutral-input)", "sidebar-border": "var(--neutral-border)", "sidebar-primary": "var(--button-primary-fill)", "sidebar-primary-foreground": "var(--button-primary-ink)", "sidebar-accent": "var(--accent-soft)", "sidebar-accent-foreground": "var(--button-link-ink)", "sidebar-ring": "var(--button-focus)", "button-paper-fill": "#f8f8f8", "button-paper-edge": "#c5c5c5", "button-outline-edge": "#c8c8c8", "button-paper-hover": "#eeeeee", "button-paper-ink": "#383838", "button-paper-text-shadow": "0 1px 0 #ffffffb3", "control-label-text-shadow": "0 0.5px 0 #ffffff80", "button-paper-pressed": "#e3e3e3", "button-paper-highlight": "#ffffffa6", "button-paper-shade": "#3333330d", "button-destructive-fill": "#ffd9cd", "button-destructive-hover": "#ffcab9", "button-destructive-pressed": "#f5b3a0", "button-destructive-ink": "#7e291b", "button-destructive-edge": "#c25038", "button-destructive-highlight": "#ffffff66", "button-destructive-shade": "#5a1b0a14", "button-highlight": "#ffffff80", "shadow-surface": "var(--background)", "shadow-color": "from color-mix(in oklab, var(--shadow-surface) 40%, black) h s l", "paper-fill": "#f9f9f9", "paper-rule": "#79797914", "paper-dot": "#79797929", "paper-rule-strong": "#79797940", "paper-notebook-mask": "url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22120%22%20height%3D%22120%22%20viewBox%3D%220%200%20120%20120%22%20fill%3D%22none%22%3E%3Cpath%20d%3D%22M0%206H120%20M0%2012H120%20M0%2018H120%20M0%2024H120%20M0%2030H120%20M0%2036H120%20M0%2042H120%20M0%2048H120%20M0%2054H120%20M0%2060H120%20M0%2066H120%20M0%2072H120%20M0%2078H120%20M0%2084H120%20M0%2090H120%20M0%2096H120%20M0%20102H120%20M0%20108H120%20M0%20114H120%22%20stroke%3D%22white%22%20stroke-opacity%3D%220.3125%22%20stroke-width%3D%221%22%20vector-effect%3D%22non-scaling-stroke%22%2F%3E%3Cpath%20d%3D%22M0%200H120V120H0Z%22%20stroke%3D%22white%22%20stroke-width%3D%222%22%20vector-effect%3D%22non-scaling-stroke%22%2F%3E%3Cpath%20d%3D%22M0%200h0%20M120%200h0%20M0%20120h0%20M120%20120h0%22%20stroke%3D%22white%22%20stroke-width%3D%226%22%20stroke-linecap%3D%22round%22%20vector-effect%3D%22non-scaling-stroke%22%2F%3E%3C%2Fsvg%3E\")", "dither-mask": "url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%228%22%20height%3D%228%22%20viewBox%3D%220%200%208%208%22%20fill%3D%22none%22%20shape-rendering%3D%22crispEdges%22%3E%3Cpath%20d%3D%22M0%200L8%208M8%200L0%208%22%20stroke%3D%22white%22%20stroke-width%3D%221%22%20vector-effect%3D%22non-scaling-stroke%22%2F%3E%3C%2Fsvg%3E\")", "overlay-flipnote-mask": "url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%228%22%20height%3D%228%22%20viewBox%3D%220%200%208%208%22%20fill%3D%22white%22%20shape-rendering%3D%22crispEdges%22%3E%3Cpath%20d%3D%22M0%200h1v1H0z%20M4%201h1v1H4z%20M2%203h1v1H2z%20M6%203h1v1H6z%20M1%205h1v1H1z%20M5%205h1v1H5z%20M3%207h1v1H3z%20M7%207h1v1H7z%22%2F%3E%3C%2Fsvg%3E\")", "overlay-dots-mask": "url(\"data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22white%22%3E%3Ccircle%20cx%3D%2250%25%22%20cy%3D%2250%25%22%20r%3D%220.75%22%2F%3E%3C%2Fsvg%3E\")", "overlay-checker-mask": "conic-gradient(black 25%, transparent 0 50%, black 0 75%, transparent 0)", "ninaero-overlay-blend-mode": "multiply", "ninaero-overlay-opacity": "0.06", "ninaero-overlay-coverage": "0.5", "ninaero-overlay-compensation": "1" }, "dark": { "background": "oklch(0.145 0 0)", "neutral-border": "#303030", "neutral-input": "#555555", "input-edge": "#454545", "input-fill": "oklch(0.18 0 0)", "input-placeholder": "var(--muted-foreground)", "input-disabled-fill": "var(--input-fill)", "tabs-track-fill": "#242424", "tabs-active-fill": "#3a3a3a", "tabs-active-top": "#454545", "tabs-active-bottom": "#2e2e2e", "tabs-active-edge": "#aaaaaa", "tabs-active-highlight": "#ffffff1a", "tabs-hover-fill": "#303030", "tabs-ink": "#b5b5b5", "recess-shade-weight": "12%", "recess-highlight-weight": "7%", "foreground": "oklch(0.985 0 0)", "display-ink-top": "oklch(0.985 0 0)", "display-ink-bottom": "oklch(0.88 0 0)", "display-ink-shadow": "#00000040", "card": "oklch(0.205 0 0)", "card-foreground": "oklch(0.985 0 0)", "popover": "var(--card)", "dialog-fill-shade": "color-mix(in oklab, var(--popover) 92%, black)", "dialog-highlight": "#ffffff0d", "dialog-shade": "#00000040", "tooltip-scanline-ink": "color-mix(in oklab, var(--background) 3%, transparent)", "popover-foreground": "var(--card-foreground)", "secondary": "oklch(0.269 0 0)", "secondary-foreground": "oklch(0.985 0 0)", "muted": "oklch(0.269 0 0)", "muted-foreground": "oklch(0.708 0 0)", "destructive": "#ff9b83", "chart-1": "oklch(0.87 0 0)", "chart-2": "oklch(0.556 0 0)", "chart-3": "oklch(0.439 0 0)", "chart-4": "oklch(0.371 0 0)", "chart-5": "oklch(0.269 0 0)", "sidebar": "oklch(0.205 0 0)", "sidebar-foreground": "oklch(0.985 0 0)", "button-primary-fill": "#79cfe5", "button-primary-hover": "#96def0", "button-primary-pressed": "#62b9d0", "button-primary-ink": "#102d3a", "button-primary-edge": "#a0e3f2", "button-link-ink": "#8adef2", "button-focus": "#7ce1f6", "accent-soft": "#203e48", "glass-mark-rim-0": "#a0e3f2", "glass-mark-rim-50": "#97d7e5", "glass-mark-rim-100": "#8fcbd9", "glass-mark-face-0": "#96def0", "glass-mark-face-50": "#7ccbe0", "glass-mark-face-100": "#62b9d0", "primary": "var(--button-primary-fill)", "primary-foreground": "var(--button-primary-ink)", "accent": "var(--accent-soft)", "accent-foreground": "var(--button-link-ink)", "ring": "var(--button-focus)", "border": "var(--neutral-border)", "input": "var(--neutral-input)", "sidebar-border": "var(--neutral-border)", "sidebar-primary": "var(--button-primary-fill)", "sidebar-primary-foreground": "var(--button-primary-ink)", "sidebar-accent": "var(--accent-soft)", "sidebar-accent-foreground": "var(--button-link-ink)", "sidebar-ring": "var(--button-focus)", "button-paper-fill": "#343434", "button-paper-edge": "#555555", "button-outline-edge": "#454545", "button-paper-hover": "#454545", "button-paper-ink": "#efefef", "button-paper-text-shadow": "0 1px 1px #00000066", "control-label-text-shadow": "0 0.5px 0.5px #00000059", "button-paper-pressed": "#2e2e2e", "button-paper-highlight": "#ffffff0d", "button-paper-shade": "#00000040", "button-destructive-fill": "#ffa088", "button-destructive-hover": "#ffb39d", "button-destructive-pressed": "#ed8b73", "button-destructive-ink": "#35160f", "button-destructive-edge": "#ffc5b5", "button-destructive-highlight": "#ffffff1a", "button-destructive-shade": "#00000040", "button-highlight": "#ffffff33", "shadow-surface": "var(--background)", "shadow-color": "from color-mix(in oklab, var(--shadow-surface) 40%, black) h s l", "paper-fill": "#232323", "paper-rule": "#cecece0f", "paper-dot": "#cecece24", "paper-rule-strong": "#cecece30", "ninaero-overlay-blend-mode": "screen" }}