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