docs(convoys): unify-glass-panel-surfaces + cleanup palette — conductor seed + architect plan (#119)
* 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>
* docs(convoys): architect plan + 7 briefs for unify-glass-panel-surfaces
Appends the ## Architecture section to .convoys/unify-glass-panel-surfaces.md
ratifying the 4 open Decisions, and writes 7 implementer-brief files under
.convoys/unify-glass-panel-surfaces/.
Decisions ratified:
D1. <GlassSurface> grows a cornerLights='subtle'|'chrome'|'none' prop,
default 'subtle' (4-layer gradient-border per PR #118 recipe).
D2. Retire .card entirely; migrate all 8 consumers to .glass-panel.
D3. Mobile drawer uses subtle tier (.glass-panel-strong).
D4. CI gate scopes to JSX inline-style usage only; 3-entry allowlist
for documented chrome exceptions.
Briefs (1-7, all <100 LOC each, disjoint files):
1. Upgrade <GlassSurface> primitive (no deps, blocks 3+4)
2. Migrate auth form cards (parallel-safe)
3. Migrate floating popovers (sidebar dropdown, mobile drawer,
TopSearchBar UserMenu)
4. Migrate BulkSelectionToolbar + interior token sweep
5. Retire .card class; migrate 8 consumers
6. Migrate landing nav bar to .page-header-glass
7. forbidden-bespoke-glass-surface CI gate (runs LAST)
Boot-the-brief check: all 4-layer recipes verified against post-PR-#118
styles/globals.css; all box-shadow preservation chains verified against
current Layout.js + TopSearchBar.js; .card deletion verified clean
(single rule at L729-733); CI gate shape modeled on existing
forbidden-modal-shell-without-primitive job.
Architecture is read-only output — no application code touched.
Awaiting human gate 1 (plan approval) before implementers run.
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs(convoys): fix Brief 3 TopSearchBar UserMenu elevation token
Self-review recheck against current TopSearchBar.js (L211-222) found
that the UserMenu dropdown uses var(--elevation-pronounced), not
var(--elevation-ambient) as Brief 3's initial sample showed. Brief 3
now documents the verbatim shape (pronounced + role="menu" +
aria-label + overflow-hidden) and the convoy's risk-list note is
updated to reflect all three popovers' verbatim chains.
No semantic change to the plan; just an accuracy fix the
implementer of Brief 3 needs to ship the right code on the first try.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 15:09:40 -04:00
|
|
|
|
---
|
|
|
|
|
|
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
|
2026-06-05 07:17:34 -04:00
|
|
|
|
status: closed
|
|
|
|
|
|
closed: 2026-06-04
|
|
|
|
|
|
prs:
|
|
|
|
|
|
- 128 # Brief 1 — CardItem.js list-mode token sweep
|
|
|
|
|
|
- 129 # Brief 2 — ShareModal.js interior token sweep
|
docs(convoys): unify-glass-panel-surfaces + cleanup palette — conductor seed + architect plan (#119)
* 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>
* docs(convoys): architect plan + 7 briefs for unify-glass-panel-surfaces
Appends the ## Architecture section to .convoys/unify-glass-panel-surfaces.md
ratifying the 4 open Decisions, and writes 7 implementer-brief files under
.convoys/unify-glass-panel-surfaces/.
Decisions ratified:
D1. <GlassSurface> grows a cornerLights='subtle'|'chrome'|'none' prop,
default 'subtle' (4-layer gradient-border per PR #118 recipe).
D2. Retire .card entirely; migrate all 8 consumers to .glass-panel.
D3. Mobile drawer uses subtle tier (.glass-panel-strong).
D4. CI gate scopes to JSX inline-style usage only; 3-entry allowlist
for documented chrome exceptions.
Briefs (1-7, all <100 LOC each, disjoint files):
1. Upgrade <GlassSurface> primitive (no deps, blocks 3+4)
2. Migrate auth form cards (parallel-safe)
3. Migrate floating popovers (sidebar dropdown, mobile drawer,
TopSearchBar UserMenu)
4. Migrate BulkSelectionToolbar + interior token sweep
5. Retire .card class; migrate 8 consumers
6. Migrate landing nav bar to .page-header-glass
7. forbidden-bespoke-glass-surface CI gate (runs LAST)
Boot-the-brief check: all 4-layer recipes verified against post-PR-#118
styles/globals.css; all box-shadow preservation chains verified against
current Layout.js + TopSearchBar.js; .card deletion verified clean
(single rule at L729-733); CI gate shape modeled on existing
forbidden-modal-shell-without-primitive job.
Architecture is read-only output — no application code touched.
Awaiting human gate 1 (plan approval) before implementers run.
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs(convoys): fix Brief 3 TopSearchBar UserMenu elevation token
Self-review recheck against current TopSearchBar.js (L211-222) found
that the UserMenu dropdown uses var(--elevation-pronounced), not
var(--elevation-ambient) as Brief 3's initial sample showed. Brief 3
now documents the verbatim shape (pronounced + role="menu" +
aria-label + overflow-hidden) and the convoy's risk-list note is
updated to reflect all three popovers' verbatim chains.
No semantic change to the plan; just an accuracy fix the
implementer of Brief 3 needs to ship the right code on the first try.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 15:09:40 -04:00
|
|
|
|
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.
|