Colors

Neutral ink for almost everything, and one accent for the few things that should stand out.

50100200300400500600700800900950neutralorangebluegreenyellowvioletpinkred
Fig. 1 The palette: fixed OKLCH ramps from 50 to 950. Every role picks its shade from here.

Warm neutrals and a few clear colors. Lower steps are lighter. The ramps stay the same in both themes; what changes is which step each role picks.

Ink and accent

Most of an interface is text, surfaces, and controls, and all of that is neutral. The main action is the darkest thing on the page, so it needs no color to be found.

Invite teammates

Theyโ€™ll join as editors. About roles

Fig. 2 One screen, one accent. Only the checked box and the link use it; the main action is ink.

The accent, primary, is for state and direction: a checked box, a link, keyboard focus, and the rare brand action such as upgrading. Because it appears so seldom, it always means something.

Do

Ink carries the action. The accent marks only whatโ€™s on and where to go.

Donโ€™t

When every button is the accent, none of them leads, and the link gets lost.

Fig. 3 The same form, colored two ways.

The roles

Components never reach for a shade directly. They use roles, and each theme decides which shade fills each role. Switch the theme in the header and every swatch below changes with it.

Surfaces

backgroundThe canvas behind your interface
surfaceCards and panels on the canvas
mutedQuiet surfaces
popoverMenus, popovers, and dialogs
sidebarThe navigation sidebar

Text

foregroundHeadings, primary text, and the main action
foreground-bodyLong-form reading text
muted-foregroundSupporting text and resting icons

Controls

controlResting fill for fields, secondary buttons, and badges
control-hoverHover fill for controls and menu rows
control-activeThe selected segment
control-borderEdges of checkboxes and toggles
control-trackSwitch and slider tracks

Accent

primaryBrand actions, checked controls, and links
primary-hoverHovered brand actions
ringVisible keyboard focus
accentSelective expressive surfaces
accent-foregroundText and icons on accent

Status

successPositive feedback and status
success-softSuccess backgrounds
destructiveErrors and destructive actions
destructive-softError backgrounds

Lines and code

borderDividers between content
code-surfaceCode panels
code-inlineInline code backgrounds
Fig. 4 Semantic roles, grouped by what they paint.

Build it

The theme file sets each role once per theme. Components only ever name the role:

styles/jzb-theme.css

To use your own accent, override primary, its hover and foreground, and ring after the theme import. Then check the contrast in both themes.

your-theme.css

Dark mode is one class on the root element. Set it before the first render to avoid a flash.

theme-switch.ts