Files
design-system/docs/superpowers/specs/2026-03-18-design-system-design.md
hsiegeln 9c9b4e06c5 Initial commit: UI mocks and design system specification
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 08:36:48 +01:00

17 KiB

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