diff --git a/.design/DESIGN.md b/.design/DESIGN.md new file mode 100644 index 0000000..70eb75b --- /dev/null +++ b/.design/DESIGN.md @@ -0,0 +1,370 @@ +# Agentic RAG — Design System v1 + +A self-contained spec for the light-theme dashboard redesign. Implementer should be able to copy `globals.css` verbatim and ship. + +Aesthetic anchor: **Linear meets Vercel** — calm warm-neutral surfaces, single indigo accent, generous whitespace, micro-soft shadows, fast-feeling interactions. No glassmorphism, no gradients on chrome, no decorative gradients except optionally on the empty-state hero. + +--- + +## 1. Design principles + +- **Calm by default.** Surfaces are warm off-white; the eye should rest on content, not chrome. Chrome (topbar, sidebar, borders) sits at very low contrast against the page. +- **One accent, used sparingly.** A single indigo accent marks the primary action, the active nav item, and focus rings. Secondary buttons and metadata stay neutral. Avoid rainbow status pills. +- **Generous whitespace, tight type.** Padding is bigger than it feels like it needs to be; type scale stays compact and dense. The contrast (air outside, density inside) is the look. +- **Soft, layered shadows over hard borders.** Cards and popovers float on `0 1px 2px` + `0 8px 24px` stacks rather than 1px slate borders. Borders exist but at very low alpha (`oklch(0 0 0 / 0.06)`). +- **Fast feel.** All transitions are 120–180ms ease-out. Hover states are subtle (background tint shift, not transform). No skeleton-shimmer waltzes; use a static muted placeholder. + +--- + +## 2. Color tokens + +Light theme only for v1. Values in OKLCH. Neutrals are slightly warm (a≈0.005, h≈80) — closer to paper than to slate. Pure `#fff` and `#000` are explicitly avoided. + +| Token | OKLCH | Hex (approx) | Use | +|---|---|---|---| +| `--bg-app` | `oklch(0.985 0.003 80)` | `#FAFAF8` | Page background | +| `--bg-surface` | `oklch(1 0 0)` | `#FFFFFF` | Cards, sidebar, topbar (the only place near-white lives) | +| `--bg-surface-elevated` | `oklch(0.995 0.002 80)` | `#FDFDFB` | Popovers, modals, dropdowns | +| `--bg-subtle` | `oklch(0.97 0.004 80)` | `#F4F3F0` | Hover states, code blocks, inactive tabs | +| `--border-subtle` | `oklch(0.92 0.004 80)` | `#E8E6E1` | Dividers inside cards | +| `--border-default` | `oklch(0.88 0.005 80)` | `#DCDAD3` | Card outlines, input borders | +| `--border-strong` | `oklch(0.78 0.006 80)` | `#C2BFB6` | Focus-adjacent, hovered inputs | +| `--text-primary` | `oklch(0.22 0.01 80)` | `#26241F` | Body, headings (warm near-black, not `#000`) | +| `--text-secondary` | `oklch(0.42 0.008 80)` | `#5A574F` | Subheads, secondary labels | +| `--text-muted` | `oklch(0.58 0.006 80)` | `#85827A` | Timestamps, helper text, placeholders | +| `--text-inverse` | `oklch(0.985 0.003 80)` | `#FAFAF8` | Text on accent/danger fills | +| `--accent` | `oklch(0.55 0.19 270)` | `#5B5BD6` | Primary buttons, active nav, focus ring (indigo) | +| `--accent-hover` | `oklch(0.50 0.20 270)` | `#4F4FC9` | Primary button hover | +| `--accent-subtle` | `oklch(0.96 0.03 270)` | `#EDEDFA` | Active nav background, accent badges | +| `--accent-foreground` | `oklch(0.985 0.003 80)` | `#FAFAF8` | Text on `--accent` fills | +| `--success` | `oklch(0.62 0.14 155)` | `#3FA46A` | Indexed/ready status | +| `--success-subtle` | `oklch(0.95 0.04 155)` | `#E5F4EB` | Success badge bg | +| `--warning` | `oklch(0.72 0.15 75)` | `#D69845` | Processing, pending | +| `--warning-subtle` | `oklch(0.96 0.05 75)` | `#FAF0DC` | Warning badge bg | +| `--danger` | `oklch(0.58 0.20 25)` | `#D14545` | Delete, error | +| `--danger-subtle` | `oklch(0.96 0.04 25)` | `#FBE8E5` | Danger badge bg | +| `--ring` | `oklch(0.55 0.19 270 / 0.35)` | — | Focus ring (3px) | + +**Justification for indigo over blue/violet:** indigo (h≈270) reads modern and "AI-native" without veering into cliché ChatGPT teal or Anthropic clay. It pairs cleanly with warm neutrals (cool accent on warm canvas = the Linear/Resend signature). + +--- + +## 3. Typography + +**Font stack:** Inter via `next/font/google` with `display: swap`, plus `'JetBrains Mono'` for code/IDs. System fallback chain for instant first paint. + +```ts +// app/layout.tsx +import { Inter, JetBrains_Mono } from 'next/font/google' +const sans = Inter({ subsets: ['latin'], variable: '--font-sans', display: 'swap' }) +const mono = JetBrains_Mono({ subsets: ['latin'], variable: '--font-mono', display: 'swap' }) +``` + +**Size scale** (compact — intentionally one notch tighter than Tailwind defaults): + +| Token | px | line-height | Use | +|---|---|---|---| +| `text-xs` | 12 | 16 | Badges, timestamps, table meta | +| `text-sm` | 13 | 20 | Body default, nav items, inputs | +| `text-base` | 14 | 22 | Chat messages, card body | +| `text-md` | 15 | 24 | Emphasized body | +| `text-lg` | 17 | 26 | Card titles, section headers | +| `text-xl` | 20 | 28 | Page titles | +| `text-2xl` | 24 | 32 | Document viewer title | +| `text-3xl` | 30 | 38 | Empty-state hero only | + +**Weights:** 400 (body), 500 (UI labels, nav), 600 (headings, button text), 700 (rare — emphasis only). +**Letter-spacing:** `-0.01em` on `text-lg` and above; `0` elsewhere. No uppercase tracking unless on a `text-xs` overline. + +--- + +## 4. Spacing & radius + +Tailwind's default 4px-step spacing scale is fine — keep it. Most padding lives in `2 / 3 / 4 / 6 / 8` (8 / 12 / 16 / 24 / 32 px). + +**Radius scale** (override Tailwind's defaults — slightly larger): + +| Token | px | Use | +|---|---|---| +| `rounded-sm` | 4 | Badges (small) | +| `rounded-md` | 8 | Inputs, small buttons | +| `rounded-lg` | 10 | Buttons, nav items, source rows | +| `rounded-xl` | 14 | Cards, dropzone, message bubbles | +| `rounded-2xl` | 18 | Modal/popover, document viewer card | +| `rounded-full` | 9999 | Avatars, pill badges, icon buttons | + +Pick `rounded-xl` (14px) as the default card radius — the v1 sweet spot between Vercel's 8 and Linear's 12. + +--- + +## 5. Shadow scale + +Tailwind's defaults are too gray and too vertical. Replace with layered, warm-tinted, low-alpha stacks. All shadows use a single hue (the warm neutral) and combine a tight contact shadow with a longer ambient one. + +| Token | Value | Use | +|---|---|---| +| `shadow-xs` | `0 1px 2px 0 oklch(0 0 0 / 0.04)` | Buttons, inputs at rest | +| `shadow-sm` | `0 1px 2px 0 oklch(0 0 0 / 0.04), 0 2px 4px -1px oklch(0 0 0 / 0.04)` | Cards | +| `shadow-md` | `0 4px 8px -2px oklch(0 0 0 / 0.06), 0 2px 4px -2px oklch(0 0 0 / 0.04)` | Hovered cards, source rows | +| `shadow-lg` | `0 12px 24px -8px oklch(0 0 0 / 0.10), 0 4px 8px -4px oklch(0 0 0 / 0.06)` | Popovers, dropdowns | +| `shadow-xl` | `0 24px 48px -12px oklch(0 0 0 / 0.14), 0 8px 16px -8px oklch(0 0 0 / 0.08)` | Modals, command palette | +| `shadow-focus` | `0 0 0 3px var(--ring)` | Focus ring (combine with border) | + +--- + +## 6. Component patterns + +### Topbar +Sticky, 56px tall, full-width, sits on `--bg-surface` with a single `--border-subtle` bottom border (no shadow). Holds: logo + product name (left), breadcrumb or page title (center-left), global search trigger (center, optional v2), user avatar + menu (right). No shadow at rest; on scroll past 4px, fade in `shadow-xs`. +```tsx +
+``` + +### Sidebar (expanded) +240px wide, full-height, `--bg-surface`, right border `--border-subtle`. Sections stacked with 24px vertical gaps. Section headers are `text-xs font-medium uppercase tracking-wide text-muted` (only place uppercase appears). Sticky. +```tsx +