Design Tokens

Role-based guidance for using UI Lab foreground, background, accent, and semantic tokens

Design Tokens

UI Lab tokens are role decisions, not a generic light-to-dark palette. A class such as text-foreground-300 or bg-background-900 should explain what the element is doing in the interface: normal text, muted support text, page depth, elevated surface, passive selection, primary action, or feedback.

Using a token is only the first check. The token also needs to fit the UI role. A selected filter chip should usually become a clearer neutral surface; it should not automatically become an accent-filled primary object.

Color Token Families

UI Lab's main color work happens through four families:

FamilyUse ForAvoid
foreground-*Text, icons, labels, code, metadataBackground fills
background-*Page layers, surfaces, borders, dividers, neutral statesHigh-emphasis brand or feedback color
accent-*Primary actions, brand emphasis, current product/location indicators, focus or small active cuesPassive selected chips, generic filter state, decorative fills
success-*, danger-*, warning-*, info-*Real feedback, validation, status, destructive or caution semanticsDecoration or arbitrary category coloring

The background and foreground scales are bounded role scales. Do not read them as "50 is a light background and 950 is dark text." In this site, foreground-50 is the brightest foreground and background-950 is a deep page/root layer in the dark theme.

Foreground Tokens

TokenRoleCommon Use
foreground-50Maximum emphasisPage titles, strong headings, labels on dark primary fills, rare high-contrast needs
foreground-100Strong emphasisActive navigation text, important labels, compact headings when foreground-50 is too loud
foreground-200High-readable emphasisSelected neutral states, section labels, important control text
foreground-300Default readable foregroundBody text, default control labels, table or list content
foreground-400Muted foregroundDescriptions, metadata, placeholders, comments, quiet secondary labels, empty states

Use foreground-50 sparingly. It is often too bright for compact controls, filter chips, metadata, secondary actions, and repeated list rows. Most ordinary UI text should start at foreground-300; supporting text usually belongs at foreground-400.

Background Tokens

TokenRoleCommon Use
background-500Strong or intentionally brighter surfaceRare highlights, generated diagrams, theme tooling where a noticeably brighter neutral is needed
background-600Secondary/highlight backgroundStrong hover borders, emphasized neutral fills, active support surfaces
background-700Default border and strong dividerBorders, dividers, selected neutral fills, scrollbar thumbs
background-800Elevated or interactive surfaceCards, panels, controls, hover fills, selected neutral rows
background-900First application layerSecondary page layer, code headers, subdued panels, unselected chips
background-950Depth/root layerPage backgrounds, header/sidebar roots, deep surrounding areas

The site commonly layers background-950 as the page or app shell, background-900 as a secondary layer, and background-800 for elevated panels, cards, popovers, and controls. Borders and dividers commonly use background-700, moving toward background-600 for stronger hover or selected edges.

Accent Restraint

Accent is load-bearing. It should draw attention to the thing the product wants users to understand or do.

Appropriate accent uses:

  • Primary actions and CTAs: bg-accent-600 text-foreground-50
  • Links or brand emphasis: text-accent-500
  • Current product/location navigation when it needs brand weight
  • Focus rings and small active indicators
  • Compact badges that communicate a special product state

Usually inappropriate accent uses:

  • Passive selected filter chips with bg-accent-* or border-accent-*
  • Category rows, generic selected pills, inactive toolbar controls, or filter popovers
  • Decorative swatches that do not mean primary, brand, focus, or active product state
  • Hover states that make an inactive item look more important than the selected item

For passive selected filters, chips, category rows, and toggles, prefer neutral state changes first:

className={isSelected
  ? "bg-background-700 border-background-600 text-foreground-200"
  : "bg-background-900 border-background-700 text-foreground-400 hover:bg-background-800 hover:border-background-600 hover:text-foreground-200"}

Semantic Tokens

Use semantic families only when the UI is communicating that semantic meaning.

FamilyUse ForExample
success-*Completed, saved, valid, healthy, availableSuccessful purchase, synced state, positive status
danger-*Error, destructive, failed, invalidValidation error, delete action, failed request
warning-*Caution, pending, risk, needs attentionUnsaved change, degraded state, warning notice
info-*Neutral information or guidanceInformational message, help state, neutral note

Do not use semantic colors as decoration or as substitute brand colors. If a status needs only low emphasis, pair a subtle semantic fill such as bg-success-600/10 with readable semantic text and a restrained border.

State Recipes

RolePreferred Tokens
Default texttext-foreground-300
Muted texttext-foreground-400
High-emphasis headingtext-foreground-50 or text-foreground-100
Page/root layerbg-background-950
Secondary application layerbg-background-900
Floating panels/popovers/cardsbg-background-800 or component defaults, with border-background-700
Borders/dividersborder-background-700, stronger as border-background-600
Hover/highlighthover:bg-background-800, hover:border-background-600, hover:text-foreground-200
Passive selected filter/chip/categorybg-background-700 or bg-background-800, border-background-600 or border-background-700, text-foreground-200 or text-foreground-300
Primary action or brand emphasisbg-accent-600 text-foreground-50, text-accent-500, or a small accent-* indicator
Error or destructive statedanger-* text, border, or fill with supporting copy/icon
Success statesuccess-* text, border, or fill with supporting copy/icon

Hover states should clarify affordance without outranking active or selected states. If an unselected item hovers to text-foreground-50 while the selected item sits at text-foreground-300, the hierarchy is backwards.

Token Class Hygiene

Avoid conflicting token classes on the same element or within a single conditional branch:

// Avoid: two background classes compete.
className={isActive ? "bg-background-700 bg-background-800 text-foreground-300" : ""}

// Prefer: one class per role.
className={isActive ? "bg-background-700 text-foreground-300" : ""}

The same rule applies to text-*, border-*, and state variants such as hover:bg-*. Competing classes make the intended role harder to audit and easier to break during refactors.

Anti-Pattern Checklist

  • Accent used as a generic selected chip, filter, category, or toolbar background
  • foreground-50 applied everywhere, especially on compact controls and metadata
  • Hover state brighter, stronger, or more colorful than the selected state
  • Multiple competing bg-*, text-*, or border-* token classes on one element
  • Semantic colors used decoratively instead of for status, feedback, validation, or destructive actions
  • A token chosen only because it is valid, not because it fits the element's UI role