Skip to content
ArcadiaDesign

Working

Components

Media

Install

Depend on it inside the Arcadia workspace.

package.json

Set up the root

Boot the theme before the first paint, load the kit's CSS before your own, and mount the three hosts once.

server.ts
app.tsx

Things that silently break

No ConfirmHost
confirm() rejects with an error instead of asking, so the action behind it never runs.
No Toaster
toast() returns an id and nothing appears.
No boot script
The theme is applied after the first paint, so anyone on a light theme sees a dark flash on every load.
CSS outside a layer
Unlayered rules beat every layer, the kit's overrides included, so they undo the reduced-motion contract and the legibility cut. Put product CSS in @layer features.

Slate and two inks
One neutral page, a step for what floats or frames code and one for what you touch. Every neutral is one slate hue with a blue-hour cast. Labels and headings are white; long reading is dove.
The beacon
One light, spent only on things that are literally lights: the horizon, unread and needs-you dots and the caret. It never fills a control, colours a link or marks a selection.
Pills and one highlight
Everything you press is a pill at weight 500, and nothing shrinks. In a list one highlight glides from row to row instead of each row lighting its own.
The pine sweep
The running glyph is the Arcadia pine, lit one stroke at a time: left arm, pine, right arm, pine. It stays neutral.
One clock
Every live loop divides a 3.2s watch, so a running agent's light, its glyph and its working text restart together.
Calm motion
Hovers change colour only. Popups slide a few pixels and never zoom; dialogs rise on a critically damped spring. Scrolling stays native, and a jump the app makes lands on a spring.
Continuous corners
Large surfaces take superellipse corners, a quarter larger so they read at the same size as a round corner.
The serifed I
SF Pro's cv05 gives the capital I a serif, so Il1 and ILineItem never collide in identifier-heavy prose.

Layers
Every CSS file wraps its rules in exactly one layer, in the order reset, tokens, brand, base, ds, features, overrides. Kit components live in ds and product code in features, which wins at any specificity, so there is no !important and no specificity game.
Tokens
Three tiers, every value written once in tokens.ts. Per theme: one --ink, the rank alphas, five opaque surfaces, hues, shadows and the only literal colours. Derived ladders and scales come next (--text-1 to --text-4, --fill-*, --space-*), then component tokens on component roots (--button-bg).
Naming
Files are kebab-case, TSX and CSS in pairs. Components are PascalCase with flat compound exports (Menu, MenuItem). Kit classes are ui-component-part, the public slot hook, and booleans are present-or-absent attributes (data-open).
Product code
Imports only from @arcadia/ui, lays primitives out and sets their documented component tokens. Semantic and scale tokens only. When a need recurs, add a variant to the kit; one anatomy per concept, so every row is a ListRow and every disclosure a Collapsible.
Focus
One treatment: keyboard focus draws a 2px ring of neutral ink, never the accent. Components choose only its offset, and text fields show focus through their border instead.
Hierarchy
Rank and medium weight. Placeholders and key hints use --text-3; --text-4 is only for disabled labels and decoration.

Variant vocabulary

The same words mean the same thing on every component, and a variant only reassigns component tokens.

PropValuesDefault
variantprimarysecondaryoutlineghosttextdangerinverseghost
sizexssmmdlgmd (IconButton sm)
shaperoundedpillrounded
toneneutralmutedaccentsuccesswarningdangerplanaskdebugneutral
renderAn element or a function: swap the element, keep the behaviour.The part's own

Two exceptions: a Shimmer's tone is a text rank (secondary or tertiary), and a toast's tone is an outcome (info, success, warning or error).