Skip to content
Twentyfirst UIPro

Theming and tokens

Both kits define exactly the same token names. The swatches show Pro in the current theme; Pro maps its own scale onto Halo's names, references like {bg-000} point at other tokens.

View as Markdown

Both kits use shadcn/ui's theme variables. A theme is any CSS that sets them, so a theme from ui.shadcn.com/themes, tweakcn or your own design system works as it is, and every component follows it: buttons, fields, menus, tables, charts, the Sidebar, in Halo and in Pro, light and dark.

css
@import "@twentyfirstmedia/pro/styles.css";

:root {
  --primary: oklch(0.546 0.245 262.881);
  --primary-foreground: oklch(0.985 0 0);
  --ring: oklch(0.546 0.245 262.881);
  --radius: 0.75rem;
}
.dark,
[data-theme="dark"] {
  --primary: oklch(0.707 0.165 254.624);
  --primary-foreground: oklch(0.145 0 0);
}

The kits define their variables with zero specificity, so your theme wins in any order. Dark mode follows data-theme="dark" or the .dark class on <html> (next-themes works as it is), and data-theme="system" follows the operating system.

The variables

VariableUsed for
--background, --foregroundPage background and default text
--card, --card-foregroundCards, panels, fields, the workspace sheet
--popover, --popover-foregroundMenus, popovers, select lists, command palette, hover cards, dialogs
--primary, --primary-foregroundPrimary buttons, checked boxes and radios, switches, selected chips and days, progress, current marks
--secondary, --secondary-foregroundSoft buttons and icon buttons
--muted, --muted-foregroundQuiet fills (tracks, search fields, code) and secondary text
--accent, --accent-foregroundThe highlight under the pointer or keyboard: menu items, rows, options, nav items
--destructive, --destructive-foregroundDanger buttons, error text and icons
--border, --input, --ringHairlines, field and checkbox borders, focus rings
--radiusThe base corner radius; every corner derives from it
--chart-1 … --chart-5Chart series (lines, dots, legends)
--sidebar, --sidebar-foreground, --sidebar-primary, --sidebar-primary-foreground, --sidebar-accent, --sidebar-accent-foreground, --sidebar-border, --sidebar-ringThe Sidebar

The kits' own names (--canvas, --surface, --ink, --line, Pro's --bg-000 … --fg-400 scale) point at these, and the in-between shades are mixed from them, so you only set the variables above. Outside the theme on purpose: the five tints and status colours (they carry meaning), Halo's dark app frame, paper (e-mail and invoice previews) and media overlays. Halo draws chart bars in soft fills of the series colours (--chart-sky … --chart-blush); the theme builder and twentyfirst-ui theme set them for you when the charts change.

Presets, CLI and builder

Six brand presets keep each kit's neutrals and change the brand colour: blue, violet, emerald, orange, rose and teal.

css
@import "@twentyfirstmedia/pro/styles.css";
@import "@twentyfirstmedia/pro/themes/violet.css";
/* both kits loaded: @twentyfirstmedia/ui-core/themes/violet.css */

The CLI writes a theme into your app and imports it: a preset, a brand colour (light, optionally dark) or a shadcn theme file.

sh
npx @twentyfirstmedia/ui theme violet
npx @twentyfirstmedia/ui theme "#2563eb,#60a5fa"
npx @twentyfirstmedia/ui theme ./shadcn-theme.css

The theme builder shows a theme live on real components and gives you the CSS.

Themes at runtime

For a brand per customer or a colour the user picks, UIProvider takes the same variables. One set applies in both modes; { light, dark } picks by the current theme.

tsx
import { UIProvider, brandTheme } from "@twentyfirstmedia/pro";

<UIProvider tokens={{ primary: tenant.color, ring: tenant.color }}>{children}</UIProvider>;
<UIProvider tokens={brandTheme("#2563eb", "#60a5fa")}>{children}</UIProvider>;

themeCss(theme, { kit }) writes the CSS for a theme (for a server-rendered <style>), parseThemeCss(css) reads a shadcn theme, and brandTheme(color) picks readable text for a brand colour.

Glass

For UI that floats over a map, a 3D scene, a video or photos, surfaces can be glass: translucent, blurred and colour-lifted over what is behind, with light catching the top edge. Halo's glass is soft and milky, Pro's is dark with a hairline edge.

tsx
import { FloatingLayout, MapView, Panel, SearchField, UIProvider } from "@twentyfirstmedia/pro";

// a layout for UI over a background: four slots, glass by default
<FloatingLayout
  height={720}
  background={<MapView background={<YourMap />} points={places} height="100%" />}
  top={<SearchField placeholder="Search places" />}
  start={<Panel title="Layers">…</Panel>}
  end={<Panel title="Harbour Museum">…</Panel>}
/>;

// one surface
<Panel surface="glass" title="Layers">…</Panel>;

// everything floating in a subtree: panels, cards, dock, map legend, menus, popovers, dialogs
<UIProvider scope="subtree" surface="glass">…</UIProvider>;
  • Inside a glass region, top-level Panels, Cards, the Dock and map legends and controls turn into glass, and so do loose controls (search field, chips, icon and secondary buttons, segmented controls). Menus, popovers, dialogs and drawers take a stronger fill because they carry more text. A surface nested in a glass surface stays clear instead of stacking glass on glass; surface="solid" opts one out.
  • What sits on glass follows it: inputs, tab tracks, badges, hover and selection highlights turn translucent too.
  • The fills keep body text at 4.5:1 or more over any backdrop. Reduced transparency and forced colours switch to solid surfaces, more contrast to the stronger fill, and browsers without backdrop blur get the stronger fill too.
  • The glass tokens are themable like every other colour: --glass, --glass-strong, --glass-control, --glass-well, --glass-hover, --glass-border, --glass-highlight, --glass-sheen, --shadow-glass and --glass-filter (the backdrop filter). By default they derive from --card and --foreground, so a theme carries over.
  • Without React: class="tf-glass" on a surface, or data-surface="glass" on any container.

See the map pattern for a full screen.

Glass dashboards

A whole app can be glass: put Ambient behind it and wrap it in <UIProvider surface="glass">. The AppShell turns into glass too (top bar, rail, sidebar; the sheet clears so the background shows through), and tinted tiles (StatTile tint="sky", tinted Cards) lay their tint over the glass instead of an opaque block.

tsx
import { Ambient, AppShell, StatTile, UIProvider } from "@twentyfirstmedia/pro";

<UIProvider surface="glass">
  <Ambient fixed />
  <AppShell brand="Bloom">
    <StatTile value="7h 42m" label="Sleep" tint="sky" />
    …
  </AppShell>
</UIProvider>;

Ambient draws soft colour fields from --ambient-1…4 (pastels in Halo, a faint aurora in Pro) that drift slowly; colors takes your own, src a photo or (with video) a video under a calming scrim, and children your own layer. The drift stops with motion off and pauses off screen. With fixed, keep body transparent so the background shows. On glass, the faintest text steps up one shade so secondary text stays readable. See the wellbeing pattern.

Tailwind

The preset maps the variables to shadcn's utility names, so bg-primary text-primary-foreground, bg-muted text-muted-foreground, border-border, ring-ring and bg-sidebar work, plus the kits' own names (bg-surface, text-ink).

Light, dark and density

Theme and density are attributes: data-theme="light|dark|system" and data-density="compact|comfortable" on <html> or any subtree, or the theme and density props of UIProvider. Halo follows the system theme and is comfortable by default; Pro is dark and compact by default.

tsx
<UIProvider theme="dark" density="compact">…</UIProvider>

Layout and breakpoints

The kits are mobile first: every component works from a 320px wide screen up, and from 1181px on the full desktop layout applies. Phones (up to 760px) get one column: the shell stacks, rail and page nav become scrolling rows, the dock sticks to the bottom, tables scroll inside their box, dialogs open as bottom sheets and popovers stay on screen. Tablets (761–1180px) move asides below the main column. On touch screens text fields use 16px so browsers do not zoom in, and a compact kit (Pro) switches to comfortable hit targets unless data-density is set.

For your own grids, tf-grid takes its columns from custom properties: --cols on desktop, --cols-md on tablets (defaults to --cols) and --cols-sm on phones (defaults to one column).

tsx
<div
  className="tf-grid"
  style={{ "--cols": "repeat(4, minmax(0, 1fr))", "--cols-sm": "1fr 1fr" } as React.CSSProperties}
>
  <StatTile value="18" label="Projects" />
  …
</div>

Control sizes

Every control sits on one height scale: Halo 32 / 40 / 48px (sm / md / lg), Pro 24 / 28 / 34px. Mix sizes only on purpose; a row of controls uses one size.

Colour

TokenHereHaloProUse
--background#f4f3f0 / #0f0f10#f6f6f7 / #09090bTheme: page background behind every panel (shadcn `background`).
--foreground#141414 / #f2f1ed#111114 / #ededf0Theme: default text and icons (shadcn `foreground`).
--card#ffffff / #1a1a1c#ffffff / #0e0e11Theme: cards, panels, inputs and the workspace sheet (shadcn `card`).
--card-foreground{foreground}{foreground}Theme: text on cards.
--popover{card}{card}Theme: menus, popovers, select lists, command palette, hover cards, dialogs.
--popover-foreground{foreground}{foreground}Theme: text on popovers.
--primary#141414 / #f2f1ed#111114 / #ededf0Theme: the brand colour. Primary buttons, checked boxes, switches, selected chips and days, progress, active marks.
--primary-foreground#ffffff / #141414#ffffff / #09090bTheme: text and icons on `primary`.
--primary-hover{primary}color-mix(in oklab, {primary} 70%, #000000) / color-mix(in oklab, {primary} 70%, #ffffff)Theme: `primary` under the pointer.
--secondary#ecebe7 / #232326#f0f0f2 / #16161aTheme: secondary actions. Soft buttons and icon buttons, neutral chips and badges.
--secondary-foreground{foreground}{foreground}Theme: text on `secondary`.
--muted#ecebe7 / #232326#f0f0f2 / #16161aTheme: quiet fills. Tracks of segmented controls, tabs and progress, search fields, code, placeholders.
--muted-foreground#5f5e5a / #a9a8a3#4f4f58 / #a6a6b0Theme: secondary text, captions, metadata.
--accent#ecebe7 / #232326#f0f0f2 / #16161aTheme: highlight under the pointer or keyboard. Menu items, list rows, options, ghost buttons, nav items.
--accent-foreground{foreground}{foreground}Theme: text on `accent`.
--destructive#8a2b23 / #f2a8a0#b0301f / #ef6f61Theme: destructive actions and errors. Danger buttons, error text and icons.
--destructive-foreground{card}{card}Theme: text on solid `destructive`.
--border#e5e4df / #2c2c30#dfdfe3 / #26262cTheme: hairlines and dividers between surfaces.
--input#a8a7a1 / #5a5a60#8a8a94 / #62626dTheme: borders of fields, checkboxes and radios, and strong lines.
--ring#2f6fe0 / #8fb8ff{foreground}Theme: keyboard focus rings.
--chart-1{sky-ink}{sky-ink}Theme: chart series 1 (sky series).
--chart-2{mint-ink}{mint-ink}Theme: chart series 2 (mint series).
--chart-3{butter-ink}{butter-ink}Theme: chart series 3 (butter series).
--chart-4{lilac-ink}{lilac-ink}Theme: chart series 4 (lilac series).
--chart-5{blush-ink}{blush-ink}Theme: chart series 5 (blush series).
--sidebar{card}{card}Theme: Sidebar background.
--sidebar-foreground{foreground}{foreground}Theme: Sidebar text.
--sidebar-primary{primary}{primary}Theme: Sidebar emphasis (current item mark, badges).
--sidebar-primary-foreground{primary-foreground}{primary-foreground}Theme: text on `sidebar-primary`.
--sidebar-accent{accent}{accent}Theme: Sidebar item under the pointer and the current item.
--sidebar-accent-foreground{accent-foreground}{accent-foreground}Theme: text on `sidebar-accent`.
--sidebar-border{border}{border}Theme: Sidebar edges and dividers.
--sidebar-ring{ring}{ring}Theme: focus rings inside the Sidebar.
--canvas{background}{bg-000}Page background behind every panel and card.
--surface{card}{bg-100}Cards, panels, inputs and the main workspace sheet.
--surface-sunken{muted}{bg-200}Search fields, segmented-tab tracks, quiet chips and icon-button fills on `surface`.
--frame#121212 / #050505{bg-100}The dark app frame: top bar, dock and the outer shell around the workspace.
--on-frame#f4f3f0 / #f2f1ed{fg-100}Text and icons on `frame`.
--on-frame-muted#a3a29d{fg-300}Secondary text and idle nav icons on `frame` (7:1+).
--ink{foreground}{fg-100}Headings, numbers and body copy on `surface`, `canvas`, `surface-sunken` and every tint.
--ink-muted{muted-foreground}{fg-200}Descriptions, meta lines and labels on `surface` and `canvas` (4.5:1+ in both themes).
--ink-subtlecolor-mix(in oklab, {muted-foreground} 91%, {background}) / color-mix(in oklab, {muted-foreground} 84%, {background}){fg-300}Placeholders, disabled text and the quietest meta; 4.5:1 on surface and surface-sunken in both themes, still clearly lighter than ink-muted.
--line{border}{line-200}Hairline dividers and table rules; decorative, never the only boundary of a control.
--line-strong{input}{line-300}Dashed placeholder tiles and input borders (3:1 on `surface`).
--sky#cfe3fb / #1e3550{info-bg}Info tint: stat tiles, event cards, the 'scheduled' state. Text on it is `ink`.
--sky-ink#1f4f8a / #9cc6f7{info}Icons and labels on `sky` or as an icon colour on `surface`.
--mint#d6efc3 / #24401a{ok-bg}Success tint: published/live states, positive stat tiles. Text on it is `ink`.
--mint-ink#2f5d16 / #a8db86{ok}Icons and labels on `mint`; success marks on `surface`.
--lilac#e9cdf4 / #3d2547{signal-bg}Highlight tint: the one featured card in a grid, lesson events. Text on it is `ink`.
--lilac-ink#6a2a82 / #dba6ee{signal}Icons and labels on `lilac`.
--butter#fbe7aa / #4a3a10{warn-bg}Money and attention tint: revenue tiles, task events, pending states. Text on it is `ink`.
--butter-ink#6b4d00 / #f3d374{warn}Icons and labels on `butter`.
--blush#f8d6d2 / #4a2320{danger-bg}Danger and support tint: errors, tickets, destructive confirms. Text on it is `ink`.
--blush-ink#8a2b23 / #f2a8a0{danger}Icons and labels on `blush`; destructive text on `surface`.
--focus{ring}{ring}Focus ring: 2px solid outline, 1px offset, on every interactive element.
--count#d6efc3{signal}Count pills on nav items and tabs (unread, open approvals).
--on-count#141414{bg-000}Text on `count`.
--on-frame-success#a8db86{ok}Success marks on frame: the brand dot, ok lines in logs.
--on-frame-danger#f2a8a0{danger}Error lines in logs and other danger marks on frame.
--scrimrgba(10, 10, 10, 0.32)rgba(17,17,20,0.36) / rgba(0,0,0,0.62)Backdrop behind dialogs, sheets and the command palette.
--scrim-strongrgba(10, 10, 10, 0.38){scrim}Backdrop behind modals and the command palette.
--glasscolor-mix(in srgb, {card} 72%, transparent) / color-mix(in srgb, {card} 70%, transparent)color-mix(in srgb, {card} 74%, transparent) / color-mix(in srgb, {card} 72%, transparent)Glass surfaces: panels, cards, docks and map overlays floating over content. Opaque enough that body text stays at 4.5:1 or more over any backdrop.
--glass-strongcolor-mix(in srgb, {card} 86%, transparent)color-mix(in srgb, {card} 88%, transparent)Glass for text-heavy floating layers: menus, popovers, dialogs, drawers.
--glass-controlcolor-mix(in srgb, {card} 62%, transparent) / color-mix(in srgb, {foreground} 10%, transparent)color-mix(in srgb, {card} 55%, transparent) / color-mix(in srgb, {foreground} 7%, transparent)Inputs, secondary buttons and chips on glass (replaces surface inside a glass surface).
--glass-wellcolor-mix(in srgb, {foreground} 6%, transparent)color-mix(in srgb, {foreground} 5%, transparent)Tab tracks, badges, search fields and nested cards on glass (replaces surface-sunken).
--glass-hovercolor-mix(in srgb, {foreground} 8%, transparent) / color-mix(in srgb, {foreground} 12%, transparent)color-mix(in srgb, {foreground} 7%, transparent) / color-mix(in srgb, {foreground} 9%, transparent)Hover and selection highlight on glass (replaces accent).
--glass-borderrgba(255, 255, 255, 0.6) / rgba(255, 255, 255, 0.1)color-mix(in srgb, {foreground} 12%, transparent) / color-mix(in srgb, {foreground} 14%, transparent)The hairline edge of a glass surface.
--glass-highlightrgba(255, 255, 255, 0.95) / rgba(255, 255, 255, 0.18)rgba(255, 255, 255, 0.7) / rgba(255, 255, 255, 0.07)Light catching the top edge of a glass surface.
--glass-sheenrgba(255, 255, 255, 0.5) / rgba(255, 255, 255, 0.05)transparentPro glass is flat: no sheen.
--ambient-base{background}{background}Ground of the Ambient background, under the colour fields.
--ambient-1color-mix(in oklab, {sky} 72%, {sky-ink}) / {sky}color-mix(in oklab, {chart-1} 22%, {background}) / color-mix(in oklab, {chart-1} 30%, {background})First colour field of the Ambient gradient (a faint aurora in Pro).
--ambient-2color-mix(in oklab, {mint} 72%, {mint-ink}) / {mint}color-mix(in oklab, {chart-4} 18%, {background}) / color-mix(in oklab, {chart-4} 26%, {background})Second colour field of the Ambient gradient.
--ambient-3color-mix(in oklab, {lilac} 72%, {lilac-ink}) / {lilac}color-mix(in oklab, {chart-2} 16%, {background}) / color-mix(in oklab, {chart-2} 22%, {background})Third colour field of the Ambient gradient.
--ambient-4color-mix(in oklab, {butter} 72%, {butter-ink}) / {butter}color-mix(in oklab, {foreground} 6%, {background}) / color-mix(in oklab, {foreground} 8%, {background})Fourth colour field of the Ambient gradient.
--ambient-scrimrgba(255, 255, 255, 0.28) / rgba(10, 10, 10, 0.45)rgba(246, 246, 247, 0.4) / rgba(9, 9, 11, 0.55)Softens a photo or video under glass so the page keeps its calm.
--bg-000{canvas}{background}Canvas: app background, behind hairline grids, inputs on surfaces.
--bg-100{surface}{card}Surface: every grid cell, panel, table, sidebar, dialog.
--bg-200{surface-sunken}{muted}Raised: row and button hover, kbd keys, skeletons, code blocks.
--bg-300{line}color-mix(in oklab, {foreground} 3%, {muted}) / color-mix(in oklab, {foreground} 5%, {muted})Selected: active nav item, selected row, pressed segment, open menu trigger.
--fg-100{ink}{foreground}Primary text and icons on every surface (bg-000 to bg-300).
--fg-200{ink-muted}{muted-foreground}Secondary text: descriptions, table cells that are not the row's key, inactive nav items. On bg-000 to bg-300.
--fg-300{ink-muted}color-mix(in oklab, {muted-foreground} 92%, {background}) / color-mix(in oklab, {muted-foreground} 90%, {background})Muted text: meta, timestamps, column headers, eyebrows. On bg-000 to bg-300.
--fg-400{ink-subtle}color-mix(in oklab, {muted-foreground} 47%, {background}) / color-mix(in oklab, {muted-foreground} 57%, {background})Disabled text and placeholders only. Below 4.5:1 by design; never for information.
--line-100{line}color-mix(in oklab, {border} 59%, {card}) / color-mix(in oklab, {border} 52%, {card})Faint hairline: row separators inside tables and lists, background grid.
--line-200{line}{border}Hairline: the structural 1px grid between cells, panel and overlay borders, table header rule. Decorative, not a control boundary.
--line-300{line-strong}{input}Control border (3:1 on bg-000, bg-100, bg-200): inputs, checkboxes, secondary buttons, crosshair marks.
--chart-grid{line}{line-100}Chart gridlines.
--chart-sky{sky} / {sky-ink}{fg-100}Chart series tinted sky: a step of the monochrome ramp (charts carry no decorative colour).
--chart-mint{mint} / {mint-ink}color-mix(in oklab, {foreground} 38%, {card}) / color-mix(in oklab, {foreground} 48%, {card})Chart series tinted mint: a step of the monochrome ramp (charts carry no decorative colour).
--chart-lilac{lilac} / {lilac-ink}{fg-300}Chart series tinted lilac: a step of the monochrome ramp (charts carry no decorative colour).
--chart-butter{butter} / {butter-ink}{fg-200}Chart series tinted butter: a step of the monochrome ramp (charts carry no decorative colour).
--chart-blush{blush} / {blush-ink}{line-300}Chart series tinted blush: a step of the monochrome ramp (charts carry no decorative colour).
--ramp-1color-mix(in oklab, {chart-1} 12%, {background})color-mix(in oklab, {chart-1} 12%, {background})Map and heatmap ramp, step 1 of 5 (lightest): calm sequential colours for values such as price per m² or density, mixed from `chart-1` and the background.
--ramp-2color-mix(in oklab, {chart-1} 28%, {background})color-mix(in oklab, {chart-1} 28%, {background})Map and heatmap ramp, step 2 of 5 (middle): calm sequential colours for values such as price per m² or density, mixed from `chart-1` and the background.
--ramp-3color-mix(in oklab, {chart-1} 46%, {background})color-mix(in oklab, {chart-1} 46%, {background})Map and heatmap ramp, step 3 of 5 (middle): calm sequential colours for values such as price per m² or density, mixed from `chart-1` and the background.
--ramp-4color-mix(in oklab, {chart-1} 66%, {background})color-mix(in oklab, {chart-1} 66%, {background})Map and heatmap ramp, step 4 of 5 (middle): calm sequential colours for values such as price per m² or density, mixed from `chart-1` and the background.
--ramp-5color-mix(in oklab, {chart-1} 88%, {background})color-mix(in oklab, {chart-1} 88%, {background})Map and heatmap ramp, step 5 of 5 (strongest): calm sequential colours for values such as price per m² or density, mixed from `chart-1` and the background.
--ok#2f5d16 / #a8db86#177447 / #5cc28fSuccess / done / healthy: text and status shapes (circle). On bg-000 to bg-300.
--ok-bg#d6efc3 / #24401argba(23,116,71,0.09) / rgba(92,194,143,0.12)Tint behind ok badges, banners and diff additions.
--info#1f4f8a / #9cc6f7#2856b0 / #7aa7f5Running / in progress / neutral notices. Shape: pulsing dot.
--info-bg#cfe3fb / #1e3550rgba(40,86,176,0.08) / rgba(122,167,245,0.12)Tint behind info badges and banners.
--warn#6b4d00 / #f3d374#765600 / #d9b44aWarning: budget at 80 %, expiring domains, degraded checks. Shape: triangle.
--warn-bg#fbe7aa / #4a3a10rgba(118,86,0,0.09) / rgba(217,180,74,0.12)Tint behind warning badges and banners.
--danger{destructive}{destructive}Error, failed run, destructive actions, budget stop. Shape: square.
--danger-bg#f8d6d2 / #4a2320rgba(176,48,31,0.08) / rgba(239,111,97,0.12)Tint behind error badges, banners, diff removals.
--signal#6a2a82 / #dba6ee#9a4807 / #f2994aNeeds a human: approval waiting, beacons, the approval count. Shape: diamond. The only hue that may draw the eye unprompted.
--signal-bg#e9cdf4 / #3d2547rgba(154,72,7,0.09) / rgba(242,153,74,0.14)Tint behind approval cards' header and signal badges.
--idle#5f5e5a / #a9a8a3{fg-300}Status idle: ready, inactive (ring). Alias of `ink-muted`.
--diff-add#2f5d16 / #a8db86{ok}Added lines in override diffs.
--diff-add-bg#d6efc3 / #24401a{ok-bg}Fill of added lines. Alias of `mint`.
--diff-del#8a2b23 / #f2a8a0{danger}Removed lines in override diffs.
--diff-del-bg#f8d6d2 / #4a2320{danger-bg}Fill of removed lines. Alias of `blush`.
--paper#ffffff#ffffffDocument previews (invoices, PDFs): paper is white in both themes.
--paper-ink#141414#111114Text on paper.
--frame-hoverrgba(255, 255, 255, 0.08){bg-200}Hover fill on the dark frame (top bar, dock, code blocks). Same in both themes.
--frame-activergba(255, 255, 255, 0.12){bg-300}Active or selected fill on the dark frame. Same in both themes.
--frame-strongrgba(255, 255, 255, 0.18){bg-300}Strong fill or divider on the dark frame. Same in both themes.
--on-frame-strong#ffffff{fg-100}Highlighted text on the frame (log highlights). Same in both themes.
--on-frame-warn#f3d374{warn}Warnings and numbers in logs and code on the frame. Same in both themes.
--on-frame-signal#d4b8f0{signal}Keywords in code on the frame. Same in both themes.
--ink-hoverrgba(0, 0, 0, 0.06){bg-200}Hover fill on tinted surfaces such as alerts. Same in both themes.
--hairlinergba(0, 0, 0, 0.12){line-200}Thin outline around swatches and colour wells. Same in both themes.
--media-scrimrgba(10, 10, 10, 0.6)rgba(10, 10, 10, 0.6)Labels and counters on top of photos. Same in both themes.
--media-ink#ffffff#ffffffText and lines on top of photos. Same in both themes.
--media-shadowrgba(0, 0, 0, 0.3)rgba(0, 0, 0, 0.3)Shadow behind lines drawn on photos. Same in both themes.
--lightboxrgba(8, 8, 8, 0.92)rgba(8, 8, 8, 0.92)Full-screen image viewer background. Same in both themes.
--lightbox-inkrgba(255, 255, 255, 0.8)rgba(255, 255, 255, 0.8)Captions in the image viewer. Same in both themes.
--paper-muted#706f6a#706f6aSecondary text on paper (4.5:1 on white).
--paper-subtle#6b6a65#6b6a65Small print on `paper`. Same in both themes.
--serp-ink#202124#202124Search ad preview: text. Same in both themes.
--serp-muted#4d5156#4d5156Search ad preview: description and URL. Same in both themes.
--serp-link#1a0dab#1a0dabSearch ad preview: headline link. Same in both themes.
--serp-chip#f1f3f4#f1f3f4Search ad preview: favicon chip. Same in both themes.

Shadow

TokenHaloProUse
--shadow-card0 1px 2px rgba(20,20,20,0.04), 0 6px 20px rgba(20,20,20,0.05) / 0 1px 2px rgba(0,0,0,0.4)noneResting cards. Pro: none, hairlines divide.
--shadow-lift0 2px 4px rgba(20,20,20,0.06), 0 14px 32px rgba(20,20,20,0.10) / 0 2px 6px rgba(0,0,0,0.5), 0 14px 32px rgba(0,0,0,0.45)noneHovered card. Pro: none.
--shadow-float0 16px 40px rgba(0,0,0,0.22) / 0 16px 40px rgba(0,0,0,0.6)0 16px 40px rgba(17,17,20,0.12) / 0 16px 40px rgba(0,0,0,0.55)Floating layers: dock, toasts, menus and dialogs.
--shadow-overlay{shadow-float}0 16px 40px rgba(17,17,20,0.12) / 0 16px 40px rgba(0,0,0,0.55)Only floating layers: menus, popovers, dialogs, toasts, command palette. Always paired with a line-200 border.
--shadow-glass0 1px 2px rgba(20, 20, 40, 0.06), 0 12px 40px rgba(20, 20, 40, 0.14) / 0 1px 2px rgba(0, 0, 0, 0.3), 0 16px 48px rgba(0, 0, 0, 0.45)0 8px 24px rgba(17, 17, 20, 0.1) / 0 12px 32px rgba(0, 0, 0, 0.5)Shadow under glass surfaces.

Component knobs

TokenHaloProUse
--button-radius{radius-pill}{radius-1}Corners of buttons (Button, IconButton). One knob for a theme's button shape.
--field-radius{radius-pill}{radius-1}Corners of inputs and selects.
--badge-radius{radius-pill}{radius-1}Corners of badges and chips.
--panel-bg{surface}{bg-100}Background of a panel: a cell of the hairline grid, a section, a side column.
--panel-bordertransparent{line-200}Border colour of a panel standing on its own; transparent where panels float.
--panel-radius{radius-lg}{radius-0}Corners of a panel. Pro: square cells of the hairline grid.
--panel-shadow{shadow-card}noneResting shadow of a panel. Pro: none, the hairlines divide.
--panel-pad{space-6}{space-4}Inner padding of a panel.
--panel-gap{space-4}1pxGap between panels in a grid: the hairline in Pro, air between cards in Halo.
--glass-filterblur(24px) saturate(180%) brightness(1.05) / blur(24px) saturate(160%) brightness(0.85)blur(16px) saturate(130%) brightness(1.04) / blur(16px) saturate(120%) brightness(0.8)backdrop-filter of glass surfaces: blur, colour lift and a nudge towards the theme's side.
--glass-tint62%40%How strongly a tint (StatTile, tinted Card) colours the glass it sits on.

Spacing

TokenHaloProUse
--space-0-52px2pxIcon-to-badge nudges, hairline offsets.
--space-14px4pxGap inside badges and kbd groups.
--space-28px8pxGap between icon and label, between buttons in a group.
--space-312px12pxCompact horizontal cell and control padding.
--space-416px16pxGrid cell padding, comfortable control padding, page gutter on mobile.
--space-520px20pxDialog body padding.
--space-624px24pxCell padding on overview grids; background-grid pitch.
--space-832px32pxGap between stacked sections inside a panel.
--space-1040px40pxOuter margin of the app frame on large screens.
--space-1248px48pxEmpty-state padding.
--space-1664px64pxLogin and onboarding vertical rhythm.

Radius

TokenHaloProUse
--radius16px2pxTheme: the base corner radius; Pro is square at 2px, larger values round cards and menus too (shadcn `radius`).
--radius-smcalc(var(--radius) * 0.625){radius-1}Inputs inside cells, row hovers, small tiles.
--radius-mdvar(--radius)calc((var(--radius) - 2px) * 1)Stat cells, event cards, menus: square in Pro.
--radius-lgcalc(var(--radius) * 1.375)calc((var(--radius) - 2px) * 1.375)Cards and panels: square cells of the hairline grid.
--radius-xlcalc(var(--radius) * 1.875)calc((var(--radius) - 2px) * 1.875)The workspace sheet and app frame: square in Pro.
--radius-pill999px{radius-1}Buttons, chips, badges, tabs and inputs: 2px in Pro.
--radius-00px0pxDefault for everything structural: cells, panels, tables, dialogs, sheets, cards.
--radius-1{radius-sm}var(--radius)Controls: buttons, inputs, badges, kbd, switches, tooltips.
--radius-full9999px9999pxOnly status dots and radio buttons.

Motion

TokenHaloProUse
--dur-fast120ms{duration-fast}Press, hover colour changes, chip toggles.
--dur-base220ms{duration-base}Tab indicator slide, menu open, card lift.
--dur-enter520ms{duration-base}Entrances: short and quiet in Pro.
--dur-slow900ms{duration-base}Count-ups, chart draw-ins and progress fills: short in Pro.
--stagger-step55ms0msNo staggered entrances in Pro.
--dur-exit160ms{duration-fast}Overlays leaving: menus, popovers, dialogs, drawers, toasts.
--motion-time11Multiplier for choreography delays (when the dock, side panels and chart data come in).
--motion-travel11Multiplier for how far things travel when they come in.
--motion-blur0px0pxBlur things come out of as they rise (expressive Halo only).
--ease-outcubic-bezier(0.22, 1, 0.36, 1){ease-standard}Entrances.
--ease-springcubic-bezier(0.34, 1.56, 0.64, 1){ease-standard}No overshoot in Pro: the standard curve.
--ease-in-outcubic-bezier(0.65, 0, 0.35, 1){ease-standard}Things moving between two resting places.
--duration-fast{dur-fast}90msHover and press feedback.
--duration-base{dur-base}140msMenus, popovers, sheets, toasts entering.
--ease-standard{ease-out}cubic-bezier(0.2, 0, 0, 1)The only easing curve.

Sizes

TokenHaloProUse
--control-sm32px24pxSmall buttons in dense toolbars.
--control-md40px{control-dense}Default control height (follows density in components).
--control-lg48px{control-comfy}Large controls.
--row-dense40px28pxRow height for tables, lists and nav items in compact density.
--row-comfy52px36pxRow height in comfortable density.
--control-dense{control-sm}28pxButton and input height in compact density.
--control-comfy{control-md}34pxButton and input height in comfortable density.
--topbar-h60px44pxTop bar height.
--sidebar-w44px232pxSidebar width, expanded.
--sidebar-w-collapsed44px52pxSidebar width, icon-only.
--subnav-w208px{sidebar-w}SubNav width.
--side-panel-w304px320pxSide panel width.
--icon-sm14px14pxIcons inside badges and dense table cells.
--icon-md16px16pxDefault Lucide icon size.
--icon-lg20px16pxIcons in navigation: same as icon-md in Pro.
--line-width1px1pxEvery border and grid line (Pro calls it hairline).