From a9ee44e7dd727003900acb0aefba1d017e542b60 Mon Sep 17 00:00:00 2001 From: m4 Date: Mon, 10 Aug 2026 13:33:19 +0800 Subject: [PATCH] docs(webui): spec for bright/premium UI polish Co-Authored-By: Claude Opus 4.7 --- .../2026-08-10-ui-polish-bright-design.md | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-10-ui-polish-bright-design.md diff --git a/docs/superpowers/specs/2026-08-10-ui-polish-bright-design.md b/docs/superpowers/specs/2026-08-10-ui-polish-bright-design.md new file mode 100644 index 0000000..b7a0574 --- /dev/null +++ b/docs/superpowers/specs/2026-08-10-ui-polish-bright-design.md @@ -0,0 +1,149 @@ +# UI Polish — Bright / Premium Theme — Design + +Date: 2026-08-10 +Status: Approved (approach A: token-first, layered) + +## Goal + +Make the WebUI feel bright (明快) and premium (高级感). The current warm-paper +palette and dense component styling read as "crude". This is a **purely +visual** change: no logic, no behavior, no API changes. Tests, tsc, and +eslint must stay green throughout. + +Decisions locked in during brainstorming: + +- **Palette direction:** bright color, Stripe-like — white/neutral surfaces, + vivid cyan accent, light-cyan active states, subtle gradient primary buttons. +- **Scope:** main UI (chat area, thread list, workspace/tasks panels) **and** + all dialogs, plus the login page. +- **Font:** self-hosted Inter variable font + typography tuning. Chinese text + keeps falling back to system fonts. +- **Dark mode:** redesigned in the same pass (cool neutral zinc dark, cyan + accents). +- **Execution:** token-first, then `ui/` primitives, then per-screen polish — + three independently shippable layers. + +## Layer 1 — Design tokens (`src/app/globals.css`) + +### Light theme + +| Token role | Value | Notes | +|---|---|---| +| App background | `#fafafa` (zinc-50) | replaces warm ivory | +| Surface / card / popover | `#ffffff` | | +| Border | `#e4e4e7` (zinc-200) | | +| Border light | `#f4f4f5` (zinc-100) | | +| Text primary | `#18181b` (zinc-900) | | +| Text secondary | `#52525b` (zinc-600) | | +| Text tertiary | `#a1a1aa` (zinc-400) | | +| Brand (links, text accents) | `#0891b2` (cyan-600) | AA on white | +| Brand hover | `#0e7490` (cyan-700) | | +| Brand solid (button fill) | gradient `cyan-500 #06b6d4` → `cyan-600 #0891b2` | see Button spec | +| Active/selected bg | `#ecfeff` (cyan-50) | paired with cyan-700 text | +| User message bubble bg | `#ecfeff` | unified with selection color | +| Success / warning / error | keep hues, shift to zinc-compatible: `#16a34a` / `#d97706` / `#dc2626` | | + +Shadows become lighter and layered: + +- `shadow-sm`: `0 1px 2px rgb(0 0 0 / 0.05)` +- popover/dropdown: `0 4px 12px -2px rgb(0 0 0 / 0.08), 0 2px 4px -2px rgb(0 0 0 / 0.04)` +- dialog: `0 20px 40px -12px rgb(0 0 0 / 0.18), 0 4px 12px -4px rgb(0 0 0 / 0.06)` + +Radii: `--radius: 0.625rem` (10px base); cards/dialogs 16px; buttons/inputs 8px. + +The shadcn HSL tokens (`--background`, `--foreground`, `--card`, `--popover`, +`--primary`, `--secondary`, `--muted`, `--accent`, `--destructive`, +`--border`, `--input`, `--ring`, `--sidebar`, `--chart-1..5`) are re-mapped to +the zinc + cyan values above so every existing `bg-muted`/`text-muted-foreground`/ +`border-border` utility follows automatically. The `--text-*`/`--bg-*`/ +`--border-*` aliases (deep-agents-ui bridge) keep working because they +reference the same color tokens. + +### Dark theme (cool neutral, same pass) + +| Token role | Value | +|---|---| +| App background | `#09090b` (zinc-950) | +| Surface / card | `#18181b` (zinc-900) | +| Border | `#27272a` (zinc-800) | +| Text primary / secondary / tertiary | `#fafafa` / `#a1a1aa` / `#71717a` | +| Brand (links) | `#22d3ee` (cyan-400) | +| Brand solid (button fill) | `#0e7490` (cyan-700, no glow) | +| Active/selected bg | cyan-950 translucent (`#083344` at ~50%) | +| User message bubble bg | dark cyan tint matching selection | + +`--chart-1..5` re-harmonized around the cyan family in both modes. + +## Layer 2 — Font & typography + +- Self-host **Inter variable** (woff2) via `next/font/local`; font files under + `src/app/fonts/`. Stack: + `Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif`. + (Inter has no CJK glyphs; Chinese falls through to PingFang/YaHei — that is + intended, Inter shapes the latin/numeric chrome.) +- Headings: `letter-spacing: -0.02em`, weight 600. +- UI chrome (sidebar, panels, buttons, menus): 13–14px; chat message body + stays 16px. +- Numeric displays (usage, token counts): `font-feature-settings: "tnum"`. + +## Layer 3 — `ui/` primitives (all consumers inherit) + +- **Button** (`src/components/ui/button.tsx`): + - `default`: vertical micro-gradient `from-[#06b6d4] to-[#0891b2]` (dark: + flat `#0e7490`), white text, `shadow-sm`, hover deepens + (`to-[#0e7490]`), focus-visible ring `cyan-500/30`. + - `outline`: white bg, zinc-200 border, hover zinc-50. + - `ghost`: hover zinc-100. + - `link`: cyan-600. +- **Input / Select / Textarea**: zinc-200 border, white bg; focus: cyan-500 + border + `ring cyan-500/20`; placeholder zinc-400; rounded-lg (8px). +- **Dialog**: content rounded-2xl (16px), layered dialog shadow, hairline + border; overlay `black/40` + `backdrop-blur-sm`. +- **Dropdown / popover / select content**: rounded-xl (12px), popover shadow, + zinc-200 border; item hover zinc-100; highlighted/selected item cyan-50 bg + + cyan-700 text. + +## Layer 4 — Screen-level polish + +- **Chat area** (`ChatInterface.tsx`, `ChatMessage.tsx`, `ToolCallBox.tsx`): + more breathing room between messages; user bubble rounded-2xl cyan-50; + assistant messages plain with prose spacing; tool-call box = card with + zinc-50 header + zinc-200 border + rounded-xl; composer rounded-2xl with + cyan focus ring and `shadow-sm`; send button = gradient circle. +- **Thread list** (`ThreadList.tsx`): active conversation = cyan-50 bg + + cyan-700 text, rounded-lg; hover zinc-100; section labels 11px uppercase + `tracking-wide` zinc-400. +- **Workspace panel** (`WorkspacePanel.tsx`): category labels = same 11px + uppercase micro style; row hover zinc-100; checkboxes `accent-cyan-600` + (replacing `accent-[var(--brand)]`); toolbar ghost icon buttons consistent + with the new Button ghost variant. +- **Login page**: centered white card, rounded-2xl, dialog shadow, brand + gradient wordmark/title. +- **All other dialogs** (Config, Skills, Usage, Account, …): inherit Layer-3 + primitives; only fix inconsistent padding/margins where a dialog visibly + deviates. + +## Error handling + +No runtime behavior changes, so no new error handling. Visual regressions are +caught by the per-layer manual verification gates below. + +## Testing & verification + +- `npx tsc --noEmit`, `npx eslint .`, `npm test` stay green after every layer + (no logic changes expected; any breakage is a signal the change touched + logic by accident). +- **Manual visual gate after each layer** (user, dev server on :4716): + 1. After Layer 1+2: overall palette/font feel in light and dark. + 2. After Layer 3: every dialog + button/input/dropdown state (hover, + focus, disabled) in both modes. + 3. After Layer 4: chat, thread list, workspace panel, login in both modes. +- Contrast: cyan-600 on white (4.5:1+), cyan-700 on cyan-50, dark-mode + equivalents must pass WCAG AA for text. + +## Out of scope + +- Layout/IA restructuring (no moving panels, no new navigation). +- Component-library migration (existing `ui/` primitives stay, just restyled). +- Any behavior, state, or API change. +- Login-captcha and other feature work in flight in the working tree.