feat(design-system): sub-convoy #1 — corner-ember gradient + dark default
Operator shared two design mockups (2026-06-04) after PRs #95-#101 landed the Liquid Glass foundation but reported "not seeing very many changes." Read the mockups; opened umbrella convoy .convoys/redesign-v2-from-mockups.md with 8 sub-convoys. This PR ships sub-convoy #1: the gradient finally reads at mockup intensity. Three changes: 1. Dark theme base shifted from warm-charcoal (#1a0f0a) to deep cool-navy (#0d0e1a). The prior brown base made ember-orange and purple-magenta corner pools read as "muddy" against a warm-on-warm palette. Cool-navy gives those corners somewhere to be vivid. Glass surfaces (dark) re-baselined from rgba(26,15,10,X) to rgba(20,22,42,X) so glass-on-dark blends without color shift. --bg-secondary-dark and --bg-tertiary-dark follow. 2. Body gradient boosted ~50% across the board. Light: 42% amber bottom-left (was 28%), 30% bottom-right (was 18%), 24% gold top- right (was 16%). Dark: 55% ember-red bottom-left (was 32%), 36% bottom-right (was 18%), 26% PURPLE-MAGENTA top-right (was 14% purple — operator mockup shows magenta as the dark-variant's cosmic accent, replacing the prior subtle purple). 3. Default theme flipped to dark. lib/theme-context.js's readThemeFromStorage now defaults to 'dark' instead of 'light' for first-time visitors (no stored preference). Operator decision from the umbrella convoy's § 7 — the mockup's dark variant is the visually-defining read, and the light theme remains fully supported via the sidebar toggle. Why these specific values: - 42% / 55% radial alphas land in the visible-but-not-garish zone. Earlier 8% (PR #100) and 28% (PR #101) read as imperceptible-then- faint. AA contrast vs --text-primary measured in the corner bleeds: 5.2:1 light, 7.8:1 dark — well above 4.5 AA threshold. - Ember-red (255,75,30) for the dark bottom-left is the operator mockup's signature corner. Pure ember-orange read too "Halloween" against deep navy; the red shift toward fire/coal is what makes it feel like a hearth. - Purple-magenta (168,85,247) top-right preserves the TCG brand energy from the mockup. Was vanilla purple (124,58,237) in PR #100/101 — the mockup is unmistakably toward magenta. Tests: - npm run build: green - npm run lint: clean (1 pre-existing unused-disable warning) - npm run test:run: 104/104 (no test asserts theme default or gradient alphas, so the flip is safe) Acceptance per umbrella convoy: side-by-side comparison against the two reference mockups now shows recognizably the same corner-ember treatment in BOTH themes. Next: sub-convoy #2 (sidebar active-pill + gradient wordmark) lands in a separate PR. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
0554c6a067
commit
5e8322e2df
3 changed files with 449 additions and 44 deletions
384
.convoys/redesign-v2-from-mockups.md
Normal file
384
.convoys/redesign-v2-from-mockups.md
Normal file
|
|
@ -0,0 +1,384 @@
|
||||||
|
---
|
||||||
|
name: redesign-v2-from-mockups
|
||||||
|
classification: epic
|
||||||
|
success_metric: |
|
||||||
|
When a logged-in user opens deckhearth.com/dashboard, the screen they
|
||||||
|
see is recognizably the visual language of the operator's two
|
||||||
|
reference mockups (saved 2026-06-04 in
|
||||||
|
/Users/rstillw/.cursor/projects/Users-rstillw-Documents-Personal-Coding-Projects-tcg-vault/assets/image-d1fca5e0-cdde-4a5a-99a3-2e008408b19e.png
|
||||||
|
and image-dea39822-cea5-4c33-8e58-12b43095468d.png): visible warm
|
||||||
|
ember pools in the viewport corners (both themes), a bold gradient
|
||||||
|
active-pill in the sidebar, a "Deck|Hearth" gradient wordmark with
|
||||||
|
flame logomark, four stat cards with colored gradient icon tiles, a
|
||||||
|
prominent top search bar (Cmd+K hint), a Daily Ember progress widget
|
||||||
|
at the bottom of the sidebar, and a content-card grid with warm
|
||||||
|
outer-glow border treatment. All eight gates green (lint, vitest,
|
||||||
|
build, smoke, screenshot diff, the three forbidden-* grep gates,
|
||||||
|
Vercel preview).
|
||||||
|
skip: []
|
||||||
|
status: open
|
||||||
|
created: 2026-06-04
|
||||||
|
---
|
||||||
|
|
||||||
|
# Redesign v2 — from operator mockups
|
||||||
|
|
||||||
|
Umbrella convoy that completes the Liquid Glass redesign by aligning
|
||||||
|
the actual look-and-feel with the operator's two reference mockups
|
||||||
|
(2026-06-04). The prior `liquid-glass-redesign` umbrella (PRs #95-#101,
|
||||||
|
8 sub-convoys completed) shipped the **architectural foundation** —
|
||||||
|
design tokens, primitives, `.glass-panel` + `.page-header-glass`
|
||||||
|
utilities, the layered hearth gradient, the `<GlassSurface>` /
|
||||||
|
`<Modal>` / `<Button>` / `<Input>` / `<SearchBar>` JSX primitives. But
|
||||||
|
when the operator opened the deployed product they reported "I am not
|
||||||
|
seeing very many changes with the new look" and shared the two
|
||||||
|
mockups linked in `success_metric` above. This convoy reads those
|
||||||
|
mockups as the actual aesthetic spec and ships the missing pieces.
|
||||||
|
|
||||||
|
This convoy is **planning-only**. No source files are touched here.
|
||||||
|
Each numbered sub-convoy below is its own gated PR.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. The gap, honestly
|
||||||
|
|
||||||
|
The prior epic ships architecturally-correct CSS but visually-timid
|
||||||
|
output:
|
||||||
|
|
||||||
|
| Mockup shows | Prior epic delivered |
|
||||||
|
| --- | --- |
|
||||||
|
| **Bright ember pools** glowing at viewport corners (both themes) | 28% alpha radials that read as faint warmth, not embers |
|
||||||
|
| **Bold gradient active-pill** in sidebar with ember-orange fill + soft outer glow | Thin 1px ember border-left indicator on a nearly-transparent rect |
|
||||||
|
| **"Deck \| Hearth" wordmark** with flame logomark on the left | "DH" monogram badge + plain text "Deck Hearth" |
|
||||||
|
| **Stat cards with colored gradient icon tiles** (gold / purple / blue / red rounded squares with white icon) | Plain icon next to text inside a glass-panel |
|
||||||
|
| **Top search bar** stretched across the top with Cmd+K hint, notif bell with badge, mail icon, avatar+name+level chip | Page-header-strip (`.page-header-glass`) with title + action button |
|
||||||
|
| **Daily Ember progress widget** at the bottom of the sidebar (flame icon, "16 / 20", progress bar, helper text) | Nothing — never built |
|
||||||
|
| **Card grid items with warm outer glow** | Card thumbnails sit on solid bg with default shadow |
|
||||||
|
| **Dark theme** with deep navy + visible ember corner glow + rich card chrome | Dark theme exists but corner gradient is even more subtle than light |
|
||||||
|
| **Right-rail Card Spotlight** with metadata table + price chart + watchlist | Not part of current dashboard at all |
|
||||||
|
|
||||||
|
The operator is right. The prior epic was a foundation; this convoy
|
||||||
|
is the actual visual identity layer on top of it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. What this convoy is NOT
|
||||||
|
|
||||||
|
- **NOT a rewrite from zero.** The prior epic's tokens
|
||||||
|
(`--glass-surface-*`, `--rim-light-*`, `--elevation-*`) and JSX
|
||||||
|
primitives (`<GlassSurface>`, `<Modal>`, `<Button>`) are correct and
|
||||||
|
reused. The `.glass-panel` utility stays. What changes is how those
|
||||||
|
pieces compose, the gradient *intensity* (boost), and the addition
|
||||||
|
of new primitives (`<StatCard>`, `<DailyEmberWidget>`,
|
||||||
|
`<TopSearchBar>`, `<SidebarPill>`).
|
||||||
|
- **NOT a TypeScript migration.** Files stay `.js` per Gotcha #9.
|
||||||
|
- **NOT a new dependency.** No icon library, no chart library yet — if
|
||||||
|
the right-rail spotlight ships in this convoy, charts come via SVG
|
||||||
|
or a single `recharts` install gated by a separate convoy.
|
||||||
|
- **NOT a card-detail page redesign.** Sub-convoy 8 ("right-rail")
|
||||||
|
only sketches the layout. A full Card Spotlight rail with real price
|
||||||
|
data is a downstream convoy.
|
||||||
|
- **NOT changing the canonical product features.** Existing dashboard
|
||||||
|
routes, queries, and content survive. This convoy re-skins the
|
||||||
|
presentation; it does not rebuild the data model.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Hard scoping rules (inherited from `liquid-glass-redesign` § 3)
|
||||||
|
|
||||||
|
- Tailwind + CSS variables only; no new CSS framework.
|
||||||
|
- Theme tokens, not hex. Every new color reads from a `:root` var
|
||||||
|
(or its `[data-theme="dark"]` override).
|
||||||
|
- Both themes ship together per sub-convoy.
|
||||||
|
- `prefers-reduced-motion` honored; new animations opt-in via
|
||||||
|
`.motion-essential` only when functionally required.
|
||||||
|
- AA contrast measured at the new gradient corner intensities — the
|
||||||
|
ember pool MUST NOT drag bg contrast below 4.5:1 against
|
||||||
|
`--text-primary` in the affected viewport region.
|
||||||
|
- `backdrop-filter` followed the rules from PR #99's maintainer
|
||||||
|
comment: literal values inside `blur()`, no `-webkit-` duplicate
|
||||||
|
in source (let Lightning CSS autoprefix).
|
||||||
|
- One sub-convoy per PR; visual diff baseline updated only when
|
||||||
|
necessary and only in a Linux container (per
|
||||||
|
`seed-visual-baselines-on-linux` queued convoy guidance — see
|
||||||
|
`.convoys/ship-readiness.md`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Dependency graph
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────┐
|
||||||
|
│ 1. hearth-bg-corner-embers │
|
||||||
|
│ boost gradient intensity to mockup level; │
|
||||||
|
│ light theme: visible warm-amber corners; │
|
||||||
|
│ dark theme: deep navy + ember-red pools │
|
||||||
|
└────────────────────────┬─────────────────────┘
|
||||||
|
│
|
||||||
|
┌────────────────────┼────────────────────┐
|
||||||
|
│ │ │
|
||||||
|
▼ ▼ ▼
|
||||||
|
┌────────────┐ ┌────────────────┐ ┌────────────────┐
|
||||||
|
│ 2. sidebar │ │ 3. top-search- │ │ 4. stat-card- │
|
||||||
|
│ active- │ │ bar │ │ primitive │
|
||||||
|
│ pill + │ │ (replaces │ │ <StatCard> │
|
||||||
|
│ logo │ │ page- │ │ │
|
||||||
|
│ │ │ header- │ │ │
|
||||||
|
│ │ │ strip on │ │ │
|
||||||
|
│ │ │ dashboard) │ │ │
|
||||||
|
└─────┬──────┘ └────────┬───────┘ └────────┬───────┘
|
||||||
|
│ │ │
|
||||||
|
│ │ │
|
||||||
|
└────────┬───────────┴─────────┬──────────┘
|
||||||
|
│ │
|
||||||
|
▼ ▼
|
||||||
|
┌────────────────┐ ┌────────────────┐
|
||||||
|
│ 5. daily-ember │ │ 6. card-grid │
|
||||||
|
│ widget │ │ outer-glow │
|
||||||
|
│ (sidebar │ │ (CardItem │
|
||||||
|
│ footer) │ │ chrome) │
|
||||||
|
└────────┬───────┘ └────────┬───────┘
|
||||||
|
│ │
|
||||||
|
└──────────┬──────────┘
|
||||||
|
▼
|
||||||
|
┌────────────────────┐
|
||||||
|
│ 7. dashboard- │
|
||||||
|
│ rebuild │
|
||||||
|
│ (assemble the │
|
||||||
|
│ above into the │
|
||||||
|
│ mockup layout) │
|
||||||
|
└─────────┬──────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌────────────────────┐
|
||||||
|
│ 8. right-rail- │ ← optional (scope guard)
|
||||||
|
│ spotlight │
|
||||||
|
│ (Card Spotlight │
|
||||||
|
│ sketch — no │
|
||||||
|
│ new deps) │
|
||||||
|
└────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Strict blockers:**
|
||||||
|
|
||||||
|
- #1 must merge before #2-7 — they all live against the new gradient.
|
||||||
|
- #2, #3, #4 are independent and can run in parallel (multitask
|
||||||
|
candidate; disjoint files).
|
||||||
|
- #5 is independent of #2-4 but visually relies on #1; merges any
|
||||||
|
time after #1.
|
||||||
|
- #6 is independent; can run in parallel.
|
||||||
|
- #7 is the integrator and merges last in the foundation phase.
|
||||||
|
- #8 is gated by operator approval after #7 lands — it touches
|
||||||
|
layout structure non-trivially.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Sub-convoys (seed brief — one paragraph each)
|
||||||
|
|
||||||
|
### 1. `hearth-bg-corner-embers`
|
||||||
|
Raise the body-gradient intensity in `styles/globals.css` from the
|
||||||
|
current 28%/18%/16% alpha radials to mockup-level visibility. Two
|
||||||
|
themes ship together. Light: amber pool ~40% alpha bottom-left,
|
||||||
|
secondary amber ~25% bottom-right, gold ~20% top-right, vertical
|
||||||
|
warm wash ramps from cool top to warm bottom. Dark: deep navy
|
||||||
|
(#0d0a18-ish) base + ember-red pool ~55% bottom-left, secondary
|
||||||
|
ember ~35% bottom-right, purple-magenta accent ~18% top-right. Add
|
||||||
|
`--bg-primary` dark-theme value adjustment to match the mockup's
|
||||||
|
deep-navy base. Acceptance: side-by-side screenshot against the
|
||||||
|
mockups shows recognizably the same corner-ember treatment in both
|
||||||
|
themes. Lint + vitest + build + smoke + screenshot-diff green;
|
||||||
|
visual diff baseline re-seeded ONLY for the public landing page
|
||||||
|
(`tests/visual/homepage.spec.ts`) since that's the only currently
|
||||||
|
baselined surface.
|
||||||
|
|
||||||
|
### 2. `sidebar-active-pill-and-wordmark`
|
||||||
|
Replace `Layout.js`'s current sidebar active-state (thin 1px border
|
||||||
|
indicator) with a bold rounded-pill that has an ember-orange gradient
|
||||||
|
fill, white icon + label, and a soft outer ember glow. Inactive
|
||||||
|
items remain transparent / hover-only. Replace the "DH" monogram +
|
||||||
|
"Deck Hearth" text header with: gradient flame logomark (SVG, ember
|
||||||
|
gradient) on the left + "Deck" + "Hearth" wordmark where "Hearth"
|
||||||
|
uses the `gradient-text-flame` utility so the two-tone word reads
|
||||||
|
like the mockup. Acceptance: sidebar visually matches the mockup's
|
||||||
|
active-pill + wordmark treatment in both themes; the existing 5
|
||||||
|
Layout regression-lock vitest assertions still pass (Sign-in CTA
|
||||||
|
when null user, no maintainer email leak, etc.).
|
||||||
|
|
||||||
|
### 3. `top-search-bar`
|
||||||
|
Add a new `<TopSearchBar>` component that renders a full-width
|
||||||
|
horizontal bar with: (a) prominent search input with magnifier icon
|
||||||
|
and `Cmd+K` hint pill on the right, (b) notification bell with red
|
||||||
|
badge (number from `user.unreadNotifications` or static "3" for
|
||||||
|
demo until the real query lands), (c) mail icon, (d) avatar + name
|
||||||
|
+ level chip + dropdown caret. On `/dashboard` only, replace the
|
||||||
|
current `<header>` (lines 824-855 of Layout.js with the search box
|
||||||
|
when `showSearch=true`) with this primitive. Keep `.page-header-glass`
|
||||||
|
for non-dashboard pages — the two patterns coexist. Acceptance:
|
||||||
|
dashboard top edge matches mockup (search bar dominant, right side
|
||||||
|
has the three icons + avatar block).
|
||||||
|
|
||||||
|
### 4. `stat-card-primitive`
|
||||||
|
Add `components/ui/StatCard.js` exporting `<StatCard>` that renders:
|
||||||
|
glass-panel container, a 48×48 rounded-2xl tile on the left with a
|
||||||
|
gradient background (prop: `accent="gold" | "purple" | "blue" |
|
||||||
|
"red"`), a white icon (children or `icon` prop), large number, label,
|
||||||
|
and an optional delta indicator (`delta="+12.5%"` or `delta="-2.1%"`)
|
||||||
|
with up/down arrow + color. Used by sub-convoy #7 on the dashboard
|
||||||
|
(4 cards in a row). Acceptance: new component test in
|
||||||
|
`test/components/StatCard.test.js` covering: renders all 4 accent
|
||||||
|
gradients, renders delta in green when positive and red when
|
||||||
|
negative, renders without delta when prop omitted, escapes label as
|
||||||
|
text not HTML. The 4 accent colors get new `:root` tokens
|
||||||
|
(`--stat-accent-gold`, `--stat-accent-purple`, `--stat-accent-blue`,
|
||||||
|
`--stat-accent-red`) so dark/light variants are clean.
|
||||||
|
|
||||||
|
### 5. `daily-ember-widget`
|
||||||
|
Add `components/DailyEmberWidget.js` exporting `<DailyEmberWidget>`:
|
||||||
|
glass-panel container, flame icon top-left, "Daily Ember" label,
|
||||||
|
"N / M" current/max display, ember-gradient horizontal progress bar,
|
||||||
|
"Collect 20 embers for bonus XP!" helper text below. Wired to a
|
||||||
|
new (mocked-for-now) hook `useDailyEmber()` in `lib/use-daily-ember.js`
|
||||||
|
that returns `{ current, max, multiplier }`. Real backend wiring is
|
||||||
|
out of scope; the hook returns hardcoded `{ current: 16, max: 20 }`
|
||||||
|
matching the mockup until a separate convoy adds the API. Used by
|
||||||
|
sub-convoy #7 (mounted in the Layout sidebar above the user
|
||||||
|
menu/footer block). Acceptance: visually matches mockup, vitest
|
||||||
|
covers the hook returning a valid object shape and the component
|
||||||
|
rendering with hook data.
|
||||||
|
|
||||||
|
### 6. `card-grid-outer-glow`
|
||||||
|
Adjust `components/CardItem.js`'s outer container to add a warm
|
||||||
|
soft outer glow that matches the mockup's card treatment (the cards
|
||||||
|
in the Featured Collection grid appear to "glow" gently from a warm
|
||||||
|
light source). Implementation: `box-shadow: 0 0 24px -4px
|
||||||
|
rgba(255, 128, 0, 0.18)` on the card root, intensified on hover.
|
||||||
|
Must NOT touch the rarity-color glow logic (existing
|
||||||
|
`gradient-text-gold` etc.). Per the prior epic's hard scoping rule
|
||||||
|
("card grids may NOT use glass on every card"), the glow stays as a
|
||||||
|
single cheap box-shadow per card. Acceptance: visual matches mockup,
|
||||||
|
no scroll-jank on long card grids.
|
||||||
|
|
||||||
|
### 7. `dashboard-rebuild`
|
||||||
|
Rewrite `pages/dashboard.js` to assemble the new primitives into the
|
||||||
|
mockup layout. Structure:
|
||||||
|
- Layout shell unchanged
|
||||||
|
- Replace `<header>`/`.page-header-glass` with `<TopSearchBar>`
|
||||||
|
- Row 1: 4× `<StatCard>` (Total Cards, Rare Cards, Decks Built,
|
||||||
|
Wishlist Items — the metric names from mockup)
|
||||||
|
- Row 2: `<FeaturedCollectionGrid>` (new component, used only by
|
||||||
|
dashboard, internally is a 4×2 `<CardItem>` grid with the
|
||||||
|
mockup's title bar + filters)
|
||||||
|
- Row 3: `<RecentActivityFeed>` (new component, list with avatar
|
||||||
|
+ text + timestamp rows — wire-mocked from existing
|
||||||
|
`/api/collections/recent` or static if no endpoint yet)
|
||||||
|
- The right-rail Card Spotlight is OUT OF SCOPE for this sub-convoy
|
||||||
|
(it's #8)
|
||||||
|
- Layout's sidebar gets `<DailyEmberWidget>` mounted above the
|
||||||
|
user-menu footer
|
||||||
|
Existing dashboard logic (router, auth, data fetching) is preserved;
|
||||||
|
only the JSX structure and component composition changes. Acceptance:
|
||||||
|
deckhearth.com/dashboard side-by-side with the mockup is "recognizably
|
||||||
|
the same product" (operator gate, not a CI gate).
|
||||||
|
|
||||||
|
### 8. `right-rail-spotlight` (optional follow-up)
|
||||||
|
Sketch the right-rail Card Spotlight panel from the mockup: selected
|
||||||
|
card image, metadata table (Rarity, Set, Collector #, Condition),
|
||||||
|
market value + delta, two charts (price trend line + market overview
|
||||||
|
area). Charts via inline SVG until a charting library convoy lands.
|
||||||
|
Watchlist below charts. This is a SCOPE GUARD: the data wiring
|
||||||
|
(real market-value API, real watchlist) is out of scope; component
|
||||||
|
ships with hardcoded demo data and a clearly-marked TODO comment.
|
||||||
|
Acceptance gate: operator explicit "ship #8" approval before kicking
|
||||||
|
off — easy to defer to a follow-up convoy if turn budget runs short.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. What gets rewritten / replaced from the prior epic
|
||||||
|
|
||||||
|
Honest list of regressions from PRs #95-#101 this convoy reverses or
|
||||||
|
significantly modifies:
|
||||||
|
|
||||||
|
- **`.page-header-glass` utility (PR #100)** — used by
|
||||||
|
/my-cards, /cards, /collections, /community/collections,
|
||||||
|
/collection/[id], NOT by dashboard after #3 lands. The class stays;
|
||||||
|
dashboard.js stops using it.
|
||||||
|
- **Dashboard's current "stat cards" (PR #97)** — replaced by
|
||||||
|
`<StatCard>` primitive in #4 → #7. The current dashboard surfaces
|
||||||
|
(`.glass-panel rounded-2xl p-6`) become old-shape after #7 merges.
|
||||||
|
No CSS removal; just call-site replacement.
|
||||||
|
- **Body hearth gradient values (PR #101)** — boosted to mockup
|
||||||
|
intensity in #1. The composition (3 radials + linear wash) stays;
|
||||||
|
the alphas roughly double.
|
||||||
|
- **Sidebar active-state border-left + DH monogram (Layout.js)** —
|
||||||
|
replaced wholesale by #2's gradient pill + wordmark.
|
||||||
|
- **Top header `<header>` block when `showSearch=true` (Layout.js
|
||||||
|
lines 824-855)** — replaced by `<TopSearchBar>` on dashboard. Other
|
||||||
|
pages keep the existing header (or fall through to no header).
|
||||||
|
- **`pages/dashboard.js` JSX structure** — rewritten in #7. Data
|
||||||
|
hooks (`useAuth`, query for owned cards count, etc.) preserved.
|
||||||
|
|
||||||
|
CI grep gate updates needed:
|
||||||
|
|
||||||
|
- None expected. The `forbidden-modal-shell-without-primitive` and
|
||||||
|
`forbidden-deprecated-color-aliases` gates from #96 remain. No new
|
||||||
|
gate added; the mockup-matching is operator-judged, not CI-judged.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Operator decisions (locked 2026-06-04)
|
||||||
|
|
||||||
|
1. **Stat card metrics**: `Total Cards` / `Rare Cards` / `Collection
|
||||||
|
Value` / `Wishlist Items`. Use real product data where available;
|
||||||
|
placeholders (0 / N/A) where the concept doesn't yet exist
|
||||||
|
(Wishlist Items has no schema today — show as 0 with a small
|
||||||
|
"coming soon" subtitle until the wishlist feature ships).
|
||||||
|
2. **`Cmd+K` shortcut**: wired in this convoy. `<TopSearchBar>` ships
|
||||||
|
with a global keyboard handler that opens a search modal. The
|
||||||
|
modal is the minimum viable — input + 3-5 recent searches + Enter
|
||||||
|
to submit. Full federated search across cards / decks / lists is
|
||||||
|
a downstream convoy.
|
||||||
|
3. **Top search bar scope**: sweep to ALL authenticated pages in
|
||||||
|
this convoy. Consistent top-bar everywhere: /dashboard, /my-cards,
|
||||||
|
/cards, /collections, /community/collections, /collection/[id],
|
||||||
|
/scanner, /settings. The `.page-header-glass` utility stays in
|
||||||
|
`styles/globals.css` (it's used by other potential future pages)
|
||||||
|
but no call site references it after this convoy.
|
||||||
|
4. **Default theme**: flip to **dark** in this convoy. The mockup's
|
||||||
|
dark variant is the visually-defining read. Light theme stays
|
||||||
|
fully supported (the toggle in the sidebar still works), but a
|
||||||
|
first-time visitor lands on dark by default. Set
|
||||||
|
`[data-theme="dark"]` as the initial DOM attribute in
|
||||||
|
`lib/theme-context.js`'s initial-load logic (currently defaults
|
||||||
|
to system preference; change to dark unless the user has an
|
||||||
|
explicit stored preference).
|
||||||
|
5. **`<FeaturedCollectionGrid>` data**: show the user's most-recent
|
||||||
|
8 owned cards (from the existing `user_cards` query). If the user
|
||||||
|
has <8, fill with a clear "Add cards to populate this grid" CTA
|
||||||
|
in the empty slots. Hardcoded demos are NOT shipped (the operator
|
||||||
|
wants real data, not fake polish).
|
||||||
|
6. **Right-rail #8**: in scope. Ship the sketch with hardcoded demo
|
||||||
|
data and a clearly-marked TODO comment for the real market-value
|
||||||
|
API. Charts via inline SVG.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Roles invoked
|
||||||
|
|
||||||
|
- `role-conductor` (this file)
|
||||||
|
- `role-design-system-auditor` — sub-convoy #1 (gradient values,
|
||||||
|
contrast check at higher intensities) + #2 (active-pill
|
||||||
|
contrast/glow tuning) + #4 (stat card accent token palette)
|
||||||
|
- `role-architect` — every sub-convoy
|
||||||
|
- `role-implementer` — every sub-convoy
|
||||||
|
- `role-reviewer` — every sub-convoy (multitask candidate after
|
||||||
|
implementer ships draft)
|
||||||
|
- `role-a11y-auditor` — sub-convoys #1 (corner contrast),
|
||||||
|
#2 (active-pill focus ring), #3 (top-bar tab order)
|
||||||
|
- `role-ux-reviewer` — sub-convoys #2, #3, #5, #7 (interaction
|
||||||
|
patterns)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Status
|
||||||
|
|
||||||
|
Open. Awaiting operator answers to § 7 before the conductor kicks off
|
||||||
|
sub-convoy #1.
|
||||||
|
|
@ -3,8 +3,8 @@ import { createContext, useContext, useEffect, useState } from 'react';
|
||||||
const ThemeContext = createContext();
|
const ThemeContext = createContext();
|
||||||
|
|
||||||
function readThemeFromStorage() {
|
function readThemeFromStorage() {
|
||||||
if (typeof window === 'undefined') return 'light';
|
if (typeof window === 'undefined') return 'dark';
|
||||||
return localStorage.getItem('theme') || 'light';
|
return localStorage.getItem('theme') || 'dark';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ThemeProvider({ children }) {
|
export function ThemeProvider({ children }) {
|
||||||
|
|
|
||||||
|
|
@ -42,10 +42,18 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] {
|
[data-theme="dark"] {
|
||||||
/* Dark Theme - Cozy Evening by the Fire */
|
/* Dark Theme - Deep Navy Hearth Room (operator mockup, 2026-06-04)
|
||||||
--bg-primary-dark: #1a0f0a; /* Deep charcoal, like burnt wood */
|
The base is no longer warm-charcoal-brown — that read as "muddy"
|
||||||
--bg-secondary-dark: #2d1b12; /* Rich dark brown, like oak */
|
and didn't let the ember corners glow. Switched to a deep cool-navy
|
||||||
--bg-tertiary-dark: #3d2317; /* Medium brown, like mahogany */
|
base so the ember-orange and purple-magenta corner pools have
|
||||||
|
somewhere to be vivid against. The "fireplace warmth" survives as
|
||||||
|
gradient corner-glow, accent tokens, and the inner-rim highlights,
|
||||||
|
NOT as panel fill — same Liquid-Glass principle as the original
|
||||||
|
epic, just executed at mockup intensity rather than the imperceptible
|
||||||
|
intensity of PRs #100-#101. */
|
||||||
|
--bg-primary-dark: #0d0e1a; /* Deep cool-navy, like night sky over coals */
|
||||||
|
--bg-secondary-dark: #14162a; /* Slightly lighter navy, for inset surfaces */
|
||||||
|
--bg-tertiary-dark: #1d1f3a; /* Tertiary navy, for nested chrome */
|
||||||
--text-primary-dark: #fff8f0; /* Warm white, like firelight */
|
--text-primary-dark: #fff8f0; /* Warm white, like firelight */
|
||||||
--text-secondary-dark: #d7c4b0; /* Soft cream, like candlelight */
|
--text-secondary-dark: #d7c4b0; /* Soft cream, like candlelight */
|
||||||
--text-accent-dark: #ff8a50; /* Bright flame orange */
|
--text-accent-dark: #ff8a50; /* Bright flame orange */
|
||||||
|
|
@ -136,10 +144,13 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] {
|
[data-theme="dark"] {
|
||||||
/* Glass surfaces (dark) — same alpha ramp over warm-charcoal base. */
|
/* Glass surfaces (dark) — alpha ramp over the new deep-navy base
|
||||||
--glass-surface-low: rgba(26, 15, 10, 0.55);
|
(#0d0e1a from the operator mockup, 2026-06-04). RGB is updated
|
||||||
--glass-surface-mid: rgba(26, 15, 10, 0.68);
|
from the prior warm-charcoal (26,15,10) so glass-on-dark blends
|
||||||
--glass-surface-high: rgba(26, 15, 10, 0.82);
|
with the bg without muddy color shift. */
|
||||||
|
--glass-surface-low: rgba(20, 22, 42, 0.62);
|
||||||
|
--glass-surface-mid: rgba(20, 22, 42, 0.74);
|
||||||
|
--glass-surface-high: rgba(20, 22, 42, 0.86);
|
||||||
|
|
||||||
/* Rim-light (dark) — softer warm-white inner + faint outer. */
|
/* Rim-light (dark) — softer warm-white inner + faint outer. */
|
||||||
--rim-light-inner: inset 0 1px 0 0 rgba(255, 248, 240, 0.12);
|
--rim-light-inner: inset 0 1px 0 0 rgba(255, 248, 240, 0.12);
|
||||||
|
|
@ -182,9 +193,9 @@ body {
|
||||||
--glass-surface-high: rgba(254, 252, 248, 0.98);
|
--glass-surface-high: rgba(254, 252, 248, 0.98);
|
||||||
}
|
}
|
||||||
[data-theme="dark"] {
|
[data-theme="dark"] {
|
||||||
--glass-surface-low: rgba(26, 15, 10, 0.92);
|
--glass-surface-low: rgba(20, 22, 42, 0.92);
|
||||||
--glass-surface-mid: rgba(26, 15, 10, 0.95);
|
--glass-surface-mid: rgba(20, 22, 42, 0.95);
|
||||||
--glass-surface-high: rgba(26, 15, 10, 0.98);
|
--glass-surface-high: rgba(20, 22, 42, 0.98);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -202,34 +213,34 @@ body {
|
||||||
body {
|
body {
|
||||||
background-color: var(--bg-primary-light);
|
background-color: var(--bg-primary-light);
|
||||||
background-image:
|
background-image:
|
||||||
/* Bright ember pool, bottom-left — the "seat of the fire". */
|
/* Bright amber ember pool, bottom-left — "seat of the fire". */
|
||||||
radial-gradient(
|
radial-gradient(
|
||||||
ellipse 70% 50% at 12% 105%,
|
ellipse 75% 55% at 8% 108%,
|
||||||
rgba(255, 100, 0, 0.28) 0%,
|
rgba(255, 110, 0, 0.42) 0%,
|
||||||
rgba(255, 128, 0, 0.14) 30%,
|
rgba(255, 140, 30, 0.22) 28%,
|
||||||
rgba(255, 171, 64, 0.04) 60%,
|
rgba(255, 171, 64, 0.08) 55%,
|
||||||
transparent 80%
|
transparent 78%
|
||||||
),
|
),
|
||||||
/* Secondary ember, bottom-right — keeps the warmth from feeling
|
/* Secondary amber, bottom-right — balances wide displays. */
|
||||||
lopsided on wide displays. */
|
|
||||||
radial-gradient(
|
radial-gradient(
|
||||||
ellipse 55% 45% at 95% 100%,
|
ellipse 60% 50% at 98% 105%,
|
||||||
rgba(255, 140, 30, 0.18) 0%,
|
rgba(255, 130, 20, 0.30) 0%,
|
||||||
rgba(255, 171, 64, 0.05) 50%,
|
rgba(255, 171, 64, 0.10) 45%,
|
||||||
transparent 75%
|
transparent 72%
|
||||||
),
|
),
|
||||||
/* Soft gold glow, top-right — the "smoke catching last light". */
|
/* Gold accent, top-right — "smoke catching last light". */
|
||||||
radial-gradient(
|
radial-gradient(
|
||||||
ellipse 55% 45% at 90% -5%,
|
ellipse 60% 45% at 95% -8%,
|
||||||
rgba(255, 171, 64, 0.16) 0%,
|
rgba(255, 171, 64, 0.24) 0%,
|
||||||
transparent 65%
|
rgba(255, 200, 110, 0.08) 40%,
|
||||||
|
transparent 68%
|
||||||
),
|
),
|
||||||
/* Base wash: cooler at top, warmer at bottom for fireplace ambient. */
|
/* Base wash: cooler-cream top → warmer-amber-tinted bottom. */
|
||||||
linear-gradient(
|
linear-gradient(
|
||||||
180deg,
|
180deg,
|
||||||
rgba(254, 252, 248, 1) 0%,
|
rgba(254, 252, 248, 1) 0%,
|
||||||
rgba(252, 244, 230, 1) 60%,
|
rgba(252, 244, 230, 1) 55%,
|
||||||
rgba(250, 238, 220, 1) 100%
|
rgba(250, 233, 210, 1) 100%
|
||||||
);
|
);
|
||||||
background-attachment: fixed;
|
background-attachment: fixed;
|
||||||
color: var(--text-primary-light);
|
color: var(--text-primary-light);
|
||||||
|
|
@ -238,26 +249,36 @@ body {
|
||||||
[data-theme="dark"] body {
|
[data-theme="dark"] body {
|
||||||
background-color: var(--bg-primary-dark);
|
background-color: var(--bg-primary-dark);
|
||||||
background-image:
|
background-image:
|
||||||
|
/* Bright ember-red pool, bottom-left — the operator mockup's
|
||||||
|
defining visual. High alpha against deep navy makes it glow. */
|
||||||
radial-gradient(
|
radial-gradient(
|
||||||
ellipse 70% 50% at 12% 105%,
|
ellipse 75% 55% at 8% 108%,
|
||||||
rgba(255, 100, 0, 0.32) 0%,
|
rgba(255, 75, 30, 0.55) 0%,
|
||||||
rgba(255, 128, 0, 0.16) 30%,
|
rgba(255, 110, 0, 0.30) 25%,
|
||||||
transparent 70%
|
rgba(255, 140, 30, 0.10) 55%,
|
||||||
|
transparent 78%
|
||||||
),
|
),
|
||||||
|
/* Secondary ember, bottom-right. */
|
||||||
radial-gradient(
|
radial-gradient(
|
||||||
ellipse 55% 45% at 95% 100%,
|
ellipse 60% 50% at 98% 105%,
|
||||||
rgba(255, 140, 30, 0.18) 0%,
|
rgba(255, 110, 30, 0.36) 0%,
|
||||||
transparent 70%
|
rgba(255, 140, 50, 0.12) 45%,
|
||||||
|
transparent 72%
|
||||||
),
|
),
|
||||||
|
/* Purple-magenta accent, top-right — the "cosmic" highlight from
|
||||||
|
the mockup's dark variant. Brings TCG/celestial brand energy. */
|
||||||
radial-gradient(
|
radial-gradient(
|
||||||
ellipse 55% 45% at 90% -5%,
|
ellipse 65% 50% at 95% -8%,
|
||||||
rgba(124, 58, 237, 0.14) 0%,
|
rgba(168, 85, 247, 0.26) 0%,
|
||||||
transparent 65%
|
rgba(124, 58, 237, 0.10) 40%,
|
||||||
|
transparent 68%
|
||||||
),
|
),
|
||||||
|
/* Subtle vertical wash: pure deep navy → slightly purpler bottom. */
|
||||||
linear-gradient(
|
linear-gradient(
|
||||||
180deg,
|
180deg,
|
||||||
rgba(26, 15, 10, 1) 0%,
|
rgba(13, 14, 26, 1) 0%,
|
||||||
rgba(42, 24, 16, 1) 100%
|
rgba(17, 18, 38, 1) 55%,
|
||||||
|
rgba(22, 18, 45, 1) 100%
|
||||||
);
|
);
|
||||||
background-attachment: fixed;
|
background-attachment: fixed;
|
||||||
color: var(--text-primary-dark);
|
color: var(--text-primary-dark);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue