--- name: ccc:design-system description: Design system and visual language — theming, CSS variables, Tailwind config, component styling patterns, icon usage, animations, and z-index layers. Use when creating or modifying UI components, working with the dark/light theme, or debugging visual issues. --- # Design System & Visual Language How the theming, color palette, component patterns, and styling conventions work. ## Theme Architecture Two themes (dark/light) driven by CSS custom properties in `src/renderer/index.css`. Toggled via `useTheme()` hook which adds/removes `light` class on `document.documentElement`. Flash prevention: a script in `index.html` applies the cached theme before React loads. ### Theme Hook ```typescript // src/renderer/hooks/useTheme.ts const { theme, resolvedTheme, isDark, isLight } = useTheme(); // theme: 'dark' | 'light' | 'system' // resolvedTheme: 'dark' | 'light' (after system resolution) ``` ## Styling Convention **Colors**: Always via CSS variables (theme-aware). Use inline `style` or Tailwind classes mapped to variables. **Layout/spacing**: Tailwind utility classes. **Icons**: `lucide-react` with `size-*` Tailwind classes. ```tsx // Preferred: inline style for theme-aware colors, Tailwind for layout
// Also valid: Tailwind classes that reference CSS variables
``` ### TypeScript Constants `src/renderer/constants/cssVariables.ts` centralizes CSS variable strings: ```typescript import { COLOR_TEXT_MUTED, CARD_BG, CARD_BORDER_STYLE } from '@renderer/constants/cssVariables'; Muted text
Card
``` Constants cover: text colors, surfaces, borders, code blocks, diff, cards, tags, prose. ## CSS Variable Reference All defined in `src/renderer/index.css` under `:root` (dark) and `:root.light`. ### Surfaces | Variable | Dark | Light | Usage | |----------|------|-------|-------| | `--color-surface` | `#141416` | `#f9f9f7` | Main background | | `--color-surface-raised` | `#27272a` | `#f0efed` | Elevated surfaces | | `--color-surface-overlay` | `#27272a` | `#e8e7e4` | Overlays/modals | | `--color-surface-sidebar` | `#0f0f11` | `#f1f0ee` | Sidebar background | ### Text | Variable | Dark | Light | Usage | |----------|------|-------|-------| | `--color-text` | `#fafafa` | `#1c1b19` | Primary text | | `--color-text-secondary` | `#a1a1aa` | `#4d4b46` | Secondary text | | `--color-text-muted` | `#71717a` | `#6d6b65` | Muted text | ### Borders | Variable | Dark | Light | |----------|------|-------| | `--color-border` | `rgba(255,255,255,0.05)` | `#d5d3cf` | | `--color-border-subtle` | `rgba(255,255,255,0.05)` | `#e3e1dd` | | `--color-border-emphasis` | `rgba(255,255,255,0.1)` | `#a8a5a0` | ### Chat Bubbles **User bubble** (right-aligned): | Variable | Dark | Light | |----------|------|-------| | `--chat-user-bg` | `#27272a` | `#eae9e6` | | `--chat-user-text` | `#a1a1aa` | `#5a5955` | | `--chat-user-border` | `rgba(255,255,255,0.08)` | `#d5d3cf` | | `--chat-user-shadow` | `0 1px 0 0 rgba(255,255,255,0.03)` | `0 1px 2px 0 rgba(0,0,0,0.04)` | | `--chat-user-tag-bg` | `rgba(255,255,255,0.08)` | `rgba(0,0,0,0.05)` | | `--chat-user-tag-text` | `#e4e4e7` | `#3a3935` | | `--chat-user-tag-border` | `rgba(255,255,255,0.12)` | `rgba(0,0,0,0.08)` | **AI message**: | Variable | Dark | Light | |----------|------|-------| | `--chat-ai-border` | `rgba(255,255,255,0.05)` | `#d5d3cf` | | `--chat-ai-icon` | `#71717a` | `#6d6b65` | **System bubble**: | Variable | Dark | Light | |----------|------|-------| | `--chat-system-bg` | `rgba(39,39,42,0.5)` | `#eae9e6` | | `--chat-system-text` | `#d4d4d8` | `#3a3935` | ### Code & Syntax | Variable | Dark | Light | |----------|------|-------| | `--code-bg` | `#1c1c1e` | `#f0efed` | | `--code-header-bg` | `#1c1c1e` | `#eae9e6` | | `--code-border` | `rgba(255,255,255,0.1)` | `#d5d3cf` | | `--code-line-number` | `#52525b` | `#a8a5a0` | | `--code-filename` | `#60a5fa` | `#2563eb` | | `--inline-code-bg` | `rgba(255,255,255,0.08)` | `rgba(0,0,0,0.05)` | | `--inline-code-text` | `#e4e4e7` | `#3a3935` | Syntax highlighting: `--syntax-string`, `--syntax-comment`, `--syntax-number`, `--syntax-keyword`, `--syntax-type`, `--syntax-operator`, `--syntax-function`. Dark uses vibrant colors; light uses GitHub-inspired palette. ### Semantic Blocks **Thinking**: Purple tones (`--thinking-bg`, `--thinking-border`, `--thinking-text`) **Tool call**: Amber tones (`--tool-call-bg`, `--tool-call-border`, `--tool-call-text`) **Tool result success**: Green tones (`--tool-result-success-bg/border/text`) **Tool result error**: Red tones (`--tool-result-error-bg/border/text`) **Output**: Gray tones (`--output-bg`, `--output-border`, `--output-text`) **Interruption**: Red (`--interruption-bg/border/text`) **Warning**: Amber (`--warning-bg/border/text`) **Plan exit**: Green (`--plan-exit-bg/header-bg/border/text`) ### Diff Viewer | Variable | Dark | Light | |----------|------|-------| | `--diff-added-bg` | `rgba(34,197,94,0.15)` | `rgba(34,197,94,0.1)` | | `--diff-added-text` | `#4ade80` | `#166534` | | `--diff-removed-bg` | `rgba(239,68,68,0.15)` | `rgba(239,68,68,0.1)` | | `--diff-removed-text` | `#f87171` | `#991b1b` | ### Cards (Subagents) | Variable | Dark | Light | |----------|------|-------| | `--card-bg` | `#121212` | `#f9f9f7` | | `--card-border` | `#27272a` | `#d5d3cf` | | `--card-header-bg` | `#18181b` | `#f0efed` | | `--card-header-hover` | `#1f1f23` | `#eae9e6` | | `--card-icon-muted` | `#52525b` | `#a8a5a0` | | `--card-separator` | `#3f3f46` | `#d5d3cf` | ### Badges Status badges: `--badge-error-bg/text`, `--badge-warning-bg/text`, `--badge-success-bg/text`, `--badge-info-bg/text`, `--badge-neutral-bg/text`. Tags: `--tag-bg`, `--tag-text`, `--tag-border`. ### Search Highlights | Variable | Dark | Light | |----------|------|-------| | `--highlight-bg` | `rgba(202,138,4,0.7)` | `#facc15` | | `--highlight-bg-inactive` | `rgba(113,63,18,0.5)` | `#fef08a` | | `--highlight-ring` | `#facc15` | `#ca8a04` | ### Scrollbar Custom scrollbar styling via `--scrollbar-thumb`, `--scrollbar-thumb-hover`, `--scrollbar-thumb-active`. ## Tailwind Config `tailwind.config.js` maps CSS variables to Tailwind classes: ```javascript colors: { surface: { DEFAULT: 'var(--color-surface)', raised: 'var(--color-surface-raised)', overlay: 'var(--color-surface-overlay)', sidebar: 'var(--color-surface-sidebar)', code: 'var(--code-bg)', }, border: { DEFAULT: 'var(--color-border)', subtle: 'var(--color-border-subtle)', emphasis: 'var(--color-border-emphasis)', }, text: { DEFAULT: 'var(--color-text)', secondary: 'var(--color-text-secondary)', muted: 'var(--color-text-muted)', }, semantic: { success: '#22c55e', error: '#ef4444', warning: '#f59e0b', info: '#3b82f6', }, // Backward-compatible alias 'claude-dark': { bg: 'var(--color-surface)', surface: 'var(--color-surface-raised)', ... }, } ``` Plugin: `@tailwindcss/typography` for markdown prose. ## Icon Library `lucide-react` across 55+ components. **Sizes**: `size-3` (tiny), `size-3.5` (small), `size-4` (standard), `size-5` (medium), `size-10` (empty states) **Pattern**: `className` for size, `style` for color: ```tsx ``` **Common icons**: `Bot` (AI), `User` (user), `ChevronRight`/`ChevronDown` (expansion), `Check`/`Copy` (clipboard), `Info` (tooltips), `Loader2` (loading), `Clock` (duration), `Terminal` (traces), `CheckCircle2` (completed). ## Team Colors `src/renderer/constants/teamColors.ts` defines 8 color sets for teammate visualization: ```typescript import { getTeamColorSet, TeamColorSet } from '@renderer/constants/teamColors'; const colors = getTeamColorSet('blue'); // colors.border: '#3b82f6' — border accent // colors.badge: 'rgba(59, 130, 246, 0.15)' — badge background // colors.text: '#60a5fa' — text color ``` Available colors: blue, green, red, yellow, purple, cyan, orange, pink. ## Component Patterns ### User Chat Bubble Right-aligned with rounded corners, subtle shadow, copy overlay on hover: ```tsx
{text}
``` ### AI Group Header Collapsible with model badge, summary, metrics: ```tsx
Claude {model.name} {itemsSummary}
``` ### Subagent Card Linear-style card with nested expansion: ```tsx
{/* colored dot + badge + description + metrics */}
{isExpanded &&
{/* content */}
}
``` ### Copy Button (Overlay) Gradient-fade overlay that appears on group hover: ```tsx
``` ### Popover via Portal Token usage and context badges use portaled popovers to escape stacking context: ```tsx {showPopover && createPortal(
{/* content */}
, document.body )} ``` ## Animations ### CSS Keyframes (in `index.css`) - `shimmer` — skeleton loading shimmer effect - `skeleton-fade-in` — staggered fade-in for skeleton cards - `splash-slide` — splash screen loading bar ### Tailwind Utilities | Class | Usage | |-------|-------| | `animate-spin` | Loading spinners (`Loader2`) | | `animate-ping` | Pulsing dots (ongoing state) | | `transition-transform` | Chevron rotation | | `transition-colors` | Hover color changes | | `transition-opacity` | Copy button fade-in | | `transition-all duration-300` | Card highlight transitions | | `duration-[3000ms]` | Highlight ring fade-out | ## Z-Index Layers | Z-Index | Usage | |---------|-------| | `z-10` | Copy button overlays, dropdown backdrops | | `z-20` | Dropdown menus, search bar | | `z-30` | Pane split drop zones | | `z-40` | Pane view overlays | | `z-50` | Context menus, command palette, settings selects | | `99999` | Portaled popovers (token usage, context badge, metrics pill) | ## Light Theme Notes The light theme uses **warm neutrals** (not pure white/gray): - Backgrounds: `#f9f9f7`, `#f0efed`, `#eae9e6` (warm off-white) - Borders: `#d5d3cf`, `#e3e1dd` (warm gray) - Text: `#1c1b19`, `#4d4b46`, `#6d6b65` (warm dark) - Syntax highlighting: GitHub-inspired palette Body transition: `background-color 0.2s ease, color 0.2s ease` for smooth theme switching.