Theming
The kit's default identity, Sodium Horizon, in dark and light from one attribute; a future product changes only the brand tokens.
Night and day
The kit's defaults are Arcadia's identity, Sodium Horizon: slate neutrals with a blue-hour cast, white ink, dove prose and one beacon.
| Name | Night | Day | Use |
|---|---|---|---|
| Ink | White labels and headings by night, the night page by day. | ||
| Prose | Dove: long reading a breath softer than labels. | ||
| Page | One slate page; the sidebar is the bezel a step below. | ||
| Card | Code windows and stream cards, a step off the page. | ||
| Raised | Charcoal by night, white by day: what you touch. | ||
| Umbra | The darkest structural step of the same slate: every divider. | ||
| Beacon | The one light, for lights only. | ||
| Plan | Breeze, a quiet distance blue. |
The thesis and the six rules that follow from it are on the Overview.
Day is calibrated on its own rather than inverted. Each value below is read live from a night and a day subtree.
Switching themes
One attribute, <html data-theme="dark|light">. There is no prefers-color-scheme block in the CSS: the boot script resolves the preference before first paint.
data-theme="dark"data-theme="light"Any element may carry its own data-theme, and its ladders recompute from its own --ink. These two panels are subtrees of this page: open their menus and hover the gear to see overlays keep the panel's theme.
Boot and preference
The boot script and the hooks read the same two storage keys, so the first paint and the running app always agree.
Four rungs, from the least reach to the most. Take the lowest one that does the job.
Brand tokens
A product that needs its own light overrides brand tokens only, in @layer brand, which comes after the kit's tokens and so wins without specificity.
| Token | Drives |
|---|---|
--hue-accent: the caret, the needs-you and finished-unseen dots, the selection, accent washes and the live signal (--horizon-*). | |
--text-accent: accent Badge and Chip text only; links stay ink. Keep it at 4.5:1 or more on every surface. | |
The ArcadiaMark colour (.ui-brand-mark). |
Use the kit's own selectors so subtree themes keep working, and declare both themes: a rule on :root alone would also win inside [data-theme="light"].
Component tokens
Each component documents its tokens (--button-bg, --ib-size, --row-fill). Product code may set them from a container, in @layer features.
Layout through class hooks
Every part carries a ui-* class.
Product CSS in @layer features beats the kit's ds layer at any specificity, so laying parts out needs no !important: margin, grid and flex, never colour or anatomy.
A new variant
When the same override comes up twice, propose a variant to the kit instead. A variant only reassigns component tokens, so every product gets it, in both themes.
Not supported
- Your own font. The type ramp, tracking and weights are measured for SF Pro and Geist Mono.
- Different greys. Every neutral is one ink at a rank alpha; new greys break the contrast ladder.
- A different radius scale. Corners nest by the scale, and continuous corners depend on its steps.
- Restyling a component's anatomy: its parts, spacing or states. Propose a variant instead.
Portals
Overlays portal to document.body.
Mount TooltipProvider, ConfirmHost and Toaster once, near the app root.
A subtree with its own theme wraps itself in PortalProvider, so its menus, tooltips, dialogs and toasts keep that theme; an overlay's own container prop still wins.
PortalProvider
containerrequired- PortalContainer | undefined
- The element every overlay inside portals into (unless it passes its own
container).