Initial commit: UI mocks and design system specification
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
446
docs/superpowers/specs/2026-03-18-design-system-design.md
Normal file
446
docs/superpowers/specs/2026-03-18-design-system-design.md
Normal file
@@ -0,0 +1,446 @@
|
||||
# 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
|
||||
--info (--running), --info-bg, --info-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
|
||||
```
|
||||
|
||||
Light theme values are taken directly from `mock-v2-light.html` `:root` block. Dark theme values will be derived with inverted lightness and reduced saturation (the existing `mock-v2-dark.html` is considered "too colorful" — dark theme should be more muted).
|
||||
|
||||
### 3.2 Deterministic Color Hashing
|
||||
|
||||
A `hashColor(name: string)` utility generates consistent HSL colors from any string:
|
||||
|
||||
1. Compute SHA-256 of the input name
|
||||
2. Extract first 2 bytes → map to hue (0-360)
|
||||
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
|
||||
|
||||
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/
|
||||
│ │ ├── Avatar/
|
||||
│ │ │ ├── Avatar.tsx
|
||||
│ │ │ └── Avatar.module.css
|
||||
│ │ ├── Badge/
|
||||
│ │ ├── Button/
|
||||
│ │ ├── Card/
|
||||
│ │ ├── Checkbox/
|
||||
│ │ ├── DateTimePicker/
|
||||
│ │ ├── DateRangePicker/
|
||||
│ │ ├── EmptyState/
|
||||
│ │ ├── FilterPill/
|
||||
│ │ ├── InfoCallout/
|
||||
│ │ ├── Input/
|
||||
│ │ ├── KeyboardHint/
|
||||
│ │ ├── MonoText/
|
||||
│ │ ├── Select/
|
||||
│ │ ├── SectionHeader/
|
||||
│ │ ├── Spinner/
|
||||
│ │ ├── StatCard/
|
||||
│ │ ├── StatusDot/
|
||||
│ │ ├── Tag/
|
||||
│ │ ├── Toggle/
|
||||
│ │ ├── Tooltip/
|
||||
│ │ └── index.ts # Barrel export
|
||||
│ ├── composites/
|
||||
│ │ ├── Breadcrumb/
|
||||
│ │ ├── CommandPalette/
|
||||
│ │ ├── DataTable/
|
||||
│ │ ├── DetailPanel/
|
||||
│ │ ├── Dropdown/
|
||||
│ │ ├── FilterBar/
|
||||
│ │ ├── MenuItem/
|
||||
│ │ ├── Modal/
|
||||
│ │ ├── Tabs/
|
||||
│ │ └── index.ts
|
||||
│ ├── layout/
|
||||
│ │ ├── AppShell/
|
||||
│ │ ├── Sidebar/
|
||||
│ │ ├── TopBar/
|
||||
│ │ └── index.ts
|
||||
│ └── providers/
|
||||
│ └── ThemeProvider.tsx
|
||||
├── pages/
|
||||
│ ├── Dashboard/
|
||||
│ ├── Metrics/
|
||||
│ ├── RouteDetail/
|
||||
│ ├── ExchangeDetail/
|
||||
│ └── AgentHealth/
|
||||
├── mocks/ # Static TypeScript mock data
|
||||
│ ├── executions.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 SHA-256 → hue mapping (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 `variant` for status badges (`success`, `warning`, `error`, `info`)
|
||||
- Props: `label: string`, `variant?: 'filled' | 'outlined' | 'dashed'`, `color?: 'success' | 'warning' | 'error' | 'info' | '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?: 'amber' | 'green' | 'error' | 'teal' | 'warning'`, `children`
|
||||
|
||||
#### Checkbox
|
||||
- Styled checkbox with warm focus ring
|
||||
- Props: standard input[checkbox] props + `label?: string`
|
||||
|
||||
#### DateTimePicker
|
||||
- Date + time input with calendar dropdown
|
||||
- Warm styling consistent with Input component
|
||||
- Props: `value`, `onChange`, `min?`, `max?`
|
||||
|
||||
#### DateRangePicker
|
||||
- Start + end datetime selection
|
||||
- Built-in presets: "Last 1h", "Last 6h", "Today", "This shift", "Last 24h", "Last 7d", "Custom"
|
||||
- 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?: 'green' | 'error' | 'teal'`, `onClick`
|
||||
|
||||
#### InfoCallout
|
||||
- Block with left accent border (3px, amber by default)
|
||||
- Light background
|
||||
- Props: `children`, `variant?: 'info' | '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?: string`, `accent?: string`
|
||||
|
||||
#### StatusDot
|
||||
- 6-7px colored circle
|
||||
- Variants: `live` (green + pulse), `stale` (amber), `dead` (gray), `success` (green), `warning` (orange), `error` (red), `info` (teal)
|
||||
- Optional pulse animation (enabled by default for `live`)
|
||||
- Props: `variant: 'live' | 'stale' | 'dead' | 'success' | 'warning' | 'error' | 'info'`, `pulse?: boolean`
|
||||
|
||||
#### Tag
|
||||
- Removable pill with x dismiss button
|
||||
- Color from `hashColor` or explicit
|
||||
- Props: `label: string`, `onRemove?: () => void`, `color?: string`
|
||||
|
||||
#### 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?: string`, `children` (trigger element)
|
||||
|
||||
### 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, Executions, Routes, Exchanges, 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) => void`, `data: SearchResult[]`
|
||||
|
||||
#### 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
|
||||
- Props: `columns: Column[]`, `data: T[]`, `onRowClick?`, `selectedId?`, `sortable?: boolean`
|
||||
|
||||
#### 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`
|
||||
|
||||
### 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` |
|
||||
|
||||
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:
|
||||
|
||||
- `executions.ts` — execution 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 SHA-based name→color mapping; design system must replicate this |
|
||||
| 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 |
|
||||
Reference in New Issue
Block a user