Skip to content
Home Theme Gallery

Colours and tokens

Every colour Home-UI uses is a semantic token: --hui-primary, --hui-border, --hui-muted-foreground. No component hard-codes a colour, so changing one token re-themes everything that reads it — in light, in dark, and across every shadow root, because custom properties inherit through a shadow boundary. The token names follow shadcn/ui, namespaced with hui.

This is the only supported theming API. Internal selectors inside a component’s shadow root are not public and may change without notice.

The semantic colours

Each token pairs a surface with the foreground that sits on it. Set the pair together, or a host can end up with unreadable combinations.

TokenLightRole
--hui-backgroundoklch(1 0 0)The page surface, when .hui-surface opts in
--hui-foregroundoklch(0.145 0.008 326)Body text on the background
--hui-card / --hui-card-foregroundwhite / near-blackRaised surfaces and their text
--hui-popover / --hui-popover-foregroundwhite / near-blackOverlay panels and their text
--hui-primary / --hui-primary-foregroundpurple / near-whiteDefault action fill and its label
--hui-primary-textoklch(0.496 0.265 301.924)Primary-coloured text, such as the link button
--hui-secondary / --hui-secondary-foregroundgrey / near-blackSecondary action fill
--hui-muted / --hui-muted-foregroundgrey / mid-greyQuiet fills and dimmed text
--hui-accent / --hui-accent-foregroundgrey / near-blackHover and highlight
--hui-destructiveoklch(0.56 0.245 27.325)Destructive borders, text and error icon
--hui-destructive-subtle / -foregroundpale red / redThe destructive tint and its label
--hui-borderoklch(0.922 0.005 325.62)Decorative hairlines
--hui-inputoklch(0.62 0.019 323.02)Control boundaries (needs 3:1)
--hui-ringoklch(0.62 0.019 323.02)The keyboard focus ring

The full declaration, including the shape, spacing, type, shadow and motion tokens, is in tokens.css, which you can read from the package. This page covers only colour; the other groups have their own pages.

The palette is the shadcn nova style at preset b5ZcnBEttq (base colour mauve, theme purple), regenerated by command rather than transcribed:

Terminal window
pnpm dlx shadcn@latest preset decode b5ZcnBEttq

A few values deliberately differ from that preset, each because the preset’s own value misses a WCAG threshold. The visual-style contract UI-CONTRACT-006 records each with its measurement. The notable ones:

  • --hui-destructive is darkened in light mode, because nova’s value measures 3.97:1 against its own tint.
  • --hui-destructive-subtle is an opaque token rather than an alpha fill, because an alpha fill’s ratio depends on what is behind it.
  • --hui-primary-text is a separate token, because nova uses one value for a button fill and for link text, and its dark value measures 2.23:1 as text.

A host overrides a token on :root, or on any ancestor of the components it wants to affect:

:root {
--hui-primary: oklch(0.55 0.2 250);
--hui-radius: 0.25rem;
}

Every component reads the token with an inline fallback — color: var(--hui-foreground, CanvasText) — so a component rendered with no token sheet at all stays legible and operable. It will not be pretty, but it will work.

scripts/measure-contrast.mjs computes every required pair from the shipped sheet and exits non-zero on a miss. The measurements are tabulated in UI-CONTRACT-002. The short version: every text pair clears AA (4.5:1) in both themes, and --hui-input and --hui-ring clear the 3:1 WCAG 1.4.11 asks of a control boundary.

Two pairs are recorded rather than enforced: --hui-border is a decorative hairline, and --hui-primary is a fill, not text. Primary-coloured text is --hui-primary-text.