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>
7.1 KiB
| convoy | brief_number | depends_on | files | |||||
|---|---|---|---|---|---|---|---|---|
| unify-glass-panel-surfaces | 5 |
|
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):.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— 3className="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— 2className="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):
// Before: <div className="card p-6 [...whatever-else]"> // After: <div className="glass-panel rounded-3xl p-6 [...whatever-else]">Verbatim: replace the standalone token
cardwithglass-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'drounded-3xl shadow-lg p-6 transition-all duration-300; we keeprounded-3xlexplicitly and let.glass-panel's composed shadow stack replaceshadow-lg(which is the right move —.glass-panel's tokens theme correctly,shadow-lgdoesn't). -
p-6handling: the legacy class baked inp-6. If the consumer wrotecard p-4(pages/settings.jsL266), the explicitp-4already wins via Tailwind's cascade —p-4overrides the@apply rounded-3xl shadow-lg p-6because they're at the same specificity andp-4is the later-defined declaration in compiled output. After migration, the explicitp-4continues to win because there's no longer any baked-inp-6. Bottom line: preserve whatever padding token the consumer wrote; don't normalize top-6. -
transition-all duration-300handling: the legacy class baked intransition-all duration-300. Most consumers ALSO wrote it explicitly (e.g.CollectionsPageView.jsL125:transition-all duration-300). If a consumer relied on the baked-in version, the swap loses the transition — addtransition-all duration-300explicitly to that consumer's className post-swap. Audit each site for whether the transition is referenced; add it back where needed. -
shadow-lghandling: the legacy class baked inshadow-lg..glass-panel's composedbox-shadow(rim + elevation-ambient) is the right replacement; do NOT carryshadow-lgover. -
pages/community/collections.jssite (L266): the consumer addshover:shadow-lgon top of the bakedshadow-lg. The base shadow becomes.glass-panel's shadow stack; thehover:shadow-lgis a hardcoded Tailwind shadow. Decision: replacehover:shadow-lgwith nohover:override — the corner-light gradient on.glass-panelis the new affordance. (If review shows this regresses hover legibility, fall back to inlinestyle={{}}boxShadow override on:hover— but the simpler clean diff is to drop it.) -
components/CollectionsPageView.jssite (L125): same logic. Drophover: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:
# 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.cssno longer contains a.card { ... }rule block.- Zero
className=".*\bcard\b.*"references remain underpages/orcomponents/(verified by the grep command above). - Each of the 8 migrated consumers renders with
.glass-panel rounded-3xland the originalp-{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/collectionscollection-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.