> ## 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.

# UI/UX Standards

> Version: 1.6.1 Last Updated: 2026-04-18 Status: Active Target Audience: Developers and AI agents (GitHub Copilot, Cursor, general AI assistants)

**Version:** 1.6.1
**Last Updated:** 2026-04-18
**Status:** Active
**Target Audience:** Developers and AI agents (GitHub Copilot, Cursor, general AI assistants)

> **Constitution Reference:** See `constitution.md` (current: see docs/VERSIONS.md) §6.7 \[UI/UX Guardrails] for non-negotiable requirements.
> **Quick Reference:** See root `AGENTS.md` (current: see docs/VERSIONS.md) §\[Quick Reference] for condensed guidelines.
> **Color Utilities:** See `docs/development/SEMANTIC_COLORS.md` (current: see docs/VERSIONS.md) for color mapping patterns.
> **Spacing Patterns:** See [SPACING\_AND\_UX\_STANDARDS.md](SPACING_AND_UX_STANDARDS.md) (current: see docs/VERSIONS.md) for detailed spacing patterns and responsive design guidelines.
> **Tab Patterns:** See [.cursor/rules/tab-patterns.md](../../.cursor/rules/tab-patterns.md) for the canonical tab variant decision table, codemod recipes, and accessibility rules.

***

**AI agents:** Use semantic tokens, skeleton loaders (never `return null`), responsive spacing, and `@/shared/ui` components. Full pattern list: [AGENTS.md](../../AGENTS.md), [.cursor/rules/quick-reference.mdc](../../.cursor/rules/quick-reference.mdc).

## Design Token Governance

The canonical token source for app/runtime parity is governed by:

* [`DESIGN_TOKEN_SYNC_POLICY.md`](DESIGN_TOKEN_SYNC_POLICY.md)

This policy defines:

* source-of-truth files and ownership,
* hard-locked vs app-extensible token families,
* backward-compat alias expectations,
* required visual and validation checks before merge.

All token changes in `src/index.css` must follow this policy.

***

## Quick Reference

### Common Patterns

**Semantic Color Tokens:**

```tsx theme={null}
// ✅ CORRECT
<Badge variant="success">Active</Badge>
<div className="bg-warning/10 text-warning-foreground">Warning</div>
<Button variant="destructive">Delete</Button>

// ❌ WRONG
<Badge className="bg-green-500">Active</Badge>
<div className="text-yellow-600">Warning</div>
```

**Component Variants:**

* `success` - Positive states (active, approved, complete)
* `warning` - Caution states (pending, at-risk, expiring)
* `destructive` - Error/danger states (rejected, error, critical)
* `info` - Informational states (in progress, new)
* `secondary` - Neutral/inactive states

**Loading States:**

```tsx theme={null}
// ✅ ALWAYS use skeleton loaders
if (isLoading) return <AppLoadingSkeleton />;

// ❌ NEVER return null
if (isLoading) return null;
```

**Spacing:** Use responsive spacing; see [SPACING\_AND\_UX\_STANDARDS.md](SPACING_AND_UX_STANDARDS.md) for patterns and breakpoints.

### Token Usage

**Colors:** Always use semantic tokens (`success`, `warning`, `destructive`, `primary`, `muted`)\
**Spacing:** Use responsive spacing (`space-y-4 md:space-y-6`)\
**Typography:** Use font utilities (`font-sans`, `font-serif`, `font-mono`)\
**Components:** Use variant props (`variant="success"`, `variant="destructive"`)

### Empty States

For list or content areas with no data (e.g. hub tab content, pipeline stages, credential renewals), use the shared **EmptyState** component from `@/shared/components/EmptyState` with a title, optional description, and primary CTA (e.g. "Add credential", "Create job posting"). Do not leave content areas blank; every empty list or empty tab view must show an explicit empty state. Avoid custom empty layouts (custom Card + icon + text + button); use a thin wrapper around EmptyState when you need domain-specific copy or branching. See constitution §6.7 (widgets handle empty states).

### Overlay naming

Use **\*Dialog** or **\*Sheet** in component names for overlay surfaces (e.g. `ClaimGenerateDialog`, `MobileMenuSheet`). Do not use *Modal* in names; the implementation should use `Dialog` or `Sheet` from `@/shared/ui`.

### N/A and Zero States

Where a KPI or count is not applicable (e.g. no data yet, metric does not apply), display "—" or "N/A" with an optional tooltip explaining why, instead of "0" or a blank value, to avoid implying incorrect data.

### Hub Entry Copy (Optional)

Hub pages MAY include a one-line "How to use" under the hub title (e.g. "Use the tabs below to switch between Report, Renewals, and My Credentials") for first-time users. If used, keep the pattern consistent across hubs.

**For detailed patterns and examples, see sections below.**

***

## Table of Contents

1. [Design System](#design-system)
2. [Button Usage](#button-usage)
3. [Tabs](#tabs)
4. [Page Templates](#page-templates)
5. [Navigation & Information Architecture](#navigation--information-architecture)
6. [Mobile-First Requirements](#mobile-first-requirements)
7. [Performance UX](#performance-ux)
8. [Accessibility](#accessibility)
9. [Related Documentation](#related-documentation)

***

## Design System

### Token System

The platform uses a **token-based design system** with HSL CSS variables mapped to Tailwind utilities using Tailwind CSS v4 CSS-first configuration. All colors, spacing, typography, and other design tokens are defined in `src/index.css`.

**Implementation References:**

* CSS Variables + token mappings: `src/index.css` (`@theme inline`, `:root`, `.dark`)

### Colors

#### Semantic Colors

**Required:** Always use semantic color tokens, never hardcoded Tailwind color classes.

**Available Semantic Colors:**

* `primary` / `primary-foreground` - Primary brand color (deep navy chrome accent; not the main CTA — use `accent`)
* `secondary` / `secondary-foreground` - Secondary actions (note: `bg-secondary` is aliased to `--card-elevated`)
* `accent` / `accent-foreground` - **Brand CTA color** (teal); pair with `Button variant="accent"` for the main action
* `destructive` / `destructive-foreground` - Errors, destructive actions
* `success` / `success-foreground` - Success states, positive feedback
* `warning` / `warning-foreground` - Warnings, caution states
* `info` / `info-foreground` - Informational states (a distinct blue, not a primary tint); pair `bg-info` with `text-info-foreground` for proper dark-mode contrast
* `muted` / `muted-foreground` - Muted text, disabled states
* `card` / `card-foreground` - Card backgrounds
* `popover` / `popover-foreground` - Popover/dropdown backgrounds

> **Full token catalog:** See [SEMANTIC\_COLORS.md § Token Catalog Reference](SEMANTIC_COLORS.md#token-catalog-reference) for the complete inventory, including subtle variants, hover tokens, sidebar slot, special tokens (overlay-scrim, ai-accent, glass, shimmer-highlight), and the **Encore brand reserved set**.

**Usage Examples:**

```tsx theme={null}
// ✅ CORRECT: Use semantic tokens
<Badge variant="success">Published</Badge>
<div className="bg-warning/10 text-warning-foreground">Warning message</div>
<Button variant="destructive">Delete</Button>

// ❌ WRONG: Hardcoded colors
<Badge className="bg-green-500">Published</Badge>
<div className="text-yellow-600">Warning message</div>
<Button className="bg-red-500">Delete</Button>
```

**Anti-Patterns:**

* ❌ Using `text-green-500`, `bg-red-100`, etc. (hardcoded Tailwind colors)
* ❌ Using `text-[#FF0000]` (arbitrary color values)
* ✅ Use semantic tokens: `text-success`, `bg-destructive/10`, etc.

#### Status Colors

**Status indicators** should use semantic color variants:

* **Success:** `success` token (green) - Completed, approved, published
* **Warning:** `warning` token (yellow/orange) - Pending, needs attention
* **Error:** `destructive` token (red) - Failed, rejected, errors
* **Info:** `info` token (blue) - Informational messages, in-progress states. **A distinct semantic state — not a primary tint.** Pair `bg-info` with `text-info-foreground` (a dedicated, mode-aware token); never with `text-primary-foreground` (which fails WCAG in dark mode).
* **Neutral:** `muted` token (gray) - Default, inactive states

#### Module Identity Colors

Each domain core has a dedicated identity color (`--module-{short-code}` plus `-hover` and `-subtle` variants). Use them for navigation accents, dashboard widget headers, and module badges — **never** for status/state.

```tsx theme={null}
// ✅ Module accent on a sidebar nav row
<a className="border-l-4 border-module-cl bg-module-cl-subtle/50">Clinical hub</a>

// ❌ Don't use module colors to convey status
<Badge className="bg-module-fa">Approved</Badge> // → Badge variant="success"
```

See [SEMANTIC\_COLORS.md § Module Identity Tokens](SEMANTIC_COLORS.md#module-identity-tokens) for the full short-code list (12 active + 4 reserved + 4 deprecated aliases) and recommended usage patterns.

#### Chart Palette

Chart configs MUST use `hsl(var(--chart-N))` (N = 1–8) for series colors — never hex literals or hardcoded HSL strings — so charts adapt to dark mode and respect tenant rebranding.

```tsx theme={null}
// ✅ Theme-aware chart series
<Bar dataKey="value" fill="hsl(var(--chart-1))" />

// ❌ Breaks dark mode and tenant theming
<Bar dataKey="value" fill="#3B82F6" />
```

See [SEMANTIC\_COLORS.md § Chart Palette](SEMANTIC_COLORS.md#chart-palette) for the full 8-color palette, the colorblind-safety note, and `ChartContainer` integration guidance.

### Typography

**Font Families:**

* **Display/Headings:** Space Grotesk - `font-heading`
* **Body/Sans:** Inter (primary UI font) - `font-sans`
* **Serif alias:** Inter fallback contract (matches body token) - `font-serif`
* **Mono:** DM Mono (for code, data) - `font-mono`

**Font Loading:**

* Fonts are loaded asynchronously in `index.html` (non-blocking); do not add `@import` for fonts in CSS.
* Critical inline faces preload Inter and Space Grotesk for first paint parity with design system.
* System font fallbacks are defined in CSS variables.

**Usage:**

* Default: `font-sans` (Inter) for all body/UI text.
* Headings: `font-heading` (Space Grotesk) for page titles and heading hierarchy.
* Mono: `font-mono` (DM Mono) for code blocks, data tables, and technical strings.
* Tenant overrides:
  * `--tenant-font-family` overrides body typography,
  * `--tenant-font-heading` overrides heading typography.
    If unset, the canonical Inter + Space Grotesk contract applies.

### Spacing

Spacing scale, container padding, and responsive patterns are defined in [SPACING\_AND\_UX\_STANDARDS.md](SPACING_AND_UX_STANDARDS.md). Use that doc for breakpoints, card padding, and form layout.

### Button Usage

Use the shared `Button` from `@/shared/ui/button` with semantic variants (`default`, `accent`, `destructive`, `outline`, `secondary`, `ghost`, `link`, `success`, `warning`, `soft`, `glass`, `ai`) and sizes (`default`, `sm`, `xs`, `lg`, `icon`).

**Primary CTA (important):** The `default` variant is a **neutral elevated** control (card surface + border), not the brand primary. For the main action on a page, form, or dialog footer, use **`variant="accent"`** (teal / brand accent) unless a domain pattern explicitly calls for `success` / `destructive` / `warning`.

Icon-only buttons **must** have `aria-label`. Prefer the `loading` prop for async actions. Dialog footers: Cancel left, primary right. Full guidance: [BUTTON\_USAGE.md](BUTTON_USAGE.md).

### Icons

**Source:** Use **lucide-react** only. Do not use heroicons, react-icons, or custom SVG icon sets.

**Size scale:** Use the design token scale for consistency. CSS variables are in `src/index.css` (`--icon-xs` … `--icon-lg`); use the equivalent Tailwind classes when applying to icons:

| Token     | Size | Tailwind      | Use case                                           |
| --------- | ---- | ------------- | -------------------------------------------------- |
| `icon-xs` | 14px | `h-3.5 w-3.5` | Inline, tight UI (e.g. trend indicators in tables) |
| `icon-sm` | 16px | `h-4 w-4`     | List items, buttons, form controls                 |
| `icon-md` | 20px | `h-5 w-5`     | Nav items, section headers, menu items             |
| `icon-lg` | 24px | `h-6 w-6`     | Empty states, feature icons, page-level visuals    |

**Decorative only:** For large hero or empty-state icons use `h-8 w-8` (32px) or `h-12 w-12` (48px). These are not part of the standard token scale and should only be used for decorative/empty-state contexts.

**Icon-only buttons:** Use `size="icon-touch"` on `Button` for icon-only actions so the tap target is at least 44×44px (accessibility). Use `size="icon"` only when the button is not the primary touch target (e.g. inside a larger clickable area).

**Nav and lists:** Standardize nav item icons to one size (e.g. `icon-md` / `h-5 w-5`). Use `icon-sm` for compact lists and data tables.

**Color:** Use semantic tokens for icon color (e.g. `text-muted-foreground`, `text-primary`, `text-success`). See [SEMANTIC\_COLORS.md](SEMANTIC_COLORS.md).

**`AppIcon` wrapper (available now, preferred for shared code):** Use `AppIcon` from `@/shared/ui/icon` for shared/reusable components. It bakes in the platform stroke width (`1.75`), the size scale (`xs`/`sm`/`md`/`lg`), and the decorative/`aria-label` accessibility contract:

```tsx theme={null}
<AppIcon icon={Check} size="md" className="text-success" />
<AppIcon icon={Trash2} size="sm" decorative={false} aria-label="Delete" />
```

For accent tones: use `tone="accent"` on subtle accent surfaces (`bg-accent/10`, `bg-accent/20`) and reserve `tone="onAccent"` for solid accent chips (`bg-accent`).

Tailwind size classes (see table above) remain acceptable for trivial one-off icons inside leaf components. See [ICON\_GUIDE.md § AppIcon wrapper](ICON_GUIDE.md) for the full prop table.

For **which icon to use**, sizing, import patterns, and accessibility, see [ICON\_GUIDE.md](ICON_GUIDE.md).

### Component Variants

#### Badge Variants

**Available Variants:**

* `default` - Primary actions, highlights
* `secondary` - Secondary information
* `destructive` - Errors, deletions
* `outline` - Subtle indicators
* `success` - Success states
* `warning` - Warning states
* `info` - Informational states (in progress, new)
* Light variants: `success-light`, `warning-light`, `destructive-light`, `primary-light`
* Specialty: `accentGlow`, `glass`, `ai` (non-status use cases)

**Usage:**

```tsx theme={null}
<Badge variant="success">Published</Badge>
<Badge variant="warning">Pending</Badge>
<Badge variant="destructive">Failed</Badge>
<Badge variant="info">In Progress</Badge>
<Badge variant="outline">Draft</Badge>
```

See [SEMANTIC\_COLORS.md](SEMANTIC_COLORS.md) for complete variant reference and status mapping patterns.

#### Button Variants

**Available Variants:**

* `default` - Neutral elevated (card-like surface); use for secondary emphasis, not the single main CTA
* `accent` - **Brand primary CTA** (accent teal); default choice for “Save”, “Submit”, “Continue” when one action dominates
* `destructive` - Destructive actions
* `outline` - Secondary actions, toolbars
* `secondary` - Tertiary / muted actions
* `ghost` - Subtle actions
* `link` - Link-style buttons
* `success`, `warning`, `soft`, `glass`, `ai` - See JSDoc on `src/shared/ui/button.tsx`

**Sizes:**

* `default` - `h-10 px-4 py-2`
* `sm` - `h-9 px-3`
* `lg` - `h-11 px-8`
* `icon` / `icon-touch` - `h-11 w-11` min 44×44px (square icon buttons; use `icon-touch` for icon-only actions to meet touch target size)

> **Note (2026-04-18 audit):** The `Button` size tokens shipped in code currently differ slightly from the table above (`default h-9`, `sm h-8`, `lg h-10`, `icon size-11`, plus an undocumented `xs h-7`). Reconciliation is tracked in `reports/ux/ux-implementation-plan-20260418.md` § Phase 1.2. Until that PR lands, prefer `Button size="default"` and trust the variant defaults rather than asserting on the height.

***

## Tabs

**Canonical rule file:** [`.cursor/rules/tab-patterns.md`](../../.cursor/rules/tab-patterns.md).
**Source primitives:** `@/shared/ui/tabs` (`Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`, `TabBadge`), `@/shared/components/ScrollableTabsList`, `@/platform/tabs/useTabUrlState`, `@/platform/settings/components/SettingsTabs`.

### Variant decision table

| Page class                                                               | Variant   | Wrapper                                                             | Size      | URL state                             | Icons       |
| ------------------------------------------------------------------------ | --------- | ------------------------------------------------------------------- | --------- | ------------------------------------- | ----------- |
| Module **hub** page (`/{core}/dashboard`, `/{core}/{hub}`)               | `default` | `ScrollableTabsList` + `tabsListClassName="sm:grid sm:grid-cols-N"` | `default` | **Required** (`useTabUrlState`)       | All-or-none |
| **Sub-module** workspace tabs                                            | `default` | `ScrollableTabsList`                                                | `default` | **Required**                          | All-or-none |
| Module/entity **settings** page                                          | `line`    | `SettingsTabs` (which uses `ScrollableTabsList`)                    | `default` | Optional                              | Optional    |
| **Detail page** section switcher (e.g. employee profile, vendor profile) | `line`    | `ScrollableTabsList variant="line"`                                 | `default` | **Required** for shareable deep links | Optional    |
| **Form dialog** internal sections                                        | `default` | none                                                                | `default` | No                                    | All-or-none |
| **In-card** section switcher (within a `<Card>`)                         | `default` | none                                                                | `compact` | No                                    | None        |
| **Filter / view-mode** toggles                                           | `pill`    | none                                                                | `compact` | Optional                              | None        |

### What AI must NEVER do (tabs)

* ❌ `<TabsList className="grid w-full grid-cols-N">` — equal-width cells break at narrow viewports, no scroll affordance. Wrap with `ScrollableTabsList tabsListClassName="sm:grid sm:grid-cols-N"`.
* ❌ `<TabsList className="flex-wrap h-auto">` — wrapping creates uneven heights and breaks the page's vertical rhythm. Use `ScrollableTabsList` and let it scroll horizontally instead.
* ❌ Inline `h-N` overrides on `<TabsList>` (`h-8`, `h-10`, `h-11`). Use `size="compact"` only when you need denser controls (e.g. inside a card).
* ❌ Inline `(${count})` in trigger labels. Use `<TabBadge count={n} />` for the canonical pill style and the `aria-label="${count} items"` accessibility contract.
* ❌ `<TabsList>` without `aria-label` (or `aria-labelledby`). Pass an explicit label when there is no visible heading.
* ❌ Mobile collapse to icon-only that drops the visible label. At `<sm`, tabs MUST scroll horizontally OR collapse into a Sheet picker.

### Quick recipes

**URL-synced hub** (deep-link friendly, refresh-stable):

```tsx theme={null}
import { useTabUrlState } from '@/platform/tabs';
import { ScrollableTabsList } from '@/shared/components/ScrollableTabsList';
import { Tabs, TabsTrigger } from '@/shared/ui/tabs';

const VALID_TABS = ['accounts', 'statements', 'reconciliations', 'transactions'] as const;
const [tab, setTab] = useTabUrlState('tab', VALID_TABS, 'accounts');

<Tabs value={tab} onValueChange={(v) => setTab(v as (typeof VALID_TABS)[number])}>
  <ScrollableTabsList tabsListClassName="sm:grid sm:grid-cols-4" aria-label="Banking sections">
    {VALID_TABS.map((t) => (
      <TabsTrigger key={t} value={t} className="snap-start">{labelFor(t)}</TabsTrigger>
    ))}
  </ScrollableTabsList>
</Tabs>
```

**Settings page** — use `SettingsTabs`:

```tsx theme={null}
import { SettingsTabs } from '@/platform/settings/components/SettingsTabs';

<SettingsTabs
  defaultValue="configuration"
  tabsListClassName="sm:grid sm:grid-cols-5"
  items={[
    { value: 'configuration', label: <><Settings className="size-4" aria-hidden="true" /> Configuration</>, content: <ConfigurationTab /> },
    { value: 'transaction-rules', label: <><Filter className="size-4" aria-hidden="true" /> Transaction Rules</>, content: <TransactionRulesTab /> },
    // ...
  ]}
/>
```

**Tab badges** — never inline `(${count})`:

```tsx theme={null}
import { TabsTrigger, TabBadge } from '@/shared/ui/tabs';

<TabsTrigger value="pending">
  Pending <TabBadge count={pendingCount} variant="destructive" />
</TabsTrigger>
```

**Migration guidance:** see `reports/ux/tabs-consistency-audit-20260418.md` (full inventory + before/after recipes) and `reports/ux/ux-implementation-plan-20260418.md` § Phase 3 for the rollout plan and codemods.

***

## Page Templates

### Required Page States

Every page MUST implement these states:

#### 1. Loading State

**Requirement:** Use skeleton loaders, never `return null`.

```tsx theme={null}
// ✅ CORRECT
if (loading) return <PageSkeleton />;

// ❌ WRONG
if (loading) return null;
```

**Skeleton Patterns by Page Type:**

* **List Pages:** Grid of skeleton cards/rows
* **Detail Pages:** Skeleton header + content sections
* **Dashboards:** Skeleton stat cards + chart placeholders
* **Forms:** Skeleton form fields

**Implementation:**

* Route-level: `RouteLoadingSkeleton` (already implemented)
* Component-level: Use `Skeleton` component from `@/shared/ui/skeleton`

#### 2. Error State

**Requirement:** Show user-friendly error messages with recovery actions.

```tsx theme={null}
if (error) {
  return (
    <ErrorState
      title="Failed to load data"
      message={error.message}
      action={<Button onClick={retry}>Try Again</Button>}
    />
  );
}
```

**Error State Components:**

* Use `Alert` component with `destructive` variant
* Include retry/refresh action when applicable
* Show specific error message (not generic "Something went wrong")

#### 3. Empty State

**Requirement:** Show helpful empty states for lists and detail pages.

```tsx theme={null}
if (items.length === 0) {
  return (
    <EmptyState
      icon={FileText}
      title="No forms yet"
      description="Create your first form to get started"
      action={<Button onClick={createForm}>Create Form</Button>}
    />
  );
}
```

**Empty State Guidelines:**

* Include icon, title, description, and primary action
* Use friendly, helpful copy
* Provide clear next steps

### Page Template Primitives

**Available Components:**

* `PageContainer` - Responsive page wrapper with padding (`src/shared/components/PageContainer.tsx`)
* `OverviewPageWrapper` - Standardized wrapper for module overview pages with pull-to-refresh (`src/shared/components/OverviewPageWrapper.tsx`)
* `ResponsiveFormLayout` - Form field layout with responsive columns (`src/shared/components/ResponsiveFormLayout.tsx`)
* `MobileTableWrapper` - Responsive table wrapper for mobile (`src/shared/components/MobileTableWrapper.tsx`)

**Page Structure:**

```tsx theme={null}
<PageContainer spacing="md">
  <PageHeader title="Page Title" description="Page description" />
  <Card>
    <CardContent>
      {/* Page content */}
    </CardContent>
  </Card>
</PageContainer>
```

### Overview Pages

**Use `OverviewPageWrapper` for all module overview/landing pages:**

```tsx theme={null}
import { OverviewPageWrapper, PageHeader, StatCard } from '@/shared/components';

export default function HROverview() {
  return (
    <OverviewPageWrapper refreshQueryKeys={['hr']} spacing="md">
      <PageHeader
        title="Workforce & HR"
        description="Manage employees and departments"
        icon={Users}
      />
      
      <QuickActionsSection moduleId="hr" ... />
      
      <div className="grid gap-3 md:gap-4 lg:gap-6 md:grid-cols-3">
        {/* Stat cards */}
      </div>
      
      {/* Area navigation cards or widgets */}
    </OverviewPageWrapper>
  );
}
```

**Features:**

* Automatic mobile pull-to-refresh with query invalidation
* Consistent responsive padding and spacing
* Standardized page structure

**When to Use Which Template:**

* **Overview Pages:** `OverviewPageWrapper` for module landing pages
* **List Pages:** `PageContainer` + `Card` for filters + table/list
* **Detail Pages:** `PageContainer` + `Card` sections for different data groups
* **Dashboards:** `PageContainer` + grid of stat cards + widgets
* **Forms:** `PageContainer` + `ResponsiveFormLayout` for form fields

***

## Navigation & Information Architecture

### Route Taxonomy

**Current Standard:** Flat route structure with nested sub-modules where appropriate.

**Route Patterns:**

* **Module Overview:** `/{module}/dashboard` (e.g., `/hr/dashboard`)
* **Module List:** `/{module}/{entity}` (e.g., `/hr/employees`)
* **Module Detail:** `/{module}/{entity}/:id` (e.g., `/hr/employees/123`)
* **Sub-Module:** `/{module}/{sub-module}/{entity}` (e.g., `/hr/ats/jobs`)

**Route Naming:**

* Use kebab-case: `/hr/employee-directory` not `/hr/employeeDirectory`
* Use plural nouns for list routes: `/hr/employees` not `/hr/employee`
* Use singular for detail routes when appropriate: `/hr/employees/:id`

### Breadcrumbs

**When to Show:**

* Detail pages (3+ route segments)
* Nested sub-module pages
* Settings pages with hierarchy

**When NOT to Show:**

* Module overview pages (`/hr/dashboard`)
* Top-level list pages (`/hr/employees`)
* Home/dashboard (`/`)

**Breadcrumb Standards:**

* **Desktop:** Full breadcrumb trail in header (via `Breadcrumbs.tsx`)
* **Mobile:** Truncated to last 2 segments (via `MobileBreadcrumbs.tsx`)
* **Depth Limit:** Maximum 5 segments (truncate if deeper)

**Implementation:**

* Auto-generated from route (via `route-labels.ts`)
* Explicit breadcrumbs via `PageContainer` `breadcrumbs` prop for dynamic entity names

### Module/Sub-Module Navigation

**Navigation Modes:**

1. **All Modules:** Shows all available modules (home, module switcher)
2. **In Module:** Shows module-specific navigation (sidebar with nav items)
3. **In Sub-Module:** Shows sub-module navigation with "Back to Parent" button

**Sub-Module Pattern:**

* Sub-modules have `overviewRoute` (e.g., `/hr/ats`)
* Sub-modules have `navGroups` or `navItems` for sub-navigation
* Sub-modules show context indicator in mobile header

**Permission Filtering:**

* Navigation items filtered by `minRole` (V1) or `permission` (V2)
* Use `useFilteredNavItems` hook for permission-aware filtering

**Related Documentation:**

* `docs/architecture/standards/NAVIGATION_STANDARD.md` - Navigation patterns and policies
* `docs/development/mobile-navigation-guide.md` - Mobile navigation patterns
* `docs/development/breadcrumb-implementation-guide.md` - Breadcrumb implementation
* `src/platform/navigation/README.md` - Navigation component documentation

***

## Mobile-First Requirements

### Touch Targets

**Requirement:** All interactive elements MUST meet minimum touch target size.

**Minimum Size:** 44x44px (iOS HIG, Material Design)

**Implementation:**

```tsx theme={null}
// ✅ CORRECT: Meets minimum
<Button size="icon" className="h-10 w-10"> {/* 40px, but padding makes it 44px+ */}
  <Icon />
</Button>

// ✅ CORRECT: Explicit minimum
<div className="min-h-[44px] min-w-[44px] flex items-center justify-center">
  <Icon />
</div>

// ❌ WRONG: Too small
<button className="h-8 w-8"> {/* 32px - too small */}
  <Icon />
</button>
```

**Common Patterns:**

* Navigation items: `min-h-[44px]`
* Icon buttons: `h-10 w-10` (40px + padding = 44px+)
* Bottom nav items: `min-w-[56px] min-h-[56px]` (generous for thumb reach)

### Safe Areas

**Requirement:** Fixed elements (header, bottom nav) MUST respect safe area insets.

**Implementation:**

```tsx theme={null}
// ✅ CORRECT: Use safe area utilities
<header className="fixed top-0 safe-area-top">
  {/* Header content */}
</header>

<nav className="fixed bottom-0 safe-area-bottom">
  {/* Bottom nav */}
</nav>
```

**CSS Variables:**

* `--safe-area-inset-top` - iOS notch, Android status bar
* `--safe-area-inset-bottom` - iOS home indicator, Android nav bar
* `--safe-area-inset-left` / `--safe-area-inset-right` - Landscape safe areas

**Utility Classes:**

* `.safe-area-top` - Adds top padding
* `.safe-area-bottom` - Adds bottom padding
* `.safe-area-left` / `.safe-area-right` - Adds side padding

### Mobile Header Density

**Requirement:** Minimize vertical space consumed by mobile header chrome.

**Current Structure:**

1. Main header (64px) - Logo, menu, actions
2. Org switcher (48px) - Organization/site selector
3. Sub-module indicator (\~40px, conditional) - Current sub-module context
4. Breadcrumbs (\~40px) - Navigation path

**Guidelines:**

* **Collapse When Possible:** Combine org switcher into header row when space allows
* **Conditional Display:** Show breadcrumbs only on 3+ depth routes
* **Sub-Module Indicator:** Show only when in sub-module context

**Target:** Reduce total header height to \< 120px on most pages.

### Bottom Navigation

**Requirement:** Bottom nav provides quick access to key modules.

**Implementation:**

* Fixed bottom position with safe area inset
* 4-5 primary shortcuts (configurable per user/role)
* Long-press for module menu (if implemented)
* Active state indicator

**Touch Targets:**

* Minimum 56x56px per nav item (generous for thumb reach)
* Adequate spacing between items

**Related Documentation:**

* `docs/development/mobile-navigation-guide.md` - Complete mobile navigation guide

***

## Performance UX

### Skeleton Loaders

**Requirement:** All loading states MUST use skeleton loaders, never `return null`.

**Skeleton Standards by Page Type:**

#### List Pages

```tsx theme={null}
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
  {Array.from({ length: 6 }).map((_, i) => (
    <Card key={i}>
      <CardHeader>
        <Skeleton className="h-6 w-3/4" />
      </CardHeader>
      <CardContent>
        <Skeleton className="h-4 w-full mb-2" />
        <Skeleton className="h-4 w-2/3" />
      </CardContent>
    </Card>
  ))}
</div>
```

#### Detail Pages

```tsx theme={null}
<div className="space-y-6">
  <Skeleton className="h-8 w-1/3" /> {/* Title */}
  <Card>
    <CardHeader>
      <Skeleton className="h-6 w-1/4" />
    </CardHeader>
    <CardContent>
      <Skeleton className="h-4 w-full mb-4" />
      <Skeleton className="h-4 w-3/4" />
    </CardContent>
  </Card>
</div>
```

#### Dashboards

```tsx theme={null}
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
  {Array.from({ length: 4 }).map((_, i) => (
    <Card key={i}>
      <CardContent className="p-6">
        <Skeleton className="h-4 w-1/2 mb-2" />
        <Skeleton className="h-8 w-3/4" />
      </CardContent>
    </Card>
  ))}
</div>
```

### Route Code Splitting

**Requirement:** All route components MUST use `React.lazy()` for code splitting.

**Implementation:**

```tsx theme={null}
// ✅ CORRECT: Lazy load route
const Dashboard = lazy(() => import("./platform/dashboard/Dashboard"));

// ❌ WRONG: Direct import
import Dashboard from "./platform/dashboard/Dashboard";
```

**Suspense Boundaries:**

```tsx theme={null}
<Suspense fallback={<RouteLoadingSkeleton />}>
  <Routes>
    <Route path="/" element={<Dashboard />} />
  </Routes>
</Suspense>
```

### QueryClient Configuration

**Requirement:** QueryClient MUST have default staleTime and gcTime configured.

**Configuration:**

```tsx theme={null}
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 5 * 60 * 1000,  // 5 minutes
      gcTime: 10 * 60 * 1000,    // 10 minutes (formerly cacheTime)
      retry: 1,
      refetchOnWindowFocus: false,
    },
  },
});
```

### Performance Targets

**Lighthouse Scores:**

* **Performance:** 85+ (target: 90+)
* **Accessibility:** 95+ (target: 100)
* **Best Practices:** 90+ (target: 95+)
* **SEO:** 90+ (target: 95+)

**Core Web Vitals:**

* **First Contentful Paint (FCP):** \< 2s
* **Largest Contentful Paint (LCP):** \< 2.5s
* **Cumulative Layout Shift (CLS):** \< 0.1
* **Time to Interactive (TTI):** \< 3.5s (on 3G)

**Related Documentation:**

* `constitution.md` §\[Performance Requirements] - Non-negotiable performance rules

***

## Form UX Standards

### Form Layout

**Requirement:** Use `ResponsiveFormLayout` for all multi-field forms.

**Import:**

```tsx theme={null}
import { ResponsiveFormLayout } from '@/shared/components';
```

**Usage:**

```tsx theme={null}
<Form {...form}>
  <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
    {/* Full-width field */}
    <FormField name="title" ... />
    
    {/* Two-column layout */}
    <ResponsiveFormLayout columns={2} spacing="md">
      <FormField name="firstName" ... />
      <FormField name="lastName" ... />
    </ResponsiveFormLayout>
    
    {/* Form actions */}
    <div className="flex justify-end gap-3 pt-4">
      <Button variant="outline">Cancel</Button>
      <Button type="submit">Save</Button>
    </div>
  </form>
</Form>
```

**Column Guidelines:**

| Columns | Use Case                                     |
| ------- | -------------------------------------------- |
| 1       | Simple forms, mobile-first, long text inputs |
| 2       | Most forms (default) - related field pairs   |
| 3       | Dense data entry forms only                  |

**Spacing:**

| Value | Use Case                              |
| ----- | ------------------------------------- |
| `sm`  | Compact forms, modal dialogs          |
| `md`  | Standard forms (default)              |
| `lg`  | Spacious layouts, accessibility focus |

### Shared Field Pilot

Use `Field`, `FieldGroup`, `FieldLabel`, `FieldDescription`, and `FieldError` from `@/shared/ui/field` for simple controlled-field layouts that otherwise repeat label/helper/error markup outside React Hook Form.

```tsx theme={null}
<FieldGroup>
  <Field>
    <FieldLabel htmlFor="tenant-id">Tenant ID</FieldLabel>
    <Input id="tenant-id" value={tenantId} onChange={handleChange} />
    <FieldDescription>Used for Microsoft Entra ID single sign-on.</FieldDescription>
  </Field>
</FieldGroup>
```

Keep `FormField` / `FormItem` for React Hook Form validation flows. The field pilot is additive and should not replace mature form abstractions unless it reduces duplicated composition without changing behavior.

### Help Text (FormDescription)

**Always use for:**

* Sensitive data fields (account numbers, passwords)
* Masked/transformed displays ("Will be masked as XXX-7890")
* Optional fields with non-obvious purpose
* Fields requiring specific formats

**Skip for:**

* Self-explanatory labels (Name, Email, Phone)
* Required fields with obvious purpose

**Example:**

```tsx theme={null}
<FormField name="accountNumber" render={({ field }) => (
  <FormItem>
    <FormLabel>Account Number *</FormLabel>
    <FormControl><Input {...field} /></FormControl>
    <FormDescription>Will be masked in displays (XXX-7890)</FormDescription>
    <FormMessage />
  </FormItem>
)} />
```

### Error Presentation

**Standard:** Inline errors only via `FormMessage` component.

**Guidelines:**

* Errors appear immediately below the field
* Use `FormMessage` component (styled with `text-destructive`)
* No error summary at form top (too noisy for most forms)
* For complex forms with many errors, consider scrolling to first error

**Example:**

```tsx theme={null}
<FormField name="email" render={({ field }) => (
  <FormItem>
    <FormLabel>Email *</FormLabel>
    <FormControl><Input type="email" {...field} /></FormControl>
    <FormMessage /> {/* Shows error inline */}
  </FormItem>
)} />
```

### Progressive Disclosure

**Use when:** Form has advanced/optional sections that most users don't need.

**Pattern:** Accordion or Collapsible for "Advanced Options"

```tsx theme={null}
<Accordion type="single" collapsible>
  <AccordionItem value="advanced">
    <AccordionTrigger>Advanced Options</AccordionTrigger>
    <AccordionContent>
      <ResponsiveFormLayout columns={2} spacing="md">
        <FormField name="advancedOption1" ... />
        <FormField name="advancedOption2" ... />
      </ResponsiveFormLayout>
    </AccordionContent>
  </AccordionItem>
</Accordion>
```

**Default state:** Collapsed (don't overwhelm users with options)

### Form Actions

**Standard layout:**

```tsx theme={null}
<div className="flex justify-end gap-3 pt-4">
  <Button type="button" variant="outline" onClick={onCancel} disabled={isSubmitting}>
    Cancel
  </Button>
  <Button type="submit" disabled={isSubmitting}>
    {isSubmitting ? 'Saving...' : 'Save'}
  </Button>
</div>
```

**Guidelines:**

* Right-aligned buttons (LTR layouts)
* Cancel on left, Submit on right
* Show loading state during submission
* Disable buttons during submission
* Use descriptive labels: "Create Account" vs "Submit"

***

## Accessibility

### Tabbed Hub Accessibility Checklist

For pages that use tabbed hubs (e.g. HR Recruiting, Credentialing, Workforce Analytics, Clinical Oversight):

* [ ] Tab panels have correct ARIA: `role="tabpanel"` and `aria-labelledby` linking to the corresponding trigger (Radix/shadcn Tabs provide these by default).
* [ ] Focus moves to the active panel on tab change when appropriate (Radix handles this).
* [ ] Keyboard navigation: arrow keys move between tabs (Radix/shadcn support this).
* [ ] Touch targets: tab triggers meet 44px minimum height (see `src/shared/ui/tabs.tsx`).
* [ ] Hub routes are in scope for accessibility audits (e.g. axe-core); see [ACCESSIBILITY\_TESTING.md](../testing/ACCESSIBILITY_TESTING.md).

### Keyboard Navigation

**Requirement:** All interactive elements MUST be keyboard accessible.

**Implementation:**

* Use semantic HTML (`<button>`, `<a>`, `<input>`, etc.)
* Maintain logical tab order
* Provide skip links for main content
* Support arrow key navigation for lists/menus

**Focus Management:**

* Visible focus indicators on all interactive elements
* Focus trap in modals/dialogs
* Return focus to trigger after closing modal

### Focus Indicators

**Requirement:** All interactive elements MUST have visible focus indicators.

**Implementation:**

```tsx theme={null}
// ✅ CORRECT: Visible focus ring
<Button className="focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2">
  Click me
</Button>

// ✅ CORRECT: Custom focus styles
.nav-focus-ring:focus-visible {
  outline: 2px solid hsl(var(--ring));
  outline-offset: 2px;
}
```

**Standards:**

* 2px solid outline
* Use `--ring` color token
* 2px offset from element
* Only visible on keyboard focus (`:focus-visible`)

### ARIA Usage

**Requirement:** Use ARIA attributes when semantic HTML is insufficient.

**Common Patterns:**

**Labels:**

```tsx theme={null}
<label htmlFor="email">Email</label>
<input id="email" type="email" aria-describedby="email-help" />
<span id="email-help">We'll never share your email</span>
```

**Roles:**

```tsx theme={null}
<div role="alert" aria-live="polite">
  {errorMessage}
</div>
```

**Live Regions:**

* `aria-live="polite"` - Non-urgent updates
* `aria-live="assertive"` - Urgent updates
* `aria-atomic="true"` - Announce entire region

### Contrast Requirements

**Requirement:** All text MUST meet WCAG AA contrast ratios.

**Minimum Contrast:**

* **Normal Text:** 4.5:1 against background
* **Large Text (18pt+):** 3:1 against background
* **UI Components:** 3:1 for non-text elements (icons, borders)

**Testing:**

* Use browser DevTools contrast checker
* Use tools like WebAIM Contrast Checker
* Test in both light and dark themes

**Related Documentation:**

* [WCAG 2.1 Guidelines](https://www.w3.org/WAI/WCAG21/quickref/)
* [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)

***

## Related Documentation

### Deep Dives

* **Navigation Patterns:** [Navigation Guide Index](./NAVIGATION_GUIDE_INDEX.md) - Complete navigation documentation index
* **Mobile Navigation:** [`docs/development/mobile-navigation-guide.md`](mobile-navigation-guide.md) - Mobile-specific patterns
* **Breadcrumbs:** [`docs/development/breadcrumb-implementation-guide.md`](breadcrumb-implementation-guide.md) - Breadcrumb standards
* **Spacing & Layout:** [Spacing & UX Standards](./SPACING_AND_UX_STANDARDS.md) - Complete spacing system guide
* **Sidebar Adaptive:** [Spacing & UX Standards](./SPACING_AND_UX_STANDARDS.md) - Sidebar patterns and adaptive layout

### Implementation References

* **Design Tokens:** `src/index.css` - CSS variable definitions
* **Tailwind token mapping:** `src/index.css` (`@theme inline`) - Token mappings
* **Shared Components:** `src/shared/components/` - Page templates and layouts
* **UI Components:** `src/shared/ui/` - Base UI component library
* **Navigation Components:** `src/platform/navigation/` - Navigation system

### Constitution & Guidelines

* **Constitution:** [`constitution.md`](../../constitution.md) - Non-negotiable engineering guardrails
* **AI Guide:** [`AI_GUIDE.md`](../../AI_GUIDE.md) - AI contribution guidelines
* **Quick Reference:** [`AGENTS.md`](../../AGENTS.md) - Condensed cross-platform reference

***

## Shared Component Primitives

### PageHeader

**Location:** `src/shared/components/PageHeader.tsx`

**Purpose:** Standardized page header with title, description, and actions.

**Usage:**

```tsx theme={null}
import { PageHeader } from '@/shared/components/PageHeader';

<PageHeader
  title="Employees"
  description="Manage your organization's workforce"
  actions={<Button>Add Employee</Button>}
/>
```

**Props:**

* `title` (required) - Page title (h1)
* `description` (optional) - Page subtitle
* `actions` (optional) - Action buttons/components
* `breadcrumbs` (optional) - Custom breadcrumb items

### StatCard

**Location:** `src/shared/components/StatCard.tsx`

**Purpose:** Standardized metric/stat card for dashboards.

**Usage:**

```tsx theme={null}
import { StatCard } from '@/shared/components/StatCard';

<StatCard
  title="Total Employees"
  value={142}
  icon={Users}
  trend={{ value: 12, direction: 'up' }}
/>
```

**Props:**

* `title` (required) - Metric label
* `value` (required) - Metric value (number or string)
* `icon` (optional) - Lucide icon component
* `trend` (optional) - Trend indicator with value and direction
* `description` (optional) - Additional context

### EmptyState

**Location:** `src/shared/components/EmptyState.tsx`

**Purpose:** Standardized empty state with icon, message, and action.

**Usage:**

```tsx theme={null}
import { EmptyState } from '@/shared/components/EmptyState';

<EmptyState
  icon={FileText}
  title="No forms yet"
  description="Create your first form to get started"
  action={<Button onClick={createForm}>Create Form</Button>}
/>
```

**MUST use for all empty lists and states.** Never create ad-hoc empty state UIs.

### CardActionsMenu

**Location:** `src/shared/components/CardActionsMenu.tsx`

**Purpose:** Touch-safe action menu for cards with consistent patterns.

**Usage:**

```tsx theme={null}
import { CardActionsMenu } from '@/shared/components/CardActionsMenu';

<CardActionsMenu
  actions={[
    { label: 'Edit', icon: Edit, onClick: handleEdit },
    { label: 'Delete', icon: Trash, onClick: handleDelete, variant: 'destructive' },
  ]}
/>
```

**Requirements:**

* Minimum 44x44px touch target
* Always visible (not hover-only)
* Uses `DropdownMenu` for mobile-friendly interaction

***

## Color Mapping Utilities

**Reference:** See `docs/development/SEMANTIC_COLORS.md` for complete documentation.

### Import Pattern

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

### Status Color Mapping

Map business statuses to semantic colors:

```typescript theme={null}
const statusMap: Record<string, SemanticStatus> = {
  active: 'success',
  pending: 'warning',
  expired: 'destructive',
  draft: 'muted',
};

const colors = getStatusColors(statusMap[status] || 'muted');
// Returns: { bg, bgSubtle, text, border, full }
```

### Anti-Patterns

**❌ WRONG: Hardcoded color maps**

```typescript theme={null}
const getColor = (status: string) => {
  switch (status) {
    case 'active': return 'bg-green-100 text-green-800';
    case 'pending': return 'bg-yellow-100 text-yellow-800';
    default: return 'bg-gray-100 text-gray-800';
  }
};
```

**✅ CORRECT: Semantic color mapping**

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

const statusMap = { active: 'success', pending: 'warning' } as const;
const colors = getStatusColors(statusMap[status] || 'muted');
```

***

## Touch-Safe Action Patterns

### Requirements

1. **Always visible actions** - Never hide actions behind hover-only states
2. **44x44px minimum** - All touch targets must meet minimum size
3. **Menu for multiple actions** - Use `CardActionsMenu` or `DropdownMenu` for 2+ actions
4. **Single primary action visible** - Show most common action directly, others in menu

### Hover-Only Anti-Pattern

**❌ WRONG: Hover-only actions**

```tsx theme={null}
<Card className="group">
  <CardActions className="opacity-0 group-hover:opacity-100">
    <Button size="icon" className="h-8 w-8">
      <Edit className="h-4 w-4" />
    </Button>
  </CardActions>
</Card>
```

**✅ CORRECT: Always visible with proper touch targets**

```tsx theme={null}
<Card>
  <CardActionsMenu
    actions={[
      { label: 'Edit', icon: Edit, onClick: handleEdit },
      { label: 'Delete', icon: Trash, onClick: handleDelete },
    ]}
  />
</Card>
```

### Mobile Considerations

* Use `DropdownMenu` instead of hover menus
* Ensure action buttons are at least 44x44px
* Place actions in consistent locations (top-right corner)
* Use swipe gestures sparingly and always provide alternatives

***

## Dialog vs Native Browser Dialogs

### CRITICAL: Never Use Native Dialogs

**Prohibited:** `window.prompt()`, `window.confirm()`, `window.alert()`

Native browser dialogs are:

* Not accessible
* Not stylable
* Inconsistent across browsers
* Not mobile-friendly
* Cannot be canceled properly

### Use Custom Dialogs Instead

**For confirmations:**

```tsx theme={null}
import { AlertDialog } from '@/shared/ui/alert-dialog';

<AlertDialog>
  <AlertDialogTrigger>Delete</AlertDialogTrigger>
  <AlertDialogContent>
    <AlertDialogHeader>
      <AlertDialogTitle>Are you sure?</AlertDialogTitle>
      <AlertDialogDescription>
        This action cannot be undone.
      </AlertDialogDescription>
    </AlertDialogHeader>
    <AlertDialogFooter>
      <AlertDialogCancel>Cancel</AlertDialogCancel>
      <AlertDialogAction onClick={handleDelete}>Delete</AlertDialogAction>
    </AlertDialogFooter>
  </AlertDialogContent>
</AlertDialog>
```

**For text input:**

```tsx theme={null}
import { Dialog } from '@/shared/ui/dialog';
import { Input } from '@/shared/ui/input';

<Dialog>
  <DialogTrigger>Rename</DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Rename Document</DialogTitle>
    </DialogHeader>
    <Input
      value={newName}
      onChange={(e) => setNewName(e.target.value)}
      placeholder="Enter new name"
    />
    <DialogFooter>
      <Button variant="outline" onClick={close}>Cancel</Button>
      <Button onClick={handleRename}>Rename</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>
```

**For notifications/alerts:**

```tsx theme={null}
import { toast } from 'sonner';

// Success
toast.success('Document saved successfully');

// Error
toast.error('Failed to save document');

// Info
toast.info('Processing your request...');
```

***

## Mobile Gestures

**Spec Reference:** PF-37 - Mobile Swipe Gestures

All mobile interfaces should leverage gesture patterns for improved UX:

### Required Gesture Patterns

| Pattern          | When to Use                    | Component           |
| ---------------- | ------------------------------ | ------------------- |
| Swipe-to-Dismiss | Sheets, dialogs, toasts        | `SwipeableSheet`    |
| Swipe Actions    | List items with quick actions  | `SwipeableListItem` |
| Pull-to-Refresh  | Refreshable data lists         | `PullToRefresh`     |
| Edge Swipe       | Detail pages (back navigation) | `useEdgeSwipe`      |

### Implementation

```tsx theme={null}
import { SwipeableListItem, PullToRefresh } from '@/platform/gestures';

// List with swipe actions and pull-to-refresh
<PullToRefresh onRefresh={handleRefresh}>
  <ul>
    {items.map(item => (
      <SwipeableListItem
        key={item.id}
        leftActions={[
          { id: 'delete', label: 'Delete', colorClass: 'destructive', onClick: () => onDelete(item.id) }
        ]}
      >
        <ItemContent item={item} />
      </SwipeableListItem>
    ))}
  </ul>
</PullToRefresh>
```

### Accessibility Requirements

* All gestures MUST have keyboard alternatives
* `Escape` closes sheets/dialogs
* `Delete` triggers destructive actions
* Tap/click fallback always available

**Full Documentation:** See [Mobile Gesture Guide](./mobile-gesture-guide.md)

***

## Troubleshooting Common UI/UX Issues

### Colors Not Applying Correctly

**Problem:** Semantic color tokens not working as expected.

**Solution:**

```tsx theme={null}
// ❌ WRONG: Hardcoded colors
<div className="text-green-500">Success</div>

// ✅ CORRECT: Use semantic tokens
<div className="text-success">Success</div>
<Badge variant="success">Active</Badge>
```

**Check:**

1. Verify token exists in `src/index.css` under `@theme inline` and/or root CSS variables
2. Check CSS variables in `src/index.css`
3. Ensure proper variant usage for components

### Loading States Showing Blank Screen

**Problem:** Page shows blank screen during loading.

**Solution:**

```tsx theme={null}
// ❌ WRONG: Return null
if (isLoading) return null;

// ✅ CORRECT: Use skeleton loader
if (isLoading) return <AppLoadingSkeleton />;
```

### Components Not Responsive

**Problem:** Components don't adapt to mobile screens.

**Solution:**

1. Check for fixed widths: Remove `w-{fixed}` classes
2. Use responsive utilities: `md:`, `lg:` breakpoints
3. Verify mobile-first approach: Start with mobile, enhance for desktop
4. Test on actual devices: Use browser dev tools mobile emulation

### Accessibility Issues

**Problem:** Components not accessible to screen readers.

**Solution:**

```tsx theme={null}
// ❌ WRONG: Missing labels
<button onClick={handleDelete}>
  <Trash2 />
</button>

// ✅ CORRECT: Include accessibility attributes
<button 
  onClick={handleDelete}
  aria-label="Delete item"
>
  <Trash2 />
</button>
```

### Performance Issues

**Problem:** Page loads slowly or feels sluggish.

**Solution:**

1. Check lazy loading: Ensure routes use `React.lazy()`
2. Verify code splitting: Check bundle sizes
3. Optimize images: Use appropriate formats and sizes
4. Check query optimization: Review TanStack Query configuration
5. See [Performance Optimization Guide](./PERFORMANCE_OPTIMIZATION_GUIDE.md) for detailed guidance

### Design System Tokens Not Found

**Problem:** TypeScript errors for design system tokens.

**Solution:**

1. Verify token exists in `src/index.css` under `@theme inline` and/or root CSS variables
2. Check TypeScript types: Ensure Tailwind types are generated
3. Restart TypeScript server: Reload VS Code window
4. Check import paths: Verify `@/` alias configuration

### Mobile Touch Targets Too Small

**Problem:** Buttons/links hard to tap on mobile.

**Solution:**

```tsx theme={null}
// ❌ WRONG: Small touch target
<button className="p-2">Action</button>

// ✅ CORRECT: Minimum 44x44px
<button className="min-w-[44px] min-h-[44px] p-3">Action</button>
```

### Inconsistent Component Variants

**Problem:** Same component looks different in different places.

**Solution:**

1. Use standard variants: `variant="success"`, `variant="destructive"`
2. Avoid custom styling: Don't override component styles
3. Check design system: Verify variant exists in component definition
4. Use semantic tokens: Don't hardcode colors

***

**Version:** 1.6.1
**Maintained By:** Platform Foundation Team
