Skip to main contentSkip to data table
Pharos
PHAROSlive stablecoin signals

Design Tokens

Design token reference for Pharos CSS variables, semantic layers, colors, spacing, component styling, and token ownership rules.

Pharos uses a 3-layer design token architecture that separates raw values from meaning from usage.

Architecture

┌─────────────────────────────────────────────────┐
│  Component Tokens  (card, table, chart, sidebar) │  ← Components reference these
├─────────────────────────────────────────────────┤
│  Semantic Tokens   (surfaces, text, severity)    │  ← Purpose-driven aliases
├─────────────────────────────────────────────────┤
│  Primitives        (color scales, spacing, type) │  ← Raw, theme-agnostic values
└─────────────────────────────────────────────────┘

Layer 1: Primitives (src/styles/tokens/primitives.css)

Raw values with no semantic meaning. Do not reference directly in components unless a documented local visualization intentionally needs a primitive ramp before a semantic token exists.

  • Color scales — 9 hue families (neutral, blue, green, teal, amber, orange, red, purple, pink) plus the dedicated --p-frost-blue brand accent; scale stops run 50–900 in OKLch (red extends to 950; neutral runs 50–975 with additional 850/925/975 stops)
  • Spacing — 4px-based scale from --p-space-0 to --p-space-20, with --p-space-0-5 (2px) and --p-space-1-5 (6px) half steps for tight UI alignment
  • Typography — Font sizes (--p-text-xs to --p-text-5xl), line heights, tracking
  • Radius--p-radius-none to --p-radius-full

Naming: --p-{category}-{value} (e.g., --p-blue-500, --p-space-4)

Layer 2: Semantic Tokens (src/styles/tokens/semantic.css)

Purpose-driven aliases that map primitives to meaning. These switch between light and dark mode.

CategoryExamplesNotes
Surfaces--surface-base, --surface-raised, --surface-overlayPage bg, elevated cards, modals
Text--text-primary, --text-secondary, --text-tertiaryContent hierarchy
Borders--border-default, --border-subtle, --border-strongSeparator hierarchy
Severity--severity-healthy through --severity-severePeg deviation bands
PSI Bands--psi-bedrock through --psi-meltdownStability index zones
DEWS Threat Bands--dews-calm through --dews-dangerDEWS threat level zones
DEWS Radar Contrast--dews-radar-spoke, --dews-radar-calm-boundary, --dews-radar-*-opacity, --dews-radar-calm-dot-*Theme-aware radar ring/spoke visibility
Score Tiers--score-green, --score-blue, --score-amber, --score-redLiquidity/durability
Interactive--interactive-hover, --interactive-active, --interactive-focus, --control-pill-*UI states and dense control pills
Chart--chart-grid-opacity, --chart-fill-opacity, --chart-primary, --chart-stage-*Chart-specific theming and chart stages
Motion--motion-duration-fast, --motion-duration-base, --motion-duration-slow, --motion-duration-entrance, --motion-ease-standard, --motion-ease-spring, --motion-ease-decelerate, --theme-transition-durationShared transition timing and theme swap

Light-Mode Contrast Baseline (March 2026)

  • --text-secondary and --text-tertiary are intentionally darker in light mode than earlier revisions to keep metadata and helper text readable on pale surfaces.
  • --ring is blue in both themes (blue-500 light, blue-400 dark) to keep keyboard focus visible against neutral backgrounds.
  • For semantic status/accent text classes used in badges and KPI callouts, use the two-theme pattern:
    • text-*-700 dark:text-*-400
  • Avoid unscoped text-*-300 / text-*-400 in app code unless the element only renders on dark-only surfaces.
  • DEWS radar guide lines should stay theme-tuned via tokens (--dews-radar-spoke, --dews-radar-calm-boundary); dark mode requires visibly higher alpha than 0.04 to keep axis spokes readable.
  • Dense interactive controls should use the --control-pill-* token family rather than ad-hoc translucent button backgrounds.
  • Dedicated chart stages should use the --chart-stage-* token family so chart canvases stay visually distinct from the outer card shell.

Hex Companion Variables

Recharts (and other SVG/canvas libraries) require literal hex color strings — CSS var() doesn't work in SVG attributes rendered by React. Selected semantic status/chart colors have -hex companions when CSS and JS both need the same token:

--psi-bedrock: var(--p-green-500); /* CSS usage */
--psi-bedrock-hex: #22c55e; /* JS/Recharts usage */

The JS-side token maps in chart-colors.ts and severity-colors.ts use those same hex values where CSS companions exist. chart-colors.ts also exports palette, risk, signal, and brand colors that are JS-only and do not have one CSS companion per export.

Layer 3: Component Tokens (in semantic.css)

Scoped to specific UI components. Optional — use when a component needs tokens that don't map cleanly to general semantic categories.

  • Card--card-bg, --card-border, --card-shadow, --card-shadow-hover, --card-shell-bg, --card-shell-highlight, --panel-header-bg
  • Table--table-header-bg, --table-row-hover, --table-row-stripe, --table-border, --table-header-shadow, --table-sticky-column-*
  • Sidebar--sidebar-bg, --sidebar-border, --sidebar-item-hover, --sidebar-width-expanded, --sidebar-width-collapsed

Bridge Layer (src/app/globals.css)

Existing shadcn/ui variables (--background, --card, --foreground, etc.) are wired to semantic tokens through a bridge layer in globals.css. This means:

  • All existing components continue working without changes
  • Migration is gradual — new code uses semantic tokens, old code works through the bridge
  • shadcn/ui primitives in src/components/ui/ should not be edited to use tokens directly
  • Bridge-level visual polish (page glow backgrounds and slot-based transition/elevation defaults) is centralized in globals.css
  • Shared layout-safe variables that are not semantic theme tokens, such as --mobile-utility-safe-offset, also live in globals.css because they coordinate app-shell spacing rather than color or component semantics
/* Bridge: shadcn var → semantic token */
:root {
  --background: var(--surface-base);
  --foreground: var(--text-primary);
  --card: var(--surface-overlay);
  --border: var(--border-default);
  /* ... */
}

Utilities

Authored shell utilities live in src/app/globals.css under @layer components. They are not tokens (they consume tokens through @apply + var(...)), but they are referenced often enough from product code to be worth indexing here:

  • pharos-control-pill — canonical small-control shell (src/app/globals.css line 490). Use for time-range controls, density toggles, lens pills, hero tertiary chips, scrollspy pills, and any dense secondary action surface. See design-language.md for the visual contract.

Other shared utilities (pharos-card-shell, pharos-kicker, pharos-focus-ring, pharos-interactive-card, pharos-chart-stage, pharos-table-shell, etc.) are catalogued in design-language.md.

JS Token Maps

For colors needed at JS runtime (Recharts, canvas, dynamic styles):

FileExportsPurpose
src/lib/chart-colors.tsCHART_PALETTE, CHART_BLUE, CHART_GREEN, CHART_ORANGE, CHART_RED, CHART_SLATE, CHART_AMBER, CHART_HEIGHT, RECHARTS_TOOLTIP_STYLESShared chart fill/stroke colors, chart-height utility, and tooltip styles (also has module-private TOKEN map)
src/lib/severity-colors.tsdeviationColorHex(), deviationColorClass(), tier helpersPeg-deviation and score-tier helpers (text classes are light/dark aware)

These maps use the same hex values as the --*-hex CSS custom properties in semantic.css where CSS companions exist. Some runtime-only exports, such as threat bands, signal colors, and brand helpers, live only in the JS maps.

Usage Guidelines

Do

  • Reference semantic tokens in CSS/Tailwind: var(--surface-base), var(--text-secondary)
  • Import from chart-colors.ts or severity-colors.ts for Recharts colors
  • Use the bridge vars (--background, --card, etc.) in existing code — no rush to migrate
  • Add new component tokens to semantic.css when needed

Don't

  • Reference primitives (--p-blue-500) directly in components, except for documented local visualizations that intentionally need a primitive ramp before a semantic token exists. Current exceptions include the PSI lead card's red/amber primitive ramp in src/components/kpi-bar-parts.tsx.
  • Hardcode hex values in chart components — use the JS token maps
  • Edit shadcn/ui primitives in src/components/ui/ to use tokens
  • Define one-off color variables in individual component files

Adding a New Token

  1. If it's a raw color/spacing value → add to primitives.css
  2. If it maps a primitive to a purpose → add to semantic.css (both :root and .dark)
  3. If it's used in Recharts → add a -hex companion in semantic.css AND update the JS token map
  4. If it's scoped to one component type → add as a component token in semantic.css