Skip to content

Design tokens

Everything visual in forte-ui resolves through a CSS custom property. Components never contain a hardcoded color, 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.

Color

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 color tokens
TokenDefaultNotes
--forte-color-backgroundvar(--forte-gray-1)
--forte-color-bordercolor-mix(in oklab, var(--forte-gray-12) 12%, transparent)retuned under prefers-contrast: more
--forte-color-border-mutedcolor-mix(in oklab, var(--forte-gray-12) 8%, transparent)retuned under prefers-contrast: more
--forte-color-border-strongcolor-mix(in oklab, var(--forte-gray-12) 20%, transparent)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 color 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 color 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 colors.

Status color 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 brand-colored and still measured: the outline is --forte-color-primary-text, the ramp step the contrast harness holds to 4.5:1 for every seed, and it sits on a gap filled with the near-page -outer tone rather than on the control itself — so a solid primary button and a ghost one ring against the same known background (WCAG SC 1.4.11). The seed itself cannot promise that, which is why it is the caret and not the ring. It grows out of the control's edge while fading in, and shrinks back while fading out, on --forte-focus-ring-duration; the distance is --forte-focus-ring-travel, a travel token, so reduced motion keeps the fade and drops the movement.

Focus ring tokens
TokenDefaultNotes
--forte-color-focus-ringvar(--forte-focus-ring-inner)
--forte-focus-ring-durationvar(--forte-duration-fast)
--forte-focus-ring-easevar(--forte-ease-standard)
--forte-focus-ring-innervar(--forte-color-primary-text)
--forte-focus-ring-offset2px
--forte-focus-ring-outerlight-dark(oklch(0.995 0 0), oklch(0.178 0 0))
--forte-focus-ring-travelvar(--forte-travel-sm)
--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 behavior 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-orbit6000mscollapsed 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.