docs(webui): spec for bright/premium UI polish
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user