docs(webui): implementation plan for welcome screen polish
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
# Welcome Screen Polish Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Add a gradient brand mark + larger title and pastel-tinted stat cards to the chat welcome screen, per the approved spec.
|
||||
|
||||
**Architecture:** Two-file, class-string-only change: the welcome block in `ChatInterface.tsx` and the stats grid in `ResearchDashboard.tsx`. No logic changes.
|
||||
|
||||
**Tech Stack:** Tailwind CSS 3 (`darkMode: "class"`), lucide-react icons, Vitest.
|
||||
|
||||
**Spec:** `docs/superpowers/specs/2026-08-10-welcome-screen-polish-design.md`
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Purely visual: `npx tsc --noEmit`, `npx eslint` on the two files, `npm test` must stay green.
|
||||
- `src/app/components/ChatInterface.tsx` contains unrelated uncommitted user changes — the implementer edits but does NOT stage or commit it; the controller stages only the task's hunk via filtered `git apply --cached`. `src/app/components/ResearchDashboard.tsx` is clean.
|
||||
- NEVER use `git add -A` / `git add .`. Do not touch `src/app/components/ContextUsageIndicator.tsx` (untracked user WIP).
|
||||
- Known pre-existing lint noise: `src/app/components/MarkdownContent.tsx` has an eslint error from user WIP — ignore it.
|
||||
- All tinted classes are full static strings per stat — no dynamic Tailwind class construction (JIT must see them).
|
||||
- Commit style: `style(webui): …`.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Gradient brand mark + pastel stat cards
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/app/components/ChatInterface.tsx` (~line 1744 welcome block; NOT committed by implementer)
|
||||
- Modify: `src/app/components/ResearchDashboard.tsx` (stats grid; clean, committed normally)
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: existing tokens (`--ring` for focus rings). Produces nothing — leaf change.
|
||||
|
||||
- [ ] **Step 1: Brand tile + larger title in the welcome block**
|
||||
|
||||
In `src/app/components/ChatInterface.tsx`, add `Sparkles` to the existing `lucide-react` import (alphabetical position among the existing named imports; there is a multi-import from `lucide-react` near the top of the file).
|
||||
|
||||
Find (lines 1744-1747):
|
||||
|
||||
```tsx
|
||||
<div className="flex min-h-[42vh] flex-col items-center justify-center px-3 pt-12 text-center sm:pt-16">
|
||||
<h2 className="text-pretty text-lg font-semibold sm:text-xl">
|
||||
Where research evolves
|
||||
</h2>
|
||||
```
|
||||
|
||||
Replace with:
|
||||
|
||||
```tsx
|
||||
<div className="flex min-h-[42vh] flex-col items-center justify-center px-3 pt-12 text-center sm:pt-16">
|
||||
<div className="mb-4 grid size-14 place-items-center rounded-2xl bg-gradient-to-br from-cyan-400 to-cyan-600 shadow-lg shadow-cyan-500/30">
|
||||
<Sparkles
|
||||
className="size-7 text-white"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<h2 className="text-pretty text-2xl font-semibold tracking-tight sm:text-3xl">
|
||||
Where research evolves
|
||||
</h2>
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Per-stat tint fields in `ResearchDashboard.tsx`**
|
||||
|
||||
In the `stats` array type (lines 172-179), add two fields to the inline type:
|
||||
|
||||
```ts
|
||||
const stats: Array<{
|
||||
value: string | number;
|
||||
unit?: string;
|
||||
label: string;
|
||||
hint: string;
|
||||
Icon: typeof Network;
|
||||
cardClass: string;
|
||||
tileClass: string;
|
||||
target: NavTarget;
|
||||
}> = [
|
||||
```
|
||||
|
||||
Add to each stat entry, respectively:
|
||||
|
||||
Knowledge:
|
||||
```ts
|
||||
cardClass:
|
||||
"border-cyan-100 bg-cyan-50 dark:border-cyan-900/50 dark:bg-cyan-950/40",
|
||||
tileClass:
|
||||
"bg-cyan-100 text-cyan-700 dark:bg-cyan-900/60 dark:text-cyan-300",
|
||||
```
|
||||
|
||||
Timeline:
|
||||
```ts
|
||||
cardClass:
|
||||
"border-violet-100 bg-violet-50 dark:border-violet-900/50 dark:bg-violet-950/40",
|
||||
tileClass:
|
||||
"bg-violet-100 text-violet-700 dark:bg-violet-900/60 dark:text-violet-300",
|
||||
```
|
||||
|
||||
Workspace:
|
||||
```ts
|
||||
cardClass:
|
||||
"border-amber-100 bg-amber-50 dark:border-amber-900/50 dark:bg-amber-950/40",
|
||||
tileClass:
|
||||
"bg-amber-100 text-amber-700 dark:bg-amber-900/60 dark:text-amber-300",
|
||||
```
|
||||
|
||||
Scheduled:
|
||||
```ts
|
||||
cardClass:
|
||||
"border-emerald-100 bg-emerald-50 dark:border-emerald-900/50 dark:bg-emerald-950/40",
|
||||
tileClass:
|
||||
"bg-emerald-100 text-emerald-700 dark:bg-emerald-900/60 dark:text-emerald-300",
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Card + icon-tile markup**
|
||||
|
||||
In the stats map (line 215), change the destructure from:
|
||||
|
||||
```ts
|
||||
{stats.map(({ value, unit, label, hint, Icon, target }) => (
|
||||
```
|
||||
|
||||
to:
|
||||
|
||||
```ts
|
||||
{stats.map(
|
||||
({ value, unit, label, hint, Icon, cardClass, tileClass, target }) => (
|
||||
```
|
||||
|
||||
Replace the button's className (line 224):
|
||||
|
||||
```ts
|
||||
className="hover:border-[var(--brand)]/40 flex min-h-[82px] flex-col items-center justify-center gap-1 rounded-md border border-border bg-[var(--color-surface)] px-2 py-2.5 transition-colors hover:bg-accent/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
```
|
||||
|
||||
with (template literal, static parts first):
|
||||
|
||||
```ts
|
||||
className={`flex min-h-[82px] flex-col items-center justify-center gap-1 rounded-xl border px-2 py-2.5 transition-all hover:-translate-y-0.5 hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ${cardClass}`}
|
||||
```
|
||||
|
||||
Replace the icon (lines 226-229):
|
||||
|
||||
```tsx
|
||||
<Icon
|
||||
className="size-4 text-[var(--brand)]"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```tsx
|
||||
<span
|
||||
className={`grid size-8 place-items-center rounded-lg ${tileClass}`}
|
||||
>
|
||||
<Icon
|
||||
className="size-4"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
```
|
||||
|
||||
(The existing prettier setup will reformat the map callback's closing parens; run `npx prettier --write src/app/components/ResearchDashboard.tsx` after edits to normalize.)
|
||||
|
||||
- [ ] **Step 4: Verify**
|
||||
|
||||
Run: `npx tsc --noEmit && npx eslint src/app/components/ChatInterface.tsx src/app/components/ResearchDashboard.tsx && npm test`
|
||||
Expected: clean, all tests pass (310 passed / 1 skipped).
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
Implementer commits ONLY ResearchDashboard.tsx:
|
||||
|
||||
```bash
|
||||
git add src/app/components/ResearchDashboard.tsx
|
||||
git commit -m "style(webui): pastel-tinted stat cards on welcome screen"
|
||||
```
|
||||
|
||||
The controller then stages the ChatInterface.tsx welcome-block hunk (filtered `git apply --cached`) and commits:
|
||||
|
||||
```bash
|
||||
git commit -m "style(webui): gradient brand mark and larger welcome title"
|
||||
```
|
||||
|
||||
- [ ] **Step 6 (controller, not implementer):** Hand to the user for the visual gate (welcome screen, light + dark).
|
||||
|
||||
---
|
||||
|
||||
## Self-Review Notes (already applied)
|
||||
|
||||
- **Spec coverage:** A → Step 1; B (tint table, icon tile, hover lift, dark mode) → Steps 2-3. Focus rings kept (`ring-ring`) per spec.
|
||||
- **Placeholder scan:** every edit has exact old/new strings with line anchors (lines may drift ±5 due to user WIP — search, don't trust numbers).
|
||||
- **Type consistency:** `cardClass`/`tileClass` names used in Steps 2 and 3 match.
|
||||
- **Risk:** the stats-map destructure line is long after adding two fields; Step 3 mandates prettier to keep formatting consistent with repo style.
|
||||
Reference in New Issue
Block a user