Why
The client already has a real design system underneath the app: ui-* surface tokens and primitives in src/client/components/ui (button, input, select, switch, badge, alert, text, layer-card, confirm-dialog), generic pieces in src/client/components/shared (skeleton, empty-state, section-card, copy-button, jobs-table, bar-sparkline), motion helpers in src/client/components/motion, and cross-cutting utilities in src/client/lib (theme, utils, highlight, file-tree, ease, timezone). None of it is separable from the app today. This issue has no dependency on the server-side extraction issues and can be done at any point.
Scope
- Create packages/ui/src holding the primitives, the generic shared components, the motion helpers, the token/theme layer from src/client/app.css and src/client/lib/theme.tsx, and the presentational utilities.
- Move feature components (src/client/components/features) and pages (src/client/pages) to apps/dashboard, which depends on packages/ui.
- Enforce the boundary with the eslint import rule from the scaffolding issue: nothing in packages/ui may import from apps/dashboard, reference a route path, call src/client/lib/api.ts, or know about domain entities. Components take props.
- Split the components that currently straddle the line, jobs-table.tsx, page-header-actions.tsx and chart-primitives.tsx, into a generic primitive in packages/ui plus a domain-aware wrapper left in apps/dashboard.
- Keep recharts/motion/lucide as peer dependencies of packages/ui so consumers control versions, and confirm the manual chunking in vite.config.ts (now apps/dashboard/vite.config.ts) still splits vendors correctly.
- Every moved component must render correctly in both light and dark theme, using tokens rather than literal colors.
Acceptance criteria
Non-goals
Redesigning anything. Visual output should be pixel-identical to main.
Why
The client already has a real design system underneath the app: ui-* surface tokens and primitives in src/client/components/ui (button, input, select, switch, badge, alert, text, layer-card, confirm-dialog), generic pieces in src/client/components/shared (skeleton, empty-state, section-card, copy-button, jobs-table, bar-sparkline), motion helpers in src/client/components/motion, and cross-cutting utilities in src/client/lib (theme, utils, highlight, file-tree, ease, timezone). None of it is separable from the app today. This issue has no dependency on the server-side extraction issues and can be done at any point.
Scope
Acceptance criteria
Non-goals
Redesigning anything. Visual output should be pixel-identical to main.