deckhearth/.convoys/unify-glass-panel-surfaces/brief-5-retire-card-class.md

88 lines
7.1 KiB
Markdown
Raw Normal View History

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
---
convoy: unify-glass-panel-surfaces
brief_number: 5
depends_on: []
files:
- pages/profile.js
- pages/settings.js
- pages/community/collections.js
- components/CollectionsPageView.js
- styles/globals.css
---
# Brief 5: Retire `.card` class; migrate 8 consumers to `.glass-panel`
## Goal (1 sentence)
Delete the legacy `.card` class from `styles/globals.css` and migrate all 8 consumers (3 in `pages/profile.js`, 2 in `pages/settings.js`, 1 in `pages/community/collections.js`, 1 in `components/CollectionsPageView.js`, plus 1 likely-missed inline `className="card hover:..."` instance) to `glass-panel rounded-3xl p-{4|6}` so the app converges on a single panel vocabulary.
## Files in scope (do not edit anything else)
- `styles/globals.css` — delete the entire `.card { ... }` rule block (currently 4 lines at L729-733):
```css
.card {
@apply rounded-3xl shadow-lg p-6 transition-all duration-300;
background-color: var(--bg-primary);
border: 1px solid var(--border);
}
```
- `pages/profile.js` — 3 `className="card p-6 [...]"` swaps. Likely sites (verify line numbers against current file): L290 (`card p-6 text-center` — the profile avatar card), L397 (`card p-6 mt-6` — the stats card), L432 (`card p-6` — the activity card).
- `pages/settings.js` — 2 `className="card ..."` swaps. Likely sites: L266 (`card p-4`), L290 (`card p-6`).
- `pages/community/collections.js` — 1 swap. Likely site: L266 (`card group cursor-pointer hover:shadow-lg transition-all duration-200`).
- `components/CollectionsPageView.js` — 1 swap. Likely site: L125 (`card hover:shadow-xl transition-all duration-300 cursor-pointer group`).
**Out of scope:** everything else in those files (page-level layout, header strips, modal wrappers, button styling, inline content). Only the `.card` className references migrate.
## Conventions to follow
- **Swap pattern (default):**
```jsx
// Before:
<div className="card p-6 [...whatever-else]">
// After:
<div className="glass-panel rounded-3xl p-6 [...whatever-else]">
```
Verbatim: replace the standalone token `card` with `glass-panel rounded-3xl`. **Preserve any sibling tokens** (`p-4` / `p-6` / `text-center` / `mt-6` / `group` / `cursor-pointer` / `hover:shadow-lg` / `transition-all duration-200`, etc.). The legacy `.card` `@apply`'d `rounded-3xl shadow-lg p-6 transition-all duration-300`; we keep `rounded-3xl` explicitly and let `.glass-panel`'s composed shadow stack replace `shadow-lg` (which is the right move — `.glass-panel`'s tokens theme correctly, `shadow-lg` doesn't).
- **`p-6` handling:** the legacy class baked in `p-6`. If the consumer wrote `card p-4` (`pages/settings.js` L266), the explicit `p-4` already wins via Tailwind's cascade — `p-4` overrides the `@apply rounded-3xl shadow-lg p-6` because they're at the same specificity and `p-4` is the later-defined declaration in compiled output. After migration, the explicit `p-4` continues to win because there's no longer any baked-in `p-6`. **Bottom line:** preserve whatever padding token the consumer wrote; don't normalize to `p-6`.
- **`transition-all duration-300` handling:** the legacy class baked in `transition-all duration-300`. Most consumers ALSO wrote it explicitly (e.g. `CollectionsPageView.js` L125: `transition-all duration-300`). If a consumer relied on the baked-in version, the swap loses the transition — add `transition-all duration-300` explicitly to that consumer's className post-swap. **Audit each site for whether the transition is referenced; add it back where needed.**
- **`shadow-lg` handling:** the legacy class baked in `shadow-lg`. `.glass-panel`'s composed `box-shadow` (rim + elevation-ambient) is the right replacement; do NOT carry `shadow-lg` over.
- **`pages/community/collections.js` site (L266):** the consumer adds `hover:shadow-lg` on top of the baked `shadow-lg`. The base shadow becomes `.glass-panel`'s shadow stack; the `hover:shadow-lg` is a hardcoded Tailwind shadow. Decision: replace `hover:shadow-lg` with no `hover:` override — the corner-light gradient on `.glass-panel` is the new affordance. (If review shows this regresses hover legibility, fall back to inline `style={{}}` boxShadow override on `:hover` — but the simpler clean diff is to drop it.)
- **`components/CollectionsPageView.js` site (L125):** same logic. Drop `hover:shadow-xl`.
- **Escape hatch (per Decision D2):** if any one of the 8 consumers breaks visually post-swap (the architect doesn't expect this), revert that one site to inline `style={{ background: 'var(--bg-secondary)' }}` rather than holding the entire class-deletion. Document each escape-hatch use in the PR description with a screenshot. If MORE than 1 of 8 needs the escape hatch, the brief's reviewer is expected to push back and hold the deletion entirely — flag in PR comments.
- **CSS deletion order:** delete the `.card { ... }` rule LAST, after all 8 className swaps are in place. (Pure ordering hygiene for diff-readability; CSS doesn't actually care.)
- **Boundaries:** do NOT touch any other class in `globals.css` (no renaming `.glass-panel``.opaque-panel`, no adding new classes). Do NOT touch any pages or components not in the 4 listed.
- **Grep verification before PR:**
```bash
# Should return 0 matches anywhere in pages/ or components/:
rg "className=[\"\\'\\\`]card\\b" pages/ components/ --type js
# And the class definition itself:
rg "^\\.card \\{" styles/
```
Both should return zero. If either returns anything, the migration is incomplete.
## Acceptance criteria
- [ ] `styles/globals.css` no longer contains a `.card { ... }` rule block.
- [ ] Zero `className=".*\bcard\b.*"` references remain under `pages/` or `components/` (verified by the grep command above).
- [ ] Each of the 8 migrated consumers renders with `.glass-panel rounded-3xl` and the original `p-{4|6}` padding intact.
- [ ] Wherever the consumer relied on baked-in `transition-all duration-300` (audit by reviewing each site), the transition is added explicitly post-migration.
- [ ] `hover:shadow-{lg,xl}` Tailwind overrides on community/CollectionsPageView are dropped (replaced by `.glass-panel`'s corner-light affordance).
- [ ] No escape-hatch sites or — if any — at most 1 escape-hatch site, documented with a screenshot in the PR description.
- [ ] `npm run lint` + `npm run test:run` (no test count change expected; 22+/22+ post Brief 1) pass.
- [ ] Manual: visit `/profile`, `/settings`, `/community/collections`, `/community/collections` collection-detail in light + dark mode and confirm all panels render with corner catch-lights and theme-correct backgrounds. Attach before/after screenshots to PR.
- [ ] No edits to files outside the 5 listed in `files:` above.
## Rationale (≤3 sentences)
`.card` predates the gradient-border system and is the only remaining "opaque solid panel" pattern in user-facing pages — a single panel vocabulary across the app is the convoy's success metric. The 8 consumers are all "panel-with-content-inside" surfaces with no GPU-budget constraint, so the migration is a clean className swap with no compositional surprises. Deleting the class outright (rather than keeping it as a documented fallback) prevents future agents from picking the wrong pattern by accident.