Skip to content

Design tokens

Everything visual in forte-ui resolves through a CSS custom property. Components never contain a hardcoded colour, size or duration — which is why re-pointing a token restyles every component at once, coherently. This page is the global token reference; the tables are generated from the shipped stylesheets at build time, so they cannot drift from what actually renders. Per-component knobs (--forte-button-*, --forte-dialog-*, …) are a separate mechanism documented on Styling components and on each component's own page.

How to override a token

Set it on :root, or on a theme scope to affect one subtree:

:root {
  --forte-radius-control: var(--forte-radius-2);
  --forte-shadow-2: 0 2px 8px oklch(0 0 0 / 0.12);
}

Your CSS always wins. The library ships every rule inside @layer forte.*, and layered rules lose to unlayered author CSS regardless of specificity — no !important needed, ever.

Two things to know before overriding:

  • A typo fails silently. var(--forte-color-pannel) is not an error — the declaration simply becomes invalid at computed-value time and the element inherits instead. Check names against these tables.
  • Preset-owned tokens live on :root alone. Tokens marked rewritten by presets below are what data-forte-radius and data-forte-density rewrite. Overriding one on :root moves the default, but any preset attribute on an ancestor still wins for its subtree.

Colour

The accent, secondary and gray ramps — --forte-accent-1…12, --forte-secondary-1…12, --forte-gray-1…12 — are derived from the seeds described on the Theming page, in both light and dark mode. Prefer changing the seed over overriding ramp steps: the derivation is what keeps the palette coherent and the contrast pairs certified.

Components consume the ramps through semantic aliases, which are the right level for surgical overrides — they re-derive per scope and follow light/dark automatically when you assign them a ramp step:

Surfaces, text and borders

Surface, text and border colour tokens
TokenDefaultNotes
--forte-color-backgroundvar(--forte-gray-1)
--forte-color-bordervar(--forte-gray-7)retuned under prefers-contrast: more
--forte-color-border-mutedvar(--forte-gray-6)retuned under prefers-contrast: more
--forte-color-border-strongvar(--forte-gray-8)retuned under prefers-contrast: more
--forte-color-foregroundvar(--forte-gray-12)
--forte-color-foreground-mutedvar(--forte-gray-11)retuned under prefers-contrast: more
--forte-color-foreground-subtlevar(--forte-gray-10)retuned under prefers-contrast: more
--forte-color-overlayvar(--forte-gray-1)
--forte-color-panelvar(--forte-gray-2)
--forte-color-panel-activevar(--forte-gray-4)
--forte-color-panel-hovervar(--forte-gray-3)

Brand

Brand colour tokens
TokenDefault
--forte-color-on-primaryoklch(0.995 0 0)
--forte-color-on-secondaryoklch(0.995 0 0)
--forte-color-primaryvar(--forte-accent-9)
--forte-color-primary-activevar(--forte-accent-11)
--forte-color-primary-bordervar(--forte-accent-7)
--forte-color-primary-hovervar(--forte-accent-10)
--forte-color-primary-softvar(--forte-accent-3)
--forte-color-primary-soft-activevar(--forte-accent-5)
--forte-color-primary-soft-hovervar(--forte-accent-4)
--forte-color-primary-textvar(--forte-accent-11)
--forte-color-secondaryvar(--forte-secondary-9)
--forte-color-secondary-activevar(--forte-secondary-11)
--forte-color-secondary-bordervar(--forte-secondary-7)
--forte-color-secondary-hovervar(--forte-secondary-10)
--forte-color-secondary-softvar(--forte-secondary-3)
--forte-color-secondary-soft-activevar(--forte-secondary-5)
--forte-color-secondary-soft-hovervar(--forte-secondary-4)
--forte-color-secondary-textvar(--forte-secondary-11)

Status

Status hues are fixed rather than seed-derived — a brand colour must not be able to make an error look reassuring. The aliases below are what components read; behind them sit fixed ramp steps (--forte-danger-2…11, --forte-success-*, --forte-warning-*, --forte-info-*) you can retune if your palette needs different alarm colours.

Status colour tokens
TokenDefault
--forte-color-dangervar(--forte-danger-9)
--forte-color-danger-bordervar(--forte-danger-7)
--forte-color-danger-hovervar(--forte-danger-10)
--forte-color-danger-softvar(--forte-danger-3)
--forte-color-danger-textvar(--forte-danger-11)
--forte-color-infovar(--forte-info-9)
--forte-color-info-bordervar(--forte-info-7)
--forte-color-info-softvar(--forte-info-3)
--forte-color-info-textvar(--forte-info-11)
--forte-color-on-dangeroklch(0.995 0 0)
--forte-color-on-infooklch(0.995 0 0)
--forte-color-on-successoklch(0.995 0 0)
--forte-color-on-warningoklch(0.145 0 0)
--forte-color-successvar(--forte-success-9)
--forte-color-success-bordervar(--forte-success-7)
--forte-color-success-softvar(--forte-success-3)
--forte-color-success-textvar(--forte-success-11)
--forte-color-warningvar(--forte-warning-9)
--forte-color-warning-bordervar(--forte-warning-7)
--forte-color-warning-softvar(--forte-warning-3)
--forte-color-warning-textvar(--forte-warning-11)

Focus ring

The ring is two tones that contrast with each other, so one boundary always clears 3:1 against whatever sits behind the control (WCAG SC 1.4.11) — a single accent-coloured ring cannot promise that.

Focus ring tokens
TokenDefaultNotes
--forte-color-focus-ringvar(--forte-focus-ring-inner)
--forte-focus-ring-innerlight-dark(oklch(0.240 0 0), oklch(0.995 0 0))
--forte-focus-ring-offset2px
--forte-focus-ring-outerlight-dark(oklch(0.995 0 0), oklch(0.178 0 0))
--forte-focus-ring-width2pxretuned under prefers-contrast: more

Spacing

An eight-step scale on a 4px base. Components use it for all internal padding and gaps.

Spacing tokens
TokenDefault
--forte-space-10.25rem
--forte-space-20.5rem
--forte-space-30.75rem
--forte-space-41rem
--forte-space-51.5rem
--forte-space-62rem
--forte-space-72.5rem
--forte-space-83rem

Typography

Typography tokens
TokenDefault
--forte-font-monoui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace
--forte-font-sansui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif
--forte-font-size-10.75rem
--forte-font-size-20.875rem
--forte-font-size-31rem
--forte-font-size-41.125rem
--forte-font-size-51.25rem
--forte-font-size-61.5rem
--forte-font-weight-bold700
--forte-font-weight-medium500
--forte-font-weight-normal400
--forte-font-weight-semibold600
--forte-letter-spacing-normal0em
--forte-letter-spacing-tight-0.01em
--forte-line-height-normal1.5
--forte-line-height-tight1.2

Radius

The numbered scale plus the three semantic radii components actually read. Prefer overriding the semantic ones — or better, a radius preset, which is the form that nests correctly.

Radius tokens
TokenDefaultNotes
--forte-radius-10.125remrewritten by the data-forte-radius presets
--forte-radius-20.25remrewritten by the data-forte-radius presets
--forte-radius-30.375remrewritten by the data-forte-radius presets
--forte-radius-40.5remrewritten by the data-forte-radius presets
--forte-radius-50.75remrewritten by the data-forte-radius presets
--forte-radius-61remrewritten by the data-forte-radius presets
--forte-radius-controlvar(--forte-radius-4)rewritten by the data-forte-radius presets
--forte-radius-full9999pxrewritten by the data-forte-radius presets
--forte-radius-pillvar(--forte-radius-full)rewritten by the data-forte-radius presets
--forte-radius-surfacevar(--forte-radius-5)rewritten by the data-forte-radius presets

Controls and density

Control heights, padding and the surface metrics — the group the density presets rewrite. The --forte-app-bar-h-* heights are here too: an AppBar is exactly that tall at each size, and the token exists so the layout around one — a sticky sidebar's offset, a page's scroll-padding-top — can read it.

Control and density tokens
TokenDefaultNotes
--forte-app-bar-h-lgcalc(4.25rem + 1px)rewritten by the data-forte-density presets
--forte-app-bar-h-mdcalc(3.75rem + 1px)rewritten by the data-forte-density presets
--forte-app-bar-h-smcalc(2.75rem + 1px)rewritten by the data-forte-density presets
--forte-control-gapvar(--forte-space-2)rewritten by the data-forte-density presets
--forte-control-h-lg2.75remrewritten by the data-forte-density presets
--forte-control-h-md2.25remrewritten by the data-forte-density presets
--forte-control-h-sm1.75remrewritten by the data-forte-density presets
--forte-control-px-lgvar(--forte-space-5)rewritten by the data-forte-density presets
--forte-control-px-mdvar(--forte-space-4)rewritten by the data-forte-density presets
--forte-control-px-smvar(--forte-space-3)rewritten by the data-forte-density presets
--forte-list-item-pyvar(--forte-space-2)rewritten by the data-forte-density presets
--forte-surface-pvar(--forte-space-5)rewritten by the data-forte-density presets
--forte-target-comfortable44px
--forte-target-min24px

Elevation

Elevation tokens
TokenDefaultNotes
--forte-glass-alpha0.72retuned under prefers-reduced-transparency
--forte-scrim-blur4pxretuned under prefers-reduced-transparency
--forte-scrim-colorlight-dark(oklch(0 0 0 / 0.45), oklch(0 0 0 / 0.65))retuned under prefers-contrast: more · retuned under prefers-reduced-transparency
--forte-shadow-10 1px 2px 0 light-dark(oklch(0 0 0 / 0.05), oklch(0 0 0 / 0.4))retuned under prefers-contrast: more
--forte-shadow-20 1px 3px 0 light-dark(oklch(0 0 0 / 0.1), oklch(0 0 0 / 0.5)), 0 1px 2px -1px light-dark(oklch(0 0 0 / 0.1), oklch(0 0 0 / 0.5))retuned under prefers-contrast: more
--forte-shadow-30 4px 6px -1px light-dark(oklch(0 0 0 / 0.1), oklch(0 0 0 / 0.5)), 0 2px 4px -2px light-dark(oklch(0 0 0 / 0.1), oklch(0 0 0 / 0.5))retuned under prefers-contrast: more
--forte-shadow-40 12px 24px -6px light-dark(oklch(0 0 0 / 0.12), oklch(0 0 0 / 0.6)), 0 4px 8px -4px light-dark(oklch(0 0 0 / 0.1), oklch(0 0 0 / 0.5))retuned under prefers-contrast: more

Motion

Durations, travels and scales. The reduced-motion behaviour is built into the tokens themselves — under the OS preference or a data-forte-motion attribute, travels collapse to 0px and scales to 1 while durations only shorten — so consuming these tokens gives you correct reduced-motion handling without writing a media query. The spring easings (--forte-ease-spring-*) are sampled damped-oscillator curves; each pairs with its matching --forte-duration-spring-*, and truncating a spring with a shorter duration looks broken.

Motion tokens
TokenDefaultNotes
--forte-duration-fast160mscollapsed under reduced motion
--forte-duration-instant1mscollapsed under reduced motion
--forte-duration-loop-pulse1600mscollapsed under reduced motion
--forte-duration-loop-spin800mscollapsed under reduced motion
--forte-duration-loop-sweep1400mscollapsed under reduced motion
--forte-duration-move220mscollapsed under reduced motion
--forte-duration-normal240mscollapsed under reduced motion
--forte-duration-slow400mscollapsed under reduced motion
--forte-duration-spring-bouncy730mscollapsed under reduced motion
--forte-duration-spring-gentle700mscollapsed under reduced motion
--forte-duration-spring-precise410mscollapsed under reduced motion
--forte-duration-spring-snappy400mscollapsed under reduced motion
--forte-pulse-dip1collapsed under reduced motion
--forte-scale-enter0.95collapsed under reduced motion
--forte-scale-exit1.02collapsed under reduced motion
--forte-scale-press0.97collapsed under reduced motion
--forte-spin-turn1turncollapsed under reduced motion
--forte-travel-lg16pxcollapsed under reduced motion
--forte-travel-md8pxcollapsed under reduced motion
--forte-travel-page32pxcollapsed under reduced motion
--forte-travel-sm4pxcollapsed under reduced motion
--forte-travel-xs2pxcollapsed under reduced motion

For tooling

This same inventory ships in the package as machine-readable JSON — every token with its default, its typed registration where one exists, and every overriding selector:

import tokens from "@forte-ui/react/docs-data/tokens.json";

These tables are rendered from that file, as is anything else that needs the full token surface — theme editors, lint rules, design-tool sync.