# Spec: UI Overhaul — Stripe/Notion aesthetic, Tailwind + shadcn-style, light+dark A full visual rebuild: clean, airy, modern, light-first with a polished dark mode. Rolled out **incrementally** — design system first, then page-by-page — so the app keeps working throughout. ## 1. Stack Add to `frontend`: - **tailwindcss** (+ `postcss`, `autoprefixer`) — utility styling. - **Radix UI primitives** (`@radix-ui/react-*`: dialog, dropdown-menu, tabs, tooltip, switch, popover, toast, separator, scroll-area) — accessible behavior. - **class-variance-authority** + **tailwind-merge** + **clsx** — the shadcn component pattern. - **lucide-react** — icon set (clean, consistent; replaces ad-hoc emoji where it helps). Config: - `tailwind.config.js` — content globs over `index.html` + `src/**/*.{js,jsx}`; theme extends map to CSS variables (below); `darkMode: 'class'`. - `postcss.config.js`. A `src/index.css` with `@tailwind base/components/utilities` + the token `:root` / `.dark` blocks. Keep the old `styles.css` importing until a page is migrated, then drop per-page. ## 2. Design tokens (CSS variables, HSL) Defined once in `src/index.css`; Tailwind theme references them so `bg-background`, `text-foreground`, `bg-primary`, etc. just work and flip with `.dark`. ```css :root { /* Neutrals — warm-tinted slate (Notion-ish paper) */ --background: 0 0% 100%; --foreground: 222 22% 12%; --card: 0 0% 100%; --muted: 220 16% 96%; --muted-foreground: 220 9% 46%; --border: 220 16% 90%; --input: 220 16% 90%; --ring: 245 75% 60%; /* Brand accent — indigo/iris (modern SaaS, Stripe-blurple cousin) */ --primary: 245 75% 59%; /* #5b5bf0-ish */ --primary-foreground: 0 0% 100%; /* Semantic */ --success: 152 56% 40%; --warning: 38 92% 50%; --danger: 0 72% 51%; --danger-foreground: 0 0% 100%; --radius: 0.625rem; /* soft, modern corners */ } .dark { --background: 224 32% 9%; /* deep slate, not pure black */ --foreground: 220 18% 92%; --card: 224 28% 12%; --muted: 223 22% 17%; --muted-foreground: 220 12% 64%; --border: 223 20% 20%; --input: 223 20% 22%; --ring: 245 80% 66%; --primary: 245 80% 67%; --primary-foreground: 224 32% 9%; --success: 152 50% 50%; --warning: 38 92% 58%; --danger: 0 70% 60%; } ``` > **Accent is swappable by design.** The accent lives in exactly one token (`--primary`, > plus its dark variant). Changing the brand color = editing those two lines. This also > makes a **future user-facing accent picker** cheap: store a chosen hue on the user (or > in `localStorage`) and write `--primary`/`--ring` at runtime. Decision (2026-06-30): > ship with **Indigo `#5b5bf0`**; leave the picker as a documented future enhancement. ### Proposed palette (for sign-off) | Token | Light | Dark | Use | |-------|-------|------|-----| | Primary (accent) | **Indigo `#5b5bf0`** | `#7c7cf5` | buttons, links, active nav, focus ring | | Background | `#ffffff` | `#11151f` | app canvas | | Card/surface | `#ffffff` | `#161b27` | panels, cards | | Muted surface | `#f3f5f9` | `#1f2533` | subtle fills, hover | | Border | `#e3e8ef` | `#2b3242` | hairlines | | Text | `#191e2b` | `#e7eaf2` | body | | Muted text | `#6b7280` | `#9aa3b2` | secondary | | Success | `#2f9e6b` | `#3dbd86` | healthy, succeeded | | Warning | `#f5a623` | `#f7b84b` | caution, pending | | Danger | `#e23b3b` | `#ef5a5a` | destructive, failed | > **Alternatives if indigo isn't your taste** (pick one and I'll swap the single token): > Emerald `#10b981` (calm, "clean"), Violet `#7c3aed` (premium), Teal `#0d9488` (fresh), > Blue `#2563eb` (classic/trustworthy). Logo stays as-is; accent just needs to sit well beside it. ## 3. Component library (`src/components/ui/`) Hand-built shadcn-style primitives, each a thin `cva` wrapper over Tailwind + (where interactive) a Radix primitive: `button`, `card`, `input`, `textarea`, `select`, `checkbox`, `switch`, `badge`, `dialog`, `sheet` (side drawer), `dropdown-menu`, `tabs`, `tooltip`, `toast` (+ a `useToast` hook to replace ad-hoc toast state), `table`, `skeleton`, `separator`, `avatar`, `empty-state`. Plus app-level shells: `PageHeader`, `Sidebar`, `Topbar`, `StatCard`, `ThemeToggle` (writes `.dark` on ``, persists to `localStorage`). ## 4. Layout language - **Sidebar**: 248px, `bg-card`, hairline border, grouped nav with section labels (Overview · Cleanup · Automation · Account). Lucide icons. Active item = soft primary tint pill. Collapsible to icon-rail on narrow widths. - **Topbar**: page title + breadcrumbs left; sync status, theme toggle, digest toggle, pending-review badge, "Sync now", avatar right. Sticky, subtle bottom border. - **Content**: max-width container, generous padding (`p-6`/`p-8`), cards with `rounded-[--radius]`, `border`, soft shadow (`shadow-sm`), 16–24px gaps. - **Density**: comfortable default; tables get a compact variant for big lists. - **Motion**: 150–200ms ease transitions on hover/expand; Radix-driven enter/exit on dialogs/sheets/toasts. Respect `prefers-reduced-motion`. ## 5. Page-by-page migration map | Order | Page | Notes | |-------|------|-------| | 0 | **Tooling + tokens + primitives** | no visual swap yet; build the system | | 1 | **App shell** (`Layout.jsx`) | sidebar + topbar + theme toggle — biggest immediate lift | | 2 | **Dashboard** | StatCards, chart cards restyled (keep chart.js, theme its colors via tokens) | | 3 | **Senders** | list/detail split, new email rows, policy dropdown (ties to sender-policy spec) | | 4 | **Unsubscribe** | table → new `table` primitive, confidence as colored `badge`/meter | | 5 | **Search / Folders / Cleanup** | shared list components, filter bar, bulk toolbar restyle | | 6 | **New feature pages** | Rules, Review queue, Screener, Activity, Privacy, Read-Later — built native | | 7 | **Landing** | polish to match the new system | | 8 | **Retire `styles.css`** | delete once nothing imports it | Each increment: convert the page, verify `npm run build`, screenshot/sanity-check, commit. ## 6. Accessibility & quality bar - Radix primitives give focus management, ESC/overlay behavior, ARIA for free — don't hand-roll dialogs/menus. - Visible focus ring (`--ring`) on all interactive elements. - Color is never the only signal (icons/labels alongside semantic colors). - Contrast ≥ WCAG AA in both themes for text and primary buttons. - Keyboard shortcuts (already present) preserved and surfaced in a `?` cheat-sheet dialog. ## 7. Out of scope - No logo redesign (keeping current `Logo.jsx`). - No mobile-dedicated layouts beyond responsive degradation (desktop-first decision). - No new charting library (theme the existing chart.js).