Design tokens & theming

View Source

Every visual value in Aurora UI resolves to a CSS custom property under the --aui-* namespace, defined in ../assets/css/aurora_ui.css. The set of --aui-* tokens is the public theming API (see ADR-0003 and compatibility.md) — you theme Aurora UI by redefining these properties in your own CSS. No recompile, no forked source, no PHXTemplates branding leaking into your app.

Never edit aurora_ui.css. Override in a layer you control.

How color tokens are stored

Color tokens are space-separated RGB channel triples, not rgb(...) or hex. They are consumed as rgb(var(--aui-x) / <alpha>), so opacity composes cleanly:

/* a token value looks like this */
--aui-action: 37 99 235;

/* and is used like this */
background: rgb(var(--aui-action));
border-color: rgb(var(--aui-action) / 0.5);   /* 50% alpha */

When you override a color token, supply the same triple format:

:root {
  --aui-action: 124 58 237;   /* your brand purple, as R G B */
}

Non-color tokens (radii, spacing, type, motion, layering) are ordinary CSS values.

Light & dark

Light is the default token set; dark is an independent, hand-authored set — not an algorithmic inversion — and both are contrast-validated (see test/theme_contrast). Theme resolution:

  • :root / [data-aui-theme="light"] → light tokens.
  • [data-aui-theme="dark"] → dark tokens.
  • @media (prefers-color-scheme: dark) on :root:not([data-aui-theme]) → dark tokens when the consumer has not pinned a theme, respecting the OS preference automatically.

To pin a theme, set data-aui-theme="light" or "dark" on <html> (or any scope). To follow the OS, set nothing. color-scheme is set alongside so native form controls and scrollbars match.

Only color tokens (and shadows) change between themes. Radii, spacing, type, sizing, layering, and motion are theme-independent.

Token reference

Canvas & surfaces (elevation ladder)

TokenLightDarkPurpose
--aui-canvas250 250 2525 9 20Page background
--aui-surface255 255 25511 19 34Default surface
--aui-surface-raised255 255 25517 26 43Raised surface (cards, menus)
--aui-surface-sunken244 244 2488 14 26Recessed surface (code blocks, wells)
--aui-surface-overlay255 255 25517 26 43Overlay/dialog surface

Text

TokenLightDarkPurpose
--aui-text17 20 28226 232 240Primary text
--aui-text-muted90 96 110148 160 178Secondary text
--aui-text-subtle128 134 148108 120 138Tertiary/subtle text
--aui-text-on-action255 255 2555 9 20Text on an action-colored fill

Borders & focus ring

TokenLightDarkPurpose
--aui-border224 226 23234 46 68Default border/separator
--aui-border-strong200 203 21251 66 92Emphasized border
--aui-ring56 132 24856 189 248Focus-visible ring color

Action / primary

TokenLightDarkPurpose
--aui-action37 99 23556 189 248Primary action fill
--aui-action-hover29 78 216125 211 252Hover state
--aui-action-active30 64 17514 165 233Active/pressed state
--aui-action-subtle219 234 25412 74 110Subtle action tint (backgrounds)

Status

Each severity has a base and a -subtle tint. Meaning is never carried by color alone — components also use text and/or icons.

TokenLightDark
--aui-success / --aui-success-subtle21 128 61 / 220 252 23174 222 128 / 5 46 22
--aui-warning / --aui-warning-subtle180 83 9 / 254 243 199251 191 36 / 66 32 6
--aui-danger / --aui-danger-subtle190 40 44 / 254 226 226248 113 113 / 69 10 10
--aui-info / --aui-info-subtle3 105 161 / 224 242 25456 189 248 / 8 47 73

Data / chart ramp

Categorical, contrast-checked against the canvas. Used by stat, badges, and any chart layer you build.

TokenLightDark
--aui-data-137 99 23556 189 248
--aui-data-25 150 10552 211 153
--aui-data-3217 119 6251 191 36
--aui-data-4190 40 44248 113 113
--aui-data-5124 58 237167 139 250
--aui-data-68 145 17834 211 238

Radii

TokenValue
--aui-radius-xs0.25rem
--aui-radius-sm0.375rem
--aui-radius-md0.5rem
--aui-radius-lg0.75rem
--aui-radius-xl1rem
--aui-radius-full9999px

Spacing (4px base)

--aui-space-1--aui-space-12: 0.25, 0.5, 0.75, 1, 1.25, 1.5, 2 (-8), 2.5 (-10), 3rem (-12). (The scale is non-linear at the top — 16 are contiguous, then 8, 10, 12.)

Type

TokenValue
--aui-font-sansui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif
--aui-font-monoui-monospace, "JetBrains Mono", "SFMono-Regular", Menlo, Consolas, monospace
--aui-text-xs--aui-text-2xl0.75, 0.875, 1 (base), 1.125, 1.375, 1.75rem
--aui-leading-tight / --aui-leading-normal1.2 / 1.5

Sizing

TokenValuePurpose
--aui-control-sm2remSmall control height
--aui-control-md2.5remDefault control height
--aui-control-lg3remLarge control height
--aui-touch-min44pxMinimum touch target
--aui-measure68chReadable line length (prose)

Layering (z-index ladder)

A single documented ladder for overlays so stacking never becomes guesswork.

TokenValue
--aui-z-base0
--aui-z-sticky100
--aui-z-dropdown1000
--aui-z-overlay1100
--aui-z-modal1200
--aui-z-toast1300
--aui-z-tooltip1400

Motion

See motion.md for how these are applied and the reduced-motion contract.

TokenValuePurpose
--aui-dur-1120msMicro: hover, press
--aui-dur-2180msSmall enter/exit
--aui-dur-3240msOverlays
--aui-dur-4360msEmphasis / spatial
--aui-ease-standardcubic-bezier(0.2, 0, 0, 1)Default easing
--aui-ease-emphasizedcubic-bezier(0.2, 0, 0, 1.2)Emphasized (slight overshoot)
--aui-ease-exitcubic-bezier(0.4, 0, 1, 1)Exit/accelerate
--aui-distance-sm / -md / -lg4px / 8px / 16pxTravel distances
--aui-stagger40msPer-item stagger delay

Elevation (shadows)

Theme-dependent. Zeroed out under @media (forced-colors: active) so the OS owns color and structure stays legible.

TokenLightDark
--aui-shadow-sm0 1px 2px 0 rgb(17 20 28 / 0.06)0 1px 2px 0 rgb(0 0 0 / 0.4)
--aui-shadow-md0 4px 12px -2px …, 0 2px 4px -2px …(darker equivalents)
--aui-shadow-lg0 16px 40px -12px rgb(17 20 28 / 0.22)0 16px 50px -12px rgb(0 0 0 / 0.7)
--aui-shadow-ring0 0 0 1px rgb(17 20 28 / 0.05)0 0 0 1px rgb(255 255 255 / 0.04)

Overriding tokens — a worked brand theme

Because tokens live on :root, you override them in your own stylesheet, which sits in a layer that beats aui.components. Set both themes:

/* assets/css/app.css — imported AFTER aurora_ui.css */

:root,
[data-aui-theme="light"] {
  /* Brand accent (violet) */
  --aui-action: 124 58 237;
  --aui-action-hover: 109 40 217;
  --aui-action-active: 91 33 182;
  --aui-action-subtle: 237 233 254;
  --aui-ring: 139 92 246;

  /* Rounder corners + a serif display face */
  --aui-radius-md: 0.75rem;
  --aui-radius-lg: 1rem;
  --aui-font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
}

[data-aui-theme="dark"] {
  --aui-action: 167 139 250;
  --aui-action-hover: 196 181 253;
  --aui-action-active: 139 92 246;
  --aui-action-subtle: 46 16 101;
  --aui-ring: 167 139 250;
  --aui-text-on-action: 20 10 40;
}

You can also scope a theme to a subtree by putting the overrides (or a data-aui-theme) on any element rather than :root — useful for a dark hero band on an otherwise light page.

Tailwind preset (optional)

Per ADR-0003, components never depend on Tailwind utilities being generated — the shipped CSS is static, so Tailwind's content purge cannot strip Aurora styles. Tailwind is purely optional.

If you do use Tailwind and want to reference Aurora tokens from utility classes (e.g. bg-[rgb(var(--aui-surface))] or a mapped theme key), map the --aui-* properties into your Tailwind theme. When you write utility-based recipes against these tokens, add Aurora's compiled modules to your content globs so class scanning sees them:

// tailwind.config.js
content: [
  "./js/**/*.js",
  "./lib/**/*.{ex,heex}",
  "../deps/aurora_ui/lib/**/*.ex"   // only needed if you use Aurora's utility recipes
]

Because color tokens are RGB triples, map them through rgb(var(...) / <alpha-value>) so Tailwind opacity modifiers keep working:

theme: {
  extend: {
    colors: {
      "aui-action": "rgb(var(--aui-action) / <alpha-value>)",
      "aui-surface": "rgb(var(--aui-surface) / <alpha-value>)"
    },
    borderRadius: { "aui-md": "var(--aui-radius-md)" }
  }
}