* 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>
5.9 KiB
| convoy | brief_number | depends_on | files | ||
|---|---|---|---|---|---|
| unify-glass-panel-surfaces | 1 |
|
Brief 1: Upgrade <GlassSurface> primitive with cornerLights prop
Goal (1 sentence)
Add a cornerLights = 'subtle' | 'chrome' | 'none' prop to <GlassSurface> and compose the 4-layer gradient-border pattern (padding-box fill + two border-box corner radials + --chip-border-base base) on top of the existing tint / blur / rim / elevation props so every consumer (Modal, StatCard, landing feature cards) picks up corner catch-lights automatically.
Files in scope (do not edit anything else)
components/ui/GlassSurface.js— add the prop, compose the 4-layerbackgroundand theborder: 1px solid transparentwhen the prop is not'none'.test/components/ui-primitives.test.js— add 3 corner-light assertions to the existingdescribe('GlassSurface', ...)block (or add the block if it doesn't exist; today the file covers Button / Input / SearchBar only, so a new top-leveldescribe('GlassSurface', ...)block at the end of the file is correct).
Out of scope: every consumer of <GlassSurface> — components/ui/Modal.js, components/ui/StatCard.js, pages/index.js landing cards. Their behavior changes via the new default; no edits required. Do NOT modify them in this PR.
Conventions to follow
- Verbatim 4-layer recipe (mirrors
.glass-panel-strongpost-PR #118; seestyles/globals.css):// When cornerLights === 'subtle' (default): background: `linear-gradient(var(--glass-surface-${tint}), var(--glass-surface-${tint})) padding-box, radial-gradient(at 0% 100%, var(--corner-light-warm-subtle) 0%, transparent 42%) border-box, radial-gradient(at 100% 0%, var(--corner-light-cool-subtle) 0%, transparent 42%) border-box, var(--chip-border-base) border-box` // When cornerLights === 'chrome': // Same recipe; swap --corner-light-warm-subtle → --corner-light-warm // and --corner-light-cool-subtle → --corner-light-cool. // When cornerLights === 'none': // Today's behavior — single layer background: var(--glass-surface-${tint}). // No border declaration. - Border declaration: when
cornerLights !== 'none', also setborder: '1px solid transparent'on the composed style. This is what reveals the border-box gradient layers. When'none', omit the border entirely (preserve today's box-model). - Shadow stack: unchanged. The existing
RIM_SHADOWS/ELEVATION_SHADOWScomposition logic stays exactly as written. The corner-light layers live inbackground, notbox-shadow. - Default value:
cornerLights = 'subtle'. This is the new default for every consumer that doesn't pass the prop explicitly. - Backdrop filter: unchanged.
backdropFilter/WebkitBackdropFiltercontinue to compose fromblur/tintexactly as today. Do not touch this line. styleprop merging: today the composed style spreads...styleLAST so caller overrides win. Preserve that exactly — a caller passingstyle={{ border: '1px solid #f00' }}overrides the transparent border. This is the explicit escape hatch any consumer who needs a real border can use without settingcornerLights='none'.- AGENTS.md / no-go zones:
styles/globals.cssis untouched in this brief (Brief 5 owns the.carddeletion; no other CSS edits in this convoy). The--corner-light-warm-subtle/--corner-light-cool-subtle/--corner-light-warm/--corner-light-cool/--chip-border-basetokens all exist in:rootand[data-theme="dark"]per PR #118 — verified.
Acceptance criteria
<GlassSurface>accepts acornerLightsprop with values'subtle'(default),'chrome','none'. Any other value falls back to'subtle'(mirror the existingRIM_SHADOWS[rim] ?? []defensive fallback shape).- When
cornerLights='subtle', rendered output includes:background:containing bothvar(--corner-light-warm-subtle)andvar(--corner-light-cool-subtle).border:value1px solid transparent.
- When
cornerLights='chrome', rendered output includes:background:containingvar(--corner-light-warm)andvar(--corner-light-cool)(NOT the-subtlevariants).border:value1px solid transparent.
- When
cornerLights='none', rendered output is identical to today: single-layerbackground: var(--glass-surface-${tint}), noborderdeclaration in the composed style. tint/blur/rim/elevation/as/style/classNameprops all behave exactly as before. Existing consumers continue to compile + render without prop changes.test/components/ui-primitives.test.jshas 3 new assertions (one percornerLightsvalue) verifying the inline style attribute or the rendered element'sstyle.background/style.borderaccordingly. Use the existingrender+screen.getByRole/getByText/containerpatterns from the file; don't pull in new test deps.npm run lintpasses.npm run test:runis 22+/22+ (today's count is 21; this brief adds at least 3 — exact count depends on whether you split assertions across multipleit(...)blocks).- No edits to files outside the two listed in
files:above. No consumer migrations in this PR. Brief 3 + 4 will compose against the new default; Briefs 2 + 5 + 6 don't use the primitive.
Rationale (≤3 sentences)
The primitive is the single largest leverage point in the convoy — upgrading it ripples through <Modal>, <StatCard>, and the landing-page feature cards in one commit. Defaulting to 'subtle' matches the PR #118 token-tier decision and means downstream consumers don't need to opt in. The 'chrome' + 'none' values exist so the prop is future-proof: floating chrome can opt up without handrolling, and any GPU-constrained tile (e.g. a future -like surface) can opt out cleanly.