Files
design-system/docs/superpowers/specs/2026-03-18-design-system-design.md
2026-03-18 15:54:32 +01:00

666 lines
29 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Cameleer3 Design System — Specification
**Date:** 2026-03-18
**Status:** Draft
**Scope:** Standalone React design system with full component library, theming, and page layouts
---
## 1. Overview
Build a React-based design system for the Cameleer3 operations dashboard. The system follows a shadcn/ui-like approach: all components are owned by the project, composable, and have no external UI library dependencies. Components are styled with CSS Modules + CSS custom properties, supporting light and dark themes.
The design language is derived from existing HTML mockups (`ui-mocks/`) and the live application's user management UI. Key characteristics:
- Warm parchment surfaces (not cold white/gray)
- Warm charcoal sidebar
- Amber-gold brand accent
- DM Sans body + JetBrains Mono data typography
- Warm status colors (olive green, burnt orange, terracotta, teal)
- Deterministic badge/avatar colors derived from name hashing
### Goals
- Standalone layouts that work without a server (mock data)
- Light theme first, dark theme support built in from the start
- Reusable primitives that compose into dashboard pages
- Visual parity with existing mockups and live application
### Non-Goals
- Server integration, API calls, real-time data
- Authentication / authorization flows
- Mobile responsiveness (dashboard targets 1920px desktop)
---
## 2. Tech Stack
| Concern | Choice |
|-----------------|---------------------------------|
| Framework | React 18+ with TypeScript |
| Build tool | Vite |
| Routing | React Router v6 |
| Styling | CSS Modules + CSS custom properties |
| Fonts | DM Sans (body), JetBrains Mono (data) |
| State | React Context (theme, sidebar) |
| Package manager | npm |
No external component libraries (no MUI, Chakra, Radix, etc.). All components built from scratch.
---
## 3. Theming Architecture
### 3.1 Design Tokens
All visual properties are defined as CSS custom properties in `tokens.css`. Two rule blocks:
- `:root` — light theme (default)
- `[data-theme="dark"]` — dark theme overrides
Token categories:
```
Surface: --bg-body, --bg-surface, --bg-raised, --bg-inset, --bg-hover
Sidebar: --sidebar-bg, --sidebar-hover, --sidebar-active, --sidebar-text, --sidebar-muted
Text: --text-primary, --text-secondary, --text-muted, --text-faint
Borders: --border, --border-subtle
Brand: --amber, --amber-light, --amber-bg, --amber-deep
Status: --success, --success-bg, --success-border
--warning, --warning-bg, --warning-border
--error, --error-bg, --error-border
--running, --running-bg, --running-border
Typography: --font-body, --font-mono
Spacing: --radius-sm (5px), --radius-md (8px), --radius-lg (12px)
Shadows: --shadow-sm, --shadow-md, --shadow-lg, --shadow-card
Chart: --chart-1 through --chart-8 (amber, olive, teal, burnt orange,
deep amber, sage, terracotta, gold) — used for multi-series
charts and sparklines
```
**Semantic color naming convention:** Components use semantic variant names that map to tokens:
- `primary``--amber` tokens
- `success``--success` tokens
- `warning``--warning` tokens
- `error``--error` tokens
- `running``--running` tokens (teal, for in-progress/active states)
This convention applies to all components with color/accent props (Card, Badge, StatusDot, FilterPill, InfoCallout, etc.). No mixing of token-level names (`teal`, `green`) with semantic names.
Light theme values are taken directly from `mock-v2-light.html` `:root` block.
**Dark theme strategy:** The existing `mock-v2-dark.html` is considered "too colorful" and will not be used as-is. The dark theme will be a muted redesign that:
- Reuses the same token names as light (no separate `--bg-deep` / `--bg-base`)
- Inverts lightness and reduces saturation
- Drops glow tokens (`--amber-glow`, `--success-glow`, etc.) — these are not canonical
- Uses the same `--radius-sm: 5px` value (the dark mock's 6px was unintentional)
- All dark overrides go in a single `[data-theme="dark"]` block remapping the same variable names
### 3.2 Deterministic Color Hashing
A `hashColor(name: string)` utility generates consistent HSL colors from any string:
1. Compute FNV-1a hash of the input name (synchronous, fast, good distribution)
2. Take hash modulo 360 → hue angle
3. Light theme: `hsl(hue, 45%, 92%)` background, `hsl(hue, 55%, 35%)` text
4. Dark theme: `hsl(hue, 35%, 20%)` background, `hsl(hue, 45%, 75%)` text
FNV-1a is chosen over SHA-256 because it is synchronous (no `crypto.subtle` async), lightweight (~10 lines), and provides sufficient distribution for color bucketing.
Used by `Badge` and `Avatar` components when no explicit color variant is provided.
### 3.3 Theme Switching
- `ThemeProvider` React context stores `'light' | 'dark'`
- Persisted to `localStorage`
- Toggling sets `document.documentElement.dataset.theme`
- All components reference tokens — no component-level theme logic needed
---
## 4. Project Structure
```
src/
├── design-system/
│ ├── tokens.css # All CSS custom properties
│ ├── reset.css # Box-sizing, scrollbar, base styles
│ ├── utils/
│ │ └── hashColor.ts # Name → HSL deterministic color
│ ├── primitives/
│ │ ├── Alert/
│ │ ├── Avatar/
│ │ │ ├── Avatar.tsx
│ │ │ └── Avatar.module.css
│ │ ├── Badge/
│ │ ├── Button/
│ │ ├── Card/
│ │ ├── Checkbox/
│ │ ├── CodeBlock/
│ │ ├── Collapsible/
│ │ ├── DateTimePicker/
│ │ ├── DateRangePicker/
│ │ ├── EmptyState/
│ │ ├── FilterPill/
│ │ ├── FormField/
│ │ ├── InfoCallout/
│ │ ├── Input/
│ │ ├── KeyboardHint/
│ │ ├── Label/
│ │ ├── MonoText/
│ │ ├── Pagination/
│ │ ├── ProgressBar/
│ │ ├── Radio/
│ │ ├── Select/
│ │ ├── SectionHeader/
│ │ ├── Skeleton/
│ │ ├── Sparkline/
│ │ ├── Spinner/
│ │ ├── StatCard/
│ │ ├── StatusDot/
│ │ ├── Tag/
│ │ ├── Textarea/
│ │ ├── Toggle/
│ │ ├── Tooltip/
│ │ └── index.ts # Barrel export
│ ├── composites/
│ │ ├── Accordion/
│ │ ├── AlertDialog/
│ │ ├── AreaChart/
│ │ ├── AvatarGroup/
│ │ ├── BarChart/
│ │ ├── Breadcrumb/
│ │ ├── CommandPalette/
│ │ ├── DataTable/
│ │ ├── DetailPanel/
│ │ ├── Dropdown/
│ │ ├── EventFeed/
│ │ ├── FilterBar/
│ │ ├── LineChart/
│ │ ├── MenuItem/
│ │ ├── Modal/
│ │ ├── Popover/
│ │ ├── ProcessorTimeline/
│ │ ├── ShortcutsBar/
│ │ ├── Tabs/
│ │ ├── Toast/
│ │ ├── TreeView/
│ │ └── index.ts
│ ├── layout/
│ │ ├── AppShell/
│ │ ├── Sidebar/
│ │ ├── TopBar/
│ │ └── index.ts
│ └── providers/
│ └── ThemeProvider.tsx
├── pages/
│ ├── Dashboard/
│ ├── Metrics/
│ ├── RouteDetail/
│ ├── ExchangeDetail/
│ ├── AgentHealth/
│ └── Inventory/ # Component showcase at /inventory
├── mocks/ # Static TypeScript mock data
│ ├── exchanges.ts
│ ├── routes.ts
│ ├── agents.ts
│ └── metrics.ts
├── App.tsx # Router setup
├── main.tsx # Entry point
└── index.css # Imports tokens.css + reset.css
```
### Naming Conventions
- Components: PascalCase directories with `.tsx` + `.module.css`
- Tokens/utilities: camelCase
- Barrel exports from each layer (`primitives/index.ts`, `composites/index.ts`, `layout/index.ts`)
---
## 5. Component Specifications
### 5.1 Foundations
#### `tokens.css`
All CSS custom properties for both themes. Light values sourced from `mock-v2-light.html`. See Section 3.1 for full token list.
#### `reset.css`
- Box-sizing border-box
- Custom scrollbar (6px, warm tones)
- Base font: DM Sans 14px, line-height 1.5
- Anti-aliased rendering
- `html { font-size: 14px; }`
#### `hashColor.ts`
- `hashColor(name: string): { bg: string; text: string; border: string }`
- Returns CSS color strings appropriate for current theme
- Uses a synchronous hash (FNV-1a) for hue mapping — SHA-256 is asynchronous in browsers and overkill for color bucketing (see Section 3.2)
### 5.2 Primitives
#### Avatar
- Circular element with initials (1-2 chars extracted from name)
- Color derived from `hashColor(name)`
- Sizes: `sm` (24px), `md` (28px), `lg` (40px)
- Props: `name: string`, `size?: 'sm' | 'md' | 'lg'`
#### Badge
- Inline label pill
- Variants: `filled` (solid bg + text from hash), `outlined` (transparent bg, colored border), `dashed` (dashed border, for inherited/transitive items)
- Color: auto from `hashColor(label)` or explicit semantic color for status badges
- Props: `label: string`, `variant?: 'filled' | 'outlined' | 'dashed'`, `color?: 'success' | 'warning' | 'error' | 'running' | 'auto'`
- Optional `onRemove` for dismissible badges
#### Button
- Variants: `primary` (amber fill), `secondary` (outline), `danger` (red outline/fill), `ghost` (no border)
- Sizes: `sm`, `md`
- Props: standard button props + `variant`, `size`, `loading?: boolean`
#### Card
- White surface container
- Subtle border (`--border-subtle`) + shadow (`--shadow-card`)
- Border radius `--radius-md`
- Optional top accent stripe (colored 2px bar)
- Props: `accent?: 'primary' | 'success' | 'error' | 'running' | 'warning'`, `children`
#### Checkbox
- Styled checkbox with warm focus ring
- Props: standard input[checkbox] props + `label?: string`
#### DateTimePicker
- Initial implementation: styled native `<input type="datetime-local">` matching the Input component's warm styling (focus ring, border, background)
- Custom calendar dropdown is a future enhancement
- Props: `value: string`, `onChange`, `min?: string`, `max?: string`
#### DateRangePicker
- Start + end datetime selection using two DateTimePicker inputs
- Built-in presets bar: "Last 1h", "Last 6h", "Today", "This shift", "Last 24h", "Last 7d", "Custom"
- Presets are FilterPill-style chips above the inputs; clicking a preset auto-fills both values
- Props: `value: { start: Date; end: Date }`, `onChange`, `presets?: Preset[]`
#### EmptyState
- Centered placeholder for empty lists/tables
- Icon slot + title + description
- Props: `icon?: ReactNode`, `title: string`, `description?: string`, `action?: ReactNode`
#### FilterPill
- Selectable pill with optional color dot + count
- States: default, hover, active (amber bg), active-colored (status-specific bg)
- Props: `label: string`, `count?: number`, `active?: boolean`, `color?: 'success' | 'error' | 'running'`, `onClick`
#### InfoCallout
- Block with left accent border (3px, amber by default)
- Light background
- Props: `children`, `variant?: 'running' | 'warning' | 'error'`
#### Input
- Text input with warm focus ring (`--amber` border, `--amber-bg` shadow)
- Optional left icon slot
- Props: standard input props + `icon?: ReactNode`
#### KeyboardHint
- Styled `<kbd>` element
- Mono font, subtle border + background
- Props: `keys: string` (e.g., "Ctrl+K")
#### MonoText
- Span wrapper with JetBrains Mono font
- Props: `children`, `size?: 'xs' | 'sm' | 'md'`
#### SectionHeader
- Uppercase, small, muted label with letter-spacing
- Props: `children`, `action?: ReactNode` (optional right-aligned element like "+ Add")
#### Select
- Styled dropdown select matching Input styling
- Warm focus ring
- Props: standard select props + `options: Option[]`
#### Spinner
- CSS-only loading indicator
- Amber colored
- Sizes: `sm` (16px), `md` (24px), `lg` (32px)
#### StatCard
- KPI display card extending Card
- Top accent stripe, label, large mono value, trend indicator, detail line
- Props: `label: string`, `value: string | number`, `unit?: string`, `trend?: { direction: 'up' | 'down' | 'flat'; value: string; sentiment: 'good' | 'bad' | 'neutral' }`, `detail?: ReactNode`, `accent?: 'primary' | 'success' | 'error' | 'running' | 'warning'`, `sparkline?: number[]`
#### StatusDot
- 6-7px colored circle
- Semantic variants (map to status tokens): `success` (green), `warning` (orange), `error` (red), `running` (teal)
- Agent-specific variants (map to tokens): `live``--success` + pulse, `stale``--warning`, `dead``--text-muted`
- Optional pulse animation (enabled by default for `live`)
- Props: `variant: 'live' | 'stale' | 'dead' | 'success' | 'warning' | 'error' | 'running'`, `pulse?: boolean`
#### Tag
- Removable pill with x dismiss button
- Color from `hashColor(label)` by default, or explicit semantic color
- Props: `label: string`, `onRemove?: () => void`, `color?: 'success' | 'warning' | 'error' | 'running' | 'auto'`
#### Toggle
- Styled on/off switch
- Props: standard input[checkbox] props + `label?: string`
#### Tooltip
- Hover-triggered info popup
- Positions: top, bottom, left, right
- Props: `content: ReactNode`, `position?: 'top' | 'bottom' | 'left' | 'right'`, `children` (trigger element)
#### CodeBlock
- Monospace preformatted text display for JSON, XML, stack traces
- Inset background (`--bg-inset`), rounded corners
- Optional line numbers (left gutter)
- Optional copy-to-clipboard button (top-right)
- Auto-detects and pretty-prints JSON
- Match highlighting support (bold amber for search matches)
- Props: `content: string`, `language?: 'json' | 'xml' | 'text'`, `lineNumbers?: boolean`, `copyable?: boolean`, `highlights?: string[]`, `maxHeight?: string`
#### Collapsible
- Expandable/collapsible section with toggle trigger
- Animated height transition
- Props: `open?: boolean`, `onToggle?: () => void`, `title: ReactNode`, `children`, `defaultOpen?: boolean`
#### Sparkline
- Inline SVG mini-chart (polyline)
- Renders a small trend line from an array of numbers
- Sizes: fits within its container (typical: 60x20px in table cells, 80x24px in stat cards)
- Color inherits from parent or explicit prop
- Props: `data: number[]`, `color?: string`, `width?: number`, `height?: number`, `strokeWidth?: number`
#### Alert
- Inline page-level attention banner with variant colors
- Left accent border (4px), variant background color, default icon per variant
- Variants: `info`, `success`, `warning`, `error`
- ARIA: `role="alert"` for error/warning, `role="status"` for info/success
- Props: `variant?: 'info' | 'success' | 'warning' | 'error'`, `title?: string`, `children`, `dismissible?: boolean`, `onDismiss?`, `icon?: ReactNode`
#### FormField
- Wrapper component: Label + children slot + hint text + error text
- Error replaces hint when both present; adds `.error` class to wrapper
- Props: `label?: string`, `htmlFor?: string`, `required?: boolean`, `error?: string`, `hint?: string`, `children: ReactNode`
#### Label
- Form label with optional required asterisk (red `*`)
- Font: `--font-body`, 12px, `--text-primary`, `font-weight: 500`
- Uses `forwardRef`
- Props: extends `LabelHTMLAttributes` + `required?: boolean`
#### Pagination
- Page navigation: `< 1 ... 4 [5] 6 ... 20 >`
- Always shows first, last, and siblingCount pages around current
- Active page: `--amber` background, white text
- Props: `page: number`, `totalPages: number`, `onPageChange`, `siblingCount?: number`
#### ProgressBar
- Track with animated fill bar
- Determinate (value 0-100) or indeterminate (shimmer animation)
- Variants: primary, success, warning, error, running
- Sizes: sm (4px height), md (8px height)
- ARIA: `role="progressbar"`, `aria-valuenow`, `aria-valuemin`, `aria-valuemax`
- Props: `value?: number`, `variant?`, `size?: 'sm' | 'md'`, `indeterminate?: boolean`, `label?: string`
#### Radio (RadioGroup + RadioItem)
- Single-select option group using React Context
- Visual pattern matches Checkbox: hidden native input, custom circle with amber fill
- Circle: 15px, `border-radius: 50%`, checked inner dot 7px
- RadioGroup: `name`, `value`, `onChange`, `orientation?: 'vertical' | 'horizontal'`
- RadioItem: `value`, `label: ReactNode`, `disabled?: boolean`
#### Skeleton
- Loading placeholder with shimmer animation
- Variants: `text` (12px bars), `circular` (50% radius, 40x40 default), `rectangular` (full width, 80px default)
- Multi-line text: `lines` prop, last line at 70% width
- Props: `variant?`, `width?`, `height?`, `lines?: number`
#### Textarea
- Multi-line text input matching Input visual styling
- Focus ring: amber border + amber-bg box-shadow
- Uses `forwardRef`
- Props: extends `TextareaHTMLAttributes` + `resize?: 'vertical' | 'horizontal' | 'none' | 'both'`
### 5.3 Composites
#### Breadcrumb
- Path segments with `/` separators
- Last segment is active (bold, primary color)
- Props: `items: { label: string; href?: string }[]`
#### CommandPalette
- Full-screen overlay modal triggered by Ctrl+K
- Search input with scoped filter tags (removable, amber-styled)
- Category tabs with counts (All, Exchanges, Routes, Agents)
- Grouped results by category with section headers
- Result items: icon + title + badges + meta + timestamp
- Inline expandable detail (JSON preview with match highlighting)
- Match highlighting (bold amber) in result text
- Keyboard navigation: arrows to navigate, enter to open, tab to filter, esc to close
- Bottom bar with keyboard shortcut hints
- Props: `open: boolean`, `onClose`, `onSelect: (result: SearchResult) => void`, `data: SearchResult[]`
- `SearchResult` interface:
```ts
type SearchCategory = 'exchange' | 'route' | 'agent'
interface SearchResult {
id: string
category: SearchCategory
title: string // e.g., route name "content-based-routing"
badges: Badge[] // status badges (Completed, Failed, etc.)
meta: string // correlation ID, duration, context line
timestamp?: string // relative time ("2s ago")
icon?: ReactNode
expandedContent?: string // JSON preview for inline expansion
matchRanges?: [number, number][] // highlight ranges in title/meta
}
```
- Scoped filter tags narrow the search by category or field (e.g., `route: order` filters to routes matching "order"); removing a tag widens the search back
#### DataTable
- Sortable columns (click header to sort, indicator arrow)
- Compact 40px rows
- Row selection (single-click to select, highlights row)
- Optional left accent border on rows (e.g., red for failed)
- Inline content below row (e.g., error preview)
- Column types: text, mono, badge, status, duration, timestamp
- Client-side pagination with configurable page size (default 25, options: 10/25/50/100)
- Pagination bar at bottom: page info ("1-25 of 3,241"), page size selector, prev/next buttons
- Props: `columns: Column[]`, `data: T[]`, `onRowClick?`, `selectedId?`, `sortable?: boolean`, `pageSize?: number`, `pageSizeOptions?: number[]`
#### DetailPanel
- Sliding panel from the right (400px width)
- Header with close button
- Tabbed content (Overview, Processors, Exchange, Error)
- Bottom action bar
- Animated slide-in/out
- Props: `open: boolean`, `onClose`, `title: string`, `tabs: Tab[]`, `actions?: ReactNode`
#### Dropdown
- Trigger + floating menu
- Menu items with optional icons, dividers
- Props: `trigger: ReactNode`, `items: DropdownItem[]`
#### FilterBar
- Composable row: search input + filter pills + separator + time presets
- Active filter tags row below (removable tags + "Clear all")
- Props: `filters: Filter[]`, `activeFilters: ActiveFilter[]`, `onFilterChange`, `searchPlaceholder?: string`
#### MenuItem
- Sidebar navigation item
- Health dot + label + meta text + count badge
- States: default, hover, active (amber accent + left border)
- Indentation support for tree hierarchy
- Props: `label: string`, `meta?: string`, `count?: number`, `health?: StatusDot variant`, `active?: boolean`, `indent?: number`
#### Modal
- Generic overlay dialog
- Backdrop + centered content card
- Close on esc, close on backdrop click
- Props: `open: boolean`, `onClose`, `title?: string`, `children`, `size?: 'sm' | 'md' | 'lg'`
#### Tabs
- Horizontal tab bar
- Optional count badges per tab
- Underline active indicator
- Props: `tabs: { label: string; count?: number; value: string }[]`, `active: string`, `onChange`
#### AreaChart / LineChart
- SVG-based time-series charts, no external charting libraries
- Features: grid lines, axis labels, optional SLA threshold line (dashed, labeled), legend
- Multiple series support using `--chart-1` through `--chart-8` tokens
- Hover tooltip showing values at cursor position
- AreaChart fills below the line; LineChart is lines only
- Props: `series: { label: string; data: { x: number | Date; y: number }[]; color?: string }[]`, `xLabel?: string`, `yLabel?: string`, `threshold?: { value: number; label: string }`, `height?: number`
#### BarChart
- SVG vertical bar chart for categorical or time-bucketed data
- Stacked or grouped mode
- Same token-based coloring as AreaChart/LineChart
- Props: `series: { label: string; data: { x: string; y: number }[]; color?: string }[]`, `stacked?: boolean`, `height?: number`
#### ProcessorTimeline
- Gantt-style horizontal bar timeline for exchange processor steps
- Each bar: processor name (left label), colored bar (green=ok, amber=slow, red=fail), duration label (right)
- Bar width proportional to duration relative to total exchange time
- Clickable bars (select processor)
- Props: `processors: { name: string; type: string; durationMs: number; status: 'ok' | 'slow' | 'fail'; startMs: number }[]`, `totalMs: number`, `onProcessorClick?`
#### EventFeed
- Scrolling list of real-time events
- Each item: icon (by severity), text content, relative timestamp
- Auto-scrolls to newest (with "pause" on manual scroll)
- Category filtering (error, warning, info, success)
- Severity uses `'error' | 'warning' | 'success' | 'running'` to stay consistent with the semantic naming convention. In event context, `running` represents informational/neutral events (teal).
- Props: `events: { id: string; severity: 'error' | 'warning' | 'success' | 'running'; message: string; timestamp: Date }[]`, `maxItems?: number`
#### ShortcutsBar
- Fixed-position bar at bottom-right of viewport
- Displays keyboard shortcut hints as a horizontal list
- Each hint: `KeyboardHint` + description text
- Props: `shortcuts: { keys: string; label: string }[]`
#### Accordion
- Multiple collapsible sections managed as a group
- Uses Collapsible internally in controlled mode
- Single mode (default): opening one closes others. Multiple mode: independent.
- Container: `--border-subtle` border, `--radius-md`, sections separated by dividers
- Props: `items: AccordionItem[]`, `multiple?: boolean`
- `AccordionItem`: `{ id: string, title: ReactNode, content: ReactNode, defaultOpen?: boolean }`
#### AlertDialog
- Confirmation dialog for destructive/important actions, built on Modal
- Fixed layout: icon area + title + description + button row
- Variant-colored icon circle (danger=✕, warning=⚠, info=)
- Auto-focuses Cancel button (safe default for destructive dialogs)
- Props: `open`, `onClose`, `onConfirm`, `title`, `description`, `variant?: 'danger' | 'warning' | 'info'`, `loading?`
#### AvatarGroup
- Renders overlapping Avatar components with overflow count
- Overlap: negative margin-left per size. Ring: 2px solid `--bg-surface`
- Overflow circle: `--bg-inset` background, `--font-mono` 10px, `+N` text
- Props: `names: string[]`, `max?: number`, `size?: 'sm' | 'md' | 'lg'`
#### Popover
- Click-triggered floating panel with CSS triangle arrow
- Positions: top, bottom, left, right. Alignment: start, center, end
- Closes on outside click or Esc. Uses `createPortal`.
- Animation: opacity + scale, 150ms ease-out
- Props: `trigger: ReactNode`, `content: ReactNode`, `position?`, `align?`
#### Toast (ToastProvider + useToast)
- App-level toast notification system via React Context
- `ToastProvider` wraps the app; `useToast()` returns `{ toast, dismiss }`
- `toast({ title, description?, variant?, duration? })` → returns id
- Container: fixed bottom-right, z-index 1100, max 5 visible
- Each toast: left accent border, variant icon, auto-dismiss (default 5000ms)
- Slide-in animation from right, fade-out on dismiss
#### TreeView
- Hierarchical tree with recursive rendering and keyboard navigation
- Controlled or uncontrolled expand state
- Selected node: amber background + left border
- Keyboard: arrows (navigate), left/right (collapse/expand), Enter (select), Home/End
- Connector lines for visual hierarchy
- ARIA: `role="tree"`, `role="treeitem"`, `aria-expanded`
- Props: `nodes: TreeNode[]`, `onSelect?`, `selectedId?`, `expandedIds?`, `onToggle?`
- `TreeNode`: `{ id, label, icon?, children?, meta? }`
### 5.4 Layout
#### AppShell
- 3-column flexbox layout: `Sidebar | Main | DetailPanel`
- Full viewport height (`100vh`), no body scroll
- Detail panel conditionally rendered (slides in/out)
- Props: `sidebar: ReactNode`, `children` (main content), `detail?: ReactNode`
#### Sidebar
- 220px fixed width, warm charcoal (`--sidebar-bg`)
- Sections from top to bottom:
1. Logo area: camel icon + "cameleer" brand (JetBrains Mono, amber-light) + version
2. Search input (filter apps)
3. Navigation section: "Applications" header + MenuItem list
4. Divider
5. Routes section: "Routes" header + indented MenuItem list
6. Agent health section: header with badge ("4/4 live") + agent items (dot, name, version, tps, last-seen)
7. Bottom: Admin, API Docs links
- Props: `apps: App[]`, `routes: Route[]`, `agents: Agent[]`, `activeItem?: string`
#### TopBar
- 48px height, white background, bottom border
- Left: Breadcrumb
- Center: Global search trigger (click opens CommandPalette)
- Right: Environment badge, shift indicator, user avatar + name
- Props: `breadcrumb: BreadcrumbItem[]`, `environment?: string`, `shift?: string`, `user?: { name: string }`
---
## 6. Pages
Pages compose design system components with mock data. Each page is a route.
| Route | Page | Description |
|--------------------|------------------|--------------------------------------------------|
| `/` | Dashboard | Main operations view from `mock-v2-light.html` |
| `/metrics` | Metrics | KPI dashboard from `mock-v3-metrics-dashboard` |
| `/routes/:id` | RouteDetail | Per-route stats from `mock-v3-route-detail` |
| `/exchanges/:id` | ExchangeDetail | Message inspector from `mock-v3-exchange-detail` |
| `/agents` | AgentHealth | Agent monitoring from `mock-v3-agent-health` |
| `/inventory` | Inventory | Component showcase with live interactive demos |
Pages are built after the design system layer is complete.
---
## 7. Mock Data
Static TypeScript objects in `src/mocks/` providing realistic data matching the HTML mockups:
- `exchanges.ts` — exchange rows with status, duration, order IDs, error messages
- `routes.ts` — route definitions with processor lists
- `agents.ts` — agent health data (name, version, status, tps, last-seen)
- `metrics.ts` — KPI values, chart data points
All data is imported directly by pages — no fetch calls, no loading states needed for standalone mode.
---
## 8. Implementation Order
1. **Project scaffold** — Vite + React + TypeScript + React Router + CSS Modules config
2. **Foundations** — `tokens.css`, `reset.css`, `hashColor.ts`, `ThemeProvider`
3. **Primitives** — all primitive components (can be built in parallel)
4. **Composites** — DataTable, CommandPalette, FilterBar, DetailPanel, etc.
5. **Layout** — AppShell, Sidebar, TopBar
6. **Mock data** — static data files
7. **Pages** — Dashboard first, then remaining pages
---
## 9. Design Decisions
| Decision | Rationale |
|----------|-----------|
| CSS Modules over Tailwind | Mocks already define a CSS variable token system; CSS Modules preserve that naturally and scope class names |
| No component library dependency | shadcn/ui-like ownership — components are part of the codebase, not an npm package |
| Light theme first | User preference; dark theme is "too colorful" in current mocks and needs a muted redesign |
| Deterministic badge colors | Existing app uses hash-based name→color mapping; design system replicates this with FNV-1a |
| 3-column layout as shell | All pages share sidebar + topbar; only main content and optional detail panel change per route |
| Mock data as TypeScript objects | Enables standalone layouts without server; type-safe; easy to replace with API calls later |
| DM Sans + JetBrains Mono | Established in existing mocks and live application |