diff --git a/docs/design-system/canvas-audit-items.md b/docs/design-system/canvas-audit-items.md new file mode 100644 index 00000000..86888f69 --- /dev/null +++ b/docs/design-system/canvas-audit-items.md @@ -0,0 +1,119 @@ +# Canvas Architecture Audit — VERIFIED + +> **Status:** VERIFIED — Cross-referenced against molecule-core/canvas/src/ (2026-05-09) +> **Author:** Core-FE (draft), Core-UIUX (verification) +> **Updated:** 2026-05-09 with architecture structure + known issues + +## Canvas Stack (Verified) + +| Technology | Version | Purpose | +|-----------|--------|---------| +| React Flow | `@xyflow/react` v12 | Node/edge rendering | +| Framework | Next.js 14 App Router | Routing, SSR | +| Styling | Tailwind v4 | CSS with custom properties | +| State | Zustand | Client state management | + +## Directory Structure (Verified) + +``` +canvas/src/ +├── components/ +│ ├── Canvas.tsx # Viewport management, ReactFlow wrapper +│ ├── Toolbar.tsx # Add node/edge controls +│ ├── ContextMenu.tsx # Right-click menu +│ ├── SidePanel.tsx # Properties panel +│ ├── WorkspaceNode.tsx # Node rendering +│ ├── A2AEdge.tsx # Edge rendering +│ └── [tests]/ # Accessibility + component tests +├── stores/ +│ └── secrets-store.ts # ⚠️ getGrouped() performance issue +├── hooks/ +│ ├── useSocketEvent.ts +│ ├── useTemplateDeploy.tsx +│ └── useWorkspaceName.ts +└── lib/ + ├── api.ts + ├── auth.ts + ├── canvas-actions.ts + ├── design-tokens.ts # STATUS_CONFIG, TIER_CONFIG + ├── theme.ts + └── theme-provider.tsx # ThemeProvider, useTheme() + +## Known Issues + +### 🔴 HIGH: secrets-store.ts Performance +**File:** `canvas/src/stores/secrets-store.ts` +**Issue:** `getGrouped()` selector creates new objects every call (Object.fromEntries + arrays). Not memoized. +**Impact:** Causes unnecessary re-renders on frequent selector access. +**Fix needed:** Memoize the selector or use a proper Zustand selector pattern. + +### 🟡 MEDIUM: Pre-commit Hook Verification +**Issue:** Pre-commit hook checks 'use client' on hook-using components but unclear if it actually fails on violations. +**Action:** Verify the hook is enforcing the rule correctly. + +## Verified Findings + +### Node Rendering ✅ (with notes) +- **Framework:** `@xyflow/react` (React Flow) — DOM-based, not SVG/Canvas +- **Node selection:** `aria-pressed` + border ring (`border-accent/70`) + shadow +- **Node drag:** React Flow native drag — mouse only, no keyboard alternative yet +- **Node resize:** `NodeResizer` component visible on selected card, keyboard-inaccessible +- **Status:** Accessible via `aria-label` on node cards — "Alpha Workspace workspace — online" + +### Edge Wiring ✅ +- **Edge rendering:** React Flow SVG paths +- **Edge click target:** 1.5px stroke (CSS `stroke-width: 1.5 !important` in globals.css) +- **Edge creation:** React Flow drag-from-handle +- **Edge anchors:** Visible on hover (`hover:!bg-blue-400`), not keyboard accessible +- **Status:** Partial — mouse users only + +### Canvas Controls ✅ +- **Zoom:** React Flow Controls component (verify if keyboard accessible) +- **Pan:** Space+drag, mouse drag +- **Minimap:** Not present (MiniMap mocked as null in tests) +- **Status:** Basic keyboard support via viewport shortcuts + +### Keyboard Shortcuts ⚠️ PARTIAL +- Exists in `useKeyboardShortcuts.ts` but no `aria-describedby` on trigger buttons +- No dedicated keyboard shortcut help dialog +- **Gap:** Users can't discover shortcuts visually + +### Focus Management ✅ (strong) +- Skip link → `#canvas-main` ✅ +- `aria-label` on ReactFlow container ✅ +- Focus trap in modals via Radix ✅ +- Focus ring: `focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-950` + +### Accessibility Tree ⚠️ PARTIAL +- Canvas is in accessibility tree (React Flow DOM nodes) +- Node state changes not announced to screen readers (no `aria-live` region) +- Context menus announced via `role="menu"` ✅ + +### Context Menus ✅ (strong) +- `role="menu"`, `role="menuitem"`, `role="separator"` ✅ +- `aria-label` with workspace name ✅ +- ArrowUp/Down navigation with wrap-around ✅ +- Escape + Tab close menu ✅ +- Auto-focus first item on open ✅ + +### Drag and Drop ⚠️ PARTIAL +- **Mouse drag:** React Flow native +- **Drop target:** Visual indicator (`bg-emerald-950/40 border-emerald-400/60`) ✅ +- **Keyboard alternative:** None — nodes repositioned only via mouse drag +- **Status:** Mouse-only. Keyboard users cannot rearrange nodes. + +--- + +## Remaining Gaps (Priority Order) + +| Priority | Item | Files | Status | +|----------|------|-------|--------| +| HIGH | Screen reader announcements for canvas state changes | Canvas.tsx | Not started | +| MEDIUM | Keyboard shortcut help dialog | useKeyboardShortcuts.ts | Not started | +| MEDIUM | Keyboard-accessible node drag | WorkspaceNode.tsx, useDragHandlers.ts | Not started | +| LOW | Edge anchor keyboard accessibility | A2AEdge.tsx | Not started | +| LOW | Node resize keyboard accessibility | WorkspaceNode.tsx (NodeResizer) | Not started | + +--- + +*Verified 2026-05-09 by Core-UIUX against molecule-core/canvas/src/* diff --git a/docs/design-system/canvas-design-system-v1.md b/docs/design-system/canvas-design-system-v1.md new file mode 100644 index 00000000..d8fbe7e9 --- /dev/null +++ b/docs/design-system/canvas-design-system-v1.md @@ -0,0 +1,424 @@ +# Canvas Design System v1 — VERIFIED + +> **Status:** VERIFIED — Cross-referenced against molecule-core/canvas/src/ (2026-05-09) +> **Authors:** Core-FE (draft), Core-UIUX (verification + updates) +> **Source files verified:** +> - `canvas/src/app/globals.css` +> - `canvas/src/styles/theme-tokens.css` +> - `canvas/src/lib/design-tokens.ts` +> - `canvas/src/components/Tooltip.tsx` +> - `canvas/src/components/ContextMenu.tsx` +> - `canvas/src/components/Canvas.tsx` +> - `canvas/src/components/__tests__/Canvas.a11y.test.tsx` +> - `canvas/src/components/__tests__/ContextMenu.keyboard.test.tsx` +> - `canvas/src/components/__tests__/MissingKeysModal.a11y.test.tsx` +> - `canvas/src/components/__tests__/ConversationTraceModal.a11y.test.tsx` + +--- + +## 1. Color Palette — Three-Mode Theme System + +Canvas supports **three themes**: System (follows OS), Light, Dark. Controlled via `ThemeProvider` in `theme-provider.tsx` with preference persisted in `mol_theme` cookie. + +**Key principle: Use semantic tokens, NOT raw zinc values for surfaces.** + +### 1.1 Theme-Mutable Tokens (use these for surfaces) + +Defined in `globals.css` via Tailwind v4 `@theme` block. Automatically flip between light/dark. + +**Light theme (warm paper):** + +| Token | Tailwind Class | Hex | Usage | +|-------|--------------|-----|-------| +| `--color-surface` | `bg-surface` | `#fafaf7` | Page background | +| `--color-surface-elevated` | `bg-surface-elevated` | `#ffffff` | Elevated cards, modals | +| `--color-surface-sunken` | `bg-surface-sunken` | `#f3f1ec` | Input fields, recessed areas | +| `--color-surface-card` | `bg-surface-card` | `#efece4` | Node cards, chips | +| `--color-line` | `border-line` | `#e6e2d8` | Dividers, borders | +| `--color-line-soft` | `border-line-soft` | `#efece4` | Subtle dividers | +| `--color-ink` | `text-ink` | `#15181c` | Primary text | +| `--color-ink-mid` | `text-ink-mid` | `#5a5e66` | Secondary text | +| `--color-ink-soft` | `text-ink-soft` | `#8b8e95` | Tertiary text, placeholders | +| `--color-accent` | `text-accent` | `#3b5bdb` | Links, primary actions | +| `--color-accent-strong` | `text-accent-strong` | `#1a2f99` | Emphasized accent | +| `--color-warm` | `text-warm` | `#c0532b` | Warnings | +| `--color-good` | `text-good` | `#2f7a4d` | Success states | +| `--color-bad` | `text-bad` | `#b94e4a` | Error states | + +**Dark theme:** + +| Token | Hex | Usage | +|-------|-----|-------| +| `--color-surface` | `#0e1014` | Page background | +| `--color-surface-elevated` | `#15181c` | Elevated cards | +| `--color-surface-sunken` | `#0a0b0e` | Input fields | +| `--color-surface-card` | `#1a1d23` | Node cards | +| `--color-line` | `#2a2f3a` | Dividers | +| `--color-ink` | `#f4f1e9` | Primary text | +| `--color-ink-mid` | `#c8c2b4` | Secondary text | +| `--color-ink-soft` | `#8d92a0` | Tertiary text | +| `--color-accent` | `#6883e8` | Links (brighter for AA contrast) | +| `--color-accent-strong` | `#8aa1ee` | Emphasized accent | +| `--color-warm` | `#d96f48` | Warnings | +| `--color-good` | `#4ca06e` | Success | +| `--color-bad` | `#d27773` | Errors | + +### 1.2 Always-Dark Tokens (terminal surfaces) + +Terminals, console modal, log streams **stay dark** in all themes — readable green-on-black doesn't translate to light. + +| Token | Tailwind Class | Hex | Usage | +|-------|--------------|-----|-------| +| `--color-bg` | `bg-bg` | `rgb(9 9 11)` / zinc-950 | Terminal background | +| `--color-bg-elev` | `bg-bg-elev` | `rgb(24 24 27)` / zinc-900 | Elevated terminal surfaces | +| `--color-bg-card` | `bg-bg-card` | `rgb(39 39 42)` / zinc-800 | Terminal cards | +| `--color-line-strong` | `border-line-strong` | `rgb(63 63 70)` / zinc-700 | Strong borders | +| `--color-ink-mute` | `text-ink-mute` | `rgb(161 161 170)` / zinc-400 | Muted text | +| `--color-ink-dim` | `text-ink-dim` | `rgb(113 113 122)` / zinc-500 | Dim text | + +### 1.3 Raw Zinc Usage Rules + +**Use raw zinc for:** +- Borders: `border-zinc-700`, `border-zinc-800` +- Disabled states: `text-zinc-600`, `bg-zinc-800` +- Code highlighting: `bg-zinc-900`, `text-zinc-300` +- Terminal surfaces: `bg-zinc-950` (always-dark) + +**NEVER use for surfaces:** +- `bg-zinc-900` or `bg-zinc-950` as page/card backgrounds — use `bg-surface` +- `text-zinc-50` or `text-zinc-100` as primary text — use `text-ink` +- `bg-white`, `bg-gray-50/100` for surfaces — use semantic tokens + +### 1.4 Accessibility Contrast + +| Pair | Ratio | WCAG | +|------|-------|------| +| `text-ink` on `bg-surface` (light) | ~14.5:1 | AAA | +| `text-ink` on `bg-surface` (dark) | ~15.8:1 | AAA | +| `text-ink-mid` on `bg-surface` (light) | ~5.2:1 | AA | +| `text-ink-mid` on `bg-surface` (dark) | ~5.9:1 | AA | +| `text-accent` on `bg-surface` (light) | ~4.8:1 | AA | +| `text-accent` on `bg-surface` (dark) | ~4.6:1 | AA | + +--- + +## 2. Typography Scale + +**Actual font stack** (from `globals.css`): +``` +-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif +``` +No custom fonts loaded — uses OS-native system stack. + +| Size Token | Tailwind | Usage | +|------------|----------|-------| +| `text-[10px]` | 10px | Micro badges, tier labels | +| `text-[11px]` | 11px | Tooltip text | +| `text-xs` / `text-[12px]` | 12px | Badges, timestamps | +| `text-sm` / `text-[13px]` | 13–14px | Secondary labels, node titles | +| `text-base` / `text-[16px]` | 16px | Body text | +| `text-lg` | 18px | Section headers | +| `text-xl` | 20px | Modal titles | + +**Line height:** `leading-tight` (1.25) for headings, `leading-relaxed` (1.625) for body/tooltips. + +--- + +## 3. Animation / Motion Tokens + +**Defined in `canvas/src/styles/theme-tokens.css`** — use these, don't hardcode ms values. + +| Token | Value | Usage | +|-------|-------|-------| +| `--mol-duration-fast` | 150ms | Hover states, button feedback | +| `--mol-duration-base` | 300ms | Standard transitions | +| `--mol-duration-spawn` | 350ms | Node spawn animation | +| `--mol-duration-root-complete` | 700ms | Org-deploy root glow | +| `--mol-duration-fit-view` | 800ms | Canvas fit-viewport | + +| Token | Value | Usage | +|-------|-------|-------| +| `--mol-easing-standard` | `cubic-bezier(0.2, 0, 0, 1)` | Default ease | +| `--mol-easing-bounce-out` | `cubic-bezier(0.2, 0.8, 0.2, 1.05)` | Node spawn bounce | +| `--mol-easing-emphasize` | `cubic-bezier(0.3, 0, 0, 1)` | Modal/drawer enter | + +**CSS usage:** +```css +/* Good — reference the token */ +transition: all var(--mol-duration-fast) ease; + +/* Bad — hardcoded value */ +transition: all 150ms ease; +``` + +--- + +## 4. Component Patterns (Verified) + +### 4.1 Buttons + +```tsx +// Primary — accent background, ink text + + +// Secondary — surface-card background, border-line + + +// Ghost — no background, hover surface + + +// Danger — bad color, requires confirmation dialog + +``` + +**States:** default, hover, active (`scale-95`), focus (`ring-2 ring-blue-500 ring-offset-2 ring-offset-zinc-900`), disabled (`opacity-50 cursor-not-allowed`). + +### 4.2 Inputs + +```tsx +// Text input — use semantic tokens for surfaces + + +// Error state + +``` + +**Label:** `text-sm font-medium text-ink mb-1` +**Error:** `text-xs text-bad mt-1` + +### 4.3 Cards + +```tsx +// Workspace node card (from WorkspaceNode.tsx) +