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

6.6 KiB
Raw Permalink Blame History

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.

: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).