# Proposal — web-ui-rework > Phase: **proposal**. Grounded in `openspec/changes/web-ui-rework/exploration.md` (verified inventory). No code changes. > Product/UX questions for the user are collected in **§8 — Proposal question round** and must be answered before this proposal is treated as locked. ## 1. Problem / motivation The Manage frontend is **mid-migration**: the app shell (`App.tsx`) and the new `ObservabilityPage.tsx` already live on shadcn/ui + Tailwind + lucide-react, but **22 files still import `@mui/material`, 2 use `@mui/x-data-grid`, and 2 use `@mui/icons-material`** (10 distinct icons). The result is a split-feeling product: two design languages, two theming systems (MUI `@emotion` vs the Tailwind v4 `@theme` tokens in `index.css`), and a no-op `theme.ts` shim left behind. Orphaned charting deps (`recharts`, `d3`) linger in `package.json` even though **no in-app charting exists**. This half-finished state collides with a second driver: Manage has moved to a **thin-dashboard observability model** where charts/metrics/logs live in external Grafana, and in-app surfaces show only Alertmanager alerts + Prometheus target health + Grafana deep-links. Finishing the migration is therefore not cosmetic — it is how we *codify* the decoupling (remove the charting deps, retire the last MUI surface, make the external-link affordance consistent) and land a coherent visual redesign on one design system. ## 2. Target outcome / product vision A single, coherent Manage UI on **shadcn/ui + Tailwind v4 + lucide-react**, visually consistent with the already-migrated shell and ObservabilityPage: - Every page uses the same tokens (primary `#4f8cff`, Inter, `rounded-lg`, one border weight) and the same layout primitives — `Card` over ad-hoc `Paper`, Tailwind flex/grid over MUI `Grid`/`Stack`. - Observability stays first-class and **thin**: status as numbers/Badges plus outbound Grafana kiosk deep-links; **no in-app charts**. - DataGrid surfaces (Media, FileBrowser) become TanStack Table on the shadcn `Table` primitive, preserving pagination / row-selection / row-click / column-visibility. - A reconciled information architecture (Backups surfaced in nav; one name for Media/Applications). - `@mui/*`, `@emotion/*`, `recharts`, `d3`, and the `theme.ts` shim are gone, and `docs/REQUIREMENTS.md` reflects the new UX. ## 3. Scope (in) - Finish MUI v9 → shadcn/ui + Tailwind + lucide-react across all remaining consumers in `frontend/src` (22 material files + 2 DataGrid files + 2 icon files). - Add the missing shadcn primitives (`tabs table dialog input label checkbox switch progress separator avatar textarea dropdown-menu scroll-area`) and `@tanstack/react-table`. - Migrate both `@mui/x-data-grid` DataGrid usages to TanStack Table + shadcn `Table`. - Deliver a visual/UX redesign aligned to the thin-dashboard model (density, typography ramp, status Badge semantics including a success variant, consistent external-link affordance). - Remove orphaned `recharts`, `d3`, the `theme.ts` shim, and all `@mui/*` + `@emotion/*` deps. - Reconcile IA: surface Backups in nav; resolve Media vs Applications naming. - Update `docs/REQUIREMENTS.md`. ## 4. Non-goals - **No in-app charting** — no recharts/d3 re-introduction, no custom charts; metrics visualisation stays in Grafana. - **No new design system** — shadcn/ui + the existing Tailwind v4 `@theme` tokens only. - **No backend API changes** — frontend `types/*` contracts stay unless a UI simplification forces one, which must be flagged separately. - **No MUI retention** — all `@mui/*` and `@emotion/*` removed by the final slice. - **No auth/routing model change** — OIDC (`auth.ts`, `react-oidc-context`) and `react-router-dom` structure unchanged; only nav *items*/IA may shift. - **No form-library introduction** — keep current uncontrolled/`useState` form parity. ## 5. Key technical risks - **DataGrid → TanStack Table (high).** The two DataGrid usages (`Media.tsx`, `FileBrowser.impl.tsx`) must reproduce pagination, row-selection, row-click, and column-visibility. Whether to also carry over client-side **resizing/sorting** is an open product question (exploration §4) and is the single highest-risk slice — sequenced **last** so the shared `Table` primitive and tokens are already settled. - **UsersPage size (high).** `pages/UsersPage.impl.tsx` is the largest consumer (25 MUI components + 9 icons + `Drawer` + `Table` + a rich-text compose UI with formatting actions and attachments). Likely needs a sub-split to stay under the 400-line slice budget. - **Cross-slice visual drift (medium).** 22 files over ~8 slices; mitigated by migrating shared building blocks first (exploration §9 slice 2) and documenting the Tailwind layout patterns up front. - **lucide-react v1 pin (low–medium).** `^1.14.0` is an unusual major; the 10 mapped icon names must be verified to exist before the UsersPage slice. - **No component test harness today (medium).** See exploration §11; whether to add Vitest in slice 1 is an open question. ## 6. High-level slice strategy Chained PRs, ≤400 changed lines each, each leaving `npm run build` + `npm run lint` green. Full table and sequencing rationale live in exploration.md **§9**; summary: 1. **Foundation** — shadcn primitives + `@tanstack/react-table`, remove `recharts`/`d3`, delete `theme.ts`. 2. **Shared components** — `SectionCard`, `SelectionRailCard`, `TabbedCard`, `MetricCard`, `DiskSpaceCard`, `HoverEditButton`, `DialogFooter`, `ConfirmDialog`, `LibraryOverview`, `NowPlaying`/`SessionActivityPanel`. 3. **Backups cluster** (+ surface `/backups` in nav). 4. **Dashboard + Applications**. 5. **Settings + Actions**. 6. **Users** (largest; may sub-split). 7. **DataGrid → TanStack Table** — Media + FileBrowser (highest risk; last). 8. **Cleanup + docs** — remove `@mui/*` + `@emotion/*`, final typecheck/lint, update `docs/REQUIREMENTS.md`. Slices 6 and 7 are the most likely to need sub-splitting to stay ≤400 lines. ## 7. Success criteria - `npm run build` (tsc -b + vite build) and `npm run lint` green; existing `node --test` suites pass. - **Zero `@mui/*` or `@emotion/*` imports remain** in `frontend/src` (verified by grep); `recharts`, `d3`, and `theme.ts` removed. - Both DataGrid surfaces rebuilt on TanStack Table with pagination / row-selection / row-click / column-visibility parity. - **ObservabilityPage parity preserved** (Alertmanager alerts + Prometheus target health + Grafana deep-links); no in-app charts introduced. - Visual consistency with the migrated shell + ObservabilityPage; Backups surfaced in nav; Media/Applications naming resolved. - `docs/REQUIREMENTS.md` updated to reflect the UX/architecture change. ## 8. Proposal question round The following product/UX questions need user answers before this proposal is treated as locked. They are intended to uncover business rules, IA intent, and scope boundaries — not delivery mechanics. *(Parent: ask these verbatim.)* **Q1 — Information architecture (parent-ask):** Today the sidebar lists Dashboard, Observability, Media, Files, Users, Actions, Settings — but "Media" routes to `/applications` (component `Applications`), and a working **Backups** page (`/backups`) is hidden from the nav. Do you want (a) **Backups as its own top-level nav item**, (b) Backups grouped under a "Media ops" section, or (c) left as URL-only? And should the Media/Applications surface be named **"Media"** or **"Applications"** going forward? **Q2 — Visual density (parent-ask):** The migrated shell uses **comfortable** spacing (`p-4 md:p-6`, `gap-4`). Should the redesign keep comfortable density across all 22 pages, or do some dense tables (Media, Users, Backups runs) need a **compact** mode to fit more rows on screen? **Q3 — TanStack Table feature parity (parent-ask):** The current DataGrids use column **visibility**; **resizing** and **sorting** usage is ambiguous. For the rebuilt tables, do you want (a) **visibility-only** parity (smallest scope), (b) add **sorting**, or (c) add **sorting + column resizing** (closest to a full DataGrid)? **Q4 — Test harness (parent-ask):** There is **no component test harness** today (only `tsc -b`, `eslint`, and `node --test` on pure-JS transforms). Should slice 1 introduce a **minimal Vitest + Testing Library** harness for the migrated components, or proceed on **static + manual smoke** evidence per slice? **Q5 — Palette (parent-ask):** The current primary is **`#4f8cff`** with `chart-1..5` tokens defined but unused. Do you want to (a) **keep `#4f8cff`** and repurpose the chart tokens as Grafana-link color cues, (b) **shift the primary** to a new accent (please specify), or (c) keep the palette as-is and **drop** the unused chart tokens entirely? --- **Recommended next phase:** `spec` — after the §8 questions are answered, refine scope/token/IA decisions and lock the slice contracts.