docs(convoys): seed unify-glass-panel-surfaces + cleanup-card-item-list-and-share-modal-palette
Two convoy seeds opened as follow-ups to the 2026-06-04 design pass (#116 corner-border-light → #117 site-wide sweep → #118 card vibrancy reduction). Both were called out in #117's PR body as deferred and are now formally tracked. ## unify-glass-panel-surfaces Migrates remaining panel-shaped surfaces to the gradient-border corner-light treatment so the app shares one surface vocabulary. The audit's key insight: `<GlassSurface>` (`components/ui/GlassSurface.js`) predates the corner-light pattern. Because `<Modal>`, `<StatCard>`, and the landing-page feature/collection cards all delegate to it, upgrading the primitive cascades to ~10 visible surfaces at once. 7 briefs, multitask-parallel after Brief 1 lands: 1. `<GlassSurface>` primitive upgrade — BLOCKING for 3, 4 2. Auth form cards (login.js, signup.js) 3. Floating popovers (mobile drawer, sidebar profile dropdown, UserMenu dropdown) 4. BulkSelectionToolbar (currently `bg-white border-gray-200` — invisible in dark mode) 5. `.card`-class consumers (4 pages); decision to ratify whether to retire `.card` entirely or keep as documented opaque fallback 6. Landing nav bar — wrong pattern; should use existing `.page-header-glass` class 7. `forbidden-bespoke-glass-surface` CI grep gate — prevents regression after the migration ships ## cleanup-card-item-list-and-share-modal-palette Targeted palette cleanup for two files whose interiors weren't addressed in #117: 1. `CardItem.js` list-mode (L183–284) — entirely hardcoded Tailwind palette (`bg-purple-50`, `border-gray-200`, `text-gray-{500-900}`, `bg-blue-100 text-blue-800` etc.); unreadable / off-brand in dark mode. 2. `ShareModal.js` interior rows — purple avatar circles, gray-50 permission row (invisible in dark mode), blue-600 Copy-link button, gray text labels. Token-only swap. 2 parallel briefs, no architect / IA / UX needed (no design decisions — palette to design tokens). ## Sequencing note The two convoys are independent and can run in parallel. The audit agent's recommended sequencing (Brief 1 of `unify-glass-panel-surfaces` first) is encoded in the multitask `slice_dependencies` blocks. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
2986171e6d
commit
b195395b82
2 changed files with 568 additions and 0 deletions
205
.convoys/cleanup-card-item-list-and-share-modal-palette.md
Normal file
205
.convoys/cleanup-card-item-list-and-share-modal-palette.md
Normal file
|
|
@ -0,0 +1,205 @@
|
||||||
|
---
|
||||||
|
name: cleanup-card-item-list-and-share-modal-palette
|
||||||
|
classification: feature
|
||||||
|
success_metric: |
|
||||||
|
No hardcoded Tailwind palette classes (`bg-purple-*`, `bg-blue-*`,
|
||||||
|
`bg-gray-*` 100-900 range, `text-gray-{500,600,700,900}`,
|
||||||
|
`bg-white`, `border-gray-200`, etc.) remain in the interior of
|
||||||
|
`components/CardItem.js` list-mode or `components/ShareModal.js`.
|
||||||
|
Both render correctly in dark theme. Verified by visual diff and
|
||||||
|
a targeted lint sweep.
|
||||||
|
skip:
|
||||||
|
- ia
|
||||||
|
- ux
|
||||||
|
- arch
|
||||||
|
status: open
|
||||||
|
created: 2026-06-04
|
||||||
|
depends_on:
|
||||||
|
- design-sweep-pass # PR #117 — established the token migration pattern
|
||||||
|
---
|
||||||
|
|
||||||
|
# Convoy: cleanup-card-item-list-and-share-modal-palette
|
||||||
|
|
||||||
|
Targeted palette cleanup. Parallel companion to
|
||||||
|
`unify-glass-panel-surfaces` — that convoy migrates panel *surfaces*;
|
||||||
|
this one cleans up the *interior content* (rows, badges, text,
|
||||||
|
buttons) of two specific files whose interiors were not addressed
|
||||||
|
by PR #117 because they're not panel surfaces — they're nested
|
||||||
|
content inside surfaces that were already on the system.
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
Two files left over after the 2026-06-04 design sweep:
|
||||||
|
|
||||||
|
1. **`components/CardItem.js` list-mode** (L183–284) — the
|
||||||
|
list-view layout (when `viewMode === 'list'`) is entirely
|
||||||
|
hardcoded Tailwind palette. The grid-mode (L290+) was already
|
||||||
|
migrated; list-mode wasn't. `border-gray-200`, `bg-purple-50`,
|
||||||
|
`text-gray-900`, `text-gray-600`, `text-green-600`,
|
||||||
|
`bg-blue-100 text-blue-800` — none of these have dark-mode
|
||||||
|
variants. In dark theme, the list view renders unreadable
|
||||||
|
(light text on light backgrounds) or off-brand (purple/blue
|
||||||
|
badges in an ember palette app).
|
||||||
|
|
||||||
|
2. **`components/ShareModal.js`** interior — the modal SURFACE is
|
||||||
|
correct (delegates to `<Modal>` → `<GlassSurface>`), and the
|
||||||
|
user-search dropdown + email-invite card were updated in PR
|
||||||
|
#117. But the interior rows still have:
|
||||||
|
- `bg-purple-600` avatar circles for current user (L194, L240).
|
||||||
|
- `bg-gray-50` current-user permission row (L238) — invisible
|
||||||
|
in dark mode.
|
||||||
|
- `bg-gray-100 rounded` permission badge (L272) — light-only.
|
||||||
|
- `bg-gray-50 border-gray-300 text-gray-600` share-link input
|
||||||
|
(L287) — invisible in dark mode.
|
||||||
|
- `bg-blue-600 hover:bg-blue-700` Copy-link button (L294) —
|
||||||
|
off-palette (should be ember).
|
||||||
|
- `text-gray-{500,700,900}` text colors throughout — light-only.
|
||||||
|
|
||||||
|
Note ShareModal is the primary collaboration entry point; the
|
||||||
|
permission rows and Copy-link button are visited every time a
|
||||||
|
user invites a collaborator.
|
||||||
|
|
||||||
|
Both files are token-only swaps. No structural changes, no
|
||||||
|
component swaps, no glass-panel migration.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
### In scope
|
||||||
|
|
||||||
|
#### Brief 1 — `CardItem.js` list-mode token sweep
|
||||||
|
|
||||||
|
Replace every hardcoded color in lines ~183–284 with the
|
||||||
|
appropriate `var(--*)` token via inline style. Categories:
|
||||||
|
|
||||||
|
- Background fills: `bg-purple-50` (selected row), `bg-white` →
|
||||||
|
`var(--bg-secondary)` or `var(--bg-tertiary)` per role; selected
|
||||||
|
state uses `var(--accent-ember)` + low alpha as background.
|
||||||
|
- Borders: `border-gray-200` / `border-gray-300` →
|
||||||
|
`var(--border)`. Selected state border `border-purple-600` →
|
||||||
|
`var(--accent-ember)`.
|
||||||
|
- Text: `text-gray-900` → `var(--text-primary)`,
|
||||||
|
`text-gray-600` / `text-gray-500` → `var(--text-secondary)`,
|
||||||
|
`text-green-600` (positive value indicators) →
|
||||||
|
`var(--accent-flame)` or keep `#16a34a` if semantic green
|
||||||
|
matters more than brand.
|
||||||
|
- Badges (`bg-blue-100 text-blue-800` etc.): convert to
|
||||||
|
`var(--bg-tertiary)` + `var(--text-primary)` or
|
||||||
|
`var(--accent-ember)` per badge role (game, rarity, type).
|
||||||
|
- Buttons / icons: `text-purple-600` (selection indicator) →
|
||||||
|
`var(--accent-ember)`.
|
||||||
|
|
||||||
|
Acceptance: visually inspect `CardItem` list-mode in BOTH themes.
|
||||||
|
All text must be legible, all interactive states (hover, selected)
|
||||||
|
must have a visible delta against the row background.
|
||||||
|
|
||||||
|
**Files:** `components/CardItem.js`.
|
||||||
|
**Risk:** LOW — purely cosmetic token swaps; no layout changes.
|
||||||
|
|
||||||
|
#### Brief 2 — `ShareModal.js` interior token sweep
|
||||||
|
|
||||||
|
Lines roughly 230–310. Targets:
|
||||||
|
|
||||||
|
- **Avatar circles** (L194, L240): `bg-purple-600` →
|
||||||
|
`linear-gradient(135deg, var(--accent-ember), var(--accent-flame))`
|
||||||
|
(matches the UserMenu avatar gradient style from
|
||||||
|
`components/ui/TopSearchBar.js`).
|
||||||
|
- **Current-user row container** (L238): `bg-gray-50 rounded-lg`
|
||||||
|
→ `var(--bg-secondary)` + `rounded-xl`, or
|
||||||
|
`var(--bg-tertiary)` for slightly more contrast.
|
||||||
|
- **Permission badge** (L272): `bg-gray-100 rounded` →
|
||||||
|
`var(--bg-tertiary)` + `rounded-xl` + `var(--text-secondary)`.
|
||||||
|
- **Share-link readonly input** (L283–288): `bg-gray-50
|
||||||
|
border-gray-300 text-gray-600` → tokenize against
|
||||||
|
`var(--bg-secondary)`, `var(--border)`, `var(--text-primary)`;
|
||||||
|
alternatively use the `.input-field` class (already in
|
||||||
|
`globals.css`).
|
||||||
|
- **Copy-link button** (L290–298): `bg-blue-600 text-white
|
||||||
|
hover:bg-blue-700` → replace with the `<Button variant="primary">`
|
||||||
|
primitive. The success state currently is
|
||||||
|
`bg-green-100 text-green-800 border border-green-200`; keep the
|
||||||
|
semantic green but use a non-Tailwind path:
|
||||||
|
`style={{ backgroundColor: 'var(--bg-secondary)', color:
|
||||||
|
'var(--accent-flame)' }}` + an ember border, or commit to a
|
||||||
|
custom `.btn-success-flash` utility.
|
||||||
|
- **Text labels** (L201, L222, L231, L246, L266, L267): all
|
||||||
|
`text-gray-{500,700,900}` → tokens.
|
||||||
|
|
||||||
|
Acceptance: ShareModal opens in dark theme and every row is
|
||||||
|
legible; Copy-link button is on the ember palette; permission
|
||||||
|
badges read as ember-system, not Tailwind-default-gray.
|
||||||
|
|
||||||
|
**Files:** `components/ShareModal.js`.
|
||||||
|
**Risk:** LOW.
|
||||||
|
|
||||||
|
### Out of scope
|
||||||
|
|
||||||
|
- Any change to the modal *surface* (handled by
|
||||||
|
`unify-glass-panel-surfaces` Brief 1, which upgrades
|
||||||
|
`<GlassSurface>` and therefore `<Modal>`).
|
||||||
|
- Any structural change (new component, new layout).
|
||||||
|
- The user-search-dropdown + email-invite-card areas of
|
||||||
|
`ShareModal.js` — those were already cleaned up in PR #117.
|
||||||
|
|
||||||
|
## Roles invoked
|
||||||
|
|
||||||
|
1. `role-implementer` — Briefs 1 and 2 are parallel-safe; can run
|
||||||
|
independently.
|
||||||
|
2. `role-design-system-auditor` — single sign-off after both
|
||||||
|
briefs land (token-only sweep; design audit is a sanity check).
|
||||||
|
3. `role-reviewer` — one review per brief.
|
||||||
|
|
||||||
|
(No architect needed — token swaps with no design decisions.
|
||||||
|
No IA, no UX — content/structure unchanged. The Design-system
|
||||||
|
auditor catches any palette regressions.)
|
||||||
|
|
||||||
|
## Todos
|
||||||
|
|
||||||
|
- [ ] Brief 1 — `CardItem.js` list-mode token sweep
|
||||||
|
- [ ] Brief 2 — `ShareModal.js` interior token sweep
|
||||||
|
- [ ] Design-system auditor sign-off
|
||||||
|
- [ ] Post-PR review per brief
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
1. `rg -e "bg-(purple|blue|gray|red)-[0-9]" -e
|
||||||
|
"text-(purple|blue|gray|red)-[0-9]" -e "border-gray-[0-9]"`
|
||||||
|
over `components/CardItem.js` and `components/ShareModal.js`
|
||||||
|
returns no matches (excluding lines that are deliberately
|
||||||
|
semantic — e.g. `bg-red-500` on a destructive Delete button,
|
||||||
|
which architect ratifies per-line during review).
|
||||||
|
2. Both files render correctly in both themes — visible text,
|
||||||
|
visible interactive states.
|
||||||
|
3. Build + lint + 113/113 vitest pass.
|
||||||
|
|
||||||
|
## CI impact
|
||||||
|
|
||||||
|
| Workflow / job | Behavior |
|
||||||
|
| --- | --- |
|
||||||
|
| `preview-smoke.yml` | Fires. |
|
||||||
|
| `visual-diff.yml` | **Fires** — expected diff in CardItem list-view and ShareModal rows. Baseline refresh required. |
|
||||||
|
| `lint` | Fires. |
|
||||||
|
| `test:` (vitest) | Fires. |
|
||||||
|
|
||||||
|
## Known constraints
|
||||||
|
|
||||||
|
- **`CardItem` is in many places** — `pages/cards.js` (grid
|
||||||
|
default), `pages/my-cards.js`, `pages/collection/[id].js`, etc.
|
||||||
|
Brief 1 must NOT touch the grid-mode (L290+), only the
|
||||||
|
list-mode branch.
|
||||||
|
|
||||||
|
## Multitask dispatch
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
slice_dependencies:
|
||||||
|
- brief: 1
|
||||||
|
depends_on: []
|
||||||
|
files:
|
||||||
|
- components/CardItem.js
|
||||||
|
- brief: 2
|
||||||
|
depends_on: []
|
||||||
|
files:
|
||||||
|
- components/ShareModal.js
|
||||||
|
```
|
||||||
|
|
||||||
|
Briefs 1 and 2 are fully parallel — disjoint files, no shared
|
||||||
|
dependencies.
|
||||||
363
.convoys/unify-glass-panel-surfaces.md
Normal file
363
.convoys/unify-glass-panel-surfaces.md
Normal file
|
|
@ -0,0 +1,363 @@
|
||||||
|
---
|
||||||
|
name: unify-glass-panel-surfaces
|
||||||
|
classification: feature
|
||||||
|
success_metric: |
|
||||||
|
Every panel-shaped surface in the app (modals, popovers, form
|
||||||
|
cards, dashboard widgets, page-level content cards) renders with
|
||||||
|
the same gradient-border corner-light treatment that the floating
|
||||||
|
chrome chips use — at the appropriate intensity tier for its
|
||||||
|
surface class (full intensity for chrome, subtle for data cards,
|
||||||
|
flat for opaque GPU-budget-constrained tiles like CardItem grid).
|
||||||
|
Verified by visual diff + a forbidden grep gate that prevents
|
||||||
|
reintroduction of bespoke `var(--glass-surface-*)` inline styles
|
||||||
|
outside the documented exception list.
|
||||||
|
skip:
|
||||||
|
- ia
|
||||||
|
status: open
|
||||||
|
created: 2026-06-04
|
||||||
|
depends_on:
|
||||||
|
- tone-down-card-corner-lights # PR #118 — establishes the subtle token tier
|
||||||
|
- design-sweep-pass # PR #117 — applies .glass-panel broadly
|
||||||
|
---
|
||||||
|
|
||||||
|
# Convoy: unify-glass-panel-surfaces
|
||||||
|
|
||||||
|
Follow-on to the `redesign-v2-from-mockups` umbrella and the
|
||||||
|
2026-06-04 corner-border-light refinement (PR #116) + design-sweep
|
||||||
|
(PR #117) + card-vibrancy reduction (PR #118).
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
The gradient-border corner-light pattern (introduced in PR #116 and
|
||||||
|
applied broadly in PR #117) is now the canonical surface treatment
|
||||||
|
for the app. But the audit run on 2026-06-04 found that several
|
||||||
|
panel-shaped surfaces still use the previous generation's "flat
|
||||||
|
translucent fill" pattern — they predate the corner-light work and
|
||||||
|
were never migrated:
|
||||||
|
|
||||||
|
1. **`<GlassSurface>` primitive** — the most consequential gap.
|
||||||
|
`components/ui/GlassSurface.js` uses a single flat
|
||||||
|
`var(--glass-surface-${tint})` background with no transparent
|
||||||
|
border and no corner radials. Because `<Modal>` (and therefore
|
||||||
|
every modal in the app), `<StatCard>` (dashboard stat tiles),
|
||||||
|
and the public landing-page feature/collection cards all delegate
|
||||||
|
to `<GlassSurface>`, the gap cascades broadly. Upgrading the
|
||||||
|
primitive fixes ~10 visible surfaces in one move.
|
||||||
|
|
||||||
|
2. **Auth form cards** (`pages/login.js` L82–88,
|
||||||
|
`pages/signup.js` L227–232) — handrolled glass imitation using
|
||||||
|
`rgba(--bg-secondary-rgb, 0.85)` + `backdrop-blur-sm` (Tailwind,
|
||||||
|
not the system blur tokens). These are the first surfaces a new
|
||||||
|
user sees; they should be canonical, not handrolled.
|
||||||
|
|
||||||
|
3. **Floating popovers / drawers** —
|
||||||
|
- `components/Layout.js` mobile drawer (L698–709)
|
||||||
|
- `components/Layout.js` sidebar profile dropdown (L88–96)
|
||||||
|
- `components/ui/TopSearchBar.js` UserMenu dropdown (L214–222)
|
||||||
|
|
||||||
|
All three render a translucent panel over arbitrary page content.
|
||||||
|
None has the gradient-border treatment. The visual cue that
|
||||||
|
says "this is an elevated surface" relies entirely on box-shadow,
|
||||||
|
not on light response.
|
||||||
|
|
||||||
|
4. **`BulkSelectionToolbar`** (L47 and L117 dropdown) — uses
|
||||||
|
hardcoded `bg-white border-gray-200`, invisible in dark mode.
|
||||||
|
This is a floating toolbar over all content during bulk-select;
|
||||||
|
should be `.glass-panel-strong`.
|
||||||
|
|
||||||
|
5. **`.card`-class consumers** — `pages/profile.js` (×3),
|
||||||
|
`pages/settings.js` (×2), `pages/community/collections.js`,
|
||||||
|
`components/CollectionsPageView.js`. The `.card` class
|
||||||
|
(`styles/globals.css` L757) is a pre-redesign opaque
|
||||||
|
solid-fill panel. Most consumers should migrate to
|
||||||
|
`.glass-panel`; the class itself can either be retired or
|
||||||
|
retained as a documented "opaque fallback" for special cases.
|
||||||
|
|
||||||
|
6. **Landing nav bar** (`pages/index.js` L61–72) — uses
|
||||||
|
`var(--glass-surface-mid)` flat + an incorrectly double-wrapped
|
||||||
|
`inset 0 1px 0` boxShadow. Not a gradient-border candidate
|
||||||
|
(full-bleed bar; corner lights would be at viewport edges, not
|
||||||
|
visible). Should use the existing `.page-header-glass` class
|
||||||
|
instead.
|
||||||
|
|
||||||
|
Unifying these means the entire app shares one surface vocabulary.
|
||||||
|
Future work — new modals, new dashboards, new public pages — picks
|
||||||
|
up the gradient-border treatment automatically because the
|
||||||
|
primitives are correct.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
### In scope
|
||||||
|
|
||||||
|
#### Brief 1 — Upgrade `<GlassSurface>` primitive
|
||||||
|
|
||||||
|
Add the 4-layer gradient-border pattern to the `<GlassSurface>`
|
||||||
|
component so its `tint`, `blur`, `rim`, `elevation` props compose
|
||||||
|
with corner catch-lights. Default to the **subtle** corner-light
|
||||||
|
tokens (`--corner-light-{warm,cool}-subtle`) since most consumers
|
||||||
|
are data cards. Add a `cornerLights="chrome" | "subtle" | "none"`
|
||||||
|
prop so floating chrome can opt up and special opaque tiles
|
||||||
|
(CardItem grid mode) can opt out.
|
||||||
|
|
||||||
|
Consumers automatically upgraded:
|
||||||
|
- `<Modal>` → corner lights on every modal
|
||||||
|
- `<StatCard>` → corner lights on dashboard stat tiles
|
||||||
|
- Landing feature cards (`pages/index.js` L132–224, L278–345)
|
||||||
|
|
||||||
|
**Files:** `components/ui/GlassSurface.js`,
|
||||||
|
`test/components/ui-primitives.test.js` (add a corner-light
|
||||||
|
assertion).
|
||||||
|
|
||||||
|
**Risk:** MED — the primitive's output structure changes (adds
|
||||||
|
`border: 1px solid transparent`). Most consumers won't notice, but
|
||||||
|
any consumer that set a custom `border` via style override could
|
||||||
|
conflict. Audit needed.
|
||||||
|
|
||||||
|
#### Brief 2 — Auth form cards
|
||||||
|
|
||||||
|
`pages/login.js` L82–88 + `pages/signup.js` L227–232: replace
|
||||||
|
the inline `className="p-8 rounded-2xl shadow-2xl backdrop-blur-sm
|
||||||
|
border border-opacity-20" style={{ backgroundColor: 'rgba(...)' }}`
|
||||||
|
shape with `className="glass-panel-strong rounded-2xl p-8"`.
|
||||||
|
|
||||||
|
**Files:** `pages/login.js`, `pages/signup.js`.
|
||||||
|
**Risk:** LOW.
|
||||||
|
|
||||||
|
#### Brief 3 — Floating popovers / drawers
|
||||||
|
|
||||||
|
Three independent floating surfaces:
|
||||||
|
- `components/Layout.js` mobile drawer (L698–709)
|
||||||
|
- `components/Layout.js` sidebar profile dropdown (L88–96)
|
||||||
|
- `components/ui/TopSearchBar.js` UserMenu dropdown (L214–222)
|
||||||
|
|
||||||
|
For each: drop the inline `background: var(--glass-surface-*)` +
|
||||||
|
`backdropFilter` pair, add `className="glass-panel-strong rounded-2xl"`
|
||||||
|
or `rounded-xl` to match existing dimensions. Preserve any
|
||||||
|
additional `boxShadow: 'var(--ember-rim-subtle)'` adornments by
|
||||||
|
chaining them onto the class's existing box-shadow (via inline
|
||||||
|
style override).
|
||||||
|
|
||||||
|
**Special handling for mobile drawer:** the drawer takes ~1/3 of
|
||||||
|
the viewport. At full corner-light intensity it would be noisy.
|
||||||
|
Use `.glass-panel-strong` (which uses the subtle tokens).
|
||||||
|
|
||||||
|
**Files:** `components/Layout.js`, `components/ui/TopSearchBar.js`,
|
||||||
|
`test/components/Layout.test.js` (regression assertions).
|
||||||
|
**Risk:** LOW–MED — popovers appear over arbitrary page content.
|
||||||
|
|
||||||
|
#### Brief 4 — `BulkSelectionToolbar`
|
||||||
|
|
||||||
|
Replace `bg-white border-gray-200` on the toolbar (L47) and its
|
||||||
|
"More actions" dropdown (L117) with `.glass-panel-strong rounded-2xl`.
|
||||||
|
Sweep interior `text-gray-700 hover:bg-gray-100`,
|
||||||
|
`text-red-600 hover:bg-red-50` to use `var(--text-primary)` /
|
||||||
|
`nav-item-hover` and tokenized red for destructive actions.
|
||||||
|
|
||||||
|
**Files:** `components/BulkSelectionToolbar.js`.
|
||||||
|
**Risk:** MED — floats over all content during bulk-select mode;
|
||||||
|
shadow/overflow bleed would be very visible.
|
||||||
|
|
||||||
|
#### Brief 5 — `.card`-class consumers
|
||||||
|
|
||||||
|
Audit every `<div className="card">` usage. For each, pick the
|
||||||
|
right migration:
|
||||||
|
- Most likely: `glass-panel rounded-3xl p-6` (preserves rounded-3xl
|
||||||
|
+ p-6 from the old class).
|
||||||
|
- Some may need to stay opaque (e.g. screenshot-friendly profile
|
||||||
|
card with photo overlay) — keep `.card` and document the
|
||||||
|
exception in `styles/globals.css`'s comment block.
|
||||||
|
|
||||||
|
Decision to ratify: **retire `.card` entirely, OR keep as a
|
||||||
|
documented opaque-panel alternative?** Architect's call. If kept,
|
||||||
|
add a comment to `globals.css` explaining when to use which.
|
||||||
|
|
||||||
|
**Files:** `pages/profile.js`, `pages/settings.js`,
|
||||||
|
`pages/community/collections.js`,
|
||||||
|
`components/CollectionsPageView.js`,
|
||||||
|
optionally `styles/globals.css` (retire or document).
|
||||||
|
**Risk:** LOW per page; sweep across 4 files.
|
||||||
|
|
||||||
|
#### Brief 6 — Landing nav bar
|
||||||
|
|
||||||
|
`pages/index.js` L61–72: replace the handrolled translucent header
|
||||||
|
with `<header className="page-header-glass border-b ...">`. The
|
||||||
|
`.page-header-glass` class already exists in `globals.css` for
|
||||||
|
exactly this "full-bleed top band" use case. The current bar also
|
||||||
|
has a malformed `boxShadow: 'inset 0 1px 0 var(--rim-light-inner)'`
|
||||||
|
(the token already contains `inset 0 1px 0`; double-wrapping breaks
|
||||||
|
the cascade). Remove the malformed shadow.
|
||||||
|
|
||||||
|
**Files:** `pages/index.js`.
|
||||||
|
**Risk:** LOW — public page, no auth dependencies.
|
||||||
|
|
||||||
|
#### Brief 7 — Forbidden grep gate
|
||||||
|
|
||||||
|
Add a `forbidden-bespoke-glass-surface` job to
|
||||||
|
`.github/workflows/ci.yml` that fails the build if
|
||||||
|
`var(--glass-surface-(low|mid|high))` appears in JSX inline styles
|
||||||
|
across `components/**` and `pages/**`, with a curated allowlist
|
||||||
|
for the documented exceptions (intentional chrome treatment in
|
||||||
|
Layout/TopSearchBar; `<GlassSurface>` component itself; any
|
||||||
|
opaque-fallback `.card` consumers ratified in Brief 5).
|
||||||
|
|
||||||
|
Prevents regression: future PRs can't reintroduce handrolled glass.
|
||||||
|
|
||||||
|
**Files:** `.github/workflows/ci.yml`.
|
||||||
|
**Risk:** LOW.
|
||||||
|
|
||||||
|
### Out of scope
|
||||||
|
|
||||||
|
- `<CardItem>` grid-mode (L290–303) — intentionally opaque per
|
||||||
|
AGENTS.md GPU-budget rule.
|
||||||
|
- `<CardItem>` list-mode token cleanup (L183–284) — covered by
|
||||||
|
the parallel `cleanup-card-item-list-and-share-modal-palette`
|
||||||
|
convoy.
|
||||||
|
- Adding any NEW surfaces or panels.
|
||||||
|
- `.card` rename to `.opaque-panel` — Brief 5 may retire the class
|
||||||
|
entirely; rename is a separate polish if it survives.
|
||||||
|
|
||||||
|
## Roles invoked
|
||||||
|
|
||||||
|
1. `role-architect` — surface-by-surface migration plan; ratifies
|
||||||
|
the `cornerLights` prop shape for `<GlassSurface>` and the
|
||||||
|
`.card` retire-vs-keep decision; writes Briefs 1–7.
|
||||||
|
2. `role-design-system-auditor` — visual regression review on each
|
||||||
|
brief; confirms no chrome-vs-data-card hierarchy regressions.
|
||||||
|
3. `role-ux-reviewer` — light pass; ensures auth form contrast +
|
||||||
|
mobile drawer legibility hold up post-migration.
|
||||||
|
4. `role-implementer` — one per brief; Briefs 2, 3, 4, 6 are
|
||||||
|
parallel-safe (disjoint files, no shared component dependency
|
||||||
|
chain). Brief 1 MUST land first because Briefs 3, 4 may end up
|
||||||
|
simplifying their inline-style code by using the upgraded
|
||||||
|
primitive instead.
|
||||||
|
5. `role-reviewer` — single post-PR review per brief.
|
||||||
|
6. `role-a11y-auditor` — focus-ring + keyboard nav for new floating
|
||||||
|
surfaces (Brief 3 popovers especially).
|
||||||
|
|
||||||
|
## Todos
|
||||||
|
|
||||||
|
- [ ] Architect: surface-by-surface migration plan + `<GlassSurface>`
|
||||||
|
API spec (the `cornerLights` prop)
|
||||||
|
- [ ] Design-system auditor: confirm subtle-tokens are the right
|
||||||
|
default for the upgraded `<GlassSurface>`
|
||||||
|
- [ ] Brief 1 — `<GlassSurface>` primitive upgrade (BLOCKING for
|
||||||
|
Briefs 3, 4)
|
||||||
|
- [ ] Brief 2 — auth form cards
|
||||||
|
- [ ] Brief 3 — floating popovers (drawer, sidebar profile,
|
||||||
|
UserMenu)
|
||||||
|
- [ ] Brief 4 — BulkSelectionToolbar
|
||||||
|
- [ ] Brief 5 — `.card` consumers; Decision: retire or keep
|
||||||
|
- [ ] Brief 6 — landing nav bar (`.page-header-glass`)
|
||||||
|
- [ ] Brief 7 — `forbidden-bespoke-glass-surface` CI gate
|
||||||
|
- [ ] Post-PR review per brief
|
||||||
|
- [ ] Visual-diff baseline refresh after Brief 1 lands
|
||||||
|
|
||||||
|
## Decisions to ratify
|
||||||
|
|
||||||
|
1. **`<GlassSurface>` API shape post-upgrade.** Add `cornerLights`
|
||||||
|
prop with values `"chrome" | "subtle" | "none"`. Default
|
||||||
|
`"subtle"`. Architect confirms or proposes alternative.
|
||||||
|
2. **Retire `.card` class entirely after Brief 5, or keep as
|
||||||
|
documented opaque fallback?** Conservative: keep + document
|
||||||
|
("use when the surface must NOT have backdrop-filter — e.g.
|
||||||
|
inside another modal, screen-reader-critical, or
|
||||||
|
GPU-budget-constrained"). Aggressive: retire and migrate the
|
||||||
|
handful of legitimate opaque cases to inline style.
|
||||||
|
3. **Mobile drawer corner-light intensity.** The drawer is a
|
||||||
|
large surface. Subtle tokens (matching `.glass-panel-strong`)
|
||||||
|
are likely right, but the architect should sanity-check
|
||||||
|
visually before locking it in.
|
||||||
|
4. **CI gate scope for `forbidden-bespoke-glass-surface`.** Should
|
||||||
|
it gate ALL `var(--glass-surface-*)` usage in JSX, or only
|
||||||
|
inline `style={{ background: ... }}` usage? Recommended: only
|
||||||
|
inline styles, since the tokens still need to be referenceable
|
||||||
|
in `styles/globals.css`.
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
1. Every in-scope surface lists either `glass-panel`,
|
||||||
|
`glass-panel-strong`, `page-header-glass`, or `<GlassSurface>`
|
||||||
|
in its className (no inline `var(--glass-surface-*)` background).
|
||||||
|
2. `<GlassSurface>` primitive's output includes the 4-layer
|
||||||
|
gradient-border pattern and a `border: 1px solid transparent`.
|
||||||
|
3. CI's `forbidden-bespoke-glass-surface` job passes; introducing
|
||||||
|
a new bespoke `var(--glass-surface-low)` inline-style usage in
|
||||||
|
a scratch commit makes it fail (negative test).
|
||||||
|
4. Build + lint + 113/113 vitest + Playwright smoke green.
|
||||||
|
5. Visual diff shows the expected differences (corner catch-lights
|
||||||
|
appear on modals, dashboard stat tiles, auth cards, dropdowns)
|
||||||
|
and no unexpected regressions on chrome/cards/CardItem grid.
|
||||||
|
|
||||||
|
## CI impact
|
||||||
|
|
||||||
|
| Workflow / job | Behavior |
|
||||||
|
| --- | --- |
|
||||||
|
| `preview-smoke.yml` | Fires per brief. |
|
||||||
|
| `visual-diff.yml` | **Fires + baseline refresh required** after Brief 1 lands (the primitive upgrade ripples through Modal/StatCard/landing). |
|
||||||
|
| `lint` | Fires + new `forbidden-bespoke-glass-surface` gate after Brief 7. |
|
||||||
|
| `test:` (vitest) | Fires; Brief 1 adds a corner-light assertion to `ui-primitives.test.js`. |
|
||||||
|
|
||||||
|
## Known constraints
|
||||||
|
|
||||||
|
- **No-go zones honoured** — `components/Layout.js.backup`,
|
||||||
|
`scripts/add-*.js` graveyard untouched.
|
||||||
|
- **GPU budget for CardItem grid** — AGENTS.md explicitly
|
||||||
|
prohibits `backdrop-filter` per card thumbnail (it
|
||||||
|
multiplies); Brief 1 must NOT default `<GlassSurface>` to
|
||||||
|
any rendering that would pull CardItem into that prohibition.
|
||||||
|
|
||||||
|
## Multitask dispatch
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
slice_dependencies:
|
||||||
|
- brief: 1
|
||||||
|
depends_on: []
|
||||||
|
files:
|
||||||
|
- components/ui/GlassSurface.js
|
||||||
|
- test/components/ui-primitives.test.js
|
||||||
|
- brief: 2
|
||||||
|
depends_on: []
|
||||||
|
files:
|
||||||
|
- pages/login.js
|
||||||
|
- pages/signup.js
|
||||||
|
- brief: 3
|
||||||
|
depends_on: [1] # may simplify by using upgraded primitive
|
||||||
|
files:
|
||||||
|
- components/Layout.js
|
||||||
|
- components/ui/TopSearchBar.js
|
||||||
|
- test/components/Layout.test.js
|
||||||
|
- brief: 4
|
||||||
|
depends_on: [1]
|
||||||
|
files:
|
||||||
|
- components/BulkSelectionToolbar.js
|
||||||
|
- brief: 5
|
||||||
|
depends_on: []
|
||||||
|
files:
|
||||||
|
- pages/profile.js
|
||||||
|
- pages/settings.js
|
||||||
|
- pages/community/collections.js
|
||||||
|
- components/CollectionsPageView.js
|
||||||
|
- styles/globals.css # if .card is retired / documented
|
||||||
|
- brief: 6
|
||||||
|
depends_on: []
|
||||||
|
files:
|
||||||
|
- pages/index.js
|
||||||
|
- brief: 7
|
||||||
|
depends_on: [1, 2, 3, 4, 5, 6] # gate goes in LAST
|
||||||
|
files:
|
||||||
|
- .github/workflows/ci.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
Briefs 2, 5, 6 can run in parallel with Brief 1. Briefs 3, 4 wait
|
||||||
|
for Brief 1 to land so they can simplify by using the upgraded
|
||||||
|
primitive. Brief 7 runs LAST so the grep gate doesn't fail the
|
||||||
|
build on in-flight migrations.
|
||||||
|
|
||||||
|
## Out of scope follow-ups (queued)
|
||||||
|
|
||||||
|
- **`retire-or-formalize-card-class`** — if Brief 5 Decision 2
|
||||||
|
keeps `.card` as opaque fallback, a future small convoy can
|
||||||
|
rename it `.opaque-panel` for clarity. P3.
|
||||||
|
- **`storybook-adoption-for-glass-surface`** — once the primitive
|
||||||
|
has the full prop matrix (`tint`, `blur`, `rim`, `elevation`,
|
||||||
|
`cornerLights`), it's a natural Storybook candidate. P2 DX.
|
||||||
Loading…
Reference in a new issue