deckhearth/.convoys/unify-glass-panel-surfaces/brief-5-retire-card-class.md
Randall Stillwell 8093f6c4c0 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>
2026-06-04 14:06:53 -05:00

7.1 KiB

convoy brief_number depends_on files
unify-glass-panel-surfaces 5
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):
    .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):

    // 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:

    # 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.