Files
Inboxintel/docs/specs/ui-overhaul.md
2026-07-01 00:33:19 +02:00

152 lines
6.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 `<html>`, 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`), 1624px gaps.
- **Density**: comfortable default; tables get a compact variant for big lists.
- **Motion**: 150200ms 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).