> ## Documentation Index
> Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Semantic Color Reference

> Version: 1.4.1 Last Updated: 2026-04-18 Status: Active Target Audience: Developers

**Version:** 1.4.1
**Last Updated:** 2026-04-18
**Status:** Active
**Target Audience:** Developers

## Overview

Use semantic color tokens from the design system (`src/index.css` with Tailwind v4 `@theme inline` mappings); avoid hardcoded Tailwind palette classes (`green-*`, `red-*`, etc.) except where documented exceptions apply.

Canonical token governance and sync workflow are defined in [DESIGN\_TOKEN\_SYNC\_POLICY.md](DESIGN_TOKEN_SYNC_POLICY.md). When this document and runtime values diverge, follow the canonical-source and synchronization process in that policy.

> **What changed in v1.4.1 (2026-04-18):** Chart Palette § "Colorblind safety" updated with the first formal audit results from `npm run audit:chart-palette-colorblind` (Brettel-Viénot-Mollon simulation + CIE76 ΔE in CIELAB). Audit reveals **9 fail / 25 warn pairs** across both modes × 3 dichromacy profiles — see the new **Known limitations & action items** subsection. The mandatory rule "always pair color with shape/pattern/label" is now load-bearing for accessibility, not just defensive practice.
>
> **What changed in v1.4.0 (2026-04-18):** Added the **Token Catalog Reference** (full surface/role/status/special/sidebar inventory), a **Module Identity Tokens** section (12 active short codes + reserved + deprecated aliases), an expanded **Chart Palette** section, a **Tenant Theming (PF-95) Mapping Table** that documents the gaps between platform tokens and tenant-customizable colors, and a **Compliance Theming (PF-91-EN-01) Mapping** section. Includes a regression note for the previous `info.full` dark-mode WCAG defect (now fixed).

## Typography Token Contract

Typography parity follows the canonical design-system contract and app runtime mapping:

* Canonical tokens:
  * `--font-display`: Space Grotesk
  * `--font-body`: Inter
  * `--font-mono`: DM Mono (code, monospace data)
* Runtime mappings:
  * `font-sans` → `var(--tenant-font-family, var(--font-body))`
  * `font-heading` → `var(--tenant-font-heading, var(--font-display))`

Tenant-specific typography remains supported via PF-95 variables:

* `--tenant-font-family` overrides body/sans
* `--tenant-font-heading` overrides heading/display

## Standard Mappings

| Meaning                 | Hardcoded (DON'T USE)             | Semantic Token                                         |
| ----------------------- | --------------------------------- | ------------------------------------------------------ |
| Success/Approved/Active | `green-*`, `emerald-*`            | `success`, `text-success`, `bg-success/10`             |
| Warning/Pending/Caution | `yellow-*`, `amber-*`, `orange-*` | `warning`, `text-warning`, `bg-warning/10`             |
| Error/Rejected/Danger   | `red-*`                           | `destructive`, `text-destructive`, `bg-destructive/10` |
| Info/In Progress        | `blue-*`                          | `info`, `text-info`, `bg-info/10`                      |
| Neutral/Inactive        | `gray-*`, `slate-*`               | `muted`, `text-muted-foreground`, `bg-muted`           |

> **Token note (PF-95 update, 2026-04):** `--color-secondary` now maps to `hsl(var(--secondary))` (shadcn-canonical) so the tenant bridge `--tenant-secondary-hsl` actually drives `bg-secondary` / `text-secondary`. For the elevated card surface, use `bg-card-elevated` directly. `--color-input` resolves to `--input`, which in `:root` shares `--tenant-border-hsl` with `--border` per shadcn convention — set the tenant border once and inputs follow. See [DESIGN\_TOKEN\_SYNC\_POLICY.md](DESIGN_TOKEN_SYNC_POLICY.md#token-aliases).

## Badge Variants

Status-style badges must use **Badge** variants or **StatusBadge** (with statusConfig) from `@/shared/components`; do not use raw color classes. Use these Badge component variants from `@/shared/ui/badge`:

| Variant       | Usage                                           | Example                                       |
| ------------- | ----------------------------------------------- | --------------------------------------------- |
| `success`     | Positive states (active, approved, complete)    | `<Badge variant="success">Active</Badge>`     |
| `warning`     | Caution states (pending, at-risk, expiring)     | `<Badge variant="warning">Pending</Badge>`    |
| `destructive` | Error/danger states (rejected, error, critical) | `<Badge variant="destructive">Error</Badge>`  |
| `info`        | Informational states (in progress, new)         | `<Badge variant="info">In Progress</Badge>`   |
| `secondary`   | Neutral/inactive states                         | `<Badge variant="secondary">Inactive</Badge>` |
| `outline`     | Border only, subtle emphasis                    | `<Badge variant="outline">Default</Badge>`    |

**Note:** Badge `info` uses `bg-info/10 text-info` — a distinct informational blue. For primary-branded soft emphasis, use Button variant `soft` or `primary-light` Badge variant instead.

### Subtle / Light Badge Variants

For low-emphasis or inline status (e.g. inside tables, alongside solid badges), use the `*-light` Badge variants from `@/shared/ui/badge`:

| Variant             | Usage                                                                        |
| ------------------- | ---------------------------------------------------------------------------- |
| `success-light`     | Subtle success (e.g. inline “Active” in a table cell)                        |
| `warning-light`     | Subtle warning (e.g. “Expiring soon” without competing with primary content) |
| `destructive-light` | Subtle error/danger (e.g. “Overdue” in a list)                               |
| `primary-light`     | Subtle primary/info (e.g. “New” or “In progress” inline)                     |

Use **solid** variants (`success`, `warning`, `destructive`, `info`) for standalone status badges (cards, headers, filters). Use **-light** when the badge should not compete visually with surrounding content.

### Specialty Badge Variants

Additional Badge variants exist for non-status use cases:

| Variant      | Usage                                                                  |
| ------------ | ---------------------------------------------------------------------- |
| `accentGlow` | Accent background with glow shadow effect (highlights, featured items) |
| `glass`      | Glass-morphism style (overlays, floating UI)                           |
| `ai`         | Gradient background for AI-powered features                            |

These are not for status indication — use the semantic/light variants above for statuses.

### Status Badge Pattern

For domain statuses (e.g. invoice status, referral status, run status), map each status to a Badge variant and label, then render with the shared Badge or the shared **StatusBadge** component:

1. **Define a config:** `statusConfig: Record<Status, { label: string; variant: BadgeVariant; icon?: LucideIcon }>`.
2. **Render:** `<Badge variant={config.variant}>{config.label}</Badge>` or use `<StatusBadge status={status} statusConfig={statusConfig} />` from `@/shared/components/StatusBadge`.
3. **Variant mapping:** Use the **canonical status-to-variant map** in `@/shared/lib/status-variants`: `CANONICAL_STATUS_VARIANTS` and `getCanonicalVariant(status)`. New status badges SHOULD use these canonical mappings; existing badges will be migrated incrementally. Fallback semantic guidance: `success` (active, approved, completed), `warning` (pending, in progress, expiring), `destructive` (rejected, error, cancelled, failed), `info` (new, submitted), `secondary` (draft, inactive, closed).

See `src/shared/components/StatusBadge.tsx` for the shared component API and `src/shared/lib/status-variants.ts` for the canonical mapping.

## When to Use Semantic vs Categorical Colors

### Use Semantic Colors For:

* ✅ Status indicators (active, pending, error, complete)
* ✅ Validation feedback (valid, invalid, warning)
* ✅ Health indicators (healthy, at-risk, critical)
* ✅ Action feedback (success, failure)
* ✅ Priority levels (critical, high, medium, low)

### Keep Categorical Colors For:

* 🎨 Data type indicators (string=blue, number=green, boolean=purple)
* 🎨 Core/module identifiers (hr=blue, rh=green, fa=amber)
* 🎨 Category distinctions that don't imply status
* 🎨 Brand colors that must remain consistent

## Utility Functions

Import from `@/shared/lib/semantic-colors`:

```typescript theme={null}
import { getStatusColors, type SemanticStatus } from '@/shared/lib/semantic-colors';

// Get all color classes for a status
const colors = getStatusColors('success');
// Returns: { bg, bgSubtle, text, border, full }

// Example usage
<div className={colors.bgSubtle}>
  <span className={colors.text}>Success message</span>
</div>
```

## Examples

### Before (DON'T DO THIS)

```tsx theme={null}
// ❌ Hardcoded colors - violates design system
<Badge className="bg-green-100 text-green-800">Active</Badge>
<Badge className="bg-yellow-100 text-yellow-800">Pending</Badge>
<span className="text-red-600">Error message</span>
<div className="bg-blue-500/10 text-blue-700">Info</div>
```

### After (DO THIS)

```tsx theme={null}
// ✅ Using semantic tokens and Badge variants
<Badge variant="success">Active</Badge>
<Badge variant="warning">Pending</Badge>
<span className="text-destructive">Error message</span>
<div className="bg-info/10 text-info">Info</div>
```

### Status Configuration Pattern

```tsx theme={null}
// ✅ Use variant mappings instead of className mappings
const statusVariants: Record<Status, BadgeVariant> = {
  active: 'success',
  pending: 'warning',
  error: 'destructive',
  draft: 'secondary',
};

// Usage
<Badge variant={statusVariants[status]}>{status}</Badge>
```

### Icon Colors

```tsx theme={null}
// ❌ Wrong
<CheckCircle className="h-4 w-4 text-green-600" />
<AlertTriangle className="h-4 w-4 text-yellow-600" />
<XCircle className="h-4 w-4 text-red-600" />

// ✅ Correct
<CheckCircle className="h-4 w-4 text-success" />
<AlertTriangle className="h-4 w-4 text-warning" />
<XCircle className="h-4 w-4 text-destructive" />
```

### Alert/Notification Styling

```tsx theme={null}
// ❌ Wrong
<Alert className="border-green-200 bg-green-50">
  <CheckCircle className="h-4 w-4 text-green-600" />
</Alert>

// ✅ Correct
<Alert className="border-success/20 bg-success/10">
  <CheckCircle className="h-4 w-4 text-success" />
</Alert>
```

### Progress/Score Indicators

```tsx theme={null}
// ✅ Semantic color based on value
const getScoreColor = (score: number) => {
  if (score >= 80) return 'text-success';
  if (score >= 50) return 'text-warning';
  return 'text-destructive';
};

// ✅ Progress bar backgrounds
const getProgressBg = (percent: number) => {
  if (percent >= 75) return 'bg-success';
  if (percent >= 50) return 'bg-primary';
  if (percent >= 25) return 'bg-warning';
  return 'bg-destructive';
};
```

## Dark Mode Considerations

When using semantic tokens, dark mode is handled automatically:

* `text-success` → Works in both light and dark mode
* `bg-success/10` → Proper opacity in both modes
* `border-success/20` → Consistent borders

**Avoid** manual dark mode overrides like:

```tsx theme={null}
// ❌ Don't do this
className="bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-400"

// ✅ Do this instead
className="bg-success/10 text-success"
```

## Common Patterns Reference

| Component Type       | Semantic Approach                                    |
| -------------------- | ---------------------------------------------------- |
| Status Badge         | Use `variant` prop                                   |
| Icon color           | Use `text-{semantic}`                                |
| Background highlight | Use `bg-{semantic}/10`                               |
| Border accent        | Use `border-{semantic}/20`                           |
| Alert styling        | Use `bg-{semantic}/10 border-{semantic}/20`          |
| Trend indicators     | Up = `text-success`, Down = `text-destructive`       |
| Form validation      | Error = `text-destructive`, Warning = `text-warning` |

## Chart Colors

Charts (Recharts, etc.) should use the **chart palette** for data series so they respect light/dark theme and stay consistent.

* **CSS variables:** `--chart-1` through `--chart-8` are defined in `src/index.css` (light and dark).
* **Tailwind:** `chart.1` … `chart.8` are mapped in `src/index.css` via Tailwind v4 `@theme inline`; use `text-chart-1`, `bg-chart-2`, etc. for UI elements that reference chart colors (e.g. legend labels).
* **In chart config:** Use `hsl(var(--chart-1))`, … `hsl(var(--chart-8))` for strokes/fills, or pass a `ChartContainer` config with `theme: { light: 'hsl(var(--chart-n))', dark: '...' }` so series colors are theme-aware.
* **Do not** use arbitrary hex or raw HSL in chart configs; use the design tokens above so charts adapt to dark mode.

## Audit Checklist

Before committing, verify:

* [ ] No direct Tailwind colors (`green-*`, `red-*`, `yellow-*`, etc.)
* [ ] All status indicators use Badge variants
* [ ] Icon colors use semantic tokens
* [ ] Alert/notification backgrounds use semantic tokens
* [ ] Progress indicators use semantic colors
* [ ] Light/dark mode both work correctly

## Domain-Specific Status Mappings

### Document Statuses

| Status                    | Semantic      |
| ------------------------- | ------------- |
| `published`, `approved`   | `success`     |
| `draft`, `pending_review` | `warning`     |
| `archived`                | `muted`       |
| `rejected`                | `destructive` |

### Credential Statuses

| Status                                  | Semantic      |
| --------------------------------------- | ------------- |
| `active`, `verified`                    | `success`     |
| `expiring_soon`, `pending_verification` | `warning`     |
| `expired`, `revoked`                    | `destructive` |
| `inactive`                              | `muted`       |

### Workflow Statuses

| Status                            | Semantic      |
| --------------------------------- | ------------- |
| `completed`, `approved`           | `success`     |
| `running`, `pending`, `waiting`   | `warning`     |
| `failed`, `rejected`, `cancelled` | `destructive` |
| `draft`, `paused`                 | `muted`       |

### Form Statuses

| Status                          | Semantic      |
| ------------------------------- | ------------- |
| `submitted`, `approved`         | `success`     |
| `in_progress`, `pending_review` | `warning`     |
| `rejected`, `expired`           | `destructive` |
| `draft`                         | `muted`       |

***

## Real-World Usage Examples

### Employee Status Card

```tsx theme={null}
// ✅ CORRECT: Using semantic tokens
function EmployeeStatusCard({ status }: { status: string }) {
  const statusConfig = {
    active: { variant: 'success' as const, icon: CheckCircle, label: 'Active' },
    pending: { variant: 'warning' as const, icon: Clock, label: 'Pending' },
    inactive: { variant: 'muted' as const, icon: UserX, label: 'Inactive' },
    terminated: { variant: 'destructive' as const, icon: XCircle, label: 'Terminated' },
  };

  const config = statusConfig[status] || statusConfig.inactive;
  const Icon = config.icon;

  return (
    <Card className="border-success/20 bg-success/5">
      <CardContent className="flex items-center gap-3 p-4">
        <Icon className="h-5 w-5 text-success" />
        <span className="font-medium">{config.label}</span>
      </CardContent>
    </Card>
  );
}
```

### Form Validation Messages

```tsx theme={null}
// ✅ CORRECT: Semantic colors for validation
function FormField({ error, warning }: { error?: string; warning?: string }) {
  return (
    <div>
      <Input />
      {error && (
        <p className="text-sm text-destructive mt-1 flex items-center gap-1">
          <AlertCircle size={14} />
          {error}
        </p>
      )}
      {warning && (
        <p className="text-sm text-warning mt-1 flex items-center gap-1">
          <AlertTriangle size={14} />
          {warning}
        </p>
      )}
    </div>
  );
}
```

### Dashboard Metrics

```tsx theme={null}
// ✅ CORRECT: Semantic colors for trends
function MetricCard({ value, trend }: { value: number; trend: 'up' | 'down' | 'neutral' }) {
  const trendConfig = {
    up: { color: 'text-success', icon: TrendingUp },
    down: { color: 'text-destructive', icon: TrendingDown },
    neutral: { color: 'text-muted-foreground', icon: Minus },
  };

  const config = trendConfig[trend];
  const Icon = config.icon;

  return (
    <Card>
      <CardContent className="p-4">
        <div className="flex items-center justify-between">
          <span className="text-2xl font-bold">{value}</span>
          <Icon className={`h-5 w-5 ${config.color}`} />
        </div>
      </CardContent>
    </Card>
  );
}
```

### Alert Notifications

```tsx theme={null}
// ✅ CORRECT: Semantic alert styling
function AlertNotification({ type, message }: { type: 'success' | 'warning' | 'error'; message: string }) {
  const config = {
    success: { bg: 'bg-success/10', border: 'border-success/20', icon: CheckCircle, text: 'text-success' },
    warning: { bg: 'bg-warning/10', border: 'border-warning/20', icon: AlertTriangle, text: 'text-warning' },
    error: { bg: 'bg-destructive/10', border: 'border-destructive/20', icon: XCircle, text: 'text-destructive' },
  };

  const { bg, border, icon: Icon, text } = config[type];

  return (
    <Alert className={`${bg} ${border}`}>
      <Icon className={`h-4 w-4 ${text}`} />
      <AlertDescription className={text}>{message}</AlertDescription>
    </Alert>
  );
}
```

### Table Row Status

```tsx theme={null}
// ✅ CORRECT: Status indicators in tables
function StatusCell({ status }: { status: string }) {
  const statusMap: Record<string, { variant: BadgeVariant; label: string }> = {
    approved: { variant: 'success', label: 'Approved' },
    pending: { variant: 'warning', label: 'Pending' },
    rejected: { variant: 'destructive', label: 'Rejected' },
    draft: { variant: 'secondary', label: 'Draft' },
  };

  const config = statusMap[status] || statusMap.draft;

  return <Badge variant={config.variant}>{config.label}</Badge>;
}
```

***

## Token Catalog Reference

The canonical token list lives in [`src/index.css`](../../src/index.css) (`:root` + `.dark` declare HSL values; the `@theme inline` block exposes them as Tailwind v4 utilities). Use this section to look up which token to reach for; consult `src/index.css` for the exact HSL values.

### Surfaces & roles (chrome)

| Token                            | Tailwind utility (examples)             | Use                                                                                                                                                                                                                                             |
| -------------------------------- | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--background`                   | `bg-background`, `text-background`      | Default page background                                                                                                                                                                                                                         |
| `--foreground` (= `--text-main`) | `text-foreground`                       | Default body text                                                                                                                                                                                                                               |
| `--card`                         | `bg-card`, `text-card-foreground`       | Card surface                                                                                                                                                                                                                                    |
| `--card-elevated`                | `bg-card-elevated`                      | Elevated card surface (slightly distinct from `--card`). **Note:** as of 2026-04 `bg-secondary` no longer aliases here — it resolves to `--secondary` (shadcn-canonical). Use `bg-card-elevated` explicitly when you want the elevated surface. |
| `--popover`                      | `bg-popover`, `text-popover-foreground` | Popover/dropdown/menu backgrounds                                                                                                                                                                                                               |
| `--muted`                        | `bg-muted`, `text-muted`                | De-emphasized chrome (cool gray light, darker slate dark)                                                                                                                                                                                       |
| `--muted-foreground`             | `text-muted-foreground`                 | Muted body text — the canonical "subtle text" utility                                                                                                                                                                                           |
| `--text-secondary`               | `text-text-secondary`                   | Mid-emphasis body text (between foreground and muted-foreground)                                                                                                                                                                                |
| `--text-muted`                   | `text-text-muted`                       | Lower-emphasis body text. Note: `text-muted-foreground` is preferred for component code                                                                                                                                                         |
| `--border`                       | `border-border`, `border`               | Default border color                                                                                                                                                                                                                            |
| `--input`                        | `border-input`, `bg-input`              | Form input border. Shares the `--tenant-border-hsl` triplet with `--border` per shadcn convention so a tenant border override flows to inputs automatically.                                                                                    |
| `--input-background`             | `bg-input-background`                   | Form input fill                                                                                                                                                                                                                                 |
| `--switch-background`            | `bg-switch-background`                  | Switch off-state track                                                                                                                                                                                                                          |
| `--ring`                         | `ring-ring`, `focus-visible:ring-ring`  | Focus ring color                                                                                                                                                                                                                                |

### Brand & action

| Token                                   | Tailwind utility             | Use                                                                                                                                                                                                                                                 |
| --------------------------------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--primary`                             | `bg-primary`, `text-primary` | Brand primary (deep navy) — chrome accents, sidebar active                                                                                                                                                                                          |
| `--primary-hover`                       | `hover:bg-primary-hover`     | Primary hover state                                                                                                                                                                                                                                 |
| `--primary-foreground` (`--primary-fg`) | `text-primary-foreground`    | Foreground on `bg-primary`                                                                                                                                                                                                                          |
| `--accent`                              | `bg-accent`, `text-accent`   | Brand accent (teal) — primary CTA color via `Button variant="accent"`                                                                                                                                                                               |
| `--accent-hover`                        | `hover:bg-accent-hover`      | Accent hover state                                                                                                                                                                                                                                  |
| `--accent-foreground` (`--accent-fg`)   | `text-accent-foreground`     | Foreground on `bg-accent`                                                                                                                                                                                                                           |
| `--secondary` (raw)                     | (consumed by PF-95 only)     | Raw HSL value used by `pf_tenant_themes.color_secondary`. **Note:** the Tailwind utility `bg-secondary` resolves to `--card-elevated` (alias) — see "Token Aliases" in [DESIGN\_TOKEN\_SYNC\_POLICY.md](DESIGN_TOKEN_SYNC_POLICY.md#token-aliases). |
| `--secondary-foreground`                | `text-secondary-foreground`  | Foreground on `bg-secondary` / `bg-card-elevated`                                                                                                                                                                                                   |

### Status

| Token                 | Subtle                    | Foreground                 | Notes                         |
| --------------------- | ------------------------- | -------------------------- | ----------------------------- |
| `--success`           | `--success-subtle`        | `--success-foreground`     | Active / approved / completed |
| `--warning`           | `--warning-subtle`        | `--warning-foreground`     | Pending / at-risk / expiring  |
| `--info`              | `--info-subtle`           | `--info-foreground`        | In progress / informational   |
| `--destructive`       | (use `bg-destructive/10`) | `--destructive-foreground` | Error / delete / critical     |
| `--destructive-hover` | —                         | —                          | Destructive button hover      |

Use the Tailwind utilities `bg-success`, `bg-success-subtle`, `text-success`, `text-success-foreground`, `border-success/20`, etc. The `*-subtle` tokens are pre-tinted backgrounds calibrated for both light and dark mode — prefer them over `bg-{token}/10` when you want a consistent, mode-aware tint.

> **Regression note:** Prior to 2026-04-18, `semanticColorClasses.info.full` paired `bg-info` with `text-primary-foreground`. In dark mode `--info` flips to a light blue and `--primary-foreground` is also light → light-on-light, WCAG AA fail. Now uses `text-info-foreground`. Cover with the Vitest in `tests/unit/shared/lib/semantic-colors.test.ts`.

### Special & utility

| Token                                              | Tailwind utility                                | Use                                                                      |
| -------------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------ |
| `--overlay-scrim`                                  | `bg-[hsl(var(--overlay-scrim))]`                | Modal/sheet/drawer backdrops (replaces ad-hoc `bg-black/50`)             |
| `--shimmer-highlight`                              | `bg-[hsl(var(--shimmer-highlight))]`            | Skeleton/loader shimmer band (replaces ad-hoc `via-white/20`)            |
| `--ai-accent`                                      | `bg-[hsl(var(--ai-accent))]`, `text-ai-accent`  | AI-feature accent (purple) — used by `Badge variant="ai"`, `accent-glow` |
| `--glass-bg` / `--glass-border` / `--glow-opacity` | `.glass`, `.glass-card`, `.glass-frosted`, etc. | Glassmorphism utilities (built into `@layer utilities`)                  |
| `--focus-ring-offset-color`                        | (consumed by component CSS)                     | Outline offset color for high-contrast focus rings                       |

### Density, Elevation & Encore v2

The `.theme-encore-v2` class is an additive opt-in layer for visual rhythm and depth. It does **not** rename existing tokens, introduce PF-95 schema fields, or hardcode tenant colors; color-bearing surfaces still resolve through the semantic/PF-95 bridge above.

| Token                                                               | Use                                                                        |
| ------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| `--density-card-padding`, `--density-card-padding-lg`               | Standard card padding for shared metric/list primitives                    |
| `--density-control-height`, `--density-touch-target`                | Control height/touch target floor; keep interactive targets at least 44px  |
| `--elevation-card`, `--elevation-card-hover`, `--elevation-popover` | Depth vocabulary for shared cards/popovers using semantic foreground alpha |

Current pilot consumers: `StatCard` and `ListFilterBar`. Prefer these tokens for new shared primitives instead of repeating `p-4 sm:p-6`, `h-9`, or bespoke shadow classes.

### Sidebar slot

The platform sidebar has its own token slot so it can be themed independently of the main app shell.

| Token                                                | Use                          |
| ---------------------------------------------------- | ---------------------------- |
| `--sidebar-background`                               | Sidebar surface              |
| `--sidebar-foreground`                               | Sidebar text                 |
| `--sidebar-primary` / `--sidebar-primary-foreground` | Active sidebar item          |
| `--sidebar-accent` / `--sidebar-accent-foreground`   | Hover/secondary sidebar item |
| `--sidebar-border`                                   | Sidebar dividers             |
| `--sidebar-ring`                                     | Sidebar focus ring           |

### Encore brand (reserved)

| Token                                                                                                                                              | Use                                                                                                                                                                                                                                                                                                                                                         |
| -------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--encore-navy`, `--encore-slate`, `--encore-teal`, `--encore-teal-light`, `--encore-teal-mid`, `--encore-gray`, `--encore-light`, `--encore-gold` | **Reserved for marketing/brand surfaces only** (login chrome, brand splash, PDF letterheads, marketing emails). Do not use in product UI without DS owner approval; product UI uses the semantic tokens above so PF-95 tenant theming overrides take effect. See [DESIGN\_TOKEN\_SYNC\_POLICY.md](DESIGN_TOKEN_SYNC_POLICY.md#deprecated--reserved-tokens). |

***

## Module Identity Tokens

Each domain core has a dedicated identity color used for navigation accents, dashboard widget headers, and badge tinting. Tokens follow the pattern `--module-{short-code}`, `--module-{short-code}-hover`, `--module-{short-code}-subtle`.

### Active short codes

| Short code | Module                    | Tailwind utility example                                  |
| ---------- | ------------------------- | --------------------------------------------------------- |
| `pf`       | Platform Foundation       | `bg-module-pf`, `bg-module-pf-subtle`, `border-module-pf` |
| `cl`       | Clinical                  | `bg-module-cl`, `bg-module-cl-subtle`, `border-module-cl` |
| `pm`       | Practice Management       | `bg-module-pm`, `bg-module-pm-subtle`, `border-module-pm` |
| `fm`       | Fleet Management          | `bg-module-fm`, `bg-module-fm-subtle`, `border-module-fm` |
| `rh`       | Recovery Housing          | `bg-module-rh`, `bg-module-rh-subtle`, `border-module-rh` |
| `it`       | IT & Security             | `bg-module-it`, `bg-module-it-subtle`, `border-module-it` |
| `hr`       | Human Resources           | `bg-module-hr`, `bg-module-hr-subtle`, `border-module-hr` |
| `lo`       | Learning & Organizational | `bg-module-lo`, `bg-module-lo-subtle`, `border-module-lo` |
| `ce`       | Community Engagement      | `bg-module-ce`, `bg-module-ce-subtle`, `border-module-ce` |
| `fa`       | Finance & Accounting      | `bg-module-fa`, `bg-module-fa-subtle`, `border-module-fa` |
| `gr`       | Governance & Risk         | `bg-module-gr`, `bg-module-gr-subtle`, `border-module-gr` |
| `fw`       | Forms & Workflow          | `bg-module-fw`, `bg-module-fw-subtle`, `border-module-fw` |

### Reserved (do not use yet)

| Short code | Status   | Notes                                                                              |
| ---------- | -------- | ---------------------------------------------------------------------------------- |
| `th`       | Reserved | Pre-allocated for a future module; declared in `src/index.css` but no consumer yet |
| `qo`       | Reserved | Same                                                                               |
| `da`       | Reserved | Same                                                                               |
| `rpt`      | Reserved | Same                                                                               |

Do not consume these in product UI without DS owner approval. See [DESIGN\_TOKEN\_SYNC\_POLICY.md](DESIGN_TOKEN_SYNC_POLICY.md#deprecated--reserved-tokens).

### Deprecated legacy aliases

| Alias                  | Use instead    | Notes                                                                |
| ---------------------- | -------------- | -------------------------------------------------------------------- |
| `--module-clinical*`   | `--module-cl*` | Alias-only, no consumers in `src/`; will be removed in next DS major |
| `--module-finance*`    | `--module-fa*` | Same                                                                 |
| `--module-operations*` | `--module-fw*` | Same                                                                 |
| `--module-people*`     | `--module-hr*` | Same                                                                 |

### Recommended usage

* **Sidebar / nav row accent:** 4px left border using `border-l-4 border-module-{id}` for the active module. Reinforces a "color = module" mental model across the 12-core ERP.
* **Dashboard widget header underline:** `border-b border-module-{id}` so each module's hub feels visually anchored.
* **Module badges:** `bg-module-{id}-subtle text-module-{id}` for tags that identify which module owns an entity (e.g. cross-cutting reports list).
* **Do NOT** use module colors for status/state — use semantic status tokens (`success`, `warning`, `destructive`, `info`) for that.

***

## Chart Palette

Charts (Recharts, SVG, custom) MUST use the **chart token palette** so series colors stay consistent across modules and adapt to dark mode.

### Tokens

| Token       | Tailwind utility                                               | Approx. light hue | Approx. dark hue |
| ----------- | -------------------------------------------------------------- | ----------------- | ---------------- |
| `--chart-1` | `text-chart-1`, `bg-chart-1`, `fill-chart-1`, `stroke-chart-1` | Sky blue 49%      | Sky blue 55%     |
| `--chart-2` | `text-chart-2`, `bg-chart-2`, …                                | Teal 40%          | Teal 50%         |
| `--chart-3` | `text-chart-3`, …                                              | Violet 66%        | Violet 72%       |
| `--chart-4` | `text-chart-4`, …                                              | Amber 50%         | Amber 55%        |
| `--chart-5` | `text-chart-5`, …                                              | Pink 60%          | Pink 66%         |
| `--chart-6` | `text-chart-6`, …                                              | Emerald 39%       | Emerald 45%      |
| `--chart-7` | `text-chart-7`, …                                              | Orange 53%        | Orange 58%       |
| `--chart-8` | `text-chart-8`, …                                              | Indigo 67%        | Indigo 72%       |

### Mandatory rules

* **Always use `hsl(var(--chart-N))` in chart configs** (Recharts `stroke` / `fill`, SVG `<rect fill={...}>`, etc.). Never hardcode hex literals or raw HSL strings — they break dark mode and tenant rebrand.
* **Use Tailwind utilities** (`text-chart-1`, `bg-chart-2`, …) for legend swatches, KPI tile accents, etc.
* **Cap series at 8 categories** before introducing a "(other)" bucket. Beyond 8, sequential blending or repetition produces unreadable charts.
* **Pair with `ChartContainer`** from shadcn-charts (`src/shared/ui/chart.tsx`) when possible; pass `theme: { light: 'hsl(var(--chart-N))', dark: '...' }` so series colors remain theme-aware.

### Colorblind safety

**Audit status:** First formal audit completed **2026-04-18** via `npm run audit:chart-palette-colorblind`. Method: Brettel-Viénot-Mollon (single-projection) simulation under deuteranopia, protanopia, and tritanopia, with pairwise CIE76 ΔE in CIELAB across all 28 category pairs.

**Result:** ⚠ The current palette is **not safe for color-only category encoding** for colorblind users. **9 fail pairs (ΔE \< 10)** and **25 warn pairs (ΔE 10–25)** across both modes × 3 profiles. Full report: `reports/audits/chart-palette-colorblind-audit.json`.

**Mandatory rule:** Charts MUST pair the chart palette color with at least one of: a category label, a marker shape, a pattern fill, or a series-name annotation. Color is for *recognition*, not for *category identity*. Never rely on color alone to distinguish data series.

#### Known problem pairs

| Pair                                   | Light min ΔE | Worst profile | Notes                                                                         |
| -------------------------------------- | ------------ | ------------- | ----------------------------------------------------------------------------- |
| `chart-2` (teal) ↔ `chart-6` (emerald) | 3.8          | tritanopia    | Indistinguishable in tritanopia (both collapse to teal); 18.2 in deuteranopia |
| `chart-2` (teal) ↔ `chart-5` (pink)    | 5.4          | deuteranopia  | Both collapse to gray                                                         |
| `chart-1` (sky) ↔ `chart-3` (violet)   | 5.8          | deuteranopia  | Both collapse to mid-blue                                                     |
| `chart-4` (amber) ↔ `chart-7` (orange) | 8.6          | deuteranopia  | Both collapse to yellow-brown                                                 |
| `chart-1` (sky) ↔ `chart-2` (teal)     | 9.6          | tritanopia    | Both collapse to cyan                                                         |

#### Action items (deferred to future palette-revision spec)

* Replace one of `chart-2` / `chart-6` to break the tritanopia teal/emerald collapse (e.g. swap `chart-6` to a yellow-green or olive).
* Replace one of `chart-4` / `chart-7` to break the amber/orange collapse (e.g. swap `chart-7` to a brown or burgundy).
* Replace one of `chart-1` / `chart-3` to break the sky/violet collapse (e.g. swap `chart-3` to a magenta with more red than blue).
* Re-run `npm run audit:chart-palette-colorblind --strict` after each swap; goal is **0 fail pairs** before wiring strict mode into `validate:governance`.

**Why ship the audit before fixing the palette?** The current palette is in production and changing series colors retrospectively would re-skin every chart in the platform. The audit + mandatory pairing rule mitigates the risk today; the swap is a coordinated visual-regression PR that needs DS owner sign-off.

***

## Tenant Theming (PF-95) Mapping Table

PF-95 (Tenant White-Labeling) lets each org override a subset of platform colors. The mapping below documents which platform tokens are tenant-customizable today and which remain platform-controlled. The primary admin UI is `BrandingSettingsPage` (`src/platform/theming/pages/BrandingSettingsPage.tsx`), which writes to `pf_tenant_themes`; on load, `TenantThemeProvider` injects `--tenant-*` overrides at `:root`.

| PF-95 field (`ThemeColors`) | Resolves to (runtime)         | Tailwind utility                               | Notes                                                                                                                                                                                                                                                                                  |
| --------------------------- | ----------------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `colorPrimary`              | `--primary`                   | `bg-primary`, `text-primary`                   |                                                                                                                                                                                                                                                                                        |
| `colorSecondary`            | `--secondary` (raw HSL)       | (consumed by PF-95 + custom CSS)               | **Known limitation:** the Tailwind utility `bg-secondary` resolves to `--card-elevated` via `@theme inline` alias. PF-95 sets the raw `--secondary` value but `bg-secondary` may not visually update. See [DESIGN\_TOKEN\_SYNC\_POLICY.md](DESIGN_TOKEN_SYNC_POLICY.md#token-aliases). |
| `colorAccent`               | `--accent`                    | `bg-accent`, `text-accent`                     | Brand CTA color                                                                                                                                                                                                                                                                        |
| `colorBackground`           | `--background`                | `bg-background`                                | Page background                                                                                                                                                                                                                                                                        |
| `colorSurface`              | `--card-elevated`             | `bg-card-elevated`                             | PF-95 calls this "surface"; runtime calls it `card-elevated`                                                                                                                                                                                                                           |
| `colorText`                 | `--text-main`, `--foreground` | `text-foreground`                              |                                                                                                                                                                                                                                                                                        |
| `colorTextMuted`            | `--text-muted`                | `text-text-muted` (or `text-muted-foreground`) | The canonical Tailwind utility for de-emphasized body text is `text-muted-foreground` — `--text-muted` is the underlying value                                                                                                                                                         |
| `colorDestructive`          | `--destructive`               | `text-destructive`, `bg-destructive`           |                                                                                                                                                                                                                                                                                        |
| `colorSuccess`              | `--success`                   | `text-success`, `bg-success`                   |                                                                                                                                                                                                                                                                                        |
| `colorWarning`              | `--warning`                   | `text-warning`, `bg-warning`                   |                                                                                                                                                                                                                                                                                        |

### Gaps (not tenant-customizable today)

These tokens exist at runtime but are **not** exposed to tenant overrides. To customize they require a PF-95 v2 expansion (separate spec):

| Token family                                     | Examples                            | Why it matters                                                    |
| ------------------------------------------------ | ----------------------------------- | ----------------------------------------------------------------- |
| `--info` / `--info-subtle` / `--info-foreground` | `text-info`, `bg-info-subtle`       | Tenants cannot rebrand the informational blue                     |
| `--accent-hover`, `--primary-hover`              | `hover:bg-accent-hover`             | Hover states keep platform default even when accent is overridden |
| `--module-{id}*`                                 | `bg-module-cl-subtle`, etc.         | Tenant cannot rebrand the 12 module identity colors               |
| `--chart-1` … `--chart-8`                        | `text-chart-1`, `bg-chart-2`        | Tenant cannot rebrand the data-viz palette                        |
| `--sidebar-*`                                    | `bg-sidebar`, `bg-sidebar-accent`   | Tenant cannot independently rebrand the sidebar shell             |
| `--muted`, `--muted-foreground`                  | `bg-muted`, `text-muted-foreground` | De-emphasized chrome stays platform-default                       |

### What tenants always get for free

* **Tenant primary / accent are mode-aware:** the `--tenant-*` override applies to both light and dark mode at `:root`; the platform handles dark-mode adaptation downstream.
* **Contrast validation:** `validateThemeContrast` (in `src/platform/theming/utils/contrast.ts`) enforces WCAG 2.1 AA on text-on-background pairs and 3:1 large-text on accent/destructive at save time.
* **Single active theme per org** is enforced by partial unique index + DB trigger.

***

## Compliance Theming (PF-91-EN-01) Mapping

PF-91-EN-01 (White-Label Compliance Dashboards) propagates PF-95 tenant theming to compliance surfaces (`/settings/compliance/*`) and edge-function exports.

* **Frontend:** `useComplianceTheme()` (`src/platform/compliance/`) reads the active tenant theme and injects `--tenant-*` CSS custom properties on the compliance page container. `useComplianceContrastValidator` flags any tenant color that fails WCAG AA on the regulatory dashboard and shows a `<ContrastFallbackIndicator>` to admins.
* **Immutable text:** Apply the `compliance-text-immutable` class (`src/platform/compliance/compliance-immutable.css`) to regulatory or legal text that must never re-skin to a tenant brand. The class forces platform-default colors via `!important`.
* **Edge function:** `_shared/compliance-contrast.ts` (Deno) exports `PLATFORM_DEFAULTS` (HEX) which mirrors `DEFAULT_THEME_COLORS` (HEX) in `src/platform/theming/types.ts`. The `generate-compliance-evidence` edge function uses these for evidence-package metadata (`branded: true/false`, `contrast_validated`).
* **Parity:** All three sources (`src/index.css`, `DEFAULT_THEME_COLORS`, `PLATFORM_DEFAULTS`) MUST resolve to the same semantic palette. The `npm run audit:token-parity` script (Phase 4 of the color-system review) will enforce this contract.

***

## Audit Checklist (extended)

Before committing UI changes, verify (in addition to the original checklist above):

* [ ] No raw hex in chart configs — use `hsl(var(--chart-N))`
* [ ] **New chart with categorical color encoding pairs each color with a label, shape, or pattern** (per the colorblind audit findings above)
* [ ] Module colors use short-code tokens (`--module-cl`), not legacy aliases (`--module-clinical`)
* [ ] Tenant-customizable surfaces use semantic tokens — not encore brand or hex literals — so PF-95 overrides take effect
* [ ] If you introduce a new `--tenant-*` consumer, add it to the **PF-95 Mapping Table** above
* [ ] If you change any `--chart-N` value, re-run `npm run audit:chart-palette-colorblind` and update the Known problem pairs table
* [ ] If you add a new shadcn-canonical token, register it in `REQUIRED_TOKENS` in `scripts/audit/verify-shadcn-tokens.mjs` AND define it under both `:root` and `.dark` in `src/index.css`. Run `npm run audit:shadcn-tokens` to confirm.

***

## Token Coverage Gate

The script `scripts/audit/verify-shadcn-tokens.mjs` (npm: `audit:shadcn-tokens`,
chained into `validate:governance`) statically asserts that every
shadcn-canonical theming token is declared in **both** the `:root` and `.dark`
blocks of `src/index.css`. Drift fails the build with a grouped diff of which
tokens are missing in which block.

**Audited tokens (33):**

* Surface + foreground pairs: `background`/`foreground`, `card`/`card-foreground`,
  `popover`/`popover-foreground`, `primary`/`primary-foreground`,
  `secondary`/`secondary-foreground`, `muted`/`muted-foreground`,
  `accent`/`accent-foreground`, `destructive`/`destructive-foreground`
* Singles: `border`, `input`, `ring`
* Chart palette: `chart-1` … `chart-5`
* Sidebar: `sidebar-background`, `sidebar-foreground`, `sidebar-primary`,
  `sidebar-primary-foreground`, `sidebar-accent`, `sidebar-accent-foreground`,
  `sidebar-border`, `sidebar-ring`
* Radius: `radius`

Project extensions (`success`/`warning`/`info` and their `*-foreground` pairs,
`--encore-*`, `--module-*`, `--tenant-*`) are intentionally **not** covered by
this gate — they're enforced by `audit:token-parity` and the PF-95 mapping
table above. To extend the gate, edit `REQUIRED_TOKENS` in the script and add
the matching declarations to `src/index.css`.

***

## Related Documentation

* `constitution.md §6.7` - UI/UX Guardrails
* `docs/development/UI_UX_STANDARDS.md` - Full UI/UX Standards
* `docs/development/DESIGN_TOKEN_SYNC_POLICY.md` - Token governance, parity contract, deprecated/reserved tokens
* `src/index.css` - Canonical token definitions (`:root`, `.dark`, `@theme inline`)
* `src/shared/lib/semantic-colors.ts` - Utility functions
* `src/shared/ui/badge.tsx` - Badge component with variants
* `src/platform/theming/` - PF-95 tenant theming (types, hooks, components)
* `src/platform/compliance/` - PF-91-EN-01 white-label compliance dashboards
* `tests/unit/shared/lib/semantic-colors.test.ts` - Regression tests for semantic class set
