nexus/.planning/phases/41-diagrams-icons-theme-engine/41-05-SUMMARY.md

11 KiB
Raw Blame History

phase plan subsystem tags requires provides affects tech-stack key-files key-decisions duration completed
41-diagrams-icons-theme-engine 05 ui
theme-engine
oklch
wcag
shadcn
react-testing-library
jsdom
tdd
phase provides
41-01 useContentJob hook, contentJobs API, progress.tsx, toggle.tsx shadcn components
phase provides
41-03 theme-renderer, PaletteRole type, ThemePaletteBundle, nexus-settings customTheme
ui/src/components/ThemeSeedInput.tsx — color picker + hex input, debounced onChange
ui/src/components/ThemePaletteGrid.tsx — swatch grid with WCAG AA/Fails AA badges, empty state
ui/src/components/ThemePreviewPanel.tsx — scoped CSS injection via ref (NOT document.documentElement)
ui/src/components/ThemePreviewPanel.test.tsx — 7 tests for THEME-04 scoped injection
ui/src/components/ThemeExportTabs.tsx — CSS/Tailwind/VS Code/JSON tabs with copy buttons
ui/src/components/ThemeApplyConfirmDialog.tsx — Apply theme / Keep current confirmation dialog
ui/src/context/ThemeContext.tsx — extended with custom theme type and applyCustomTheme()
ui/src/pages/ContentStudio.tsx — Themes tab fully wired
ui/src/api/contentJobs.ts — submitContentJob, getContentJob, getContentJobAsset
ui/src/hooks/useContentJob.ts — SSE EventSource progress hook
theme-apply-flow
nexus-settings
added patterns
@testing-library/react@^16.3.2 (UI devDep)
@testing-library/jest-dom@^6.9.1 (UI devDep)
jsdom@^28.1.0 (UI devDep)
@vitejs/plugin-react added to ui/vitest.config.ts for JSX transform
ThemePreviewPanel pattern: useRef + useEffect + container.style.setProperty() — CSS vars scoped to .nexus-theme-preview, NEVER on document.documentElement
applyCustomTheme pattern: sets CSS vars on document.documentElement only on explicit user apply action
TDD in jsdom environment: // @vitest-environment jsdom override in individual test files
useContentJob hook: companyId parameter, SSE EventSource progress tracking, fetchAsset helper
created modified
ui/src/components/ThemeSeedInput.tsx
ui/src/components/ThemePaletteGrid.tsx
ui/src/components/ThemePreviewPanel.tsx
ui/src/components/ThemePreviewPanel.test.tsx
ui/src/components/ThemeExportTabs.tsx
ui/src/components/ThemeApplyConfirmDialog.tsx
ui/src/pages/ContentStudio.tsx
ui/src/api/contentJobs.ts
ui/src/hooks/useContentJob.ts
ui/src/components/ui/progress.tsx
ui/src/components/ui/toggle.tsx
ui/src/context/ThemeContext.tsx (extended Theme type, added applyCustomTheme, on-mount customTheme restore)
ui/vitest.config.ts (added @vitejs/plugin-react for JSX transform in tests)
ui/package.json (added @testing-library/react, jest-dom, jsdom as devDeps)
pnpm-lock.yaml
Used @testing-library/react + jsdom for ThemePreviewPanel tests instead of project's renderToStaticMarkup pattern — required because tests need to verify imperative DOM style.setProperty calls, which only work in a real DOM environment
ThemeContext extended from light/dark to light/dark/custom — custom treated as dark-mode for toggle/classList purposes
applyCustomTheme() sets CSS vars on document.documentElement (global apply) while ThemePreviewPanel sets on scoped container ref (preview only) — two distinct patterns for two distinct purposes
contentJobs.ts and useContentJob.ts created as prerequisites in this worktree — these files from Plan 41-01 are not available in this git worktree
On-mount customTheme restore: ThemeProvider fetches /api/nexus/settings on mount if stored theme is 'custom' to rehydrate CSS vars across page loads
17min 2026-04-04

Phase 41 Plan 05: Theme UI Components Summary

All theme UI components built and wired: seed input, palette grid with WCAG badges, scoped live preview (with TDD test coverage for THEME-04), 4-format export tabs, apply confirmation dialog, ThemeContext custom theme extension, ContentStudio Themes tab fully functional

Performance

  • Duration: ~17 min
  • Started: 2026-04-04T20:49:03Z
  • Completed: 2026-04-04T21:06:26Z
  • Tasks: 2
  • Files modified: 14

Accomplishments

  • ThemeSeedInput: <input type="color"> + hex text Input side-by-side with htmlFor="seed-color" label, debounced onChange (150ms), helper text "We'll generate a full palette in OKLCH."
  • ThemePaletteGrid: Dark and light swatch rows, 40×40px swatches, hex labels, WCAG AA / Fails AA badges using --chart-2 green and --destructive red. Empty state with exact copywriting from UI-SPEC.
  • ThemePreviewPanel: .nexus-theme-preview container with useRef, CSS vars injected imperatively via container.style.setProperty() only on the scoped element (never document.documentElement). Mini Nexus UI mock (sidebar + card + button). aria-live="polite" announces "Palette updated" on palette changes.
  • ThemePreviewPanel.test.tsx: 7 TDD tests (jsdom environment) verifying class, aria-label, aria-live, dark/light CSS var values, and document scope isolation — all passing.
  • ThemeExportTabs: CSS Variables / Tailwind Config / VS Code Theme / JSON tabs with pre/code blocks. Copy button per tab with aria-label="Copy {tab name}", "Copied!" feedback (2s), keyboard accessible.
  • ThemeApplyConfirmDialog: Dialog with "Apply theme?" heading, "This will update your Nexus color scheme. You can revert from Settings." body, "Apply theme" primary button, "Keep current" ghost button.
  • ThemeContext extended: Theme type = "light" | "dark" | "custom". applyCustomTheme(palette, variant) sets CSS vars on document.documentElement, updates state to "custom", stores to localStorage. On-mount: if stored theme is "custom", fetches /api/nexus/settings to restore customTheme.palette vars.
  • ContentStudio page: Themes tab with full generate/preview/export/apply flow using useContentJob SSE hook.
  • contentJobs.ts and useContentJob.ts created as prerequisites (not present in this worktree from Plan 41-01).
  • progress.tsx and toggle.tsx shadcn components added.

Task Commits

  1. TDD RED — failing ThemePreviewPanel tests + infrastructure - 78e50189 (test)
  2. Task 1 + Task 2: All components + ThemeContext + ContentStudio - 05ce37df (feat)

Files Created/Modified

  • ui/src/components/ThemeSeedInput.tsx — Color picker + hex input, debounced onChange
  • ui/src/components/ThemePaletteGrid.tsx — Swatch grid with WCAG badges
  • ui/src/components/ThemePreviewPanel.tsx — Scoped CSS injection preview panel
  • ui/src/components/ThemePreviewPanel.test.tsx — 7 TDD tests for THEME-04
  • ui/src/components/ThemeExportTabs.tsx — 4-format export tabs with copy
  • ui/src/components/ThemeApplyConfirmDialog.tsx — Confirm dialog
  • ui/src/context/ThemeContext.tsx — Extended with custom theme type + applyCustomTheme
  • ui/src/pages/ContentStudio.tsx — Themes tab fully wired
  • ui/src/api/contentJobs.ts — Content job API helpers
  • ui/src/hooks/useContentJob.ts — SSE progress hook
  • ui/src/components/ui/progress.tsx — shadcn Progress component
  • ui/src/components/ui/toggle.tsx — shadcn Toggle component
  • ui/vitest.config.ts — Added @vitejs/plugin-react for JSX transform
  • ui/package.json — Added testing devDeps
  • pnpm-lock.yaml — Updated lockfile

Decisions Made

  • @testing-library/react and jsdom installed as devDeps — the project's renderToStaticMarkup pattern cannot test imperative DOM calls (style.setProperty); THEME-04 specifically requires verifying that CSS variables are set on the scoped container, not on document.documentElement. jsdom environment is required.
  • ThemeContext Theme type extended to include "custom" — "custom" is treated as dark for classList.toggle("dark") purposes so the app shell continues to use dark color scheme when a custom palette is active.
  • Two CSS injection patterns established: ThemePreviewPanel → scoped container ref (live preview only); applyCustomTheme()document.documentElement (global apply on user confirmation).
  • contentJobs.ts, useContentJob.ts, progress.tsx, toggle.tsx created in this plan — these were supposed to come from Plan 41-01 but that plan's work exists only on the parallel branch, not in this worktree.

Deviations from Plan

Auto-fixed Issues

1. [Rule 3 - Blocking] Added @testing-library/react + jsdom for TDD requirement

  • Found during: TDD RED phase
  • Issue: The plan requires testing style.setProperty DOM calls on a scoped container element. The project's existing test pattern uses // @vitest-environment node with renderToStaticMarkup (server-side rendering), which cannot test imperative DOM manipulation. No @testing-library/react or jsdom was installed.
  • Fix: Added @testing-library/react@^16.3.2, @testing-library/jest-dom@^6.9.1, and jsdom@^28.1.0 as UI devDeps. Updated ui/vitest.config.ts to include @vitejs/plugin-react for JSX transform support. Individual test files use // @vitest-environment jsdom override to avoid affecting existing node-env tests.
  • Files modified: ui/package.json, ui/vitest.config.ts, pnpm-lock.yaml
  • Verification: All 7 ThemePreviewPanel tests pass

2. [Rule 3 - Blocking] Created contentJobs.ts, useContentJob.ts, progress.tsx, toggle.tsx as prerequisites

  • Found during: Task 1 (ContentStudio wiring)
  • Issue: These files from Plan 41-01 are not present in this worktree (parallel branch divergence, same issue documented in 41-03 SUMMARY)
  • Fix: Created all four files from scratch matching the interfaces documented in Plan 41-01 SUMMARY
  • Files modified: ui/src/api/contentJobs.ts, ui/src/hooks/useContentJob.ts, ui/src/components/ui/progress.tsx, ui/src/components/ui/toggle.tsx

3. [Rule 1 - Bug] Removed @testing-library/jest-dom top-level import from test file

  • Found during: TDD RED phase
  • Issue: import "@testing-library/jest-dom" calls expect.extend() globally before vitest's expect is initialized, causing ReferenceError: expect is not defined
  • Fix: Removed the import; tests use vitest's built-in assertions which cover all required test cases without jest-dom matchers

Total deviations: 3 auto-fixed (Rules 1, 3, 3) Impact on plan: All plan goals met. Testing infrastructure added as required for THEME-04 verification. No plan scope expanded.

Known Stubs

None — all components are fully implemented. ThemePreviewPanel renders a real mini Nexus UI mock with CSS variables applied from the palette. ContentStudio is wired to the real content job API.

Self-Check: PASSED

  • FOUND: ui/src/components/ThemeSeedInput.tsx
  • FOUND: ui/src/components/ThemePaletteGrid.tsx
  • FOUND: ui/src/components/ThemePreviewPanel.tsx
  • FOUND: ui/src/components/ThemePreviewPanel.test.tsx
  • FOUND: ui/src/components/ThemeExportTabs.tsx
  • FOUND: ui/src/components/ThemeApplyConfirmDialog.tsx
  • FOUND: ui/src/context/ThemeContext.tsx (extended)
  • FOUND: ui/src/pages/ContentStudio.tsx
  • FOUND: ui/src/api/contentJobs.ts
  • FOUND: ui/src/hooks/useContentJob.ts
  • FOUND: ui/src/components/ui/progress.tsx
  • FOUND: ui/src/components/ui/toggle.tsx
  • FOUND commit: 78e50189 (test - TDD RED)
  • FOUND commit: 05ce37df (feat - Task 1+2 implementation)
  • All 7 ThemePreviewPanel tests pass
  • TypeScript compiles without errors (pnpm tsc --noEmit --project ui/tsconfig.json from worktree)

Phase: 41-diagrams-icons-theme-engine Completed: 2026-04-04