Skip to main content

Tokens and theming

Every colour, space, radius, type size and duration in Zeno is a design token. The tokens live in @usezeno/tokens as JSON, and Style Dictionary builds them into a stylesheet and a JavaScript module. There are 279 of them. The tables on this page are built from that package and drawn with the tokens themselves, so they always show the current values.

Load the stylesheet​

Load the stylesheet once per page, before any Zeno element renders:

import '@usezeno/tokens/css';

It defines each token as a custom property that starts with --zeno-. The Zeno elements read those properties and carry no fallback values. An element on a page without the stylesheet has no colours or spacing.

Light and dark​

Light is the default theme. The stylesheet switches to dark when the operating system asks for dark. To override that, set data-theme:

MarkupResult
<html>Follows the operating system
<html data-theme="light">Light, whatever the operating system says
<html data-theme="dark">Dark, whatever the operating system says

data-theme also works on any element, and themes that element and everything inside it:

<section data-theme="dark">
<zeno-button>Dark, on a light page</zeno-button>
</section>

@usezeno/core exports helpers that set the attribute for you:

import { getTheme, setTheme, onSystemThemeChange } from '@usezeno/core';

setTheme('dark'); // sets data-theme="dark" on <html>
setTheme('system'); // removes the attribute
getTheme(); // 'light' or 'dark', after resolving 'system'
const stop = onSystemThemeChange(theme => console.log(theme));

Colour​

Colour tokens change with the theme. Each row shows the token in the light theme and in the dark theme, whatever the theme of this page is.

Background​

TokenLightDark
--zeno-color-background-page#f9fafd#101215
--zeno-color-background-surface#ffffff#1a1c20
--zeno-color-background-subtle#f2f3f6#25282c
--zeno-color-background-muted#e5e6e9#33353a
--zeno-color-background-hoverrgba(16, 18, 21, 0.06)rgba(255, 255, 255, 0.08)
--zeno-color-background-activergba(16, 18, 21, 0.1)rgba(255, 255, 255, 0.12)
--zeno-color-background-selected#f2f7fe#001b47
--zeno-color-background-disabled#f2f3f6#25282c
--zeno-color-background-inverse#1a1c20#f2f3f6
--zeno-color-background-scrimrgba(16, 18, 21, 0.5)rgba(0, 0, 0, 0.65)

Text​

TokenLightDark
--zeno-color-text-primary#101215#f9fafd
--zeno-color-text-secondary#4d5054#d1d4da
--zeno-color-text-tertiary#66696e#a8abb0
--zeno-color-text-placeholder#66696e#a8abb0
--zeno-color-text-disabled#a8abb0#66696e
--zeno-color-text-inverse#f9fafd#101215
--zeno-color-text-invalid#aa0216#fdaca3

Border​

TokenLightDark
--zeno-color-border-subtle#e5e6e9#33353a
--zeno-color-border-default#d1d4da#4d5054
--zeno-color-border-strong#a8abb0#66696e
--zeno-color-border-control#85888d#85888d
--zeno-color-border-control-hover#66696e#a8abb0
--zeno-color-border-disabled#d1d4da#4d5054
--zeno-color-border-invalid#c72d2e#f57a71
TokenLightDark
--zeno-color-focus-ring#136adc#68a5ff
TokenLightDark
--zeno-color-link-default#0053b7#a0c6fe
--zeno-color-link-hover#003f8e#c8deff
--zeno-color-link-active#002c6a#e3eefe
--zeno-color-link-visited#732eac#d3b4f7
TokenLightDark
--zeno-color-selection-background#c8deff#003f8e
--zeno-color-selection-text#101215#ffffff

Colour families​

The colour families are accent, neutral, success, warning, danger and info. Each one has the same eight tokens:

TokenUse
solidA filled control or shape
solidHover, solidActiveThe same fill when hovered or pressed
onSolidText and icons on the solid fill
subtle, subtleHoverA tinted background
borderAn outline in the family colour
textColoured text on a page or subtle background

Accent​

TokenLightDark
--zeno-color-accent-solid#136adc#68a5ff
--zeno-color-accent-solid-hover#0053b7#a0c6fe
--zeno-color-accent-solid-active#003f8e#c8deff
--zeno-color-accent-on-solid#ffffff#101215
--zeno-color-accent-subtle#f2f7fe#001b47
--zeno-color-accent-subtle-hover#e3eefe#002c6a
--zeno-color-accent-border#3485f9#3485f9
--zeno-color-accent-text#0053b7#a0c6fe

Neutral​

TokenLightDark
--zeno-color-neutral-solid#33353a#e5e6e9
--zeno-color-neutral-solid-hover#4d5054#f2f3f6
--zeno-color-neutral-solid-active#101215#ffffff
--zeno-color-neutral-on-solid#ffffff#101215
--zeno-color-neutral-subtle#f2f3f6#25282c
--zeno-color-neutral-subtle-hover#e5e6e9#33353a
--zeno-color-neutral-border#85888d#85888d
--zeno-color-neutral-text#4d5054#d1d4da

Success​

TokenLightDark
--zeno-color-success-solid#058644#66ba7f
--zeno-color-success-solid-hover#016c35#9fd3ac
--zeno-color-success-solid-active#005226#c7e6ce
--zeno-color-success-on-solid#ffffff#101215
--zeno-color-success-subtle#eefbf1#00260e
--zeno-color-success-subtle-hover#dff3e3#003b1a
--zeno-color-success-border#29a259#29a259
--zeno-color-success-text#016c35#9fd3ac

Warning​

TokenLightDark
--zeno-color-warning-solid#9d6000#d6954a
--zeno-color-warning-solid-hover#7e4d00#e6bb8e
--zeno-color-warning-solid-active#603a01#f1d8bd
--zeno-color-warning-on-solid#ffffff#101215
--zeno-color-warning-subtle#fef5ec#2e1800
--zeno-color-warning-subtle-hover#fbead8#472800
--zeno-color-warning-border#bf7601#bf7601
--zeno-color-warning-text#7e4d00#e6bb8e

Danger​

TokenLightDark
--zeno-color-danger-solid#c72d2e#f57a71
--zeno-color-danger-solid-hover#aa0216#fdaca3
--zeno-color-danger-solid-active#83010e#fed0ca
--zeno-color-danger-on-solid#ffffff#101215
--zeno-color-danger-subtle#fef4f3#410002
--zeno-color-danger-subtle-hover#ffe6e4#610007
--zeno-color-danger-border#e54c48#e54c48
--zeno-color-danger-text#aa0216#fdaca3

Info​

TokenLightDark
--zeno-color-info-solid#037c9a#58b2d0
--zeno-color-info-solid-hover#00647c#98cee1
--zeno-color-info-solid-active#014c5f#c4e2ee
--zeno-color-info-on-solid#ffffff#101215
--zeno-color-info-subtle#edf9fe#00222d
--zeno-color-info-subtle-hover#dcf1f9#003645
--zeno-color-info-border#0398bb#0398bb
--zeno-color-info-text#00647c#98cee1

Palette​

The theme tokens above point at these steps. The palette is the same in both themes. Use a theme token in your own styles where one fits, so your colours follow the theme.

gray0501002003004005006007008008509009501000
blue50100200300400500600700800900950
green50100200300400500600700800900950
amber50100200300400500600700800900950
red50100200300400500600700800900950
cyan50100200300400500600700800900950
violet50100200300400500600700800900950

States​

StateTokens
Hovercolor.background.hover, color.border.controlHover, solidHover, subtleHover
Activecolor.background.active, solidActive
Selectedcolor.background.selected
Focuscolor.focus.ring, focus.ring.width, focus.ring.offset
Disabledcolor.text.disabled, color.background.disabled, color.border.disabled
Invalidcolor.text.invalid, color.border.invalid

Change the accent​

The theme tokens point at palette steps, so you can re-theme by overriding a few properties after the Zeno stylesheet:

:root {
--zeno-color-accent-solid: #0f766e;
--zeno-color-accent-solid-hover: #115e59;
--zeno-color-accent-solid-active: #134e4a;
}

Zeno checks its own colour pairs for contrast. It cannot check yours, so test any colour you override against the text and backgrounds it appears with.

Fonts​

The font tokens name IBM Plex Sans and IBM Plex Mono first, followed by system fonts. Zeno ships no font files. Load IBM Plex yourself if you want it. If you do not, the system fonts apply.

Contrast​

The token package tests a list of colour pairs against WCAG 2.2 AA in both themes: 4.5:1 for text, and 3:1 for control boundaries, focus rings and filled shapes. The list is in packages/tokens/test/contrast-pairs.mjs, and the test runs as part of pnpm test.

Disabled controls and decorative separators are not in the list. WCAG exempts them from these two criteria.

Type​

Families​

TokenValueSample
--zeno-font-family-sansIBM Plex Sans,system-ui,-apple-system,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serifA small black dog 0123
--zeno-font-family-monoIBM Plex Mono,ui-monospace,SFMono-Regular,Menlo,Consolas,Liberation Mono,monospaceA small black dog 0123

Sizes​

TokenValueSample
--zeno-font-size-2xs0.6875remZeno
--zeno-font-size-xs0.75remZeno
--zeno-font-size-sm0.875remZeno
--zeno-font-size-md1remZeno
--zeno-font-size-lg1.125remZeno
--zeno-font-size-xl1.25remZeno
--zeno-font-size-2xl1.5remZeno
--zeno-font-size-3xl1.875remZeno
--zeno-font-size-4xl2.25remZeno
--zeno-font-size-5xl3remZeno
--zeno-font-size-6xl3.75remZeno

Weights​

TokenValueSample
--zeno-font-weight-regular400A small black dog
--zeno-font-weight-medium500A small black dog
--zeno-font-weight-semibold600A small black dog
--zeno-font-weight-bold700A small black dog

Line heights​

TokenValue
--zeno-font-line-height-none1
--zeno-font-line-height-tight1.25
--zeno-font-line-height-snug1.375
--zeno-font-line-height-normal1.5
--zeno-font-line-height-relaxed1.625

Letter spacing​

TokenValueSample
--zeno-font-letter-spacing-tight-0.02emA small black dog
--zeno-font-letter-spacing-normal0A small black dog
--zeno-font-letter-spacing-wide0.02emA small black dog
--zeno-font-letter-spacing-wider0.05emA small black dog

Spacing​

TokenValueSample
--zeno-spacing-00
--zeno-spacing-0-50.125rem
--zeno-spacing-10.25rem
--zeno-spacing-1-50.375rem
--zeno-spacing-20.5rem
--zeno-spacing-2-50.625rem
--zeno-spacing-30.75rem
--zeno-spacing-3-50.875rem
--zeno-spacing-41rem
--zeno-spacing-51.25rem
--zeno-spacing-61.5rem
--zeno-spacing-71.75rem
--zeno-spacing-82rem
--zeno-spacing-92.25rem
--zeno-spacing-102.5rem
--zeno-spacing-112.75rem
--zeno-spacing-123rem
--zeno-spacing-164rem
--zeno-spacing-205rem
--zeno-spacing-246rem
--zeno-spacing-328rem

Radius​

TokenValueSample
--zeno-radius-none0
--zeno-radius-xs2px
--zeno-radius-sm4px
--zeno-radius-md6px
--zeno-radius-lg8px
--zeno-radius-xl12px
--zeno-radius-2xl16px
--zeno-radius-3xl24px
--zeno-radius-full9999px

Shadow​

Shadows are stronger in the dark theme, where a soft shadow would not show.

TokenLightDark
--zeno-shadow-nonenonenone
--zeno-shadow-xs0 1px 2px 0 rgba(16, 18, 21, 0.06)0 1px 2px 0 rgba(0, 0, 0, 0.4)
--zeno-shadow-sm0 1px 3px 0 rgba(16, 18, 21, 0.1), 0 1px 2px -1px rgba(16, 18, 21, 0.08)0 1px 3px 0 rgba(0, 0, 0, 0.5), 0 1px 2px -1px rgba(0, 0, 0, 0.4)
--zeno-shadow-md0 4px 8px -2px rgba(16, 18, 21, 0.1), 0 2px 4px -2px rgba(16, 18, 21, 0.06)0 4px 8px -2px rgba(0, 0, 0, 0.5), 0 2px 4px -2px rgba(0, 0, 0, 0.4)
--zeno-shadow-lg0 12px 20px -4px rgba(16, 18, 21, 0.12), 0 4px 8px -4px rgba(16, 18, 21, 0.06)0 12px 20px -4px rgba(0, 0, 0, 0.55), 0 4px 8px -4px rgba(0, 0, 0, 0.4)
--zeno-shadow-xl0 20px 32px -8px rgba(16, 18, 21, 0.16), 0 8px 12px -6px rgba(16, 18, 21, 0.06)0 20px 32px -8px rgba(0, 0, 0, 0.6), 0 8px 12px -6px rgba(0, 0, 0, 0.4)
--zeno-shadow-2xl0 28px 56px -12px rgba(16, 18, 21, 0.24)0 28px 56px -12px rgba(0, 0, 0, 0.7)
--zeno-shadow-insetinset 0 1px 2px 0 rgba(16, 18, 21, 0.08)inset 0 1px 2px 0 rgba(0, 0, 0, 0.4)

Motion​

Press Play to move a marker along each row with that row's token. If your system asks for reduced motion, the markers jump without moving.

Durations​

TokenValueSample
--zeno-motion-duration-instant0ms
--zeno-motion-duration-xs75ms
--zeno-motion-duration-sm150ms
--zeno-motion-duration-md200ms
--zeno-motion-duration-lg300ms
--zeno-motion-duration-xl500ms
--zeno-motion-duration-2xl700ms

Easings​

TokenValueSample
--zeno-motion-easing-linearcubic-bezier(0, 0, 1, 1)
--zeno-motion-easing-easecubic-bezier(0.25, 0.1, 0.25, 1)
--zeno-motion-easing-ease-incubic-bezier(0.42, 0, 1, 1)
--zeno-motion-easing-ease-outcubic-bezier(0, 0, 0.58, 1)
--zeno-motion-easing-ease-in-outcubic-bezier(0.42, 0, 0.58, 1)
--zeno-motion-easing-standardcubic-bezier(0.2, 0, 0, 1)
--zeno-motion-easing-springcubic-bezier(0.175, 0.885, 0.32, 1.275)
--zeno-motion-easing-bouncecubic-bezier(0.68, -0.55, 0.265, 1.55)

Sizes​

Controls​

TokenValueSample
--zeno-size-control-xs20px
--zeno-size-control-sm24px
--zeno-size-control-md32px
--zeno-size-control-lg40px
--zeno-size-control-xl48px
--zeno-size-control-2xl56px

Icons​

TokenValueSample
--zeno-size-icon-xs12px
--zeno-size-icon-sm16px
--zeno-size-icon-md20px
--zeno-size-icon-lg24px
--zeno-size-icon-xl32px
--zeno-size-icon-2xl48px

Avatars​

TokenValueSample
--zeno-size-avatar-xs24px
--zeno-size-avatar-sm32px
--zeno-size-avatar-md40px
--zeno-size-avatar-lg48px
--zeno-size-avatar-xl64px
--zeno-size-avatar-2xl96px

Smallest target​

TokenValueSample
--zeno-size-target-min24px

Borders and the focus ring​

TokenValueSample
--zeno-border-width-thin1px
--zeno-border-width-medium2px
--zeno-border-width-thick4px
TokenValue
--zeno-focus-ring-width2px
--zeno-focus-ring-offset2px

Opacity​

TokenValueSample
--zeno-opacity-disabled0.5
--zeno-opacity-loading0.7

Breakpoints​

CSS custom properties cannot be used in a media query. These are for JavaScript and for reference.

TokenValue
--zeno-breakpoint-xs480px
--zeno-breakpoint-sm640px
--zeno-breakpoint-md768px
--zeno-breakpoint-lg1024px
--zeno-breakpoint-xl1280px
--zeno-breakpoint-2xl1536px

Layers​

TokenValue
--zeno-z-index-base0
--zeno-z-index-raised1
--zeno-z-index-dropdown1000
--zeno-z-index-sticky1100
--zeno-z-index-drawer1200
--zeno-z-index-modal1300
--zeno-z-index-popover1400
--zeno-z-index-toast1500
--zeno-z-index-tooltip1600

Use tokens in JavaScript​

import { tokens, light, dark, vars } from '@usezeno/tokens';

tokens.spacing[4]; // '1rem'
light.color.text.primary; // the resolved light value
vars.color.text.primary; // 'var(--zeno-color-text-primary)'