# epilot Design System

## Overview

epilot UI spans three fundamentally different contexts. **Identify your context first** — it determines density, motion, bundle discipline, and complexity defaults.

Uses **@epilot/volt-ui**. For React components see `.epilot-docs/10-frameworks/volt-ui/`.

## UI Contexts

### 1. Executing User Facing (Operational)

**epilot360, Entity UI, Messaging, Dashboards, Inbox, Workflows, Task Processing**

Where employees live 8+ hours a day. Get out of their way.

- **Performance is everything.** Hundreds of interactions per session. Every millisecond counts.
- **Functional, clear, efficient.** No decoration. Every pixel earns its place.
- **Data-dense.** Show as much relevant info as possible without scrolling.
- **Keyboard-driven.** Power users navigate without mouse.
- No animation libraries (CSS transitions only, no Framer Motion / GSAP / Lottie)
- Interaction response <100ms, page load <1s
- Loading states for anything >200ms (skeleton or spinner, never blank)
- Compact density default for tables, lists, sidebars
- Motion: instant/fast only (≤100ms). No entrance animations.
- Minimize bundle size — justify every dependency

```
Priority: Performance > Clarity > Information density > Aesthetics
```

### 2. Configuring User Facing (Configuration)

**Journey Builder, Pricing, Entity Builder, Blueprints, Templates, Automation, Portal Builder**

A power tool with a gentle learning curve.

- **Simple on the surface.** Good results fast without reading docs.
- **Powerful under the hood.** Advanced options behind progressive disclosure.
- **Capability matters.** Feature richness justifies moderate complexity.
- **Guided flows.** Wizards, live preview, step-by-step processes.
- Comfortable density default
- Motion: ≤300ms OK for reveals (accordion, panel slide, preview transitions)
- Animation libraries: avoid, small targeted use acceptable if justified
- Progressive disclosure: simple defaults visible, advanced options behind expandable sections
- Live preview where possible — preview > save-and-check

```
Priority: Capability > Simplicity > Discoverability > Aesthetics
```

### 3. End Customer Facing

**Journeys, Portals, Mobile App, Email Templates**

The shop window. It sells the product.

- **Aesthetics matter.** Beautiful, polished, delightful by default.
- **Animations & effects: yes.** Transitions, micro-interactions, visual effects — this is where they pay off.
- **Whitelabel:** Super flexible. Every customer has different brand guidelines. UI must adapt completely via accent theming (single seed color → full Radix 12-step scale). Status colors (error/success/warning) stay fixed regardless of brand accent.

```
Priority: Brand flexibility > Visual polish > Delight > Performance
```

## Colors

Radix 12-step scale. Semantic palettes: `accent`, `gray`, `error`, `success`, `info`, `warning`.

Use alias tokens (pattern: `--[palette]-[alias]`), never raw scale steps or hardcoded hex:

```css
/* backgrounds */
var(--accent-solid)       /* primary button fill (step 9) */
var(--accent-solid\:hover) /* primary button hover (step 10) */
var(--accent-soft)        /* tinted background (badge, alert) */
var(--accent-surface)     /* component surface (input, dropdown) */
var(--gray-level-1)       /* page background */
var(--gray-level-2)       /* card, sidebar, panel */

/* text */
var(--accent-contrast)    /* text on solid fill */
var(--accent-default)     /* high-contrast accent text */
var(--accent-light)       /* subdued accent text */
var(--gray-default)       /* body text */
var(--gray-light)         /* secondary text */
var(--gray-disabled)      /* disabled text */

/* borders */
var(--gray-default)       /* decorative (cards, separators) */
var(--gray-ui)            /* interactive (inputs, buttons) */
var(--gray-ui\:hover)     /* interactive hover */
var(--error-default)      /* error border */
```

As Tailwind utilities:

```tsx
<div className="bg-accent-solid text-accent-contrast border-accent-ui" />
<div className="bg-error-soft text-error-default border-error-default" />
```

Colors auto-adapt to light/dark themes. See `volt-ui/colors.md`, `volt-ui/theming.md`.

## Typography

Primary font in epilot360: **Proxima Nova**. volt-ui optionally provides Geist via `@epilot/volt-ui/font.css`.

volt-ui `Text` component variants (note: numbering is largest→smallest within each group):

```tsx
import { Text } from "@epilot/volt-ui"

// Titles — page-level landmarks
<Text variant="title3">28px bold — main page headings</Text>
<Text variant="title2">24px bold — section headings, dialog titles</Text>
<Text variant="title1">20px semibold — sub-section headings</Text>

// Headings — card/section structure
<Text variant="heading3">18px semibold — card headings</Text>
<Text variant="heading2">16px semibold — group headings</Text>
<Text variant="heading1">14px semibold — column headers, tab labels</Text>

// Body — content text
<Text variant="body2">14px regular — default UI text (the workhorse)</Text>
<Text variant="body1">12px regular — compact text</Text>

// Helper — metadata
<Text variant="helper2">14px regular — captions</Text>
<Text variant="helper1">12px regular — timestamps, fine print</Text>

// Use asChild for semantic HTML
<Text asChild variant="title3"><h1>Page Title</h1></Text>
<Text asChild variant="heading2"><h2>Section</h2></Text>
```

Default to `body2` (14px) for most UI text. Never skip heading levels.

## Spacing

4px base grid. Three tiers of gap tokens:

```tsx
// Element tier — inside a component (icon + label, input + helper)
<div className="gap-element-1" /> // 4px — tightly coupled
<div className="gap-element-2" /> // 8px — hierarchical

// Group tier — between related components (form fields, checkbox list)
<div className="gap-group-1" />   // 8px — closely related siblings
<div className="gap-group-2" />   // 16px — group title to content

// Layout tier — between groups/sections
<div className="gap-layout-1" />  // 24px — related groups (stacked cards)
<div className="gap-layout-2" />  // 32px — distant groups (page sections)
```

Key principle: **spacing increases from inner to outer.** Element < Group < Layout.

Available as any Tailwind spacing utility: `gap-*`, `p-*`, `m-*`, `space-x-*`. See `volt-ui/spacing.md`.

## Elevation

```
elevation.sm  — 0 1px 2px rgba(0,0,0,0.05)     — switches, toggles
elevation.md  — 0 4px 6px -1px                   — cards, popovers, toasts
elevation.lg  — 0 10px 15px -3px                  — dialogs, drawers, dropdowns
elevation.xl  — 0 20px 25px -5px                  — full-screen overlays

Z-index layers: Base(0) → Dropdown(100) → Sticky(200) → Overlay(300) → Modal(400) → Toast(500)

Surface levels: level-1 (page bg) → level-2 (card, sidebar) → level-3 (nested/elevated)
```

## Motion

Functional, not decorative. Minimal motion for maximum clarity.

```
instant  0ms   — checkbox toggle, radio select
fast     100ms — hover, focus ring, color transitions
normal   200ms — accordion, tab switch, content transition
slow     300ms — modal enter/exit, drawer slide

Easing: ease-out (entering) | ease-in (exiting) | ease-in-out (morphing) | linear (progress)
```

All animations must respect `prefers-reduced-motion`. No animation on initial page load.

## Density

```
Comfortable (48px row, 12px padding) — config work, forms, settings, onboarding
Compact     (36px row, 8px padding)  — operational work, tables, lists, kanban

Mobile always uses comfortable (44px min touch target).
Use min-h-* with py-* instead of fixed heights — let content grow.
```

## Iconography

Use **@epilot360/icons**. Shipped as an external system module in the 360 portal.

```tsx
import { Edit as EditIcon, EpilotIcon } from '@epilot360/icons'

<EditIcon />
<EpilotIcon name="edit" />

// Outside 360 portal — direct import to avoid bundling entire library
import EditIcon from '@epilot360/icons/react/Edit'
```

Icons inherit `currentColor`. Always pair icon-only buttons with `aria-label`.

## States

Every interactive component handles: default, hover (step 4), focus (2px accent ring), active (step 5), disabled (opacity 0.5), loading, readOnly, error.

**disabled** = action not available, not focusable, NOT in form data. **readOnly** = value locked, focusable (user can copy), IS in form data.

**Loading**: Skeleton when layout is known, Spinner for in-progress actions. Never full-page spinners.

**Errors**: Inline below field for validation. Banner at section top for multi-field errors. Toast for transient server errors only. Never use toast for form validation.

## Accessibility

**WCAG 2.1 AA** (legally required by BFSG).

- Color contrast: normal text 4.5:1, large text and UI components 3:1
- Keyboard: Tab to reach, Enter/Space to activate, Escape to dismiss overlays
- Focus: visible 2px ring, focus trap in modals, focus restoration on close
- Semantic HTML: `<button>` not `<div onClick>`, `<nav>`, `<main>`, `<dialog>`
- `aria-label` for icon buttons, `aria-live` for dynamic updates
- Touch targets: 44x44px mobile, 24x24px desktop

## Components

```tsx
import { Button, Dialog, Field, FieldInput, DataTable, Toast } from "@epilot/volt-ui"
```

- **Button**: `primary` (once per container), `secondary`, `tertiary`. Sizes: `xs`, `sm`, `base`, `lg`. `destructive` prop for danger. Labels: active verbs ("Save changes", not "Submit").
- **Field**: `FieldInput`, `FieldSelect`, `FieldTextarea`, `FieldCombobox` + `FieldLabel`, `FieldDescription`, `FieldMessage`
- **Dialog** / **AlertDialog** (destructive) / **Drawer** (side panel)
- **DataTable** with pagination, column filtering, selection actions
- **Toast** (transient) / **Callout** (inline persistent) / **Badge** (status)
- **Card**, **Tabs**, **Accordion**, **CollapsibleSidebar**, **Separator**

Full API: `volt-ui/button.md`, `volt-ui/dialog.md`, `volt-ui/field.md`, `volt-ui/data-table.md`, etc.

## Don'ts

- Hardcoded hex values — use semantic tokens
- Arbitrary spacing values — use the gap token scale
- Multiple `primary` buttons per container
- `<div onClick>` — use `<button>`
- `outline: none` without replacement focus indicator
- Color-only error indication — pair with icon or text
- Placeholder text as labels
- External icon libraries, MUI Icons, or inline SVGs — use `@epilot360/icons`
- Arbitrary z-index (9999) — use the layer scale
- `window.innerWidth` — use CSS media queries or `useMediaQuery`
- Disabled buttons without explanation — provide tooltip/helper text
- Full-page spinners — use skeletons
- Toast for form validation errors
- Animation libraries or entrance animations in operational UI
