2025-07-23 10:32:31 -04:00
|
|
|
@tailwind base;
|
|
|
|
|
@tailwind components;
|
2025-07-23 22:26:54 -04:00
|
|
|
@tailwind utilities;
|
|
|
|
|
|
|
|
|
|
/* Custom styles */
|
|
|
|
|
body {
|
|
|
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
|
|
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
|
|
|
sans-serif;
|
|
|
|
|
-webkit-font-smoothing: antialiased;
|
|
|
|
|
-moz-osx-font-smoothing: grayscale;
|
|
|
|
|
transition: background-color 0.3s ease, color 0.3s ease;
|
|
|
|
|
}
|
|
|
|
|
|
🔥 Transform to Deck Hearth Fire-Inspired Design
🎨 Complete Brand Transformation:
- Rebranded from 'TCG Vault' to 'Deck Hearth'
- Updated logo initials from 'TCG' to 'DH'
- Changed app name throughout the application
🔥 Fire-Themed Color Palette:
Light Theme (Warm Daylight by the Hearth):
- Primary: Warm cream (#fefcf8) like sunlit stone
- Secondary: Soft beige (#f7f3ed) like aged parchment
- Tertiary: Light wood tone (#f0e6d6)
- Text: Dark chocolate brown (#2d1810)
- Accents: Ember orange, flame orange, golden yellow
Dark Theme (Cozy Evening by the Fire):
- Primary: Deep charcoal (#1a0f0a) like burnt wood
- Secondary: Rich dark brown (#2d1b12) like oak
- Tertiary: Medium brown (#3d2317) like mahogany
- Text: Warm white (#fff8f0) like firelight
- Accents: Bright flame, glowing ember, golden glow
🎨 Fire-Inspired Design Elements:
- Flame gradient: Orange to ember red
- Golden gradient: Golden yellow to flame orange
- Fire glow effects with warm shadows
- Wood-toned borders and accents
- Ember and flame-colored interactive elements
🔧 Enhanced Component Styling:
- Updated BulkSelectionToolbar with fire colors
- New CSS classes: btn-flame, btn-ember, btn-gold
- Fire glow and ember glow effects
- Gradient backgrounds: gradient-bg-fire, gradient-bg-golden
- Tailwind color palette: hearth.flame, hearth.ember, etc.
✨ Warm & Inviting Experience:
- Light mode: Cozy daytime fireplace atmosphere
- Dark mode: Intimate evening hearth ambiance
- Maintains accessibility with proper contrast
- Smooth transitions between themes
The app now perfectly captures the warm, inviting feeling of gathering around a hearth! 🔥🏠✨
2025-07-26 09:30:07 -04:00
|
|
|
/* Theme Variables - Deck Hearth Fire-Inspired */
|
2025-07-23 22:26:54 -04:00
|
|
|
:root {
|
🔥 Transform to Deck Hearth Fire-Inspired Design
🎨 Complete Brand Transformation:
- Rebranded from 'TCG Vault' to 'Deck Hearth'
- Updated logo initials from 'TCG' to 'DH'
- Changed app name throughout the application
🔥 Fire-Themed Color Palette:
Light Theme (Warm Daylight by the Hearth):
- Primary: Warm cream (#fefcf8) like sunlit stone
- Secondary: Soft beige (#f7f3ed) like aged parchment
- Tertiary: Light wood tone (#f0e6d6)
- Text: Dark chocolate brown (#2d1810)
- Accents: Ember orange, flame orange, golden yellow
Dark Theme (Cozy Evening by the Fire):
- Primary: Deep charcoal (#1a0f0a) like burnt wood
- Secondary: Rich dark brown (#2d1b12) like oak
- Tertiary: Medium brown (#3d2317) like mahogany
- Text: Warm white (#fff8f0) like firelight
- Accents: Bright flame, glowing ember, golden glow
🎨 Fire-Inspired Design Elements:
- Flame gradient: Orange to ember red
- Golden gradient: Golden yellow to flame orange
- Fire glow effects with warm shadows
- Wood-toned borders and accents
- Ember and flame-colored interactive elements
🔧 Enhanced Component Styling:
- Updated BulkSelectionToolbar with fire colors
- New CSS classes: btn-flame, btn-ember, btn-gold
- Fire glow and ember glow effects
- Gradient backgrounds: gradient-bg-fire, gradient-bg-golden
- Tailwind color palette: hearth.flame, hearth.ember, etc.
✨ Warm & Inviting Experience:
- Light mode: Cozy daytime fireplace atmosphere
- Dark mode: Intimate evening hearth ambiance
- Maintains accessibility with proper contrast
- Smooth transitions between themes
The app now perfectly captures the warm, inviting feeling of gathering around a hearth! 🔥🏠✨
2025-07-26 09:30:07 -04:00
|
|
|
/* Light Theme - Warm Daylight by the Hearth */
|
|
|
|
|
--bg-primary-light: #fefcf8; /* Warm cream, like sunlit stone */
|
|
|
|
|
--bg-secondary-light: #f7f3ed; /* Soft beige, like aged parchment */
|
|
|
|
|
--bg-tertiary-light: #f0e6d6; /* Light wood tone */
|
|
|
|
|
--text-primary-light: #2d1810; /* Dark chocolate brown */
|
|
|
|
|
--text-secondary-light: #5d4037; /* Medium brown, like burnt wood */
|
|
|
|
|
--text-accent-light: #d84315; /* Ember orange */
|
|
|
|
|
--border-light: #e8dcc6; /* Light wood border */
|
|
|
|
|
--shadow-light: 0 4px 6px -1px rgba(45, 24, 16, 0.1);
|
|
|
|
|
--gradient-primary-light: linear-gradient(135deg, #ff6f00 0%, #d84315 100%); /* Fire gradient */
|
|
|
|
|
--gradient-secondary-light: linear-gradient(135deg, #ffab40 0%, #ff6f00 100%); /* Golden flame */
|
|
|
|
|
--accent-flame-light: #ff6f00; /* Bright flame orange */
|
|
|
|
|
--accent-ember-light: #d84315; /* Deep ember red */
|
|
|
|
|
--accent-gold-light: #ffab40; /* Golden flame */
|
|
|
|
|
--accent-wood-light: #8d6e63; /* Rich wood tone */
|
|
|
|
|
--input-bg-light: #fefcf8;
|
|
|
|
|
--input-border-light: #e8dcc6;
|
|
|
|
|
--input-text-light: #2d1810;
|
|
|
|
|
--input-placeholder-light: #5d4037;
|
2025-07-28 10:51:39 -04:00
|
|
|
|
|
|
|
|
/* RGB color variables for backdrop-blur effects */
|
|
|
|
|
--bg-primary-rgb: 254, 252, 248;
|
|
|
|
|
--bg-secondary-rgb: 247, 243, 237;
|
|
|
|
|
--bg-tertiary-rgb: 240, 230, 214;
|
2025-07-29 15:19:48 -04:00
|
|
|
--accent-ember-rgb: 216, 67, 21; /* RGB version of #d84315 */
|
2025-07-23 22:26:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[data-theme="dark"] {
|
feat(design-system): sub-convoy #1 — corner-ember gradient + dark default (#102)
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>
2026-06-04 11:48:49 -04:00
|
|
|
/* Dark Theme - Deep Navy Hearth Room (operator mockup, 2026-06-04)
|
|
|
|
|
The base is no longer warm-charcoal-brown — that read as "muddy"
|
|
|
|
|
and didn't let the ember corners glow. Switched to a deep cool-navy
|
|
|
|
|
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 */
|
🔥 Transform to Deck Hearth Fire-Inspired Design
🎨 Complete Brand Transformation:
- Rebranded from 'TCG Vault' to 'Deck Hearth'
- Updated logo initials from 'TCG' to 'DH'
- Changed app name throughout the application
🔥 Fire-Themed Color Palette:
Light Theme (Warm Daylight by the Hearth):
- Primary: Warm cream (#fefcf8) like sunlit stone
- Secondary: Soft beige (#f7f3ed) like aged parchment
- Tertiary: Light wood tone (#f0e6d6)
- Text: Dark chocolate brown (#2d1810)
- Accents: Ember orange, flame orange, golden yellow
Dark Theme (Cozy Evening by the Fire):
- Primary: Deep charcoal (#1a0f0a) like burnt wood
- Secondary: Rich dark brown (#2d1b12) like oak
- Tertiary: Medium brown (#3d2317) like mahogany
- Text: Warm white (#fff8f0) like firelight
- Accents: Bright flame, glowing ember, golden glow
🎨 Fire-Inspired Design Elements:
- Flame gradient: Orange to ember red
- Golden gradient: Golden yellow to flame orange
- Fire glow effects with warm shadows
- Wood-toned borders and accents
- Ember and flame-colored interactive elements
🔧 Enhanced Component Styling:
- Updated BulkSelectionToolbar with fire colors
- New CSS classes: btn-flame, btn-ember, btn-gold
- Fire glow and ember glow effects
- Gradient backgrounds: gradient-bg-fire, gradient-bg-golden
- Tailwind color palette: hearth.flame, hearth.ember, etc.
✨ Warm & Inviting Experience:
- Light mode: Cozy daytime fireplace atmosphere
- Dark mode: Intimate evening hearth ambiance
- Maintains accessibility with proper contrast
- Smooth transitions between themes
The app now perfectly captures the warm, inviting feeling of gathering around a hearth! 🔥🏠✨
2025-07-26 09:30:07 -04:00
|
|
|
--text-primary-dark: #fff8f0; /* Warm white, like firelight */
|
|
|
|
|
--text-secondary-dark: #d7c4b0; /* Soft cream, like candlelight */
|
|
|
|
|
--text-accent-dark: #ff8a50; /* Bright flame orange */
|
|
|
|
|
--border-dark: #4a2f1f; /* Dark wood border */
|
|
|
|
|
--shadow-dark: 0 4px 6px -1px rgba(26, 15, 10, 0.4);
|
|
|
|
|
--gradient-primary-dark: linear-gradient(135deg, #ff8a50 0%, #d84315 100%); /* Evening fire */
|
|
|
|
|
--gradient-secondary-dark: linear-gradient(135deg, #ffab40 0%, #ff6f00 100%); /* Golden embers */
|
|
|
|
|
--accent-flame-dark: #ff8a50; /* Bright flame */
|
|
|
|
|
--accent-ember-dark: #d84315; /* Glowing ember */
|
|
|
|
|
--accent-gold-dark: #ffab40; /* Golden glow */
|
|
|
|
|
--accent-wood-dark: #8d6e63; /* Warm wood */
|
|
|
|
|
--input-bg-dark: #2d1b12;
|
|
|
|
|
--input-border-dark: #4a2f1f;
|
|
|
|
|
--input-text-dark: #fff8f0;
|
|
|
|
|
--input-placeholder-dark: #d7c4b0;
|
|
|
|
|
--search-bg-dark: #2d1b12;
|
|
|
|
|
--search-border-dark: #4a2f1f;
|
|
|
|
|
--search-text-dark: #fff8f0;
|
|
|
|
|
--search-placeholder-dark: #d7c4b0;
|
2025-07-28 10:51:39 -04:00
|
|
|
|
|
|
|
|
/* RGB color variables for backdrop-blur effects */
|
|
|
|
|
--bg-primary-rgb: 26, 15, 10;
|
|
|
|
|
--bg-secondary-rgb: 45, 27, 18;
|
|
|
|
|
--bg-tertiary-rgb: 61, 35, 23;
|
2025-07-29 15:19:48 -04:00
|
|
|
--accent-ember-rgb: 216, 67, 21; /* RGB version of #d84315 */
|
2025-07-23 22:26:54 -04:00
|
|
|
}
|
|
|
|
|
|
feat(design-system): Liquid Glass redesign portfolio — foundation + primitives + Layout (#95)
* feat(design-system): Liquid Glass redesign portfolio — foundation + primitive kit + Layout shell
Operator-requested epic to migrate the UI from the current "warm panel + side-highlight + heavy gradient" visual language to a Liquid Glass aesthetic that retains Deck Hearth's fireplace warmth as accent / gradient / motion (not as panel fill). This squash carries the full 8-convoy portfolio drive-through; 5 sub-convoys reach merged state, 3 land architecture-only and queue impl for follow-up turns gated on dedicated visual-diff baseline re-seeds.
Sub-convoy #1 (liquid-glass-design-tokens) — MERGED. 29 CSS custom properties: glass-surface {low,mid,high} alpha ramp + blur/saturate + rim-light (inner/outer) + ember-rim (subtle/pronounced; RGB triple) + 3-tier elevation + modal-scrim, both light + dark themes with eye-perception-corrected alphas; @supports not (backdrop-filter) fallback collapsing surfaces toward solid (preserves ramp ordering). Authored docs/DESIGN_TOKENS.md (270 LOC reference with WCAG AA contrast tables, composite recipes, when-NOT-to-use-glass guidance, per-card grid GPU budget). AGENTS.md gains a § Visual language section as the new agent-contract surface.
Sub-convoy #2 (liquid-glass-modal-and-surface-primitive) — Brief 1 MERGED. Adds <GlassSurface> (forwardRef composable; tint / rim / elevation / blur props) and <Modal> primitive (focus-trap, ESC + backdrop close, body-scroll lock, ARIA dialog shape, built-in close button) consuming the token surface. lib/use-focus-trap.js — homegrown hook (~60 LOC, no dep). 10 new vitest cases covering open/close render, ARIA, ESC + closeOnEsc gate, backdrop gate, hideCloseButton, body-scroll lock + restore. 4 reference modal migrations as proof-of-pattern: ShareModal, CollectionDeleteModal, CollectionsCreateModal, CardDetailQuantityModal. Brief 2 (11 remaining modals) queued; CI grandfather list locks the pattern in.
Sub-convoy #3 (liquid-glass-form-primitives) — Brief 1 MERGED. Adds <Button> (primary ember-gradient with ember-rim-pronounced; secondary glass-mid; danger; ghost), <Input> (glass-high with ember focus ring + label + helperText + error + aria-invalid + describedby wiring + leadingIcon decorative + trailingAction interactive), <SearchBar> (composes Input with leading search icon + conditional clear button). 10 new vitest cases. pages/login.js + pages/signup.js fully migrated — 2 submit buttons + 7 inputs total; existing test/pages/login.test.js assertion ("Sign in to Deck Hearth" button text) preserved. Brief 2 (profile/settings + deck-builder + scanner + card-editor + collection-cluster modal forms) queued.
Sub-convoy #4 (liquid-glass-layout-shell) — MERGED. 6 shell surfaces glass-migrated: desktop sidebar rail (glass-mid + rim + ambient elevation), mobile drawer (glass-mid + pronounced elevation), mobile overlay scrim (modal-scrim + blur-high — visually consistent with <Modal>), search header strip (glass-mid + rim), UserProfileDropdown popover (glass-high + ember-rim-subtle + ambient — matches popover recipe), MobileNavigation bottom bar (replaces legacy mobile-nav-backdrop class). The 5 Layout regression-lock tests (logged-out CTA, no maintainer-email default, "Sign in" link present, supplied email renders, no "Guest" placeholder) all still pass — every edit preserved the documented contract.
Sub-convoy #5 (liquid-glass-card-surfaces) — ARCHITECTURE RATIFIED; implementation queued. Pixel-sensitive (rarity-glow reconciliation) so wants a dedicated visual-diff baseline re-seed PR. Pre-blocked on a fix-card3d-state convoy (Card3D has pre-existing state-management bug: state setters used without useState declarations).
Sub-convoy #6 (liquid-glass-public-and-auth) — ARCHITECTURE RATIFIED; partial impl shipped via #3 (login + signup form primitives migrated). Landing page editorial + public collection/deck views + login/signup outer-wrapper sweep queued.
Sub-convoy #7 (motion-system-pass) — MERGED. 8 motion tokens (5-tier duration taxonomy: instant/quick/default/slow/deliberate; 3 easings: ease-out default, spring for delight, linear for progress) added to the token surface. prefers-reduced-motion upgraded from a narrow nav-item rule to a site-wide universal sweep collapsing animation-duration + transition-duration to 0.01ms (preserves end states, no flicker); .motion-essential class is the opt-in escape hatch for state-meaningful animation (loading spinners, scan reticles). Authored docs/MOTION_SYSTEM.md with WCAG SC 2.3.3 contract, composition recipes, audit of existing keyframes, and adding-new-animation checklist.
Sub-convoy #8 (cleanup-legacy-design-css) — Brief 1 MERGED. Two new CI jobs in .github/workflows/ci.yml: (1) forbidden-modal-shell-without-primitive (BLOCKING) — fails build if any new file outside the 9 grandfathered legacy modals uses the fixed inset-0 bg-black bg-opacity- shell pattern; locks in the discipline that every modal must compose <Modal> from components/ui. (2) forbidden-deprecated-color-aliases (WARN-only) — audits pre-Deck-Hearth blue/purple/pink aliases (gradient-text-purple/pink/blue, glow-purple/pink/blue, gradient-bg-purple/blue/pink) as a baseline; graduates to FAIL after #8 Brief 2 sweeps consumers. .cursor/rules/ui-and-theming.mdc updated to document the components/ui/ primitive kit and point at the new canonical reference modals.
Verification: lint 0 errors (2 pre-existing warnings in unrelated CardEditorForm.js + CollectionsPageView.js — out of scope); vitest 104/104 passing (was 84 — +20 from new primitive tests: 10 Modal + 10 ui-primitives); ci.yml valid YAML; both new CI gates locally exercised and pass on the current tree.
Operator follow-ups documented in .convoys/ship-readiness.md § "Design-system redesign portfolio":
- Re-seed Linux visual-diff baselines via Docker workflow (AGENTS.md § 6) after this merges.
- preview-smoke.yml runs against the preview; auth + scanner specs touch the migrated surfaces.
- Vercel promote to production once smoke + visual gates pass.
- Queued follow-up implementer turns: #2 Brief 2 (11 modals), #3 Brief 2 (other forms), #5 Brief 1 (cards, after fix-card3d-state), #6 Brief 1 (landing editorial), #8 Brief 2 (legacy CSS deletion + WARN→FAIL graduation).
The user-visible promise — "modern fireplace aesthetic; modals blur the page behind them; reusable components" — is delivered TODAY by the merged work.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(use-focus-trap): preserve named useFocusTrap export for ScannerPageView
The portfolio squash inadvertently overwrote the pre-existing
lib/use-focus-trap.js (named `export function useFocusTrap(active)`
returning a ref — used by ScannerPageView, line 21) with a default-
only export shaped for the new `<Modal>` primitive. Vercel build
failed: "Export useFocusTrap doesn't exist in target module".
Fix: the file now exports BOTH —
- `useFocusTrap(active)` (named, original) — returns a ref;
pre-Liquid-Glass call sites (ScannerPageView) keep working.
- `useFocusTrapContainer({ active, containerRef, ... })` (default,
new) — takes a caller-owned ref so panel refs can forward through
forwardRef chains (Modal.js consumes this shape).
Both hooks are commented to document which to use when. Modal.js
imports default already, so no change needed there.
Verified: npm run build passes (was failing in CI); lint 0 errors;
vitest 104/104 still green.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 21:12:33 -04:00
|
|
|
/* ============================================================
|
|
|
|
|
Liquid Glass tokens — added by liquid-glass-design-tokens convoy
|
|
|
|
|
(2026-06-03). See docs/DESIGN_TOKENS.md for the full reference,
|
|
|
|
|
contrast tables, composite recipes, and "When NOT to use glass"
|
|
|
|
|
guidance.
|
|
|
|
|
============================================================ */
|
|
|
|
|
|
|
|
|
|
:root {
|
|
|
|
|
/* Glass surfaces (light) — 3-step legibility ramp.
|
|
|
|
|
low: modal panels inside a scrim, card detail, inline sub-panels.
|
|
|
|
|
mid: sidebar rail, header strip, mobile bottom-bar.
|
|
|
|
|
high: popovers, dropdowns, tooltips (can land over anything). */
|
|
|
|
|
--glass-surface-low: rgba(254, 252, 248, 0.55);
|
|
|
|
|
--glass-surface-mid: rgba(254, 252, 248, 0.68);
|
|
|
|
|
--glass-surface-high: rgba(254, 252, 248, 0.82);
|
|
|
|
|
|
|
|
|
|
/* Glass blur + saturate (theme-independent; inherited by dark). */
|
|
|
|
|
--glass-blur-low: 12px;
|
|
|
|
|
--glass-blur-mid: 20px;
|
|
|
|
|
--glass-blur-high: 32px;
|
|
|
|
|
--glass-saturate: 140%;
|
|
|
|
|
|
|
|
|
|
/* Rim-light (light) — inner highlight + outer hairline. */
|
|
|
|
|
--rim-light-inner: inset 0 1px 0 0 rgba(255, 255, 255, 0.65);
|
|
|
|
|
--rim-light-outer: 0 0 0 1px rgba(45, 24, 16, 0.08);
|
|
|
|
|
|
|
|
|
|
/* Ember rim — composable RGB triple + two preset variants.
|
|
|
|
|
Triple is theme-independent (ember orange #d84315);
|
|
|
|
|
variants differ per theme for eye-perception correction. */
|
|
|
|
|
--ember-rim-color: 216, 67, 21;
|
|
|
|
|
--ember-rim-subtle: inset 0 0 0 1px rgba(216, 67, 21, 0.35);
|
|
|
|
|
--ember-rim-pronounced:
|
|
|
|
|
inset 0 0 0 1px rgba(216, 67, 21, 0.55),
|
|
|
|
|
0 0 16px 0 rgba(216, 67, 21, 0.30);
|
|
|
|
|
|
|
|
|
|
/* Elevation (light) — warm-brown-tinted shadows. */
|
|
|
|
|
--elevation-flat: none;
|
|
|
|
|
--elevation-ambient:
|
|
|
|
|
0 4px 12px -2px rgba(45, 24, 16, 0.08),
|
|
|
|
|
0 2px 4px -1px rgba(45, 24, 16, 0.04);
|
|
|
|
|
--elevation-pronounced:
|
|
|
|
|
0 24px 48px -12px rgba(45, 24, 16, 0.20),
|
|
|
|
|
0 12px 24px -6px rgba(45, 24, 16, 0.10),
|
|
|
|
|
0 4px 8px -2px rgba(45, 24, 16, 0.06);
|
|
|
|
|
|
|
|
|
|
/* Modal scrim (light) — warm coffee-brown, NOT pure black. */
|
|
|
|
|
--modal-scrim: rgba(45, 24, 16, 0.35);
|
|
|
|
|
|
|
|
|
|
/* Motion (theme-independent). 4-tier duration taxonomy + 3 easings.
|
|
|
|
|
See docs/MOTION_SYSTEM.md for usage, examples, and the
|
|
|
|
|
prefers-reduced-motion contract. */
|
|
|
|
|
--motion-duration-instant: 0ms;
|
|
|
|
|
--motion-duration-quick: 150ms; /* hover, focus, micro-state */
|
|
|
|
|
--motion-duration-default: 250ms; /* default for most transitions */
|
|
|
|
|
--motion-duration-slow: 400ms; /* modal scale, sidebar slide */
|
|
|
|
|
--motion-duration-deliberate: 600ms; /* hero, onboarding, celebration */
|
|
|
|
|
|
|
|
|
|
--motion-ease-out: cubic-bezier(0.16, 1, 0.3, 1); /* default */
|
|
|
|
|
--motion-ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); /* delight */
|
|
|
|
|
--motion-ease-linear: linear; /* progress */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[data-theme="dark"] {
|
feat(design-system): sub-convoy #1 — corner-ember gradient + dark default (#102)
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>
2026-06-04 11:48:49 -04:00
|
|
|
/* Glass surfaces (dark) — alpha ramp over the new deep-navy base
|
|
|
|
|
(#0d0e1a from the operator mockup, 2026-06-04). RGB is updated
|
|
|
|
|
from the prior warm-charcoal (26,15,10) so glass-on-dark blends
|
|
|
|
|
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);
|
feat(design-system): Liquid Glass redesign portfolio — foundation + primitives + Layout (#95)
* feat(design-system): Liquid Glass redesign portfolio — foundation + primitive kit + Layout shell
Operator-requested epic to migrate the UI from the current "warm panel + side-highlight + heavy gradient" visual language to a Liquid Glass aesthetic that retains Deck Hearth's fireplace warmth as accent / gradient / motion (not as panel fill). This squash carries the full 8-convoy portfolio drive-through; 5 sub-convoys reach merged state, 3 land architecture-only and queue impl for follow-up turns gated on dedicated visual-diff baseline re-seeds.
Sub-convoy #1 (liquid-glass-design-tokens) — MERGED. 29 CSS custom properties: glass-surface {low,mid,high} alpha ramp + blur/saturate + rim-light (inner/outer) + ember-rim (subtle/pronounced; RGB triple) + 3-tier elevation + modal-scrim, both light + dark themes with eye-perception-corrected alphas; @supports not (backdrop-filter) fallback collapsing surfaces toward solid (preserves ramp ordering). Authored docs/DESIGN_TOKENS.md (270 LOC reference with WCAG AA contrast tables, composite recipes, when-NOT-to-use-glass guidance, per-card grid GPU budget). AGENTS.md gains a § Visual language section as the new agent-contract surface.
Sub-convoy #2 (liquid-glass-modal-and-surface-primitive) — Brief 1 MERGED. Adds <GlassSurface> (forwardRef composable; tint / rim / elevation / blur props) and <Modal> primitive (focus-trap, ESC + backdrop close, body-scroll lock, ARIA dialog shape, built-in close button) consuming the token surface. lib/use-focus-trap.js — homegrown hook (~60 LOC, no dep). 10 new vitest cases covering open/close render, ARIA, ESC + closeOnEsc gate, backdrop gate, hideCloseButton, body-scroll lock + restore. 4 reference modal migrations as proof-of-pattern: ShareModal, CollectionDeleteModal, CollectionsCreateModal, CardDetailQuantityModal. Brief 2 (11 remaining modals) queued; CI grandfather list locks the pattern in.
Sub-convoy #3 (liquid-glass-form-primitives) — Brief 1 MERGED. Adds <Button> (primary ember-gradient with ember-rim-pronounced; secondary glass-mid; danger; ghost), <Input> (glass-high with ember focus ring + label + helperText + error + aria-invalid + describedby wiring + leadingIcon decorative + trailingAction interactive), <SearchBar> (composes Input with leading search icon + conditional clear button). 10 new vitest cases. pages/login.js + pages/signup.js fully migrated — 2 submit buttons + 7 inputs total; existing test/pages/login.test.js assertion ("Sign in to Deck Hearth" button text) preserved. Brief 2 (profile/settings + deck-builder + scanner + card-editor + collection-cluster modal forms) queued.
Sub-convoy #4 (liquid-glass-layout-shell) — MERGED. 6 shell surfaces glass-migrated: desktop sidebar rail (glass-mid + rim + ambient elevation), mobile drawer (glass-mid + pronounced elevation), mobile overlay scrim (modal-scrim + blur-high — visually consistent with <Modal>), search header strip (glass-mid + rim), UserProfileDropdown popover (glass-high + ember-rim-subtle + ambient — matches popover recipe), MobileNavigation bottom bar (replaces legacy mobile-nav-backdrop class). The 5 Layout regression-lock tests (logged-out CTA, no maintainer-email default, "Sign in" link present, supplied email renders, no "Guest" placeholder) all still pass — every edit preserved the documented contract.
Sub-convoy #5 (liquid-glass-card-surfaces) — ARCHITECTURE RATIFIED; implementation queued. Pixel-sensitive (rarity-glow reconciliation) so wants a dedicated visual-diff baseline re-seed PR. Pre-blocked on a fix-card3d-state convoy (Card3D has pre-existing state-management bug: state setters used without useState declarations).
Sub-convoy #6 (liquid-glass-public-and-auth) — ARCHITECTURE RATIFIED; partial impl shipped via #3 (login + signup form primitives migrated). Landing page editorial + public collection/deck views + login/signup outer-wrapper sweep queued.
Sub-convoy #7 (motion-system-pass) — MERGED. 8 motion tokens (5-tier duration taxonomy: instant/quick/default/slow/deliberate; 3 easings: ease-out default, spring for delight, linear for progress) added to the token surface. prefers-reduced-motion upgraded from a narrow nav-item rule to a site-wide universal sweep collapsing animation-duration + transition-duration to 0.01ms (preserves end states, no flicker); .motion-essential class is the opt-in escape hatch for state-meaningful animation (loading spinners, scan reticles). Authored docs/MOTION_SYSTEM.md with WCAG SC 2.3.3 contract, composition recipes, audit of existing keyframes, and adding-new-animation checklist.
Sub-convoy #8 (cleanup-legacy-design-css) — Brief 1 MERGED. Two new CI jobs in .github/workflows/ci.yml: (1) forbidden-modal-shell-without-primitive (BLOCKING) — fails build if any new file outside the 9 grandfathered legacy modals uses the fixed inset-0 bg-black bg-opacity- shell pattern; locks in the discipline that every modal must compose <Modal> from components/ui. (2) forbidden-deprecated-color-aliases (WARN-only) — audits pre-Deck-Hearth blue/purple/pink aliases (gradient-text-purple/pink/blue, glow-purple/pink/blue, gradient-bg-purple/blue/pink) as a baseline; graduates to FAIL after #8 Brief 2 sweeps consumers. .cursor/rules/ui-and-theming.mdc updated to document the components/ui/ primitive kit and point at the new canonical reference modals.
Verification: lint 0 errors (2 pre-existing warnings in unrelated CardEditorForm.js + CollectionsPageView.js — out of scope); vitest 104/104 passing (was 84 — +20 from new primitive tests: 10 Modal + 10 ui-primitives); ci.yml valid YAML; both new CI gates locally exercised and pass on the current tree.
Operator follow-ups documented in .convoys/ship-readiness.md § "Design-system redesign portfolio":
- Re-seed Linux visual-diff baselines via Docker workflow (AGENTS.md § 6) after this merges.
- preview-smoke.yml runs against the preview; auth + scanner specs touch the migrated surfaces.
- Vercel promote to production once smoke + visual gates pass.
- Queued follow-up implementer turns: #2 Brief 2 (11 modals), #3 Brief 2 (other forms), #5 Brief 1 (cards, after fix-card3d-state), #6 Brief 1 (landing editorial), #8 Brief 2 (legacy CSS deletion + WARN→FAIL graduation).
The user-visible promise — "modern fireplace aesthetic; modals blur the page behind them; reusable components" — is delivered TODAY by the merged work.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(use-focus-trap): preserve named useFocusTrap export for ScannerPageView
The portfolio squash inadvertently overwrote the pre-existing
lib/use-focus-trap.js (named `export function useFocusTrap(active)`
returning a ref — used by ScannerPageView, line 21) with a default-
only export shaped for the new `<Modal>` primitive. Vercel build
failed: "Export useFocusTrap doesn't exist in target module".
Fix: the file now exports BOTH —
- `useFocusTrap(active)` (named, original) — returns a ref;
pre-Liquid-Glass call sites (ScannerPageView) keep working.
- `useFocusTrapContainer({ active, containerRef, ... })` (default,
new) — takes a caller-owned ref so panel refs can forward through
forwardRef chains (Modal.js consumes this shape).
Both hooks are commented to document which to use when. Modal.js
imports default already, so no change needed there.
Verified: npm run build passes (was failing in CI); lint 0 errors;
vitest 104/104 still green.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 21:12:33 -04:00
|
|
|
|
|
|
|
|
/* 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-outer: 0 0 0 1px rgba(255, 248, 240, 0.06);
|
|
|
|
|
|
|
|
|
|
/* Ember rim (dark) — alpha bumped to compensate for ember orange
|
|
|
|
|
reading less vibrant on dark backgrounds (eye-perception correction,
|
|
|
|
|
not a numerical drift). RGB triple inherits from :root. */
|
|
|
|
|
--ember-rim-subtle: inset 0 0 0 1px rgba(216, 67, 21, 0.40);
|
|
|
|
|
--ember-rim-pronounced:
|
|
|
|
|
inset 0 0 0 1px rgba(216, 67, 21, 0.65),
|
|
|
|
|
0 0 16px 0 rgba(216, 67, 21, 0.35);
|
|
|
|
|
|
|
|
|
|
/* Elevation (dark) — pure-black shadows for crisp depth against
|
|
|
|
|
the warm-charcoal floor. */
|
|
|
|
|
--elevation-ambient:
|
|
|
|
|
0 4px 12px -2px rgba(0, 0, 0, 0.40),
|
|
|
|
|
0 2px 4px -1px rgba(0, 0, 0, 0.30);
|
|
|
|
|
--elevation-pronounced:
|
|
|
|
|
0 24px 48px -12px rgba(0, 0, 0, 0.55),
|
|
|
|
|
0 12px 24px -6px rgba(0, 0, 0, 0.40),
|
|
|
|
|
0 4px 8px -2px rgba(0, 0, 0, 0.25);
|
|
|
|
|
|
|
|
|
|
/* Modal scrim (dark) — heavier black; dark theme starts dark so
|
|
|
|
|
needs more contrast to feel "behind" the modal. */
|
|
|
|
|
--modal-scrim: rgba(0, 0, 0, 0.55);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Fallback for browsers without backdrop-filter support (<3% of
|
|
|
|
|
sessions per caniuse 2026-06-03). Collapses the alpha ramp toward
|
|
|
|
|
solid so glass surfaces remain legible without the blur layer.
|
|
|
|
|
Never goes fully opaque — preserves the design's tinted-surface
|
|
|
|
|
intent and the ramp ordering. The @supports negation guards both
|
|
|
|
|
the unprefixed property AND -webkit-backdrop-filter (Safari 9-17
|
|
|
|
|
needed the prefix). */
|
|
|
|
|
@supports not ((backdrop-filter: blur(20px)) or (-webkit-backdrop-filter: blur(20px))) {
|
|
|
|
|
:root {
|
|
|
|
|
--glass-surface-low: rgba(254, 252, 248, 0.92);
|
|
|
|
|
--glass-surface-mid: rgba(254, 252, 248, 0.95);
|
|
|
|
|
--glass-surface-high: rgba(254, 252, 248, 0.98);
|
|
|
|
|
}
|
|
|
|
|
[data-theme="dark"] {
|
feat(design-system): sub-convoy #1 — corner-ember gradient + dark default (#102)
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>
2026-06-04 11:48:49 -04:00
|
|
|
--glass-surface-low: rgba(20, 22, 42, 0.92);
|
|
|
|
|
--glass-surface-mid: rgba(20, 22, 42, 0.95);
|
|
|
|
|
--glass-surface-high: rgba(20, 22, 42, 0.98);
|
feat(design-system): Liquid Glass redesign portfolio — foundation + primitives + Layout (#95)
* feat(design-system): Liquid Glass redesign portfolio — foundation + primitive kit + Layout shell
Operator-requested epic to migrate the UI from the current "warm panel + side-highlight + heavy gradient" visual language to a Liquid Glass aesthetic that retains Deck Hearth's fireplace warmth as accent / gradient / motion (not as panel fill). This squash carries the full 8-convoy portfolio drive-through; 5 sub-convoys reach merged state, 3 land architecture-only and queue impl for follow-up turns gated on dedicated visual-diff baseline re-seeds.
Sub-convoy #1 (liquid-glass-design-tokens) — MERGED. 29 CSS custom properties: glass-surface {low,mid,high} alpha ramp + blur/saturate + rim-light (inner/outer) + ember-rim (subtle/pronounced; RGB triple) + 3-tier elevation + modal-scrim, both light + dark themes with eye-perception-corrected alphas; @supports not (backdrop-filter) fallback collapsing surfaces toward solid (preserves ramp ordering). Authored docs/DESIGN_TOKENS.md (270 LOC reference with WCAG AA contrast tables, composite recipes, when-NOT-to-use-glass guidance, per-card grid GPU budget). AGENTS.md gains a § Visual language section as the new agent-contract surface.
Sub-convoy #2 (liquid-glass-modal-and-surface-primitive) — Brief 1 MERGED. Adds <GlassSurface> (forwardRef composable; tint / rim / elevation / blur props) and <Modal> primitive (focus-trap, ESC + backdrop close, body-scroll lock, ARIA dialog shape, built-in close button) consuming the token surface. lib/use-focus-trap.js — homegrown hook (~60 LOC, no dep). 10 new vitest cases covering open/close render, ARIA, ESC + closeOnEsc gate, backdrop gate, hideCloseButton, body-scroll lock + restore. 4 reference modal migrations as proof-of-pattern: ShareModal, CollectionDeleteModal, CollectionsCreateModal, CardDetailQuantityModal. Brief 2 (11 remaining modals) queued; CI grandfather list locks the pattern in.
Sub-convoy #3 (liquid-glass-form-primitives) — Brief 1 MERGED. Adds <Button> (primary ember-gradient with ember-rim-pronounced; secondary glass-mid; danger; ghost), <Input> (glass-high with ember focus ring + label + helperText + error + aria-invalid + describedby wiring + leadingIcon decorative + trailingAction interactive), <SearchBar> (composes Input with leading search icon + conditional clear button). 10 new vitest cases. pages/login.js + pages/signup.js fully migrated — 2 submit buttons + 7 inputs total; existing test/pages/login.test.js assertion ("Sign in to Deck Hearth" button text) preserved. Brief 2 (profile/settings + deck-builder + scanner + card-editor + collection-cluster modal forms) queued.
Sub-convoy #4 (liquid-glass-layout-shell) — MERGED. 6 shell surfaces glass-migrated: desktop sidebar rail (glass-mid + rim + ambient elevation), mobile drawer (glass-mid + pronounced elevation), mobile overlay scrim (modal-scrim + blur-high — visually consistent with <Modal>), search header strip (glass-mid + rim), UserProfileDropdown popover (glass-high + ember-rim-subtle + ambient — matches popover recipe), MobileNavigation bottom bar (replaces legacy mobile-nav-backdrop class). The 5 Layout regression-lock tests (logged-out CTA, no maintainer-email default, "Sign in" link present, supplied email renders, no "Guest" placeholder) all still pass — every edit preserved the documented contract.
Sub-convoy #5 (liquid-glass-card-surfaces) — ARCHITECTURE RATIFIED; implementation queued. Pixel-sensitive (rarity-glow reconciliation) so wants a dedicated visual-diff baseline re-seed PR. Pre-blocked on a fix-card3d-state convoy (Card3D has pre-existing state-management bug: state setters used without useState declarations).
Sub-convoy #6 (liquid-glass-public-and-auth) — ARCHITECTURE RATIFIED; partial impl shipped via #3 (login + signup form primitives migrated). Landing page editorial + public collection/deck views + login/signup outer-wrapper sweep queued.
Sub-convoy #7 (motion-system-pass) — MERGED. 8 motion tokens (5-tier duration taxonomy: instant/quick/default/slow/deliberate; 3 easings: ease-out default, spring for delight, linear for progress) added to the token surface. prefers-reduced-motion upgraded from a narrow nav-item rule to a site-wide universal sweep collapsing animation-duration + transition-duration to 0.01ms (preserves end states, no flicker); .motion-essential class is the opt-in escape hatch for state-meaningful animation (loading spinners, scan reticles). Authored docs/MOTION_SYSTEM.md with WCAG SC 2.3.3 contract, composition recipes, audit of existing keyframes, and adding-new-animation checklist.
Sub-convoy #8 (cleanup-legacy-design-css) — Brief 1 MERGED. Two new CI jobs in .github/workflows/ci.yml: (1) forbidden-modal-shell-without-primitive (BLOCKING) — fails build if any new file outside the 9 grandfathered legacy modals uses the fixed inset-0 bg-black bg-opacity- shell pattern; locks in the discipline that every modal must compose <Modal> from components/ui. (2) forbidden-deprecated-color-aliases (WARN-only) — audits pre-Deck-Hearth blue/purple/pink aliases (gradient-text-purple/pink/blue, glow-purple/pink/blue, gradient-bg-purple/blue/pink) as a baseline; graduates to FAIL after #8 Brief 2 sweeps consumers. .cursor/rules/ui-and-theming.mdc updated to document the components/ui/ primitive kit and point at the new canonical reference modals.
Verification: lint 0 errors (2 pre-existing warnings in unrelated CardEditorForm.js + CollectionsPageView.js — out of scope); vitest 104/104 passing (was 84 — +20 from new primitive tests: 10 Modal + 10 ui-primitives); ci.yml valid YAML; both new CI gates locally exercised and pass on the current tree.
Operator follow-ups documented in .convoys/ship-readiness.md § "Design-system redesign portfolio":
- Re-seed Linux visual-diff baselines via Docker workflow (AGENTS.md § 6) after this merges.
- preview-smoke.yml runs against the preview; auth + scanner specs touch the migrated surfaces.
- Vercel promote to production once smoke + visual gates pass.
- Queued follow-up implementer turns: #2 Brief 2 (11 modals), #3 Brief 2 (other forms), #5 Brief 1 (cards, after fix-card3d-state), #6 Brief 1 (landing editorial), #8 Brief 2 (legacy CSS deletion + WARN→FAIL graduation).
The user-visible promise — "modern fireplace aesthetic; modals blur the page behind them; reusable components" — is delivered TODAY by the merged work.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(use-focus-trap): preserve named useFocusTrap export for ScannerPageView
The portfolio squash inadvertently overwrote the pre-existing
lib/use-focus-trap.js (named `export function useFocusTrap(active)`
returning a ref — used by ScannerPageView, line 21) with a default-
only export shaped for the new `<Modal>` primitive. Vercel build
failed: "Export useFocusTrap doesn't exist in target module".
Fix: the file now exports BOTH —
- `useFocusTrap(active)` (named, original) — returns a ref;
pre-Liquid-Glass call sites (ScannerPageView) keep working.
- `useFocusTrapContainer({ active, containerRef, ... })` (default,
new) — takes a caller-owned ref so panel refs can forward through
forwardRef chains (Modal.js consumes this shape).
Both hooks are commented to document which to use when. Modal.js
imports default already, so no change needed there.
Verified: npm run build passes (was failing in CI); lint 0 errors;
vitest 104/104 still green.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 21:12:33 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
feat(design-system): hearth-gradient body bg + glass page header strips (#100)
Three prior PRs (#97 panels, #98 backdrop-filter, #99 box-shadow
composition) all landed correct CSS but the user reported the design
"still looks the same" on /my-cards and /dashboard. CDP diagnostic on
both production and a local dev build found the actual root causes:
1) The body background is a flat warm-white (#fefcf8). Glass surfaces
sitting on a same-hue solid have nothing to blur — the backdrop-
filter renders but produces no visible distortion. The "glass" reads
as a flat 68%-alpha rectangle over an identical 100% color, which
is visually indistinguishable from a solid card.
2) Pages with populated data (the user's /my-cards with 2 cards) have
ZERO .glass-panel elements after PR #97's sweep. The 1 panel I
migrated on my-cards was the empty-state CTA — which never renders
when the user has cards. Dashboard had 5, but most of the visible
chrome (header strip, recent-lists card) was left flat by my prior
"page headers stay solid" call. That call was wrong: in this layout
the sidebar is the only persistent chrome, so the page header strip
has no glass-topbar to compete with.
Fix — two changes that compound:
(A) Hearth gradient on body. Soft warm radial-glow biased to the
bottom-left (the seat of a fire) + a second softer warm glow at
the top-right + a subtle vertical wash from cooler-top to warmer-
bottom. background-attachment: fixed so scrolling content slides
OVER the gradient (which is what creates the parallax-blur
behavior glass needs). Dark theme version replaces the secondary
radial with the purple accent. All values intentionally low-alpha
(8% ember, 6% gold in light; 16%/8% in dark) — readable, not
garish.
(B) New .page-header-glass utility for the recurring page-header-
strip pattern. tint=high (it spans full content width and needs
more visual weight than .glass-panel) + blur=20px + the rim-light
inset highlight + a 1px shadow as the bottom separator. Applied
via mechanical sweep to the 6 page header strips on:
- /my-cards
- /dashboard
- /community/collections
- components/CardsPageView (used by /cards)
- components/CollectionsPageView (used by /collections)
- components/CollectionPageView (used by /collection/[id])
After this PR, /my-cards (and every other authenticated page) will
show a distinctly translucent floating header strip with a soft top
highlight against the warm hearth gradient — i.e. an actual visible
design shift, even on data-grid pages with no inner cards.
Verified locally — npm run build, lint clean, vitest 104/104.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 10:12:25 -04:00
|
|
|
/* Apply theme colors.
|
feat(design): warm↔cool gradient + edge-light response on chips (#115)
Operator feedback 2026-06-04: "increase the gradient in the
background that goes from our nice warm red to a nice purpley
blue. And then let's have the corners or the edges of our sections
respond as if those two are light sources."
Two coordinated changes that together create the "lit by two light
sources" effect.
1) Body gradient pushed harder + diagonal warm↔cool story
(styles/globals.css body { ... } + [data-theme="dark"] body)
- Dark theme:
* Warm ember pool bottom-left: alpha 0.55 → 0.72, ellipse
enlarged from 75%/55% to 95%/70%.
* Top-right corner: switched from secondary ember + dim
purple-magenta (0.26) to a unified bright purpley-blue pool
(alpha 0.58, indigo→violet ramp, 90%/70% ellipse).
* Base linear-gradient: 180deg vertical wash → 45deg diagonal
(warm aubergine bottom-left → indigo top-right) so the whole
page reads as one continuous warm↔cool dialogue, not just
two corner pools.
- Light theme:
* Top-right gold accent replaced with purpley-blue (indigo
0.42 → violet 0.20 → light violet 0.08) so both themes share
the same diagonal story.
* Bottom-left ember boosted (0.42 → 0.55, ellipse enlarged).
* Base linear-gradient: 180deg → 45deg diagonal (warm-cream
→ cool-violet).
2) Edge-light tokens on every floating chip's shadow stack
(styles/globals.css + components/Layout.js + components/ui/TopSearchBar.js)
- New design tokens in :root and [data-theme="dark"]:
--edge-light-warm (offset -X +Y → glow on the chip's
bottom-left edge, matching the warm
ember light source)
--edge-light-cool (offset +X -Y → glow on the chip's
top-right edge, matching the cool
purpley-blue light source)
Light: 0.16 warm / 0.12 cool. Dark: 0.34 warm / 0.28 cool.
- .glass-panel and .glass-panel-strong shadow stacks updated to
prepend the two edge-light tokens. Every glass surface in the
app now picks up the directional rim glows from the body
gradient without per-component changes (DailyEmberWidget,
CommandPaletteModal popover, the logged-out sidebar Support/
Theme tray, UserMenu dropdown, etc).
- components/Layout.js nav-chip and components/ui/TopSearchBar.js
inline boxShadow strings prepended with the same two tokens
(they use inline styles rather than the .glass-panel class so
they need the explicit shadow stack).
Result: the body now visibly transitions from warm ember (bottom-
left) through a neutral midpoint to purpley-blue (top-right). Every
floating chip's bottom-left and top-right edges pick up subtle
directional glow from the matching corner, reading as light wrapping
around the chip's rim from the two off-screen-ish light sources.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:23:22 -04:00
|
|
|
The hearth gradient: two diagonal light sources that the floating
|
refactor(design): move light response onto chip borders; cut directional drop shadows (#116)
Operator feedback 2026-06-04 (after PR #115's two-light-source pass):
"A lot of gradients happening, though I want the gradient in the
background to be the focus, and then the border gets a highlight,
perhaps, in a corner that's closest to the background gradients...
the borders are reflecting the color, not necessarily the drop
shadows behind the cards. In fact, you could probably reduce those
box shadows quite a bit. At least they shouldn't span the entire
edge of the card. Just a subtle bit in the corner."
PR #115's outer directional drop-shadows (`--edge-light-warm` /
`--edge-light-cool`) DID make the chips respond to the two light
sources but spanned the entire bottom-left + top-right edges, which
competed visually with the body gradient. This PR moves the
warm/cool response onto the chip's 1px BORDER, concentrated in the
two corners closest to the matching light sources, and cuts the
directional drop-shadows entirely (just rim-light-inner + a single
neutral elevation-ambient remain).
Technique: the modern gradient-border pattern.
background:
/* surface fill, clipped to padding-box */
linear-gradient(surface, surface) padding-box,
/* warm catch-light at bottom-left, border-box-clipped */
radial-gradient(at 0% 100%, warm 0%, transparent 42%) border-box,
/* cool catch-light at top-right, border-box-clipped */
radial-gradient(at 100% 0%, cool 0%, transparent 42%) border-box,
/* faint diagonal white tint for the rest of the rim */
chip-border-base border-box;
border: 1px solid transparent;
The transparent border is what exposes the border-box-clipped
gradient layers as the visible rim.
Token changes (styles/globals.css):
- Retired: --edge-light-warm, --edge-light-cool (both themes).
These were complete box-shadow declarations with offsets spanning
the full bottom-left + top-right edges of every chip.
- Added: --corner-light-warm, --corner-light-cool — plain RGBA
colors for radial-gradient consumption.
Light: 0.50 warm / 0.38 cool.
Dark: 0.68 warm / 0.58 cool (bumped so catch-lights read
against the deep substrate).
- Added: --chip-border-base — faint 135deg white tint so the rest
of the rim isn't invisible (sensible chip edge far from either
light source). Lower alpha on dark.
Application sites:
- styles/globals.css `.glass-panel` and `.glass-panel-strong` —
updated to multi-layer background + transparent border. Picks
up DailyEmberWidget, CommandPaletteModal popover, UserMenu
dropdown, the logged-out sidebar Support/Theme tray, etc.
- components/Layout.js nav-chip inline style — same treatment.
- components/ui/TopSearchBar.js inline style — same treatment.
Shadow stacks across all four sites are now just:
var(--rim-light-inner), var(--elevation-ambient)
(or elevation-pronounced for .glass-panel-strong). The body
gradient stays unchanged from PR #115 — it remains the dominant
gradient story per operator's "I want the gradient in the
background to be the focus" framing.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:31:53 -04:00
|
|
|
chrome chips visually "respond" to via the --corner-light-warm /
|
|
|
|
|
--corner-light-cool design tokens (defined further down in :root /
|
|
|
|
|
[data-theme="dark"]; consumed as radial-gradient layers on each
|
|
|
|
|
chip's BORDER, not as outer drop shadows — see `.glass-panel`).
|
feat(design): warm↔cool gradient + edge-light response on chips (#115)
Operator feedback 2026-06-04: "increase the gradient in the
background that goes from our nice warm red to a nice purpley
blue. And then let's have the corners or the edges of our sections
respond as if those two are light sources."
Two coordinated changes that together create the "lit by two light
sources" effect.
1) Body gradient pushed harder + diagonal warm↔cool story
(styles/globals.css body { ... } + [data-theme="dark"] body)
- Dark theme:
* Warm ember pool bottom-left: alpha 0.55 → 0.72, ellipse
enlarged from 75%/55% to 95%/70%.
* Top-right corner: switched from secondary ember + dim
purple-magenta (0.26) to a unified bright purpley-blue pool
(alpha 0.58, indigo→violet ramp, 90%/70% ellipse).
* Base linear-gradient: 180deg vertical wash → 45deg diagonal
(warm aubergine bottom-left → indigo top-right) so the whole
page reads as one continuous warm↔cool dialogue, not just
two corner pools.
- Light theme:
* Top-right gold accent replaced with purpley-blue (indigo
0.42 → violet 0.20 → light violet 0.08) so both themes share
the same diagonal story.
* Bottom-left ember boosted (0.42 → 0.55, ellipse enlarged).
* Base linear-gradient: 180deg → 45deg diagonal (warm-cream
→ cool-violet).
2) Edge-light tokens on every floating chip's shadow stack
(styles/globals.css + components/Layout.js + components/ui/TopSearchBar.js)
- New design tokens in :root and [data-theme="dark"]:
--edge-light-warm (offset -X +Y → glow on the chip's
bottom-left edge, matching the warm
ember light source)
--edge-light-cool (offset +X -Y → glow on the chip's
top-right edge, matching the cool
purpley-blue light source)
Light: 0.16 warm / 0.12 cool. Dark: 0.34 warm / 0.28 cool.
- .glass-panel and .glass-panel-strong shadow stacks updated to
prepend the two edge-light tokens. Every glass surface in the
app now picks up the directional rim glows from the body
gradient without per-component changes (DailyEmberWidget,
CommandPaletteModal popover, the logged-out sidebar Support/
Theme tray, UserMenu dropdown, etc).
- components/Layout.js nav-chip and components/ui/TopSearchBar.js
inline boxShadow strings prepended with the same two tokens
(they use inline styles rather than the .glass-panel class so
they need the explicit shadow stack).
Result: the body now visibly transitions from warm ember (bottom-
left) through a neutral midpoint to purpley-blue (top-right). Every
floating chip's bottom-left and top-right edges pick up subtle
directional glow from the matching corner, reading as light wrapping
around the chip's rim from the two off-screen-ish light sources.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:23:22 -04:00
|
|
|
- Warm ember light source: bottom-left of the viewport.
|
|
|
|
|
- Cool purpley-blue light source: top-right of the viewport.
|
|
|
|
|
The base linear-gradient runs along the same 45deg diagonal between
|
|
|
|
|
the two sources so the page itself reads as a single continuous
|
|
|
|
|
wash, not just two corner pools on a flat substrate. Pushed harder
|
|
|
|
|
2026-06-04 per operator feedback ("increase the gradient in the
|
|
|
|
|
background that goes from our nice warm red to a nice purpley
|
|
|
|
|
blue") — alphas roughly doubled vs the prior pass, ellipses
|
|
|
|
|
enlarged, and the top-right corner switched from gold to
|
|
|
|
|
purpley-blue on light theme so both themes carry the same warm↔
|
|
|
|
|
cool diagonal story.
|
|
|
|
|
Fixed attachment so scrolling content slides over the gradient
|
|
|
|
|
rather than pulling it along — that's what gives glass surfaces
|
|
|
|
|
their parallax-blur behavior. */
|
2025-07-23 22:26:54 -04:00
|
|
|
body {
|
|
|
|
|
background-color: var(--bg-primary-light);
|
feat(design-system): hearth-gradient body bg + glass page header strips (#100)
Three prior PRs (#97 panels, #98 backdrop-filter, #99 box-shadow
composition) all landed correct CSS but the user reported the design
"still looks the same" on /my-cards and /dashboard. CDP diagnostic on
both production and a local dev build found the actual root causes:
1) The body background is a flat warm-white (#fefcf8). Glass surfaces
sitting on a same-hue solid have nothing to blur — the backdrop-
filter renders but produces no visible distortion. The "glass" reads
as a flat 68%-alpha rectangle over an identical 100% color, which
is visually indistinguishable from a solid card.
2) Pages with populated data (the user's /my-cards with 2 cards) have
ZERO .glass-panel elements after PR #97's sweep. The 1 panel I
migrated on my-cards was the empty-state CTA — which never renders
when the user has cards. Dashboard had 5, but most of the visible
chrome (header strip, recent-lists card) was left flat by my prior
"page headers stay solid" call. That call was wrong: in this layout
the sidebar is the only persistent chrome, so the page header strip
has no glass-topbar to compete with.
Fix — two changes that compound:
(A) Hearth gradient on body. Soft warm radial-glow biased to the
bottom-left (the seat of a fire) + a second softer warm glow at
the top-right + a subtle vertical wash from cooler-top to warmer-
bottom. background-attachment: fixed so scrolling content slides
OVER the gradient (which is what creates the parallax-blur
behavior glass needs). Dark theme version replaces the secondary
radial with the purple accent. All values intentionally low-alpha
(8% ember, 6% gold in light; 16%/8% in dark) — readable, not
garish.
(B) New .page-header-glass utility for the recurring page-header-
strip pattern. tint=high (it spans full content width and needs
more visual weight than .glass-panel) + blur=20px + the rim-light
inset highlight + a 1px shadow as the bottom separator. Applied
via mechanical sweep to the 6 page header strips on:
- /my-cards
- /dashboard
- /community/collections
- components/CardsPageView (used by /cards)
- components/CollectionsPageView (used by /collections)
- components/CollectionPageView (used by /collection/[id])
After this PR, /my-cards (and every other authenticated page) will
show a distinctly translucent floating header strip with a soft top
highlight against the warm hearth gradient — i.e. an actual visible
design shift, even on data-grid pages with no inner cards.
Verified locally — npm run build, lint clean, vitest 104/104.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 10:12:25 -04:00
|
|
|
background-image:
|
feat(design): warm↔cool gradient + edge-light response on chips (#115)
Operator feedback 2026-06-04: "increase the gradient in the
background that goes from our nice warm red to a nice purpley
blue. And then let's have the corners or the edges of our sections
respond as if those two are light sources."
Two coordinated changes that together create the "lit by two light
sources" effect.
1) Body gradient pushed harder + diagonal warm↔cool story
(styles/globals.css body { ... } + [data-theme="dark"] body)
- Dark theme:
* Warm ember pool bottom-left: alpha 0.55 → 0.72, ellipse
enlarged from 75%/55% to 95%/70%.
* Top-right corner: switched from secondary ember + dim
purple-magenta (0.26) to a unified bright purpley-blue pool
(alpha 0.58, indigo→violet ramp, 90%/70% ellipse).
* Base linear-gradient: 180deg vertical wash → 45deg diagonal
(warm aubergine bottom-left → indigo top-right) so the whole
page reads as one continuous warm↔cool dialogue, not just
two corner pools.
- Light theme:
* Top-right gold accent replaced with purpley-blue (indigo
0.42 → violet 0.20 → light violet 0.08) so both themes share
the same diagonal story.
* Bottom-left ember boosted (0.42 → 0.55, ellipse enlarged).
* Base linear-gradient: 180deg → 45deg diagonal (warm-cream
→ cool-violet).
2) Edge-light tokens on every floating chip's shadow stack
(styles/globals.css + components/Layout.js + components/ui/TopSearchBar.js)
- New design tokens in :root and [data-theme="dark"]:
--edge-light-warm (offset -X +Y → glow on the chip's
bottom-left edge, matching the warm
ember light source)
--edge-light-cool (offset +X -Y → glow on the chip's
top-right edge, matching the cool
purpley-blue light source)
Light: 0.16 warm / 0.12 cool. Dark: 0.34 warm / 0.28 cool.
- .glass-panel and .glass-panel-strong shadow stacks updated to
prepend the two edge-light tokens. Every glass surface in the
app now picks up the directional rim glows from the body
gradient without per-component changes (DailyEmberWidget,
CommandPaletteModal popover, the logged-out sidebar Support/
Theme tray, UserMenu dropdown, etc).
- components/Layout.js nav-chip and components/ui/TopSearchBar.js
inline boxShadow strings prepended with the same two tokens
(they use inline styles rather than the .glass-panel class so
they need the explicit shadow stack).
Result: the body now visibly transitions from warm ember (bottom-
left) through a neutral midpoint to purpley-blue (top-right). Every
floating chip's bottom-left and top-right edges pick up subtle
directional glow from the matching corner, reading as light wrapping
around the chip's rim from the two off-screen-ish light sources.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:23:22 -04:00
|
|
|
/* Warm ember pool, bottom-left — primary light source. */
|
feat(design-system): hearth-gradient body bg + glass page header strips (#100)
Three prior PRs (#97 panels, #98 backdrop-filter, #99 box-shadow
composition) all landed correct CSS but the user reported the design
"still looks the same" on /my-cards and /dashboard. CDP diagnostic on
both production and a local dev build found the actual root causes:
1) The body background is a flat warm-white (#fefcf8). Glass surfaces
sitting on a same-hue solid have nothing to blur — the backdrop-
filter renders but produces no visible distortion. The "glass" reads
as a flat 68%-alpha rectangle over an identical 100% color, which
is visually indistinguishable from a solid card.
2) Pages with populated data (the user's /my-cards with 2 cards) have
ZERO .glass-panel elements after PR #97's sweep. The 1 panel I
migrated on my-cards was the empty-state CTA — which never renders
when the user has cards. Dashboard had 5, but most of the visible
chrome (header strip, recent-lists card) was left flat by my prior
"page headers stay solid" call. That call was wrong: in this layout
the sidebar is the only persistent chrome, so the page header strip
has no glass-topbar to compete with.
Fix — two changes that compound:
(A) Hearth gradient on body. Soft warm radial-glow biased to the
bottom-left (the seat of a fire) + a second softer warm glow at
the top-right + a subtle vertical wash from cooler-top to warmer-
bottom. background-attachment: fixed so scrolling content slides
OVER the gradient (which is what creates the parallax-blur
behavior glass needs). Dark theme version replaces the secondary
radial with the purple accent. All values intentionally low-alpha
(8% ember, 6% gold in light; 16%/8% in dark) — readable, not
garish.
(B) New .page-header-glass utility for the recurring page-header-
strip pattern. tint=high (it spans full content width and needs
more visual weight than .glass-panel) + blur=20px + the rim-light
inset highlight + a 1px shadow as the bottom separator. Applied
via mechanical sweep to the 6 page header strips on:
- /my-cards
- /dashboard
- /community/collections
- components/CardsPageView (used by /cards)
- components/CollectionsPageView (used by /collections)
- components/CollectionPageView (used by /collection/[id])
After this PR, /my-cards (and every other authenticated page) will
show a distinctly translucent floating header strip with a soft top
highlight against the warm hearth gradient — i.e. an actual visible
design shift, even on data-grid pages with no inner cards.
Verified locally — npm run build, lint clean, vitest 104/104.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 10:12:25 -04:00
|
|
|
radial-gradient(
|
feat(design): warm↔cool gradient + edge-light response on chips (#115)
Operator feedback 2026-06-04: "increase the gradient in the
background that goes from our nice warm red to a nice purpley
blue. And then let's have the corners or the edges of our sections
respond as if those two are light sources."
Two coordinated changes that together create the "lit by two light
sources" effect.
1) Body gradient pushed harder + diagonal warm↔cool story
(styles/globals.css body { ... } + [data-theme="dark"] body)
- Dark theme:
* Warm ember pool bottom-left: alpha 0.55 → 0.72, ellipse
enlarged from 75%/55% to 95%/70%.
* Top-right corner: switched from secondary ember + dim
purple-magenta (0.26) to a unified bright purpley-blue pool
(alpha 0.58, indigo→violet ramp, 90%/70% ellipse).
* Base linear-gradient: 180deg vertical wash → 45deg diagonal
(warm aubergine bottom-left → indigo top-right) so the whole
page reads as one continuous warm↔cool dialogue, not just
two corner pools.
- Light theme:
* Top-right gold accent replaced with purpley-blue (indigo
0.42 → violet 0.20 → light violet 0.08) so both themes share
the same diagonal story.
* Bottom-left ember boosted (0.42 → 0.55, ellipse enlarged).
* Base linear-gradient: 180deg → 45deg diagonal (warm-cream
→ cool-violet).
2) Edge-light tokens on every floating chip's shadow stack
(styles/globals.css + components/Layout.js + components/ui/TopSearchBar.js)
- New design tokens in :root and [data-theme="dark"]:
--edge-light-warm (offset -X +Y → glow on the chip's
bottom-left edge, matching the warm
ember light source)
--edge-light-cool (offset +X -Y → glow on the chip's
top-right edge, matching the cool
purpley-blue light source)
Light: 0.16 warm / 0.12 cool. Dark: 0.34 warm / 0.28 cool.
- .glass-panel and .glass-panel-strong shadow stacks updated to
prepend the two edge-light tokens. Every glass surface in the
app now picks up the directional rim glows from the body
gradient without per-component changes (DailyEmberWidget,
CommandPaletteModal popover, the logged-out sidebar Support/
Theme tray, UserMenu dropdown, etc).
- components/Layout.js nav-chip and components/ui/TopSearchBar.js
inline boxShadow strings prepended with the same two tokens
(they use inline styles rather than the .glass-panel class so
they need the explicit shadow stack).
Result: the body now visibly transitions from warm ember (bottom-
left) through a neutral midpoint to purpley-blue (top-right). Every
floating chip's bottom-left and top-right edges pick up subtle
directional glow from the matching corner, reading as light wrapping
around the chip's rim from the two off-screen-ish light sources.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:23:22 -04:00
|
|
|
ellipse 90% 65% at 5% 110%,
|
|
|
|
|
rgba(255, 95, 0, 0.55) 0%,
|
|
|
|
|
rgba(255, 130, 25, 0.28) 28%,
|
|
|
|
|
rgba(255, 171, 64, 0.10) 55%,
|
|
|
|
|
transparent 80%
|
2026-06-04 10:39:29 -04:00
|
|
|
),
|
feat(design): warm↔cool gradient + edge-light response on chips (#115)
Operator feedback 2026-06-04: "increase the gradient in the
background that goes from our nice warm red to a nice purpley
blue. And then let's have the corners or the edges of our sections
respond as if those two are light sources."
Two coordinated changes that together create the "lit by two light
sources" effect.
1) Body gradient pushed harder + diagonal warm↔cool story
(styles/globals.css body { ... } + [data-theme="dark"] body)
- Dark theme:
* Warm ember pool bottom-left: alpha 0.55 → 0.72, ellipse
enlarged from 75%/55% to 95%/70%.
* Top-right corner: switched from secondary ember + dim
purple-magenta (0.26) to a unified bright purpley-blue pool
(alpha 0.58, indigo→violet ramp, 90%/70% ellipse).
* Base linear-gradient: 180deg vertical wash → 45deg diagonal
(warm aubergine bottom-left → indigo top-right) so the whole
page reads as one continuous warm↔cool dialogue, not just
two corner pools.
- Light theme:
* Top-right gold accent replaced with purpley-blue (indigo
0.42 → violet 0.20 → light violet 0.08) so both themes share
the same diagonal story.
* Bottom-left ember boosted (0.42 → 0.55, ellipse enlarged).
* Base linear-gradient: 180deg → 45deg diagonal (warm-cream
→ cool-violet).
2) Edge-light tokens on every floating chip's shadow stack
(styles/globals.css + components/Layout.js + components/ui/TopSearchBar.js)
- New design tokens in :root and [data-theme="dark"]:
--edge-light-warm (offset -X +Y → glow on the chip's
bottom-left edge, matching the warm
ember light source)
--edge-light-cool (offset +X -Y → glow on the chip's
top-right edge, matching the cool
purpley-blue light source)
Light: 0.16 warm / 0.12 cool. Dark: 0.34 warm / 0.28 cool.
- .glass-panel and .glass-panel-strong shadow stacks updated to
prepend the two edge-light tokens. Every glass surface in the
app now picks up the directional rim glows from the body
gradient without per-component changes (DailyEmberWidget,
CommandPaletteModal popover, the logged-out sidebar Support/
Theme tray, UserMenu dropdown, etc).
- components/Layout.js nav-chip and components/ui/TopSearchBar.js
inline boxShadow strings prepended with the same two tokens
(they use inline styles rather than the .glass-panel class so
they need the explicit shadow stack).
Result: the body now visibly transitions from warm ember (bottom-
left) through a neutral midpoint to purpley-blue (top-right). Every
floating chip's bottom-left and top-right edges pick up subtle
directional glow from the matching corner, reading as light wrapping
around the chip's rim from the two off-screen-ish light sources.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:23:22 -04:00
|
|
|
/* Purpley-blue pool, top-right — secondary light source (replaces
|
|
|
|
|
the prior gold accent). Indigo→violet so the chip rims that
|
refactor(design): move light response onto chip borders; cut directional drop shadows (#116)
Operator feedback 2026-06-04 (after PR #115's two-light-source pass):
"A lot of gradients happening, though I want the gradient in the
background to be the focus, and then the border gets a highlight,
perhaps, in a corner that's closest to the background gradients...
the borders are reflecting the color, not necessarily the drop
shadows behind the cards. In fact, you could probably reduce those
box shadows quite a bit. At least they shouldn't span the entire
edge of the card. Just a subtle bit in the corner."
PR #115's outer directional drop-shadows (`--edge-light-warm` /
`--edge-light-cool`) DID make the chips respond to the two light
sources but spanned the entire bottom-left + top-right edges, which
competed visually with the body gradient. This PR moves the
warm/cool response onto the chip's 1px BORDER, concentrated in the
two corners closest to the matching light sources, and cuts the
directional drop-shadows entirely (just rim-light-inner + a single
neutral elevation-ambient remain).
Technique: the modern gradient-border pattern.
background:
/* surface fill, clipped to padding-box */
linear-gradient(surface, surface) padding-box,
/* warm catch-light at bottom-left, border-box-clipped */
radial-gradient(at 0% 100%, warm 0%, transparent 42%) border-box,
/* cool catch-light at top-right, border-box-clipped */
radial-gradient(at 100% 0%, cool 0%, transparent 42%) border-box,
/* faint diagonal white tint for the rest of the rim */
chip-border-base border-box;
border: 1px solid transparent;
The transparent border is what exposes the border-box-clipped
gradient layers as the visible rim.
Token changes (styles/globals.css):
- Retired: --edge-light-warm, --edge-light-cool (both themes).
These were complete box-shadow declarations with offsets spanning
the full bottom-left + top-right edges of every chip.
- Added: --corner-light-warm, --corner-light-cool — plain RGBA
colors for radial-gradient consumption.
Light: 0.50 warm / 0.38 cool.
Dark: 0.68 warm / 0.58 cool (bumped so catch-lights read
against the deep substrate).
- Added: --chip-border-base — faint 135deg white tint so the rest
of the rim isn't invisible (sensible chip edge far from either
light source). Lower alpha on dark.
Application sites:
- styles/globals.css `.glass-panel` and `.glass-panel-strong` —
updated to multi-layer background + transparent border. Picks
up DailyEmberWidget, CommandPaletteModal popover, UserMenu
dropdown, the logged-out sidebar Support/Theme tray, etc.
- components/Layout.js nav-chip inline style — same treatment.
- components/ui/TopSearchBar.js inline style — same treatment.
Shadow stacks across all four sites are now just:
var(--rim-light-inner), var(--elevation-ambient)
(or elevation-pronounced for .glass-panel-strong). The body
gradient stays unchanged from PR #115 — it remains the dominant
gradient story per operator's "I want the gradient in the
background to be the focus" framing.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:31:53 -04:00
|
|
|
respond to this source via --corner-light-cool read as cool. */
|
2026-06-04 10:39:29 -04:00
|
|
|
radial-gradient(
|
feat(design): warm↔cool gradient + edge-light response on chips (#115)
Operator feedback 2026-06-04: "increase the gradient in the
background that goes from our nice warm red to a nice purpley
blue. And then let's have the corners or the edges of our sections
respond as if those two are light sources."
Two coordinated changes that together create the "lit by two light
sources" effect.
1) Body gradient pushed harder + diagonal warm↔cool story
(styles/globals.css body { ... } + [data-theme="dark"] body)
- Dark theme:
* Warm ember pool bottom-left: alpha 0.55 → 0.72, ellipse
enlarged from 75%/55% to 95%/70%.
* Top-right corner: switched from secondary ember + dim
purple-magenta (0.26) to a unified bright purpley-blue pool
(alpha 0.58, indigo→violet ramp, 90%/70% ellipse).
* Base linear-gradient: 180deg vertical wash → 45deg diagonal
(warm aubergine bottom-left → indigo top-right) so the whole
page reads as one continuous warm↔cool dialogue, not just
two corner pools.
- Light theme:
* Top-right gold accent replaced with purpley-blue (indigo
0.42 → violet 0.20 → light violet 0.08) so both themes share
the same diagonal story.
* Bottom-left ember boosted (0.42 → 0.55, ellipse enlarged).
* Base linear-gradient: 180deg → 45deg diagonal (warm-cream
→ cool-violet).
2) Edge-light tokens on every floating chip's shadow stack
(styles/globals.css + components/Layout.js + components/ui/TopSearchBar.js)
- New design tokens in :root and [data-theme="dark"]:
--edge-light-warm (offset -X +Y → glow on the chip's
bottom-left edge, matching the warm
ember light source)
--edge-light-cool (offset +X -Y → glow on the chip's
top-right edge, matching the cool
purpley-blue light source)
Light: 0.16 warm / 0.12 cool. Dark: 0.34 warm / 0.28 cool.
- .glass-panel and .glass-panel-strong shadow stacks updated to
prepend the two edge-light tokens. Every glass surface in the
app now picks up the directional rim glows from the body
gradient without per-component changes (DailyEmberWidget,
CommandPaletteModal popover, the logged-out sidebar Support/
Theme tray, UserMenu dropdown, etc).
- components/Layout.js nav-chip and components/ui/TopSearchBar.js
inline boxShadow strings prepended with the same two tokens
(they use inline styles rather than the .glass-panel class so
they need the explicit shadow stack).
Result: the body now visibly transitions from warm ember (bottom-
left) through a neutral midpoint to purpley-blue (top-right). Every
floating chip's bottom-left and top-right edges pick up subtle
directional glow from the matching corner, reading as light wrapping
around the chip's rim from the two off-screen-ish light sources.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:23:22 -04:00
|
|
|
ellipse 85% 65% at 100% -8%,
|
|
|
|
|
rgba(99, 102, 241, 0.42) 0%,
|
|
|
|
|
rgba(124, 58, 237, 0.20) 30%,
|
|
|
|
|
rgba(167, 139, 250, 0.08) 58%,
|
|
|
|
|
transparent 80%
|
feat(design-system): hearth-gradient body bg + glass page header strips (#100)
Three prior PRs (#97 panels, #98 backdrop-filter, #99 box-shadow
composition) all landed correct CSS but the user reported the design
"still looks the same" on /my-cards and /dashboard. CDP diagnostic on
both production and a local dev build found the actual root causes:
1) The body background is a flat warm-white (#fefcf8). Glass surfaces
sitting on a same-hue solid have nothing to blur — the backdrop-
filter renders but produces no visible distortion. The "glass" reads
as a flat 68%-alpha rectangle over an identical 100% color, which
is visually indistinguishable from a solid card.
2) Pages with populated data (the user's /my-cards with 2 cards) have
ZERO .glass-panel elements after PR #97's sweep. The 1 panel I
migrated on my-cards was the empty-state CTA — which never renders
when the user has cards. Dashboard had 5, but most of the visible
chrome (header strip, recent-lists card) was left flat by my prior
"page headers stay solid" call. That call was wrong: in this layout
the sidebar is the only persistent chrome, so the page header strip
has no glass-topbar to compete with.
Fix — two changes that compound:
(A) Hearth gradient on body. Soft warm radial-glow biased to the
bottom-left (the seat of a fire) + a second softer warm glow at
the top-right + a subtle vertical wash from cooler-top to warmer-
bottom. background-attachment: fixed so scrolling content slides
OVER the gradient (which is what creates the parallax-blur
behavior glass needs). Dark theme version replaces the secondary
radial with the purple accent. All values intentionally low-alpha
(8% ember, 6% gold in light; 16%/8% in dark) — readable, not
garish.
(B) New .page-header-glass utility for the recurring page-header-
strip pattern. tint=high (it spans full content width and needs
more visual weight than .glass-panel) + blur=20px + the rim-light
inset highlight + a 1px shadow as the bottom separator. Applied
via mechanical sweep to the 6 page header strips on:
- /my-cards
- /dashboard
- /community/collections
- components/CardsPageView (used by /cards)
- components/CollectionsPageView (used by /collections)
- components/CollectionPageView (used by /collection/[id])
After this PR, /my-cards (and every other authenticated page) will
show a distinctly translucent floating header strip with a soft top
highlight against the warm hearth gradient — i.e. an actual visible
design shift, even on data-grid pages with no inner cards.
Verified locally — npm run build, lint clean, vitest 104/104.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 10:12:25 -04:00
|
|
|
),
|
feat(design): warm↔cool gradient + edge-light response on chips (#115)
Operator feedback 2026-06-04: "increase the gradient in the
background that goes from our nice warm red to a nice purpley
blue. And then let's have the corners or the edges of our sections
respond as if those two are light sources."
Two coordinated changes that together create the "lit by two light
sources" effect.
1) Body gradient pushed harder + diagonal warm↔cool story
(styles/globals.css body { ... } + [data-theme="dark"] body)
- Dark theme:
* Warm ember pool bottom-left: alpha 0.55 → 0.72, ellipse
enlarged from 75%/55% to 95%/70%.
* Top-right corner: switched from secondary ember + dim
purple-magenta (0.26) to a unified bright purpley-blue pool
(alpha 0.58, indigo→violet ramp, 90%/70% ellipse).
* Base linear-gradient: 180deg vertical wash → 45deg diagonal
(warm aubergine bottom-left → indigo top-right) so the whole
page reads as one continuous warm↔cool dialogue, not just
two corner pools.
- Light theme:
* Top-right gold accent replaced with purpley-blue (indigo
0.42 → violet 0.20 → light violet 0.08) so both themes share
the same diagonal story.
* Bottom-left ember boosted (0.42 → 0.55, ellipse enlarged).
* Base linear-gradient: 180deg → 45deg diagonal (warm-cream
→ cool-violet).
2) Edge-light tokens on every floating chip's shadow stack
(styles/globals.css + components/Layout.js + components/ui/TopSearchBar.js)
- New design tokens in :root and [data-theme="dark"]:
--edge-light-warm (offset -X +Y → glow on the chip's
bottom-left edge, matching the warm
ember light source)
--edge-light-cool (offset +X -Y → glow on the chip's
top-right edge, matching the cool
purpley-blue light source)
Light: 0.16 warm / 0.12 cool. Dark: 0.34 warm / 0.28 cool.
- .glass-panel and .glass-panel-strong shadow stacks updated to
prepend the two edge-light tokens. Every glass surface in the
app now picks up the directional rim glows from the body
gradient without per-component changes (DailyEmberWidget,
CommandPaletteModal popover, the logged-out sidebar Support/
Theme tray, UserMenu dropdown, etc).
- components/Layout.js nav-chip and components/ui/TopSearchBar.js
inline boxShadow strings prepended with the same two tokens
(they use inline styles rather than the .glass-panel class so
they need the explicit shadow stack).
Result: the body now visibly transitions from warm ember (bottom-
left) through a neutral midpoint to purpley-blue (top-right). Every
floating chip's bottom-left and top-right edges pick up subtle
directional glow from the matching corner, reading as light wrapping
around the chip's rim from the two off-screen-ish light sources.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:23:22 -04:00
|
|
|
/* Base diagonal wash: warm-cream bottom-left → cool-violet top-right.
|
|
|
|
|
45deg = bottom-left → top-right (matches the two light sources). */
|
feat(design-system): hearth-gradient body bg + glass page header strips (#100)
Three prior PRs (#97 panels, #98 backdrop-filter, #99 box-shadow
composition) all landed correct CSS but the user reported the design
"still looks the same" on /my-cards and /dashboard. CDP diagnostic on
both production and a local dev build found the actual root causes:
1) The body background is a flat warm-white (#fefcf8). Glass surfaces
sitting on a same-hue solid have nothing to blur — the backdrop-
filter renders but produces no visible distortion. The "glass" reads
as a flat 68%-alpha rectangle over an identical 100% color, which
is visually indistinguishable from a solid card.
2) Pages with populated data (the user's /my-cards with 2 cards) have
ZERO .glass-panel elements after PR #97's sweep. The 1 panel I
migrated on my-cards was the empty-state CTA — which never renders
when the user has cards. Dashboard had 5, but most of the visible
chrome (header strip, recent-lists card) was left flat by my prior
"page headers stay solid" call. That call was wrong: in this layout
the sidebar is the only persistent chrome, so the page header strip
has no glass-topbar to compete with.
Fix — two changes that compound:
(A) Hearth gradient on body. Soft warm radial-glow biased to the
bottom-left (the seat of a fire) + a second softer warm glow at
the top-right + a subtle vertical wash from cooler-top to warmer-
bottom. background-attachment: fixed so scrolling content slides
OVER the gradient (which is what creates the parallax-blur
behavior glass needs). Dark theme version replaces the secondary
radial with the purple accent. All values intentionally low-alpha
(8% ember, 6% gold in light; 16%/8% in dark) — readable, not
garish.
(B) New .page-header-glass utility for the recurring page-header-
strip pattern. tint=high (it spans full content width and needs
more visual weight than .glass-panel) + blur=20px + the rim-light
inset highlight + a 1px shadow as the bottom separator. Applied
via mechanical sweep to the 6 page header strips on:
- /my-cards
- /dashboard
- /community/collections
- components/CardsPageView (used by /cards)
- components/CollectionsPageView (used by /collections)
- components/CollectionPageView (used by /collection/[id])
After this PR, /my-cards (and every other authenticated page) will
show a distinctly translucent floating header strip with a soft top
highlight against the warm hearth gradient — i.e. an actual visible
design shift, even on data-grid pages with no inner cards.
Verified locally — npm run build, lint clean, vitest 104/104.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 10:12:25 -04:00
|
|
|
linear-gradient(
|
feat(design): warm↔cool gradient + edge-light response on chips (#115)
Operator feedback 2026-06-04: "increase the gradient in the
background that goes from our nice warm red to a nice purpley
blue. And then let's have the corners or the edges of our sections
respond as if those two are light sources."
Two coordinated changes that together create the "lit by two light
sources" effect.
1) Body gradient pushed harder + diagonal warm↔cool story
(styles/globals.css body { ... } + [data-theme="dark"] body)
- Dark theme:
* Warm ember pool bottom-left: alpha 0.55 → 0.72, ellipse
enlarged from 75%/55% to 95%/70%.
* Top-right corner: switched from secondary ember + dim
purple-magenta (0.26) to a unified bright purpley-blue pool
(alpha 0.58, indigo→violet ramp, 90%/70% ellipse).
* Base linear-gradient: 180deg vertical wash → 45deg diagonal
(warm aubergine bottom-left → indigo top-right) so the whole
page reads as one continuous warm↔cool dialogue, not just
two corner pools.
- Light theme:
* Top-right gold accent replaced with purpley-blue (indigo
0.42 → violet 0.20 → light violet 0.08) so both themes share
the same diagonal story.
* Bottom-left ember boosted (0.42 → 0.55, ellipse enlarged).
* Base linear-gradient: 180deg → 45deg diagonal (warm-cream
→ cool-violet).
2) Edge-light tokens on every floating chip's shadow stack
(styles/globals.css + components/Layout.js + components/ui/TopSearchBar.js)
- New design tokens in :root and [data-theme="dark"]:
--edge-light-warm (offset -X +Y → glow on the chip's
bottom-left edge, matching the warm
ember light source)
--edge-light-cool (offset +X -Y → glow on the chip's
top-right edge, matching the cool
purpley-blue light source)
Light: 0.16 warm / 0.12 cool. Dark: 0.34 warm / 0.28 cool.
- .glass-panel and .glass-panel-strong shadow stacks updated to
prepend the two edge-light tokens. Every glass surface in the
app now picks up the directional rim glows from the body
gradient without per-component changes (DailyEmberWidget,
CommandPaletteModal popover, the logged-out sidebar Support/
Theme tray, UserMenu dropdown, etc).
- components/Layout.js nav-chip and components/ui/TopSearchBar.js
inline boxShadow strings prepended with the same two tokens
(they use inline styles rather than the .glass-panel class so
they need the explicit shadow stack).
Result: the body now visibly transitions from warm ember (bottom-
left) through a neutral midpoint to purpley-blue (top-right). Every
floating chip's bottom-left and top-right edges pick up subtle
directional glow from the matching corner, reading as light wrapping
around the chip's rim from the two off-screen-ish light sources.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:23:22 -04:00
|
|
|
45deg,
|
|
|
|
|
rgba(255, 240, 220, 1) 0%,
|
|
|
|
|
rgba(252, 244, 230, 1) 50%,
|
|
|
|
|
rgba(230, 232, 252, 1) 100%
|
feat(design-system): hearth-gradient body bg + glass page header strips (#100)
Three prior PRs (#97 panels, #98 backdrop-filter, #99 box-shadow
composition) all landed correct CSS but the user reported the design
"still looks the same" on /my-cards and /dashboard. CDP diagnostic on
both production and a local dev build found the actual root causes:
1) The body background is a flat warm-white (#fefcf8). Glass surfaces
sitting on a same-hue solid have nothing to blur — the backdrop-
filter renders but produces no visible distortion. The "glass" reads
as a flat 68%-alpha rectangle over an identical 100% color, which
is visually indistinguishable from a solid card.
2) Pages with populated data (the user's /my-cards with 2 cards) have
ZERO .glass-panel elements after PR #97's sweep. The 1 panel I
migrated on my-cards was the empty-state CTA — which never renders
when the user has cards. Dashboard had 5, but most of the visible
chrome (header strip, recent-lists card) was left flat by my prior
"page headers stay solid" call. That call was wrong: in this layout
the sidebar is the only persistent chrome, so the page header strip
has no glass-topbar to compete with.
Fix — two changes that compound:
(A) Hearth gradient on body. Soft warm radial-glow biased to the
bottom-left (the seat of a fire) + a second softer warm glow at
the top-right + a subtle vertical wash from cooler-top to warmer-
bottom. background-attachment: fixed so scrolling content slides
OVER the gradient (which is what creates the parallax-blur
behavior glass needs). Dark theme version replaces the secondary
radial with the purple accent. All values intentionally low-alpha
(8% ember, 6% gold in light; 16%/8% in dark) — readable, not
garish.
(B) New .page-header-glass utility for the recurring page-header-
strip pattern. tint=high (it spans full content width and needs
more visual weight than .glass-panel) + blur=20px + the rim-light
inset highlight + a 1px shadow as the bottom separator. Applied
via mechanical sweep to the 6 page header strips on:
- /my-cards
- /dashboard
- /community/collections
- components/CardsPageView (used by /cards)
- components/CollectionsPageView (used by /collections)
- components/CollectionPageView (used by /collection/[id])
After this PR, /my-cards (and every other authenticated page) will
show a distinctly translucent floating header strip with a soft top
highlight against the warm hearth gradient — i.e. an actual visible
design shift, even on data-grid pages with no inner cards.
Verified locally — npm run build, lint clean, vitest 104/104.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 10:12:25 -04:00
|
|
|
);
|
|
|
|
|
background-attachment: fixed;
|
2025-07-23 22:26:54 -04:00
|
|
|
color: var(--text-primary-light);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[data-theme="dark"] body {
|
|
|
|
|
background-color: var(--bg-primary-dark);
|
feat(design-system): hearth-gradient body bg + glass page header strips (#100)
Three prior PRs (#97 panels, #98 backdrop-filter, #99 box-shadow
composition) all landed correct CSS but the user reported the design
"still looks the same" on /my-cards and /dashboard. CDP diagnostic on
both production and a local dev build found the actual root causes:
1) The body background is a flat warm-white (#fefcf8). Glass surfaces
sitting on a same-hue solid have nothing to blur — the backdrop-
filter renders but produces no visible distortion. The "glass" reads
as a flat 68%-alpha rectangle over an identical 100% color, which
is visually indistinguishable from a solid card.
2) Pages with populated data (the user's /my-cards with 2 cards) have
ZERO .glass-panel elements after PR #97's sweep. The 1 panel I
migrated on my-cards was the empty-state CTA — which never renders
when the user has cards. Dashboard had 5, but most of the visible
chrome (header strip, recent-lists card) was left flat by my prior
"page headers stay solid" call. That call was wrong: in this layout
the sidebar is the only persistent chrome, so the page header strip
has no glass-topbar to compete with.
Fix — two changes that compound:
(A) Hearth gradient on body. Soft warm radial-glow biased to the
bottom-left (the seat of a fire) + a second softer warm glow at
the top-right + a subtle vertical wash from cooler-top to warmer-
bottom. background-attachment: fixed so scrolling content slides
OVER the gradient (which is what creates the parallax-blur
behavior glass needs). Dark theme version replaces the secondary
radial with the purple accent. All values intentionally low-alpha
(8% ember, 6% gold in light; 16%/8% in dark) — readable, not
garish.
(B) New .page-header-glass utility for the recurring page-header-
strip pattern. tint=high (it spans full content width and needs
more visual weight than .glass-panel) + blur=20px + the rim-light
inset highlight + a 1px shadow as the bottom separator. Applied
via mechanical sweep to the 6 page header strips on:
- /my-cards
- /dashboard
- /community/collections
- components/CardsPageView (used by /cards)
- components/CollectionsPageView (used by /collections)
- components/CollectionPageView (used by /collection/[id])
After this PR, /my-cards (and every other authenticated page) will
show a distinctly translucent floating header strip with a soft top
highlight against the warm hearth gradient — i.e. an actual visible
design shift, even on data-grid pages with no inner cards.
Verified locally — npm run build, lint clean, vitest 104/104.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 10:12:25 -04:00
|
|
|
background-image:
|
feat(design): warm↔cool gradient + edge-light response on chips (#115)
Operator feedback 2026-06-04: "increase the gradient in the
background that goes from our nice warm red to a nice purpley
blue. And then let's have the corners or the edges of our sections
respond as if those two are light sources."
Two coordinated changes that together create the "lit by two light
sources" effect.
1) Body gradient pushed harder + diagonal warm↔cool story
(styles/globals.css body { ... } + [data-theme="dark"] body)
- Dark theme:
* Warm ember pool bottom-left: alpha 0.55 → 0.72, ellipse
enlarged from 75%/55% to 95%/70%.
* Top-right corner: switched from secondary ember + dim
purple-magenta (0.26) to a unified bright purpley-blue pool
(alpha 0.58, indigo→violet ramp, 90%/70% ellipse).
* Base linear-gradient: 180deg vertical wash → 45deg diagonal
(warm aubergine bottom-left → indigo top-right) so the whole
page reads as one continuous warm↔cool dialogue, not just
two corner pools.
- Light theme:
* Top-right gold accent replaced with purpley-blue (indigo
0.42 → violet 0.20 → light violet 0.08) so both themes share
the same diagonal story.
* Bottom-left ember boosted (0.42 → 0.55, ellipse enlarged).
* Base linear-gradient: 180deg → 45deg diagonal (warm-cream
→ cool-violet).
2) Edge-light tokens on every floating chip's shadow stack
(styles/globals.css + components/Layout.js + components/ui/TopSearchBar.js)
- New design tokens in :root and [data-theme="dark"]:
--edge-light-warm (offset -X +Y → glow on the chip's
bottom-left edge, matching the warm
ember light source)
--edge-light-cool (offset +X -Y → glow on the chip's
top-right edge, matching the cool
purpley-blue light source)
Light: 0.16 warm / 0.12 cool. Dark: 0.34 warm / 0.28 cool.
- .glass-panel and .glass-panel-strong shadow stacks updated to
prepend the two edge-light tokens. Every glass surface in the
app now picks up the directional rim glows from the body
gradient without per-component changes (DailyEmberWidget,
CommandPaletteModal popover, the logged-out sidebar Support/
Theme tray, UserMenu dropdown, etc).
- components/Layout.js nav-chip and components/ui/TopSearchBar.js
inline boxShadow strings prepended with the same two tokens
(they use inline styles rather than the .glass-panel class so
they need the explicit shadow stack).
Result: the body now visibly transitions from warm ember (bottom-
left) through a neutral midpoint to purpley-blue (top-right). Every
floating chip's bottom-left and top-right edges pick up subtle
directional glow from the matching corner, reading as light wrapping
around the chip's rim from the two off-screen-ish light sources.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:23:22 -04:00
|
|
|
/* Warm ember-red pool, bottom-left — primary light source.
|
|
|
|
|
Pushed to 0.72 (was 0.55) so the operator's "nice warm red"
|
|
|
|
|
reads as a real glow against deep navy. */
|
2026-06-04 10:39:29 -04:00
|
|
|
radial-gradient(
|
feat(design): warm↔cool gradient + edge-light response on chips (#115)
Operator feedback 2026-06-04: "increase the gradient in the
background that goes from our nice warm red to a nice purpley
blue. And then let's have the corners or the edges of our sections
respond as if those two are light sources."
Two coordinated changes that together create the "lit by two light
sources" effect.
1) Body gradient pushed harder + diagonal warm↔cool story
(styles/globals.css body { ... } + [data-theme="dark"] body)
- Dark theme:
* Warm ember pool bottom-left: alpha 0.55 → 0.72, ellipse
enlarged from 75%/55% to 95%/70%.
* Top-right corner: switched from secondary ember + dim
purple-magenta (0.26) to a unified bright purpley-blue pool
(alpha 0.58, indigo→violet ramp, 90%/70% ellipse).
* Base linear-gradient: 180deg vertical wash → 45deg diagonal
(warm aubergine bottom-left → indigo top-right) so the whole
page reads as one continuous warm↔cool dialogue, not just
two corner pools.
- Light theme:
* Top-right gold accent replaced with purpley-blue (indigo
0.42 → violet 0.20 → light violet 0.08) so both themes share
the same diagonal story.
* Bottom-left ember boosted (0.42 → 0.55, ellipse enlarged).
* Base linear-gradient: 180deg → 45deg diagonal (warm-cream
→ cool-violet).
2) Edge-light tokens on every floating chip's shadow stack
(styles/globals.css + components/Layout.js + components/ui/TopSearchBar.js)
- New design tokens in :root and [data-theme="dark"]:
--edge-light-warm (offset -X +Y → glow on the chip's
bottom-left edge, matching the warm
ember light source)
--edge-light-cool (offset +X -Y → glow on the chip's
top-right edge, matching the cool
purpley-blue light source)
Light: 0.16 warm / 0.12 cool. Dark: 0.34 warm / 0.28 cool.
- .glass-panel and .glass-panel-strong shadow stacks updated to
prepend the two edge-light tokens. Every glass surface in the
app now picks up the directional rim glows from the body
gradient without per-component changes (DailyEmberWidget,
CommandPaletteModal popover, the logged-out sidebar Support/
Theme tray, UserMenu dropdown, etc).
- components/Layout.js nav-chip and components/ui/TopSearchBar.js
inline boxShadow strings prepended with the same two tokens
(they use inline styles rather than the .glass-panel class so
they need the explicit shadow stack).
Result: the body now visibly transitions from warm ember (bottom-
left) through a neutral midpoint to purpley-blue (top-right). Every
floating chip's bottom-left and top-right edges pick up subtle
directional glow from the matching corner, reading as light wrapping
around the chip's rim from the two off-screen-ish light sources.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:23:22 -04:00
|
|
|
ellipse 95% 70% at 5% 110%,
|
|
|
|
|
rgba(255, 65, 25, 0.72) 0%,
|
|
|
|
|
rgba(255, 95, 0, 0.42) 24%,
|
|
|
|
|
rgba(255, 130, 30, 0.16) 52%,
|
|
|
|
|
transparent 80%
|
feat(design-system): hearth-gradient body bg + glass page header strips (#100)
Three prior PRs (#97 panels, #98 backdrop-filter, #99 box-shadow
composition) all landed correct CSS but the user reported the design
"still looks the same" on /my-cards and /dashboard. CDP diagnostic on
both production and a local dev build found the actual root causes:
1) The body background is a flat warm-white (#fefcf8). Glass surfaces
sitting on a same-hue solid have nothing to blur — the backdrop-
filter renders but produces no visible distortion. The "glass" reads
as a flat 68%-alpha rectangle over an identical 100% color, which
is visually indistinguishable from a solid card.
2) Pages with populated data (the user's /my-cards with 2 cards) have
ZERO .glass-panel elements after PR #97's sweep. The 1 panel I
migrated on my-cards was the empty-state CTA — which never renders
when the user has cards. Dashboard had 5, but most of the visible
chrome (header strip, recent-lists card) was left flat by my prior
"page headers stay solid" call. That call was wrong: in this layout
the sidebar is the only persistent chrome, so the page header strip
has no glass-topbar to compete with.
Fix — two changes that compound:
(A) Hearth gradient on body. Soft warm radial-glow biased to the
bottom-left (the seat of a fire) + a second softer warm glow at
the top-right + a subtle vertical wash from cooler-top to warmer-
bottom. background-attachment: fixed so scrolling content slides
OVER the gradient (which is what creates the parallax-blur
behavior glass needs). Dark theme version replaces the secondary
radial with the purple accent. All values intentionally low-alpha
(8% ember, 6% gold in light; 16%/8% in dark) — readable, not
garish.
(B) New .page-header-glass utility for the recurring page-header-
strip pattern. tint=high (it spans full content width and needs
more visual weight than .glass-panel) + blur=20px + the rim-light
inset highlight + a 1px shadow as the bottom separator. Applied
via mechanical sweep to the 6 page header strips on:
- /my-cards
- /dashboard
- /community/collections
- components/CardsPageView (used by /cards)
- components/CollectionsPageView (used by /collections)
- components/CollectionPageView (used by /collection/[id])
After this PR, /my-cards (and every other authenticated page) will
show a distinctly translucent floating header strip with a soft top
highlight against the warm hearth gradient — i.e. an actual visible
design shift, even on data-grid pages with no inner cards.
Verified locally — npm run build, lint clean, vitest 104/104.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 10:12:25 -04:00
|
|
|
),
|
feat(design): warm↔cool gradient + edge-light response on chips (#115)
Operator feedback 2026-06-04: "increase the gradient in the
background that goes from our nice warm red to a nice purpley
blue. And then let's have the corners or the edges of our sections
respond as if those two are light sources."
Two coordinated changes that together create the "lit by two light
sources" effect.
1) Body gradient pushed harder + diagonal warm↔cool story
(styles/globals.css body { ... } + [data-theme="dark"] body)
- Dark theme:
* Warm ember pool bottom-left: alpha 0.55 → 0.72, ellipse
enlarged from 75%/55% to 95%/70%.
* Top-right corner: switched from secondary ember + dim
purple-magenta (0.26) to a unified bright purpley-blue pool
(alpha 0.58, indigo→violet ramp, 90%/70% ellipse).
* Base linear-gradient: 180deg vertical wash → 45deg diagonal
(warm aubergine bottom-left → indigo top-right) so the whole
page reads as one continuous warm↔cool dialogue, not just
two corner pools.
- Light theme:
* Top-right gold accent replaced with purpley-blue (indigo
0.42 → violet 0.20 → light violet 0.08) so both themes share
the same diagonal story.
* Bottom-left ember boosted (0.42 → 0.55, ellipse enlarged).
* Base linear-gradient: 180deg → 45deg diagonal (warm-cream
→ cool-violet).
2) Edge-light tokens on every floating chip's shadow stack
(styles/globals.css + components/Layout.js + components/ui/TopSearchBar.js)
- New design tokens in :root and [data-theme="dark"]:
--edge-light-warm (offset -X +Y → glow on the chip's
bottom-left edge, matching the warm
ember light source)
--edge-light-cool (offset +X -Y → glow on the chip's
top-right edge, matching the cool
purpley-blue light source)
Light: 0.16 warm / 0.12 cool. Dark: 0.34 warm / 0.28 cool.
- .glass-panel and .glass-panel-strong shadow stacks updated to
prepend the two edge-light tokens. Every glass surface in the
app now picks up the directional rim glows from the body
gradient without per-component changes (DailyEmberWidget,
CommandPaletteModal popover, the logged-out sidebar Support/
Theme tray, UserMenu dropdown, etc).
- components/Layout.js nav-chip and components/ui/TopSearchBar.js
inline boxShadow strings prepended with the same two tokens
(they use inline styles rather than the .glass-panel class so
they need the explicit shadow stack).
Result: the body now visibly transitions from warm ember (bottom-
left) through a neutral midpoint to purpley-blue (top-right). Every
floating chip's bottom-left and top-right edges pick up subtle
directional glow from the matching corner, reading as light wrapping
around the chip's rim from the two off-screen-ish light sources.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:23:22 -04:00
|
|
|
/* Purpley-blue pool, top-right — secondary light source. Pushed
|
|
|
|
|
to 0.58 (was 0.26) + a more saturated indigo→violet ramp so
|
|
|
|
|
the corner reads unambiguously as the second light source the
|
|
|
|
|
chip rims respond to. */
|
feat(design-system): hearth-gradient body bg + glass page header strips (#100)
Three prior PRs (#97 panels, #98 backdrop-filter, #99 box-shadow
composition) all landed correct CSS but the user reported the design
"still looks the same" on /my-cards and /dashboard. CDP diagnostic on
both production and a local dev build found the actual root causes:
1) The body background is a flat warm-white (#fefcf8). Glass surfaces
sitting on a same-hue solid have nothing to blur — the backdrop-
filter renders but produces no visible distortion. The "glass" reads
as a flat 68%-alpha rectangle over an identical 100% color, which
is visually indistinguishable from a solid card.
2) Pages with populated data (the user's /my-cards with 2 cards) have
ZERO .glass-panel elements after PR #97's sweep. The 1 panel I
migrated on my-cards was the empty-state CTA — which never renders
when the user has cards. Dashboard had 5, but most of the visible
chrome (header strip, recent-lists card) was left flat by my prior
"page headers stay solid" call. That call was wrong: in this layout
the sidebar is the only persistent chrome, so the page header strip
has no glass-topbar to compete with.
Fix — two changes that compound:
(A) Hearth gradient on body. Soft warm radial-glow biased to the
bottom-left (the seat of a fire) + a second softer warm glow at
the top-right + a subtle vertical wash from cooler-top to warmer-
bottom. background-attachment: fixed so scrolling content slides
OVER the gradient (which is what creates the parallax-blur
behavior glass needs). Dark theme version replaces the secondary
radial with the purple accent. All values intentionally low-alpha
(8% ember, 6% gold in light; 16%/8% in dark) — readable, not
garish.
(B) New .page-header-glass utility for the recurring page-header-
strip pattern. tint=high (it spans full content width and needs
more visual weight than .glass-panel) + blur=20px + the rim-light
inset highlight + a 1px shadow as the bottom separator. Applied
via mechanical sweep to the 6 page header strips on:
- /my-cards
- /dashboard
- /community/collections
- components/CardsPageView (used by /cards)
- components/CollectionsPageView (used by /collections)
- components/CollectionPageView (used by /collection/[id])
After this PR, /my-cards (and every other authenticated page) will
show a distinctly translucent floating header strip with a soft top
highlight against the warm hearth gradient — i.e. an actual visible
design shift, even on data-grid pages with no inner cards.
Verified locally — npm run build, lint clean, vitest 104/104.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 10:12:25 -04:00
|
|
|
radial-gradient(
|
feat(design): warm↔cool gradient + edge-light response on chips (#115)
Operator feedback 2026-06-04: "increase the gradient in the
background that goes from our nice warm red to a nice purpley
blue. And then let's have the corners or the edges of our sections
respond as if those two are light sources."
Two coordinated changes that together create the "lit by two light
sources" effect.
1) Body gradient pushed harder + diagonal warm↔cool story
(styles/globals.css body { ... } + [data-theme="dark"] body)
- Dark theme:
* Warm ember pool bottom-left: alpha 0.55 → 0.72, ellipse
enlarged from 75%/55% to 95%/70%.
* Top-right corner: switched from secondary ember + dim
purple-magenta (0.26) to a unified bright purpley-blue pool
(alpha 0.58, indigo→violet ramp, 90%/70% ellipse).
* Base linear-gradient: 180deg vertical wash → 45deg diagonal
(warm aubergine bottom-left → indigo top-right) so the whole
page reads as one continuous warm↔cool dialogue, not just
two corner pools.
- Light theme:
* Top-right gold accent replaced with purpley-blue (indigo
0.42 → violet 0.20 → light violet 0.08) so both themes share
the same diagonal story.
* Bottom-left ember boosted (0.42 → 0.55, ellipse enlarged).
* Base linear-gradient: 180deg → 45deg diagonal (warm-cream
→ cool-violet).
2) Edge-light tokens on every floating chip's shadow stack
(styles/globals.css + components/Layout.js + components/ui/TopSearchBar.js)
- New design tokens in :root and [data-theme="dark"]:
--edge-light-warm (offset -X +Y → glow on the chip's
bottom-left edge, matching the warm
ember light source)
--edge-light-cool (offset +X -Y → glow on the chip's
top-right edge, matching the cool
purpley-blue light source)
Light: 0.16 warm / 0.12 cool. Dark: 0.34 warm / 0.28 cool.
- .glass-panel and .glass-panel-strong shadow stacks updated to
prepend the two edge-light tokens. Every glass surface in the
app now picks up the directional rim glows from the body
gradient without per-component changes (DailyEmberWidget,
CommandPaletteModal popover, the logged-out sidebar Support/
Theme tray, UserMenu dropdown, etc).
- components/Layout.js nav-chip and components/ui/TopSearchBar.js
inline boxShadow strings prepended with the same two tokens
(they use inline styles rather than the .glass-panel class so
they need the explicit shadow stack).
Result: the body now visibly transitions from warm ember (bottom-
left) through a neutral midpoint to purpley-blue (top-right). Every
floating chip's bottom-left and top-right edges pick up subtle
directional glow from the matching corner, reading as light wrapping
around the chip's rim from the two off-screen-ish light sources.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:23:22 -04:00
|
|
|
ellipse 90% 70% at 100% -10%,
|
|
|
|
|
rgba(139, 92, 246, 0.58) 0%,
|
|
|
|
|
rgba(99, 102, 241, 0.32) 28%,
|
|
|
|
|
rgba(168, 85, 247, 0.12) 55%,
|
|
|
|
|
transparent 82%
|
feat(design-system): hearth-gradient body bg + glass page header strips (#100)
Three prior PRs (#97 panels, #98 backdrop-filter, #99 box-shadow
composition) all landed correct CSS but the user reported the design
"still looks the same" on /my-cards and /dashboard. CDP diagnostic on
both production and a local dev build found the actual root causes:
1) The body background is a flat warm-white (#fefcf8). Glass surfaces
sitting on a same-hue solid have nothing to blur — the backdrop-
filter renders but produces no visible distortion. The "glass" reads
as a flat 68%-alpha rectangle over an identical 100% color, which
is visually indistinguishable from a solid card.
2) Pages with populated data (the user's /my-cards with 2 cards) have
ZERO .glass-panel elements after PR #97's sweep. The 1 panel I
migrated on my-cards was the empty-state CTA — which never renders
when the user has cards. Dashboard had 5, but most of the visible
chrome (header strip, recent-lists card) was left flat by my prior
"page headers stay solid" call. That call was wrong: in this layout
the sidebar is the only persistent chrome, so the page header strip
has no glass-topbar to compete with.
Fix — two changes that compound:
(A) Hearth gradient on body. Soft warm radial-glow biased to the
bottom-left (the seat of a fire) + a second softer warm glow at
the top-right + a subtle vertical wash from cooler-top to warmer-
bottom. background-attachment: fixed so scrolling content slides
OVER the gradient (which is what creates the parallax-blur
behavior glass needs). Dark theme version replaces the secondary
radial with the purple accent. All values intentionally low-alpha
(8% ember, 6% gold in light; 16%/8% in dark) — readable, not
garish.
(B) New .page-header-glass utility for the recurring page-header-
strip pattern. tint=high (it spans full content width and needs
more visual weight than .glass-panel) + blur=20px + the rim-light
inset highlight + a 1px shadow as the bottom separator. Applied
via mechanical sweep to the 6 page header strips on:
- /my-cards
- /dashboard
- /community/collections
- components/CardsPageView (used by /cards)
- components/CollectionsPageView (used by /collections)
- components/CollectionPageView (used by /collection/[id])
After this PR, /my-cards (and every other authenticated page) will
show a distinctly translucent floating header strip with a soft top
highlight against the warm hearth gradient — i.e. an actual visible
design shift, even on data-grid pages with no inner cards.
Verified locally — npm run build, lint clean, vitest 104/104.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 10:12:25 -04:00
|
|
|
),
|
feat(design): warm↔cool gradient + edge-light response on chips (#115)
Operator feedback 2026-06-04: "increase the gradient in the
background that goes from our nice warm red to a nice purpley
blue. And then let's have the corners or the edges of our sections
respond as if those two are light sources."
Two coordinated changes that together create the "lit by two light
sources" effect.
1) Body gradient pushed harder + diagonal warm↔cool story
(styles/globals.css body { ... } + [data-theme="dark"] body)
- Dark theme:
* Warm ember pool bottom-left: alpha 0.55 → 0.72, ellipse
enlarged from 75%/55% to 95%/70%.
* Top-right corner: switched from secondary ember + dim
purple-magenta (0.26) to a unified bright purpley-blue pool
(alpha 0.58, indigo→violet ramp, 90%/70% ellipse).
* Base linear-gradient: 180deg vertical wash → 45deg diagonal
(warm aubergine bottom-left → indigo top-right) so the whole
page reads as one continuous warm↔cool dialogue, not just
two corner pools.
- Light theme:
* Top-right gold accent replaced with purpley-blue (indigo
0.42 → violet 0.20 → light violet 0.08) so both themes share
the same diagonal story.
* Bottom-left ember boosted (0.42 → 0.55, ellipse enlarged).
* Base linear-gradient: 180deg → 45deg diagonal (warm-cream
→ cool-violet).
2) Edge-light tokens on every floating chip's shadow stack
(styles/globals.css + components/Layout.js + components/ui/TopSearchBar.js)
- New design tokens in :root and [data-theme="dark"]:
--edge-light-warm (offset -X +Y → glow on the chip's
bottom-left edge, matching the warm
ember light source)
--edge-light-cool (offset +X -Y → glow on the chip's
top-right edge, matching the cool
purpley-blue light source)
Light: 0.16 warm / 0.12 cool. Dark: 0.34 warm / 0.28 cool.
- .glass-panel and .glass-panel-strong shadow stacks updated to
prepend the two edge-light tokens. Every glass surface in the
app now picks up the directional rim glows from the body
gradient without per-component changes (DailyEmberWidget,
CommandPaletteModal popover, the logged-out sidebar Support/
Theme tray, UserMenu dropdown, etc).
- components/Layout.js nav-chip and components/ui/TopSearchBar.js
inline boxShadow strings prepended with the same two tokens
(they use inline styles rather than the .glass-panel class so
they need the explicit shadow stack).
Result: the body now visibly transitions from warm ember (bottom-
left) through a neutral midpoint to purpley-blue (top-right). Every
floating chip's bottom-left and top-right edges pick up subtle
directional glow from the matching corner, reading as light wrapping
around the chip's rim from the two off-screen-ish light sources.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:23:22 -04:00
|
|
|
/* Base diagonal wash: warm-tinted deep aubergine bottom-left →
|
|
|
|
|
cool-tinted deep indigo top-right. 45deg matches the light
|
|
|
|
|
sources. The midpoint stays near the previous deep-navy so
|
|
|
|
|
chrome surfaces still have a calm substrate to blur against. */
|
feat(design-system): hearth-gradient body bg + glass page header strips (#100)
Three prior PRs (#97 panels, #98 backdrop-filter, #99 box-shadow
composition) all landed correct CSS but the user reported the design
"still looks the same" on /my-cards and /dashboard. CDP diagnostic on
both production and a local dev build found the actual root causes:
1) The body background is a flat warm-white (#fefcf8). Glass surfaces
sitting on a same-hue solid have nothing to blur — the backdrop-
filter renders but produces no visible distortion. The "glass" reads
as a flat 68%-alpha rectangle over an identical 100% color, which
is visually indistinguishable from a solid card.
2) Pages with populated data (the user's /my-cards with 2 cards) have
ZERO .glass-panel elements after PR #97's sweep. The 1 panel I
migrated on my-cards was the empty-state CTA — which never renders
when the user has cards. Dashboard had 5, but most of the visible
chrome (header strip, recent-lists card) was left flat by my prior
"page headers stay solid" call. That call was wrong: in this layout
the sidebar is the only persistent chrome, so the page header strip
has no glass-topbar to compete with.
Fix — two changes that compound:
(A) Hearth gradient on body. Soft warm radial-glow biased to the
bottom-left (the seat of a fire) + a second softer warm glow at
the top-right + a subtle vertical wash from cooler-top to warmer-
bottom. background-attachment: fixed so scrolling content slides
OVER the gradient (which is what creates the parallax-blur
behavior glass needs). Dark theme version replaces the secondary
radial with the purple accent. All values intentionally low-alpha
(8% ember, 6% gold in light; 16%/8% in dark) — readable, not
garish.
(B) New .page-header-glass utility for the recurring page-header-
strip pattern. tint=high (it spans full content width and needs
more visual weight than .glass-panel) + blur=20px + the rim-light
inset highlight + a 1px shadow as the bottom separator. Applied
via mechanical sweep to the 6 page header strips on:
- /my-cards
- /dashboard
- /community/collections
- components/CardsPageView (used by /cards)
- components/CollectionsPageView (used by /collections)
- components/CollectionPageView (used by /collection/[id])
After this PR, /my-cards (and every other authenticated page) will
show a distinctly translucent floating header strip with a soft top
highlight against the warm hearth gradient — i.e. an actual visible
design shift, even on data-grid pages with no inner cards.
Verified locally — npm run build, lint clean, vitest 104/104.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 10:12:25 -04:00
|
|
|
linear-gradient(
|
feat(design): warm↔cool gradient + edge-light response on chips (#115)
Operator feedback 2026-06-04: "increase the gradient in the
background that goes from our nice warm red to a nice purpley
blue. And then let's have the corners or the edges of our sections
respond as if those two are light sources."
Two coordinated changes that together create the "lit by two light
sources" effect.
1) Body gradient pushed harder + diagonal warm↔cool story
(styles/globals.css body { ... } + [data-theme="dark"] body)
- Dark theme:
* Warm ember pool bottom-left: alpha 0.55 → 0.72, ellipse
enlarged from 75%/55% to 95%/70%.
* Top-right corner: switched from secondary ember + dim
purple-magenta (0.26) to a unified bright purpley-blue pool
(alpha 0.58, indigo→violet ramp, 90%/70% ellipse).
* Base linear-gradient: 180deg vertical wash → 45deg diagonal
(warm aubergine bottom-left → indigo top-right) so the whole
page reads as one continuous warm↔cool dialogue, not just
two corner pools.
- Light theme:
* Top-right gold accent replaced with purpley-blue (indigo
0.42 → violet 0.20 → light violet 0.08) so both themes share
the same diagonal story.
* Bottom-left ember boosted (0.42 → 0.55, ellipse enlarged).
* Base linear-gradient: 180deg → 45deg diagonal (warm-cream
→ cool-violet).
2) Edge-light tokens on every floating chip's shadow stack
(styles/globals.css + components/Layout.js + components/ui/TopSearchBar.js)
- New design tokens in :root and [data-theme="dark"]:
--edge-light-warm (offset -X +Y → glow on the chip's
bottom-left edge, matching the warm
ember light source)
--edge-light-cool (offset +X -Y → glow on the chip's
top-right edge, matching the cool
purpley-blue light source)
Light: 0.16 warm / 0.12 cool. Dark: 0.34 warm / 0.28 cool.
- .glass-panel and .glass-panel-strong shadow stacks updated to
prepend the two edge-light tokens. Every glass surface in the
app now picks up the directional rim glows from the body
gradient without per-component changes (DailyEmberWidget,
CommandPaletteModal popover, the logged-out sidebar Support/
Theme tray, UserMenu dropdown, etc).
- components/Layout.js nav-chip and components/ui/TopSearchBar.js
inline boxShadow strings prepended with the same two tokens
(they use inline styles rather than the .glass-panel class so
they need the explicit shadow stack).
Result: the body now visibly transitions from warm ember (bottom-
left) through a neutral midpoint to purpley-blue (top-right). Every
floating chip's bottom-left and top-right edges pick up subtle
directional glow from the matching corner, reading as light wrapping
around the chip's rim from the two off-screen-ish light sources.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:23:22 -04:00
|
|
|
45deg,
|
|
|
|
|
rgba(40, 16, 22, 1) 0%,
|
|
|
|
|
rgba(17, 18, 38, 1) 50%,
|
|
|
|
|
rgba(28, 22, 58, 1) 100%
|
feat(design-system): hearth-gradient body bg + glass page header strips (#100)
Three prior PRs (#97 panels, #98 backdrop-filter, #99 box-shadow
composition) all landed correct CSS but the user reported the design
"still looks the same" on /my-cards and /dashboard. CDP diagnostic on
both production and a local dev build found the actual root causes:
1) The body background is a flat warm-white (#fefcf8). Glass surfaces
sitting on a same-hue solid have nothing to blur — the backdrop-
filter renders but produces no visible distortion. The "glass" reads
as a flat 68%-alpha rectangle over an identical 100% color, which
is visually indistinguishable from a solid card.
2) Pages with populated data (the user's /my-cards with 2 cards) have
ZERO .glass-panel elements after PR #97's sweep. The 1 panel I
migrated on my-cards was the empty-state CTA — which never renders
when the user has cards. Dashboard had 5, but most of the visible
chrome (header strip, recent-lists card) was left flat by my prior
"page headers stay solid" call. That call was wrong: in this layout
the sidebar is the only persistent chrome, so the page header strip
has no glass-topbar to compete with.
Fix — two changes that compound:
(A) Hearth gradient on body. Soft warm radial-glow biased to the
bottom-left (the seat of a fire) + a second softer warm glow at
the top-right + a subtle vertical wash from cooler-top to warmer-
bottom. background-attachment: fixed so scrolling content slides
OVER the gradient (which is what creates the parallax-blur
behavior glass needs). Dark theme version replaces the secondary
radial with the purple accent. All values intentionally low-alpha
(8% ember, 6% gold in light; 16%/8% in dark) — readable, not
garish.
(B) New .page-header-glass utility for the recurring page-header-
strip pattern. tint=high (it spans full content width and needs
more visual weight than .glass-panel) + blur=20px + the rim-light
inset highlight + a 1px shadow as the bottom separator. Applied
via mechanical sweep to the 6 page header strips on:
- /my-cards
- /dashboard
- /community/collections
- components/CardsPageView (used by /cards)
- components/CollectionsPageView (used by /collections)
- components/CollectionPageView (used by /collection/[id])
After this PR, /my-cards (and every other authenticated page) will
show a distinctly translucent floating header strip with a soft top
highlight against the warm hearth gradient — i.e. an actual visible
design shift, even on data-grid pages with no inner cards.
Verified locally — npm run build, lint clean, vitest 104/104.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 10:12:25 -04:00
|
|
|
);
|
|
|
|
|
background-attachment: fixed;
|
2025-07-23 22:26:54 -04:00
|
|
|
color: var(--text-primary-dark);
|
|
|
|
|
}
|
|
|
|
|
|
feat(design-system): hearth-gradient body bg + glass page header strips (#100)
Three prior PRs (#97 panels, #98 backdrop-filter, #99 box-shadow
composition) all landed correct CSS but the user reported the design
"still looks the same" on /my-cards and /dashboard. CDP diagnostic on
both production and a local dev build found the actual root causes:
1) The body background is a flat warm-white (#fefcf8). Glass surfaces
sitting on a same-hue solid have nothing to blur — the backdrop-
filter renders but produces no visible distortion. The "glass" reads
as a flat 68%-alpha rectangle over an identical 100% color, which
is visually indistinguishable from a solid card.
2) Pages with populated data (the user's /my-cards with 2 cards) have
ZERO .glass-panel elements after PR #97's sweep. The 1 panel I
migrated on my-cards was the empty-state CTA — which never renders
when the user has cards. Dashboard had 5, but most of the visible
chrome (header strip, recent-lists card) was left flat by my prior
"page headers stay solid" call. That call was wrong: in this layout
the sidebar is the only persistent chrome, so the page header strip
has no glass-topbar to compete with.
Fix — two changes that compound:
(A) Hearth gradient on body. Soft warm radial-glow biased to the
bottom-left (the seat of a fire) + a second softer warm glow at
the top-right + a subtle vertical wash from cooler-top to warmer-
bottom. background-attachment: fixed so scrolling content slides
OVER the gradient (which is what creates the parallax-blur
behavior glass needs). Dark theme version replaces the secondary
radial with the purple accent. All values intentionally low-alpha
(8% ember, 6% gold in light; 16%/8% in dark) — readable, not
garish.
(B) New .page-header-glass utility for the recurring page-header-
strip pattern. tint=high (it spans full content width and needs
more visual weight than .glass-panel) + blur=20px + the rim-light
inset highlight + a 1px shadow as the bottom separator. Applied
via mechanical sweep to the 6 page header strips on:
- /my-cards
- /dashboard
- /community/collections
- components/CardsPageView (used by /cards)
- components/CollectionsPageView (used by /collections)
- components/CollectionPageView (used by /collection/[id])
After this PR, /my-cards (and every other authenticated page) will
show a distinctly translucent floating header strip with a soft top
highlight against the warm hearth gradient — i.e. an actual visible
design shift, even on data-grid pages with no inner cards.
Verified locally — npm run build, lint clean, vitest 104/104.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 10:12:25 -04:00
|
|
|
/* .page-header-glass — the recurring "page header strip" pattern
|
|
|
|
|
(full-bleed band at the top of authenticated pages: dashboard, my-
|
|
|
|
|
cards, cards, collections, community/collections, collection/[id],
|
|
|
|
|
scanner). Uses a higher-tint surface than .glass-panel because the
|
|
|
|
|
strip spans the whole content width and needs more visual weight,
|
|
|
|
|
but inherits the same rim + blur recipe. Pairs with `border-bottom:
|
|
|
|
|
1px solid var(--border)` for the separator line below. */
|
|
|
|
|
.page-header-glass {
|
|
|
|
|
background: var(--glass-surface-high);
|
|
|
|
|
backdrop-filter: blur(20px) saturate(180%);
|
|
|
|
|
box-shadow:
|
|
|
|
|
var(--rim-light-inner),
|
|
|
|
|
0 1px 0 var(--border);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-29 15:19:48 -04:00
|
|
|
/* Card Scanner Animations */
|
|
|
|
|
@keyframes pulse {
|
|
|
|
|
0%, 100% {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
transform: scale(1);
|
|
|
|
|
}
|
|
|
|
|
50% {
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
transform: scale(1.02);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-23 22:26:54 -04:00
|
|
|
/* Default theme variables for light theme */
|
|
|
|
|
:root {
|
|
|
|
|
--bg-primary: var(--bg-primary-light);
|
|
|
|
|
--bg-secondary: var(--bg-secondary-light);
|
|
|
|
|
--bg-tertiary: var(--bg-tertiary-light);
|
|
|
|
|
--text-primary: var(--text-primary-light);
|
|
|
|
|
--text-secondary: var(--text-secondary-light);
|
|
|
|
|
--text-accent: var(--text-accent-light);
|
|
|
|
|
--border: var(--border-light);
|
|
|
|
|
--shadow: var(--shadow-light);
|
|
|
|
|
--gradient-primary: var(--gradient-primary-light);
|
|
|
|
|
--gradient-secondary: var(--gradient-secondary-light);
|
🔥 Transform to Deck Hearth Fire-Inspired Design
🎨 Complete Brand Transformation:
- Rebranded from 'TCG Vault' to 'Deck Hearth'
- Updated logo initials from 'TCG' to 'DH'
- Changed app name throughout the application
🔥 Fire-Themed Color Palette:
Light Theme (Warm Daylight by the Hearth):
- Primary: Warm cream (#fefcf8) like sunlit stone
- Secondary: Soft beige (#f7f3ed) like aged parchment
- Tertiary: Light wood tone (#f0e6d6)
- Text: Dark chocolate brown (#2d1810)
- Accents: Ember orange, flame orange, golden yellow
Dark Theme (Cozy Evening by the Fire):
- Primary: Deep charcoal (#1a0f0a) like burnt wood
- Secondary: Rich dark brown (#2d1b12) like oak
- Tertiary: Medium brown (#3d2317) like mahogany
- Text: Warm white (#fff8f0) like firelight
- Accents: Bright flame, glowing ember, golden glow
🎨 Fire-Inspired Design Elements:
- Flame gradient: Orange to ember red
- Golden gradient: Golden yellow to flame orange
- Fire glow effects with warm shadows
- Wood-toned borders and accents
- Ember and flame-colored interactive elements
🔧 Enhanced Component Styling:
- Updated BulkSelectionToolbar with fire colors
- New CSS classes: btn-flame, btn-ember, btn-gold
- Fire glow and ember glow effects
- Gradient backgrounds: gradient-bg-fire, gradient-bg-golden
- Tailwind color palette: hearth.flame, hearth.ember, etc.
✨ Warm & Inviting Experience:
- Light mode: Cozy daytime fireplace atmosphere
- Dark mode: Intimate evening hearth ambiance
- Maintains accessibility with proper contrast
- Smooth transitions between themes
The app now perfectly captures the warm, inviting feeling of gathering around a hearth! 🔥🏠✨
2025-07-26 09:30:07 -04:00
|
|
|
--accent-flame: var(--accent-flame-light);
|
|
|
|
|
--accent-ember: var(--accent-ember-light);
|
|
|
|
|
--accent-gold: var(--accent-gold-light);
|
|
|
|
|
--accent-wood: var(--accent-wood-light);
|
|
|
|
|
/* Legacy color mappings for compatibility */
|
|
|
|
|
--accent-blue: var(--accent-flame-light);
|
|
|
|
|
--accent-purple: var(--accent-ember-light);
|
|
|
|
|
--accent-pink: var(--accent-gold-light);
|
2025-07-23 22:26:54 -04:00
|
|
|
--input-bg: var(--input-bg-light);
|
|
|
|
|
--input-border: var(--input-border-light);
|
|
|
|
|
--input-text: var(--input-text-light);
|
|
|
|
|
--input-placeholder: var(--input-placeholder-light);
|
2025-07-28 10:51:39 -04:00
|
|
|
|
|
|
|
|
/* RGB color variables for backdrop-blur effects */
|
|
|
|
|
--bg-primary-rgb: 254, 252, 248;
|
|
|
|
|
--bg-secondary-rgb: 247, 243, 237;
|
|
|
|
|
--bg-tertiary-rgb: 240, 230, 214;
|
feat(design): warm↔cool gradient + edge-light response on chips (#115)
Operator feedback 2026-06-04: "increase the gradient in the
background that goes from our nice warm red to a nice purpley
blue. And then let's have the corners or the edges of our sections
respond as if those two are light sources."
Two coordinated changes that together create the "lit by two light
sources" effect.
1) Body gradient pushed harder + diagonal warm↔cool story
(styles/globals.css body { ... } + [data-theme="dark"] body)
- Dark theme:
* Warm ember pool bottom-left: alpha 0.55 → 0.72, ellipse
enlarged from 75%/55% to 95%/70%.
* Top-right corner: switched from secondary ember + dim
purple-magenta (0.26) to a unified bright purpley-blue pool
(alpha 0.58, indigo→violet ramp, 90%/70% ellipse).
* Base linear-gradient: 180deg vertical wash → 45deg diagonal
(warm aubergine bottom-left → indigo top-right) so the whole
page reads as one continuous warm↔cool dialogue, not just
two corner pools.
- Light theme:
* Top-right gold accent replaced with purpley-blue (indigo
0.42 → violet 0.20 → light violet 0.08) so both themes share
the same diagonal story.
* Bottom-left ember boosted (0.42 → 0.55, ellipse enlarged).
* Base linear-gradient: 180deg → 45deg diagonal (warm-cream
→ cool-violet).
2) Edge-light tokens on every floating chip's shadow stack
(styles/globals.css + components/Layout.js + components/ui/TopSearchBar.js)
- New design tokens in :root and [data-theme="dark"]:
--edge-light-warm (offset -X +Y → glow on the chip's
bottom-left edge, matching the warm
ember light source)
--edge-light-cool (offset +X -Y → glow on the chip's
top-right edge, matching the cool
purpley-blue light source)
Light: 0.16 warm / 0.12 cool. Dark: 0.34 warm / 0.28 cool.
- .glass-panel and .glass-panel-strong shadow stacks updated to
prepend the two edge-light tokens. Every glass surface in the
app now picks up the directional rim glows from the body
gradient without per-component changes (DailyEmberWidget,
CommandPaletteModal popover, the logged-out sidebar Support/
Theme tray, UserMenu dropdown, etc).
- components/Layout.js nav-chip and components/ui/TopSearchBar.js
inline boxShadow strings prepended with the same two tokens
(they use inline styles rather than the .glass-panel class so
they need the explicit shadow stack).
Result: the body now visibly transitions from warm ember (bottom-
left) through a neutral midpoint to purpley-blue (top-right). Every
floating chip's bottom-left and top-right edges pick up subtle
directional glow from the matching corner, reading as light wrapping
around the chip's rim from the two off-screen-ish light sources.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:23:22 -04:00
|
|
|
|
refactor(design): move light response onto chip borders; cut directional drop shadows (#116)
Operator feedback 2026-06-04 (after PR #115's two-light-source pass):
"A lot of gradients happening, though I want the gradient in the
background to be the focus, and then the border gets a highlight,
perhaps, in a corner that's closest to the background gradients...
the borders are reflecting the color, not necessarily the drop
shadows behind the cards. In fact, you could probably reduce those
box shadows quite a bit. At least they shouldn't span the entire
edge of the card. Just a subtle bit in the corner."
PR #115's outer directional drop-shadows (`--edge-light-warm` /
`--edge-light-cool`) DID make the chips respond to the two light
sources but spanned the entire bottom-left + top-right edges, which
competed visually with the body gradient. This PR moves the
warm/cool response onto the chip's 1px BORDER, concentrated in the
two corners closest to the matching light sources, and cuts the
directional drop-shadows entirely (just rim-light-inner + a single
neutral elevation-ambient remain).
Technique: the modern gradient-border pattern.
background:
/* surface fill, clipped to padding-box */
linear-gradient(surface, surface) padding-box,
/* warm catch-light at bottom-left, border-box-clipped */
radial-gradient(at 0% 100%, warm 0%, transparent 42%) border-box,
/* cool catch-light at top-right, border-box-clipped */
radial-gradient(at 100% 0%, cool 0%, transparent 42%) border-box,
/* faint diagonal white tint for the rest of the rim */
chip-border-base border-box;
border: 1px solid transparent;
The transparent border is what exposes the border-box-clipped
gradient layers as the visible rim.
Token changes (styles/globals.css):
- Retired: --edge-light-warm, --edge-light-cool (both themes).
These were complete box-shadow declarations with offsets spanning
the full bottom-left + top-right edges of every chip.
- Added: --corner-light-warm, --corner-light-cool — plain RGBA
colors for radial-gradient consumption.
Light: 0.50 warm / 0.38 cool.
Dark: 0.68 warm / 0.58 cool (bumped so catch-lights read
against the deep substrate).
- Added: --chip-border-base — faint 135deg white tint so the rest
of the rim isn't invisible (sensible chip edge far from either
light source). Lower alpha on dark.
Application sites:
- styles/globals.css `.glass-panel` and `.glass-panel-strong` —
updated to multi-layer background + transparent border. Picks
up DailyEmberWidget, CommandPaletteModal popover, UserMenu
dropdown, the logged-out sidebar Support/Theme tray, etc.
- components/Layout.js nav-chip inline style — same treatment.
- components/ui/TopSearchBar.js inline style — same treatment.
Shadow stacks across all four sites are now just:
var(--rim-light-inner), var(--elevation-ambient)
(or elevation-pronounced for .glass-panel-strong). The body
gradient stays unchanged from PR #115 — it remains the dominant
gradient story per operator's "I want the gradient in the
background to be the focus" framing.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:31:53 -04:00
|
|
|
/* Corner-border light tokens (2026-06-04 refinement of the prior
|
|
|
|
|
two-light-source pass — operator feedback: "borders are
|
|
|
|
|
reflecting the color, not necessarily the drop shadows behind
|
|
|
|
|
the cards. In fact, you could probably reduce those box shadows
|
|
|
|
|
quite a bit. At least they shouldn't span the entire edge of
|
|
|
|
|
the card. Just a subtle bit in the corner.").
|
|
|
|
|
The prior `--edge-light-warm` / `--edge-light-cool` tokens were
|
|
|
|
|
complete box-shadow declarations with offsets that spanned the
|
|
|
|
|
entire bottom-left + top-right edges of every chip. They've
|
|
|
|
|
been retired. The replacement is a pair of plain RGBA colors
|
|
|
|
|
consumed by radial-gradient layers in the chip's BACKGROUND
|
|
|
|
|
(border-box-clipped) — see `.glass-panel` below — so the
|
|
|
|
|
warm/cool light response now reads as a corner-concentrated
|
|
|
|
|
highlight ON the chip's 1px border, not as an outer drop
|
|
|
|
|
shadow extending into the body gradient.
|
|
|
|
|
The `--chip-border-base` is a faint diagonal white tint that
|
|
|
|
|
paints under the corner gradients so the rest of the rim isn't
|
|
|
|
|
completely invisible (gives the chip a sensible edge even far
|
refactor(design): tone down card corner-light gradients to ~55% intensity (#118)
Operator feedback after the design-sweep PR went out: the corner-light
gradients on data cards (the `.glass-panel` / `.glass-panel-strong`
class) read too vibrant once the pattern was applied broadly to deck
cards, stat cards, list cards, modals, dropdowns, etc.
The same intensity worked fine on the floating chrome chips (sidebar
nav, top header) because there are only two of those per viewport and
they SHOULD anchor the page visually as the surfaces "responding" to
the background light sources. Data cards tile densely; at full
intensity they compete with the chrome and the catch-lights become
visual noise instead of an environmental cue.
Two intensity tiers now live in `styles/globals.css`:
1. `--corner-light-{warm,cool}` (full intensity, unchanged) — used
by the floating chrome chips via inline `boxShadow` in
`components/Layout.js` and `components/ui/TopSearchBar.js`.
2. `--corner-light-{warm,cool}-subtle` (NEW, ~55% of full alpha) —
used by `.glass-panel` and `.glass-panel-strong` for data cards.
Alpha values:
| Token | Light theme | Dark theme |
|----------------------------------|-------------|------------|
| `--corner-light-warm` | 0.50 | 0.68 |
| `--corner-light-cool` | 0.38 | 0.58 |
| `--corner-light-warm-subtle` | 0.28 | 0.38 |
| `--corner-light-cool-subtle` | 0.20 | 0.32 |
No other surfaces changed, no token names removed (so any custom
inline `boxShadow` styles in other components continue to compile).
The hearth body gradient + `--chip-border-base` faint white tint are
untouched.
Verified: build + lint + 113/113 vitest tests pass.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 15:06:25 -04:00
|
|
|
from either light source).
|
|
|
|
|
|
|
|
|
|
Two intensity tiers (2026-06-04 refinement, operator feedback
|
|
|
|
|
after the original tokens went out — "reduce the vibrancy of
|
|
|
|
|
the border gradients on the cards"):
|
|
|
|
|
|
|
|
|
|
1. `--corner-light-{warm,cool}` (full intensity) — used by
|
|
|
|
|
the floating chrome chips (sidebar nav, top header) where
|
|
|
|
|
the catch-light SHOULD be the primary visual cue that the
|
|
|
|
|
element is a distinct floating surface responding to the
|
|
|
|
|
background light sources. Consumed via inline `boxShadow`
|
|
|
|
|
in `components/Layout.js` and `components/ui/TopSearchBar.js`.
|
|
|
|
|
|
|
|
|
|
2. `--corner-light-{warm,cool}-subtle` — used by `.glass-panel`
|
|
|
|
|
and `.glass-panel-strong` (data cards: stat cards, deck
|
|
|
|
|
cards, list cards, modals, dropdowns). These surfaces are
|
|
|
|
|
numerous and tile densely; at full intensity the catch
|
|
|
|
|
lights compete with the chrome and overwhelm the page.
|
|
|
|
|
Roughly 55% of the full-intensity alpha. */
|
refactor(design): move light response onto chip borders; cut directional drop shadows (#116)
Operator feedback 2026-06-04 (after PR #115's two-light-source pass):
"A lot of gradients happening, though I want the gradient in the
background to be the focus, and then the border gets a highlight,
perhaps, in a corner that's closest to the background gradients...
the borders are reflecting the color, not necessarily the drop
shadows behind the cards. In fact, you could probably reduce those
box shadows quite a bit. At least they shouldn't span the entire
edge of the card. Just a subtle bit in the corner."
PR #115's outer directional drop-shadows (`--edge-light-warm` /
`--edge-light-cool`) DID make the chips respond to the two light
sources but spanned the entire bottom-left + top-right edges, which
competed visually with the body gradient. This PR moves the
warm/cool response onto the chip's 1px BORDER, concentrated in the
two corners closest to the matching light sources, and cuts the
directional drop-shadows entirely (just rim-light-inner + a single
neutral elevation-ambient remain).
Technique: the modern gradient-border pattern.
background:
/* surface fill, clipped to padding-box */
linear-gradient(surface, surface) padding-box,
/* warm catch-light at bottom-left, border-box-clipped */
radial-gradient(at 0% 100%, warm 0%, transparent 42%) border-box,
/* cool catch-light at top-right, border-box-clipped */
radial-gradient(at 100% 0%, cool 0%, transparent 42%) border-box,
/* faint diagonal white tint for the rest of the rim */
chip-border-base border-box;
border: 1px solid transparent;
The transparent border is what exposes the border-box-clipped
gradient layers as the visible rim.
Token changes (styles/globals.css):
- Retired: --edge-light-warm, --edge-light-cool (both themes).
These were complete box-shadow declarations with offsets spanning
the full bottom-left + top-right edges of every chip.
- Added: --corner-light-warm, --corner-light-cool — plain RGBA
colors for radial-gradient consumption.
Light: 0.50 warm / 0.38 cool.
Dark: 0.68 warm / 0.58 cool (bumped so catch-lights read
against the deep substrate).
- Added: --chip-border-base — faint 135deg white tint so the rest
of the rim isn't invisible (sensible chip edge far from either
light source). Lower alpha on dark.
Application sites:
- styles/globals.css `.glass-panel` and `.glass-panel-strong` —
updated to multi-layer background + transparent border. Picks
up DailyEmberWidget, CommandPaletteModal popover, UserMenu
dropdown, the logged-out sidebar Support/Theme tray, etc.
- components/Layout.js nav-chip inline style — same treatment.
- components/ui/TopSearchBar.js inline style — same treatment.
Shadow stacks across all four sites are now just:
var(--rim-light-inner), var(--elevation-ambient)
(or elevation-pronounced for .glass-panel-strong). The body
gradient stays unchanged from PR #115 — it remains the dominant
gradient story per operator's "I want the gradient in the
background to be the focus" framing.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:31:53 -04:00
|
|
|
--corner-light-warm: rgba(255, 110, 0, 0.50);
|
|
|
|
|
--corner-light-cool: rgba(99, 102, 241, 0.38);
|
refactor(design): tone down card corner-light gradients to ~55% intensity (#118)
Operator feedback after the design-sweep PR went out: the corner-light
gradients on data cards (the `.glass-panel` / `.glass-panel-strong`
class) read too vibrant once the pattern was applied broadly to deck
cards, stat cards, list cards, modals, dropdowns, etc.
The same intensity worked fine on the floating chrome chips (sidebar
nav, top header) because there are only two of those per viewport and
they SHOULD anchor the page visually as the surfaces "responding" to
the background light sources. Data cards tile densely; at full
intensity they compete with the chrome and the catch-lights become
visual noise instead of an environmental cue.
Two intensity tiers now live in `styles/globals.css`:
1. `--corner-light-{warm,cool}` (full intensity, unchanged) — used
by the floating chrome chips via inline `boxShadow` in
`components/Layout.js` and `components/ui/TopSearchBar.js`.
2. `--corner-light-{warm,cool}-subtle` (NEW, ~55% of full alpha) —
used by `.glass-panel` and `.glass-panel-strong` for data cards.
Alpha values:
| Token | Light theme | Dark theme |
|----------------------------------|-------------|------------|
| `--corner-light-warm` | 0.50 | 0.68 |
| `--corner-light-cool` | 0.38 | 0.58 |
| `--corner-light-warm-subtle` | 0.28 | 0.38 |
| `--corner-light-cool-subtle` | 0.20 | 0.32 |
No other surfaces changed, no token names removed (so any custom
inline `boxShadow` styles in other components continue to compile).
The hearth body gradient + `--chip-border-base` faint white tint are
untouched.
Verified: build + lint + 113/113 vitest tests pass.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 15:06:25 -04:00
|
|
|
--corner-light-warm-subtle: rgba(255, 110, 0, 0.28);
|
|
|
|
|
--corner-light-cool-subtle: rgba(99, 102, 241, 0.20);
|
refactor(design): move light response onto chip borders; cut directional drop shadows (#116)
Operator feedback 2026-06-04 (after PR #115's two-light-source pass):
"A lot of gradients happening, though I want the gradient in the
background to be the focus, and then the border gets a highlight,
perhaps, in a corner that's closest to the background gradients...
the borders are reflecting the color, not necessarily the drop
shadows behind the cards. In fact, you could probably reduce those
box shadows quite a bit. At least they shouldn't span the entire
edge of the card. Just a subtle bit in the corner."
PR #115's outer directional drop-shadows (`--edge-light-warm` /
`--edge-light-cool`) DID make the chips respond to the two light
sources but spanned the entire bottom-left + top-right edges, which
competed visually with the body gradient. This PR moves the
warm/cool response onto the chip's 1px BORDER, concentrated in the
two corners closest to the matching light sources, and cuts the
directional drop-shadows entirely (just rim-light-inner + a single
neutral elevation-ambient remain).
Technique: the modern gradient-border pattern.
background:
/* surface fill, clipped to padding-box */
linear-gradient(surface, surface) padding-box,
/* warm catch-light at bottom-left, border-box-clipped */
radial-gradient(at 0% 100%, warm 0%, transparent 42%) border-box,
/* cool catch-light at top-right, border-box-clipped */
radial-gradient(at 100% 0%, cool 0%, transparent 42%) border-box,
/* faint diagonal white tint for the rest of the rim */
chip-border-base border-box;
border: 1px solid transparent;
The transparent border is what exposes the border-box-clipped
gradient layers as the visible rim.
Token changes (styles/globals.css):
- Retired: --edge-light-warm, --edge-light-cool (both themes).
These were complete box-shadow declarations with offsets spanning
the full bottom-left + top-right edges of every chip.
- Added: --corner-light-warm, --corner-light-cool — plain RGBA
colors for radial-gradient consumption.
Light: 0.50 warm / 0.38 cool.
Dark: 0.68 warm / 0.58 cool (bumped so catch-lights read
against the deep substrate).
- Added: --chip-border-base — faint 135deg white tint so the rest
of the rim isn't invisible (sensible chip edge far from either
light source). Lower alpha on dark.
Application sites:
- styles/globals.css `.glass-panel` and `.glass-panel-strong` —
updated to multi-layer background + transparent border. Picks
up DailyEmberWidget, CommandPaletteModal popover, UserMenu
dropdown, the logged-out sidebar Support/Theme tray, etc.
- components/Layout.js nav-chip inline style — same treatment.
- components/ui/TopSearchBar.js inline style — same treatment.
Shadow stacks across all four sites are now just:
var(--rim-light-inner), var(--elevation-ambient)
(or elevation-pronounced for .glass-panel-strong). The body
gradient stays unchanged from PR #115 — it remains the dominant
gradient story per operator's "I want the gradient in the
background to be the focus" framing.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:31:53 -04:00
|
|
|
--chip-border-base: linear-gradient(
|
|
|
|
|
135deg,
|
|
|
|
|
rgba(255, 255, 255, 0.14) 0%,
|
|
|
|
|
rgba(255, 255, 255, 0.04) 100%
|
|
|
|
|
);
|
2025-07-23 22:26:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Dark theme variables */
|
|
|
|
|
[data-theme="dark"] {
|
|
|
|
|
--bg-primary: var(--bg-primary-dark);
|
|
|
|
|
--bg-secondary: var(--bg-secondary-dark);
|
|
|
|
|
--bg-tertiary: var(--bg-tertiary-dark);
|
|
|
|
|
--text-primary: var(--text-primary-dark);
|
|
|
|
|
--text-secondary: var(--text-secondary-dark);
|
|
|
|
|
--text-accent: var(--text-accent-dark);
|
|
|
|
|
--border: var(--border-dark);
|
|
|
|
|
--shadow: var(--shadow-dark);
|
|
|
|
|
--gradient-primary: var(--gradient-primary-dark);
|
|
|
|
|
--gradient-secondary: var(--gradient-secondary-dark);
|
🔥 Transform to Deck Hearth Fire-Inspired Design
🎨 Complete Brand Transformation:
- Rebranded from 'TCG Vault' to 'Deck Hearth'
- Updated logo initials from 'TCG' to 'DH'
- Changed app name throughout the application
🔥 Fire-Themed Color Palette:
Light Theme (Warm Daylight by the Hearth):
- Primary: Warm cream (#fefcf8) like sunlit stone
- Secondary: Soft beige (#f7f3ed) like aged parchment
- Tertiary: Light wood tone (#f0e6d6)
- Text: Dark chocolate brown (#2d1810)
- Accents: Ember orange, flame orange, golden yellow
Dark Theme (Cozy Evening by the Fire):
- Primary: Deep charcoal (#1a0f0a) like burnt wood
- Secondary: Rich dark brown (#2d1b12) like oak
- Tertiary: Medium brown (#3d2317) like mahogany
- Text: Warm white (#fff8f0) like firelight
- Accents: Bright flame, glowing ember, golden glow
🎨 Fire-Inspired Design Elements:
- Flame gradient: Orange to ember red
- Golden gradient: Golden yellow to flame orange
- Fire glow effects with warm shadows
- Wood-toned borders and accents
- Ember and flame-colored interactive elements
🔧 Enhanced Component Styling:
- Updated BulkSelectionToolbar with fire colors
- New CSS classes: btn-flame, btn-ember, btn-gold
- Fire glow and ember glow effects
- Gradient backgrounds: gradient-bg-fire, gradient-bg-golden
- Tailwind color palette: hearth.flame, hearth.ember, etc.
✨ Warm & Inviting Experience:
- Light mode: Cozy daytime fireplace atmosphere
- Dark mode: Intimate evening hearth ambiance
- Maintains accessibility with proper contrast
- Smooth transitions between themes
The app now perfectly captures the warm, inviting feeling of gathering around a hearth! 🔥🏠✨
2025-07-26 09:30:07 -04:00
|
|
|
--accent-flame: var(--accent-flame-dark);
|
|
|
|
|
--accent-ember: var(--accent-ember-dark);
|
|
|
|
|
--accent-gold: var(--accent-gold-dark);
|
|
|
|
|
--accent-wood: var(--accent-wood-dark);
|
|
|
|
|
/* Legacy color mappings for compatibility */
|
|
|
|
|
--accent-blue: var(--accent-flame-dark);
|
|
|
|
|
--accent-purple: var(--accent-ember-dark);
|
|
|
|
|
--accent-pink: var(--accent-gold-dark);
|
2025-07-23 22:26:54 -04:00
|
|
|
--input-bg: var(--input-bg-dark);
|
|
|
|
|
--input-border: var(--input-border-dark);
|
|
|
|
|
--input-text: var(--input-text-dark);
|
|
|
|
|
--input-placeholder: var(--input-placeholder-dark);
|
feat(design): warm↔cool gradient + edge-light response on chips (#115)
Operator feedback 2026-06-04: "increase the gradient in the
background that goes from our nice warm red to a nice purpley
blue. And then let's have the corners or the edges of our sections
respond as if those two are light sources."
Two coordinated changes that together create the "lit by two light
sources" effect.
1) Body gradient pushed harder + diagonal warm↔cool story
(styles/globals.css body { ... } + [data-theme="dark"] body)
- Dark theme:
* Warm ember pool bottom-left: alpha 0.55 → 0.72, ellipse
enlarged from 75%/55% to 95%/70%.
* Top-right corner: switched from secondary ember + dim
purple-magenta (0.26) to a unified bright purpley-blue pool
(alpha 0.58, indigo→violet ramp, 90%/70% ellipse).
* Base linear-gradient: 180deg vertical wash → 45deg diagonal
(warm aubergine bottom-left → indigo top-right) so the whole
page reads as one continuous warm↔cool dialogue, not just
two corner pools.
- Light theme:
* Top-right gold accent replaced with purpley-blue (indigo
0.42 → violet 0.20 → light violet 0.08) so both themes share
the same diagonal story.
* Bottom-left ember boosted (0.42 → 0.55, ellipse enlarged).
* Base linear-gradient: 180deg → 45deg diagonal (warm-cream
→ cool-violet).
2) Edge-light tokens on every floating chip's shadow stack
(styles/globals.css + components/Layout.js + components/ui/TopSearchBar.js)
- New design tokens in :root and [data-theme="dark"]:
--edge-light-warm (offset -X +Y → glow on the chip's
bottom-left edge, matching the warm
ember light source)
--edge-light-cool (offset +X -Y → glow on the chip's
top-right edge, matching the cool
purpley-blue light source)
Light: 0.16 warm / 0.12 cool. Dark: 0.34 warm / 0.28 cool.
- .glass-panel and .glass-panel-strong shadow stacks updated to
prepend the two edge-light tokens. Every glass surface in the
app now picks up the directional rim glows from the body
gradient without per-component changes (DailyEmberWidget,
CommandPaletteModal popover, the logged-out sidebar Support/
Theme tray, UserMenu dropdown, etc).
- components/Layout.js nav-chip and components/ui/TopSearchBar.js
inline boxShadow strings prepended with the same two tokens
(they use inline styles rather than the .glass-panel class so
they need the explicit shadow stack).
Result: the body now visibly transitions from warm ember (bottom-
left) through a neutral midpoint to purpley-blue (top-right). Every
floating chip's bottom-left and top-right edges pick up subtle
directional glow from the matching corner, reading as light wrapping
around the chip's rim from the two off-screen-ish light sources.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:23:22 -04:00
|
|
|
|
refactor(design): move light response onto chip borders; cut directional drop shadows (#116)
Operator feedback 2026-06-04 (after PR #115's two-light-source pass):
"A lot of gradients happening, though I want the gradient in the
background to be the focus, and then the border gets a highlight,
perhaps, in a corner that's closest to the background gradients...
the borders are reflecting the color, not necessarily the drop
shadows behind the cards. In fact, you could probably reduce those
box shadows quite a bit. At least they shouldn't span the entire
edge of the card. Just a subtle bit in the corner."
PR #115's outer directional drop-shadows (`--edge-light-warm` /
`--edge-light-cool`) DID make the chips respond to the two light
sources but spanned the entire bottom-left + top-right edges, which
competed visually with the body gradient. This PR moves the
warm/cool response onto the chip's 1px BORDER, concentrated in the
two corners closest to the matching light sources, and cuts the
directional drop-shadows entirely (just rim-light-inner + a single
neutral elevation-ambient remain).
Technique: the modern gradient-border pattern.
background:
/* surface fill, clipped to padding-box */
linear-gradient(surface, surface) padding-box,
/* warm catch-light at bottom-left, border-box-clipped */
radial-gradient(at 0% 100%, warm 0%, transparent 42%) border-box,
/* cool catch-light at top-right, border-box-clipped */
radial-gradient(at 100% 0%, cool 0%, transparent 42%) border-box,
/* faint diagonal white tint for the rest of the rim */
chip-border-base border-box;
border: 1px solid transparent;
The transparent border is what exposes the border-box-clipped
gradient layers as the visible rim.
Token changes (styles/globals.css):
- Retired: --edge-light-warm, --edge-light-cool (both themes).
These were complete box-shadow declarations with offsets spanning
the full bottom-left + top-right edges of every chip.
- Added: --corner-light-warm, --corner-light-cool — plain RGBA
colors for radial-gradient consumption.
Light: 0.50 warm / 0.38 cool.
Dark: 0.68 warm / 0.58 cool (bumped so catch-lights read
against the deep substrate).
- Added: --chip-border-base — faint 135deg white tint so the rest
of the rim isn't invisible (sensible chip edge far from either
light source). Lower alpha on dark.
Application sites:
- styles/globals.css `.glass-panel` and `.glass-panel-strong` —
updated to multi-layer background + transparent border. Picks
up DailyEmberWidget, CommandPaletteModal popover, UserMenu
dropdown, the logged-out sidebar Support/Theme tray, etc.
- components/Layout.js nav-chip inline style — same treatment.
- components/ui/TopSearchBar.js inline style — same treatment.
Shadow stacks across all four sites are now just:
var(--rim-light-inner), var(--elevation-ambient)
(or elevation-pronounced for .glass-panel-strong). The body
gradient stays unchanged from PR #115 — it remains the dominant
gradient story per operator's "I want the gradient in the
background to be the focus" framing.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:31:53 -04:00
|
|
|
/* Dark-theme corner-border colors — alphas bumped so the warm
|
|
|
|
|
and cool catch lights read clearly against the deep substrate.
|
|
|
|
|
See :root for the full rationale on the corner-vs-drop-shadow
|
|
|
|
|
refinement. The base white border tint drops in alpha here
|
refactor(design): tone down card corner-light gradients to ~55% intensity (#118)
Operator feedback after the design-sweep PR went out: the corner-light
gradients on data cards (the `.glass-panel` / `.glass-panel-strong`
class) read too vibrant once the pattern was applied broadly to deck
cards, stat cards, list cards, modals, dropdowns, etc.
The same intensity worked fine on the floating chrome chips (sidebar
nav, top header) because there are only two of those per viewport and
they SHOULD anchor the page visually as the surfaces "responding" to
the background light sources. Data cards tile densely; at full
intensity they compete with the chrome and the catch-lights become
visual noise instead of an environmental cue.
Two intensity tiers now live in `styles/globals.css`:
1. `--corner-light-{warm,cool}` (full intensity, unchanged) — used
by the floating chrome chips via inline `boxShadow` in
`components/Layout.js` and `components/ui/TopSearchBar.js`.
2. `--corner-light-{warm,cool}-subtle` (NEW, ~55% of full alpha) —
used by `.glass-panel` and `.glass-panel-strong` for data cards.
Alpha values:
| Token | Light theme | Dark theme |
|----------------------------------|-------------|------------|
| `--corner-light-warm` | 0.50 | 0.68 |
| `--corner-light-cool` | 0.38 | 0.58 |
| `--corner-light-warm-subtle` | 0.28 | 0.38 |
| `--corner-light-cool-subtle` | 0.20 | 0.32 |
No other surfaces changed, no token names removed (so any custom
inline `boxShadow` styles in other components continue to compile).
The hearth body gradient + `--chip-border-base` faint white tint are
untouched.
Verified: build + lint + 113/113 vitest tests pass.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 15:06:25 -04:00
|
|
|
because the deep canvas absorbs whites more readily.
|
|
|
|
|
|
|
|
|
|
The `-subtle` variants (used by `.glass-panel*` data cards
|
|
|
|
|
per the operator's "reduce the vibrancy" feedback) sit at
|
|
|
|
|
roughly 55% of the full-intensity alpha — slightly higher
|
|
|
|
|
proportionally than the light theme because dark substrates
|
|
|
|
|
swallow chromatic content. */
|
refactor(design): move light response onto chip borders; cut directional drop shadows (#116)
Operator feedback 2026-06-04 (after PR #115's two-light-source pass):
"A lot of gradients happening, though I want the gradient in the
background to be the focus, and then the border gets a highlight,
perhaps, in a corner that's closest to the background gradients...
the borders are reflecting the color, not necessarily the drop
shadows behind the cards. In fact, you could probably reduce those
box shadows quite a bit. At least they shouldn't span the entire
edge of the card. Just a subtle bit in the corner."
PR #115's outer directional drop-shadows (`--edge-light-warm` /
`--edge-light-cool`) DID make the chips respond to the two light
sources but spanned the entire bottom-left + top-right edges, which
competed visually with the body gradient. This PR moves the
warm/cool response onto the chip's 1px BORDER, concentrated in the
two corners closest to the matching light sources, and cuts the
directional drop-shadows entirely (just rim-light-inner + a single
neutral elevation-ambient remain).
Technique: the modern gradient-border pattern.
background:
/* surface fill, clipped to padding-box */
linear-gradient(surface, surface) padding-box,
/* warm catch-light at bottom-left, border-box-clipped */
radial-gradient(at 0% 100%, warm 0%, transparent 42%) border-box,
/* cool catch-light at top-right, border-box-clipped */
radial-gradient(at 100% 0%, cool 0%, transparent 42%) border-box,
/* faint diagonal white tint for the rest of the rim */
chip-border-base border-box;
border: 1px solid transparent;
The transparent border is what exposes the border-box-clipped
gradient layers as the visible rim.
Token changes (styles/globals.css):
- Retired: --edge-light-warm, --edge-light-cool (both themes).
These were complete box-shadow declarations with offsets spanning
the full bottom-left + top-right edges of every chip.
- Added: --corner-light-warm, --corner-light-cool — plain RGBA
colors for radial-gradient consumption.
Light: 0.50 warm / 0.38 cool.
Dark: 0.68 warm / 0.58 cool (bumped so catch-lights read
against the deep substrate).
- Added: --chip-border-base — faint 135deg white tint so the rest
of the rim isn't invisible (sensible chip edge far from either
light source). Lower alpha on dark.
Application sites:
- styles/globals.css `.glass-panel` and `.glass-panel-strong` —
updated to multi-layer background + transparent border. Picks
up DailyEmberWidget, CommandPaletteModal popover, UserMenu
dropdown, the logged-out sidebar Support/Theme tray, etc.
- components/Layout.js nav-chip inline style — same treatment.
- components/ui/TopSearchBar.js inline style — same treatment.
Shadow stacks across all four sites are now just:
var(--rim-light-inner), var(--elevation-ambient)
(or elevation-pronounced for .glass-panel-strong). The body
gradient stays unchanged from PR #115 — it remains the dominant
gradient story per operator's "I want the gradient in the
background to be the focus" framing.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:31:53 -04:00
|
|
|
--corner-light-warm: rgba(255, 130, 30, 0.68);
|
|
|
|
|
--corner-light-cool: rgba(139, 92, 246, 0.58);
|
refactor(design): tone down card corner-light gradients to ~55% intensity (#118)
Operator feedback after the design-sweep PR went out: the corner-light
gradients on data cards (the `.glass-panel` / `.glass-panel-strong`
class) read too vibrant once the pattern was applied broadly to deck
cards, stat cards, list cards, modals, dropdowns, etc.
The same intensity worked fine on the floating chrome chips (sidebar
nav, top header) because there are only two of those per viewport and
they SHOULD anchor the page visually as the surfaces "responding" to
the background light sources. Data cards tile densely; at full
intensity they compete with the chrome and the catch-lights become
visual noise instead of an environmental cue.
Two intensity tiers now live in `styles/globals.css`:
1. `--corner-light-{warm,cool}` (full intensity, unchanged) — used
by the floating chrome chips via inline `boxShadow` in
`components/Layout.js` and `components/ui/TopSearchBar.js`.
2. `--corner-light-{warm,cool}-subtle` (NEW, ~55% of full alpha) —
used by `.glass-panel` and `.glass-panel-strong` for data cards.
Alpha values:
| Token | Light theme | Dark theme |
|----------------------------------|-------------|------------|
| `--corner-light-warm` | 0.50 | 0.68 |
| `--corner-light-cool` | 0.38 | 0.58 |
| `--corner-light-warm-subtle` | 0.28 | 0.38 |
| `--corner-light-cool-subtle` | 0.20 | 0.32 |
No other surfaces changed, no token names removed (so any custom
inline `boxShadow` styles in other components continue to compile).
The hearth body gradient + `--chip-border-base` faint white tint are
untouched.
Verified: build + lint + 113/113 vitest tests pass.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 15:06:25 -04:00
|
|
|
--corner-light-warm-subtle: rgba(255, 130, 30, 0.38);
|
|
|
|
|
--corner-light-cool-subtle: rgba(139, 92, 246, 0.32);
|
refactor(design): move light response onto chip borders; cut directional drop shadows (#116)
Operator feedback 2026-06-04 (after PR #115's two-light-source pass):
"A lot of gradients happening, though I want the gradient in the
background to be the focus, and then the border gets a highlight,
perhaps, in a corner that's closest to the background gradients...
the borders are reflecting the color, not necessarily the drop
shadows behind the cards. In fact, you could probably reduce those
box shadows quite a bit. At least they shouldn't span the entire
edge of the card. Just a subtle bit in the corner."
PR #115's outer directional drop-shadows (`--edge-light-warm` /
`--edge-light-cool`) DID make the chips respond to the two light
sources but spanned the entire bottom-left + top-right edges, which
competed visually with the body gradient. This PR moves the
warm/cool response onto the chip's 1px BORDER, concentrated in the
two corners closest to the matching light sources, and cuts the
directional drop-shadows entirely (just rim-light-inner + a single
neutral elevation-ambient remain).
Technique: the modern gradient-border pattern.
background:
/* surface fill, clipped to padding-box */
linear-gradient(surface, surface) padding-box,
/* warm catch-light at bottom-left, border-box-clipped */
radial-gradient(at 0% 100%, warm 0%, transparent 42%) border-box,
/* cool catch-light at top-right, border-box-clipped */
radial-gradient(at 100% 0%, cool 0%, transparent 42%) border-box,
/* faint diagonal white tint for the rest of the rim */
chip-border-base border-box;
border: 1px solid transparent;
The transparent border is what exposes the border-box-clipped
gradient layers as the visible rim.
Token changes (styles/globals.css):
- Retired: --edge-light-warm, --edge-light-cool (both themes).
These were complete box-shadow declarations with offsets spanning
the full bottom-left + top-right edges of every chip.
- Added: --corner-light-warm, --corner-light-cool — plain RGBA
colors for radial-gradient consumption.
Light: 0.50 warm / 0.38 cool.
Dark: 0.68 warm / 0.58 cool (bumped so catch-lights read
against the deep substrate).
- Added: --chip-border-base — faint 135deg white tint so the rest
of the rim isn't invisible (sensible chip edge far from either
light source). Lower alpha on dark.
Application sites:
- styles/globals.css `.glass-panel` and `.glass-panel-strong` —
updated to multi-layer background + transparent border. Picks
up DailyEmberWidget, CommandPaletteModal popover, UserMenu
dropdown, the logged-out sidebar Support/Theme tray, etc.
- components/Layout.js nav-chip inline style — same treatment.
- components/ui/TopSearchBar.js inline style — same treatment.
Shadow stacks across all four sites are now just:
var(--rim-light-inner), var(--elevation-ambient)
(or elevation-pronounced for .glass-panel-strong). The body
gradient stays unchanged from PR #115 — it remains the dominant
gradient story per operator's "I want the gradient in the
background to be the focus" framing.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:31:53 -04:00
|
|
|
--chip-border-base: linear-gradient(
|
|
|
|
|
135deg,
|
|
|
|
|
rgba(255, 255, 255, 0.10) 0%,
|
|
|
|
|
rgba(255, 255, 255, 0.02) 100%
|
|
|
|
|
);
|
2025-07-23 22:26:54 -04:00
|
|
|
}
|
|
|
|
|
|
🔥 Transform to Deck Hearth Fire-Inspired Design
🎨 Complete Brand Transformation:
- Rebranded from 'TCG Vault' to 'Deck Hearth'
- Updated logo initials from 'TCG' to 'DH'
- Changed app name throughout the application
🔥 Fire-Themed Color Palette:
Light Theme (Warm Daylight by the Hearth):
- Primary: Warm cream (#fefcf8) like sunlit stone
- Secondary: Soft beige (#f7f3ed) like aged parchment
- Tertiary: Light wood tone (#f0e6d6)
- Text: Dark chocolate brown (#2d1810)
- Accents: Ember orange, flame orange, golden yellow
Dark Theme (Cozy Evening by the Fire):
- Primary: Deep charcoal (#1a0f0a) like burnt wood
- Secondary: Rich dark brown (#2d1b12) like oak
- Tertiary: Medium brown (#3d2317) like mahogany
- Text: Warm white (#fff8f0) like firelight
- Accents: Bright flame, glowing ember, golden glow
🎨 Fire-Inspired Design Elements:
- Flame gradient: Orange to ember red
- Golden gradient: Golden yellow to flame orange
- Fire glow effects with warm shadows
- Wood-toned borders and accents
- Ember and flame-colored interactive elements
🔧 Enhanced Component Styling:
- Updated BulkSelectionToolbar with fire colors
- New CSS classes: btn-flame, btn-ember, btn-gold
- Fire glow and ember glow effects
- Gradient backgrounds: gradient-bg-fire, gradient-bg-golden
- Tailwind color palette: hearth.flame, hearth.ember, etc.
✨ Warm & Inviting Experience:
- Light mode: Cozy daytime fireplace atmosphere
- Dark mode: Intimate evening hearth ambiance
- Maintains accessibility with proper contrast
- Smooth transitions between themes
The app now perfectly captures the warm, inviting feeling of gathering around a hearth! 🔥🏠✨
2025-07-26 09:30:07 -04:00
|
|
|
/* Custom component styles - Fire-themed */
|
2025-07-23 22:26:54 -04:00
|
|
|
.btn-primary {
|
|
|
|
|
@apply font-bold py-3 px-6 rounded-2xl transition-all duration-300 shadow-lg hover:shadow-xl;
|
|
|
|
|
background: var(--gradient-primary);
|
|
|
|
|
color: white;
|
|
|
|
|
}
|
|
|
|
|
|
🔥 Transform to Deck Hearth Fire-Inspired Design
🎨 Complete Brand Transformation:
- Rebranded from 'TCG Vault' to 'Deck Hearth'
- Updated logo initials from 'TCG' to 'DH'
- Changed app name throughout the application
🔥 Fire-Themed Color Palette:
Light Theme (Warm Daylight by the Hearth):
- Primary: Warm cream (#fefcf8) like sunlit stone
- Secondary: Soft beige (#f7f3ed) like aged parchment
- Tertiary: Light wood tone (#f0e6d6)
- Text: Dark chocolate brown (#2d1810)
- Accents: Ember orange, flame orange, golden yellow
Dark Theme (Cozy Evening by the Fire):
- Primary: Deep charcoal (#1a0f0a) like burnt wood
- Secondary: Rich dark brown (#2d1b12) like oak
- Tertiary: Medium brown (#3d2317) like mahogany
- Text: Warm white (#fff8f0) like firelight
- Accents: Bright flame, glowing ember, golden glow
🎨 Fire-Inspired Design Elements:
- Flame gradient: Orange to ember red
- Golden gradient: Golden yellow to flame orange
- Fire glow effects with warm shadows
- Wood-toned borders and accents
- Ember and flame-colored interactive elements
🔧 Enhanced Component Styling:
- Updated BulkSelectionToolbar with fire colors
- New CSS classes: btn-flame, btn-ember, btn-gold
- Fire glow and ember glow effects
- Gradient backgrounds: gradient-bg-fire, gradient-bg-golden
- Tailwind color palette: hearth.flame, hearth.ember, etc.
✨ Warm & Inviting Experience:
- Light mode: Cozy daytime fireplace atmosphere
- Dark mode: Intimate evening hearth ambiance
- Maintains accessibility with proper contrast
- Smooth transitions between themes
The app now perfectly captures the warm, inviting feeling of gathering around a hearth! 🔥🏠✨
2025-07-26 09:30:07 -04:00
|
|
|
.btn-flame {
|
|
|
|
|
@apply font-bold py-2 px-4 rounded-lg transition-all duration-300 shadow-md hover:shadow-lg;
|
|
|
|
|
background-color: var(--accent-flame);
|
|
|
|
|
color: white;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-flame:hover {
|
|
|
|
|
background-color: var(--accent-ember);
|
|
|
|
|
transform: translateY(-1px);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-ember {
|
|
|
|
|
@apply font-bold py-2 px-4 rounded-lg transition-all duration-300 shadow-md hover:shadow-lg;
|
|
|
|
|
background-color: var(--accent-ember);
|
|
|
|
|
color: white;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-ember:hover {
|
|
|
|
|
background: var(--gradient-primary);
|
|
|
|
|
transform: translateY(-1px);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-gold {
|
|
|
|
|
@apply font-bold py-2 px-4 rounded-lg transition-all duration-300 shadow-md hover:shadow-lg;
|
|
|
|
|
background-color: var(--accent-gold);
|
|
|
|
|
color: white;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.btn-gold:hover {
|
|
|
|
|
background: var(--gradient-secondary);
|
|
|
|
|
transform: translateY(-1px);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.gradient-bg-fire {
|
|
|
|
|
background: var(--gradient-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.gradient-bg-golden {
|
|
|
|
|
background: var(--gradient-secondary);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-28 10:51:39 -04:00
|
|
|
.gradient-bg-ember {
|
|
|
|
|
background: linear-gradient(135deg, #d84315 0%, #bf360c 100%);
|
|
|
|
|
}
|
|
|
|
|
|
🔥 Transform to Deck Hearth Fire-Inspired Design
🎨 Complete Brand Transformation:
- Rebranded from 'TCG Vault' to 'Deck Hearth'
- Updated logo initials from 'TCG' to 'DH'
- Changed app name throughout the application
🔥 Fire-Themed Color Palette:
Light Theme (Warm Daylight by the Hearth):
- Primary: Warm cream (#fefcf8) like sunlit stone
- Secondary: Soft beige (#f7f3ed) like aged parchment
- Tertiary: Light wood tone (#f0e6d6)
- Text: Dark chocolate brown (#2d1810)
- Accents: Ember orange, flame orange, golden yellow
Dark Theme (Cozy Evening by the Fire):
- Primary: Deep charcoal (#1a0f0a) like burnt wood
- Secondary: Rich dark brown (#2d1b12) like oak
- Tertiary: Medium brown (#3d2317) like mahogany
- Text: Warm white (#fff8f0) like firelight
- Accents: Bright flame, glowing ember, golden glow
🎨 Fire-Inspired Design Elements:
- Flame gradient: Orange to ember red
- Golden gradient: Golden yellow to flame orange
- Fire glow effects with warm shadows
- Wood-toned borders and accents
- Ember and flame-colored interactive elements
🔧 Enhanced Component Styling:
- Updated BulkSelectionToolbar with fire colors
- New CSS classes: btn-flame, btn-ember, btn-gold
- Fire glow and ember glow effects
- Gradient backgrounds: gradient-bg-fire, gradient-bg-golden
- Tailwind color palette: hearth.flame, hearth.ember, etc.
✨ Warm & Inviting Experience:
- Light mode: Cozy daytime fireplace atmosphere
- Dark mode: Intimate evening hearth ambiance
- Maintains accessibility with proper contrast
- Smooth transitions between themes
The app now perfectly captures the warm, inviting feeling of gathering around a hearth! 🔥🏠✨
2025-07-26 09:30:07 -04:00
|
|
|
/* Fire glow effects */
|
|
|
|
|
.fire-glow {
|
|
|
|
|
box-shadow: 0 0 20px rgba(255, 111, 0, 0.3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ember-glow {
|
|
|
|
|
box-shadow: 0 0 15px rgba(216, 67, 21, 0.4);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-26 10:38:18 -04:00
|
|
|
/* Navigation Accessibility Enhancements */
|
2026-06-04 12:57:10 -04:00
|
|
|
/* Nav-item hover/focus — third pass 2026-06-04. Operator feedback
|
|
|
|
|
after the spotlight landed (PR #110): "remove the shading around
|
|
|
|
|
the hover that makes it feel like it's floating" + "the gradient
|
|
|
|
|
is a little too bold." Two changes from the prior pass:
|
|
|
|
|
1) Dropped the base background-color tint on the host. With the
|
|
|
|
|
bg-tint gone, the item no longer reads as a "floating chip"
|
|
|
|
|
against the sidebar — just the radial spotlight remains,
|
|
|
|
|
which feels like light pooling under the cursor rather than
|
|
|
|
|
a state-color rectangle. Text color shift to --text-primary
|
|
|
|
|
stays as the secondary affordance (and for AA contrast on the
|
|
|
|
|
label).
|
|
|
|
|
2) Toned down the radial-gradient alphas. Light: 35%/18% → 22%/
|
|
|
|
|
10%. Dark: 40%/22% → 26%/12%. Same shape, gentler glow. */
|
2026-06-04 13:00:33 -04:00
|
|
|
/* Hover/focus also surfaces a subtle 1px ember border so the
|
|
|
|
|
hit-target of the chip is unambiguous even when the cursor is
|
|
|
|
|
near an edge (operator feedback after PR #111: the spotlight-only
|
|
|
|
|
hover hid the exact bounds of the interactive surface). The
|
|
|
|
|
border is 28% alpha — distinctly visible but lighter than the
|
|
|
|
|
active state's full-saturation ember border so the two states
|
|
|
|
|
remain readable side-by-side (hover < active). Implemented via
|
|
|
|
|
box-shadow inset rather than border: 1px to avoid the 1px layout
|
|
|
|
|
shift a real border would cause when toggling on hover. */
|
2025-07-26 10:38:18 -04:00
|
|
|
.nav-item:hover,
|
refactor(design-system): redesign-v2 refinements — tone down active/hover states + dedupe profile + seamless header (#109)
Operator feedback after the redesign-v2 epic shipped (PRs #102-#108):
the bold ember-gradient pill, the left-shifting hover, the duplicate
profile dropdown, and the divider below the header all read too
heavy. Four targeted refinements in one PR.
1. Move profile from sidebar bottom → TopSearchBar user-menu chip
(top-right). The chip already existed (sub-convoy #3, PR #105);
the sidebar's UserProfileDropdown was redundant. Removed from
BOTH desktop sidebar and mobile drawer. Kept for logged-out
visitors only (the top bar renders null when user is null, so
the sidebar still surfaces the auth path via the existing
Sign-in CTA branch).
2. Active state: bold ember-gradient pill → 1px ember border on
transparent background.
- styles/globals.css .nav-item-active: dropped the
linear-gradient + 3-stop box-shadow glow. Now: transparent bg,
accent-ember text color, inset 0 0 0 1px var(--accent-ember).
- Dark theme variant uses a slightly hotter ember
(rgb(255,138,80)) for eye-perception correction against the
deep-navy substrate. AA contrast measured: 5.4:1 on dark
navy bg, 4.6:1 on light cream bg — both pass 4.5:1 normal-
text threshold.
3. Hover state: left-shifting border + transform → static
transparent ember-tinted background.
- Removed `border-left: 3px solid var(--accent-flame)` +
`padding-left: calc(1rem - 3px)` on .nav-item-hover:hover
(and focus-within). These were causing the 3px-width shift
the operator called "movement with the left align."
- Removed `transform: translateX(4px)` on .nav-item:hover and
.nav-item-bottom:hover — the horizontal-jitter the operator
also flagged.
- Both classes now apply a flat `background-color:
rgba(216, 67, 21, 0.08)` (light) / `rgba(255, 138, 80, 0.10)`
(dark) on hover/focus-within with zero geometry shift.
4. TopSearchBar bottom divider removed.
- styles change in components/ui/TopSearchBar.js: dropped the
`0 1px 0 var(--border)` segment from the box-shadow
composition. The rim-light-inner top highlight stays so the
bar still reads as elevated chrome against the gradient body,
but there's no longer a hairline below — page content flows
visually seamlessly out of the header.
Test fix:
- test/components/Layout.test.js test #4 ("renders the supplied
user email") asserted the FULL email `foo@bar.com`. The
sidebar UserProfileDropdown used to render that; the TopSearchBar
chip renders the username (or email's local-part as fallback) —
`'foo'` for `foo@bar.com`. The assertion now checks for `'foo'`
+ retains the maintainer-email negative check. Renamed the
test to "flows the supplied user through to the rendered
surface (TopSearchBar chip)" with an inline comment explaining
the shift; the three other P0 #7 regression-lock cases are
unchanged and still pass.
Tests:
- npm run test:run: 113/113
- npm run lint: clean (1 pre-existing unused-disable warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 12:45:57 -04:00
|
|
|
.nav-item-bottom:hover,
|
refactor(design-system): redesign-v2 — bolder hover + mouse-tracking spotlight + active gradient (#110)
Operator feedback after the first refinement (PR #109): "It's hard
to see which one you're hovering" + asked for a fun mouse-tracking
radial gradient AND a subtle gradient on the active state. Three
targeted refinements in one PR.
1. Active state: subtle ember linear-gradient added.
- Kept the 1px ember border + ember text from PR #109's first
refinement (operator approved the toned-down read).
- Added a 135deg linear-gradient at 14% → 6% alpha (light) /
18% → 8% alpha (dark) so the active item has visible warmth
without returning to the heavy saturated pill from sub-convoy
#2. Background shorthand replaces the prior transparent value.
2. Hover state: base tint bolder.
- rgba(216, 67, 21, 0.08) → rgba(216, 67, 21, 0.18) on light
(per operator: "hard to see which one you're hovering")
- rgba(255, 138, 80, 0.10) → rgba(255, 138, 80, 0.22) on dark
- Also expanded the selector list to include .nav-item-hover
consistently with .nav-item and .nav-item-bottom (the three
hoverable nav surface classes).
3. Hover state: mouse-tracking radial spotlight overlay.
- New ::before pseudo-element on .nav-item / .nav-item-bottom /
.nav-item-hover. Uses a radial-gradient positioned via two
CSS custom properties --mouse-x and --mouse-y (default 50%).
- Light theme: rgba(255, 140, 30, 0.35) center → rgba(216, 67,
21, 0.18) at 35% → transparent at 70%. 120px radius.
- Dark theme: rgba(255, 160, 70, 0.40) center → rgba(255, 110,
0, 0.22) → transparent. Brighter for visibility against navy.
- Opacity transitions in/out via --motion-duration-quick.
- pointer-events: none so it never blocks clicks or focus.
- Content z-index: 1 so icons + labels paint above the gradient.
4. Layout.js: single delegated mousemove listener at document level.
- Cheaper than per-item React onMouseMove handlers (the
NavigationContent function has 10+ different nav-item render
paths). closest() returns null for non-nav events so the
listener cost is negligible.
- Writes --mouse-x / --mouse-y as percentages onto the closest
matching nav element. CSS consumes those vars to position the
radial-gradient via inheritance through the ::before.
- prefers-reduced-motion: respected by the existing global
0.01ms transition sweep — the spotlight opacity fade is
collapsed but the static gradient position update is a
positional change (not an animation), so it stays per WCAG
SC 2.3.3 without special-casing.
Tests:
- npm run test:run: 113/113
- npm run lint: clean (1 pre-existing unused-disable warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 12:52:10 -04:00
|
|
|
.nav-item-hover:hover,
|
2025-07-26 10:38:18 -04:00
|
|
|
.nav-item:focus-within,
|
refactor(design-system): redesign-v2 — bolder hover + mouse-tracking spotlight + active gradient (#110)
Operator feedback after the first refinement (PR #109): "It's hard
to see which one you're hovering" + asked for a fun mouse-tracking
radial gradient AND a subtle gradient on the active state. Three
targeted refinements in one PR.
1. Active state: subtle ember linear-gradient added.
- Kept the 1px ember border + ember text from PR #109's first
refinement (operator approved the toned-down read).
- Added a 135deg linear-gradient at 14% → 6% alpha (light) /
18% → 8% alpha (dark) so the active item has visible warmth
without returning to the heavy saturated pill from sub-convoy
#2. Background shorthand replaces the prior transparent value.
2. Hover state: base tint bolder.
- rgba(216, 67, 21, 0.08) → rgba(216, 67, 21, 0.18) on light
(per operator: "hard to see which one you're hovering")
- rgba(255, 138, 80, 0.10) → rgba(255, 138, 80, 0.22) on dark
- Also expanded the selector list to include .nav-item-hover
consistently with .nav-item and .nav-item-bottom (the three
hoverable nav surface classes).
3. Hover state: mouse-tracking radial spotlight overlay.
- New ::before pseudo-element on .nav-item / .nav-item-bottom /
.nav-item-hover. Uses a radial-gradient positioned via two
CSS custom properties --mouse-x and --mouse-y (default 50%).
- Light theme: rgba(255, 140, 30, 0.35) center → rgba(216, 67,
21, 0.18) at 35% → transparent at 70%. 120px radius.
- Dark theme: rgba(255, 160, 70, 0.40) center → rgba(255, 110,
0, 0.22) → transparent. Brighter for visibility against navy.
- Opacity transitions in/out via --motion-duration-quick.
- pointer-events: none so it never blocks clicks or focus.
- Content z-index: 1 so icons + labels paint above the gradient.
4. Layout.js: single delegated mousemove listener at document level.
- Cheaper than per-item React onMouseMove handlers (the
NavigationContent function has 10+ different nav-item render
paths). closest() returns null for non-nav events so the
listener cost is negligible.
- Writes --mouse-x / --mouse-y as percentages onto the closest
matching nav element. CSS consumes those vars to position the
radial-gradient via inheritance through the ::before.
- prefers-reduced-motion: respected by the existing global
0.01ms transition sweep — the spotlight opacity fade is
collapsed but the static gradient position update is a
positional change (not an animation), so it stays per WCAG
SC 2.3.3 without special-casing.
Tests:
- npm run test:run: 113/113
- npm run lint: clean (1 pre-existing unused-disable warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 12:52:10 -04:00
|
|
|
.nav-item-bottom:focus-within,
|
|
|
|
|
.nav-item-hover:focus-within {
|
2025-07-26 10:38:18 -04:00
|
|
|
color: var(--text-primary) !important;
|
2026-06-04 13:00:33 -04:00
|
|
|
box-shadow: inset 0 0 0 1px rgba(216, 67, 21, 0.28) !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[data-theme="dark"] .nav-item:hover,
|
|
|
|
|
[data-theme="dark"] .nav-item-bottom:hover,
|
|
|
|
|
[data-theme="dark"] .nav-item-hover:hover,
|
|
|
|
|
[data-theme="dark"] .nav-item:focus-within,
|
|
|
|
|
[data-theme="dark"] .nav-item-bottom:focus-within,
|
|
|
|
|
[data-theme="dark"] .nav-item-hover:focus-within {
|
|
|
|
|
box-shadow: inset 0 0 0 1px rgba(255, 138, 80, 0.34) !important;
|
refactor(design-system): redesign-v2 refinements — tone down active/hover states + dedupe profile + seamless header (#109)
Operator feedback after the redesign-v2 epic shipped (PRs #102-#108):
the bold ember-gradient pill, the left-shifting hover, the duplicate
profile dropdown, and the divider below the header all read too
heavy. Four targeted refinements in one PR.
1. Move profile from sidebar bottom → TopSearchBar user-menu chip
(top-right). The chip already existed (sub-convoy #3, PR #105);
the sidebar's UserProfileDropdown was redundant. Removed from
BOTH desktop sidebar and mobile drawer. Kept for logged-out
visitors only (the top bar renders null when user is null, so
the sidebar still surfaces the auth path via the existing
Sign-in CTA branch).
2. Active state: bold ember-gradient pill → 1px ember border on
transparent background.
- styles/globals.css .nav-item-active: dropped the
linear-gradient + 3-stop box-shadow glow. Now: transparent bg,
accent-ember text color, inset 0 0 0 1px var(--accent-ember).
- Dark theme variant uses a slightly hotter ember
(rgb(255,138,80)) for eye-perception correction against the
deep-navy substrate. AA contrast measured: 5.4:1 on dark
navy bg, 4.6:1 on light cream bg — both pass 4.5:1 normal-
text threshold.
3. Hover state: left-shifting border + transform → static
transparent ember-tinted background.
- Removed `border-left: 3px solid var(--accent-flame)` +
`padding-left: calc(1rem - 3px)` on .nav-item-hover:hover
(and focus-within). These were causing the 3px-width shift
the operator called "movement with the left align."
- Removed `transform: translateX(4px)` on .nav-item:hover and
.nav-item-bottom:hover — the horizontal-jitter the operator
also flagged.
- Both classes now apply a flat `background-color:
rgba(216, 67, 21, 0.08)` (light) / `rgba(255, 138, 80, 0.10)`
(dark) on hover/focus-within with zero geometry shift.
4. TopSearchBar bottom divider removed.
- styles change in components/ui/TopSearchBar.js: dropped the
`0 1px 0 var(--border)` segment from the box-shadow
composition. The rim-light-inner top highlight stays so the
bar still reads as elevated chrome against the gradient body,
but there's no longer a hairline below — page content flows
visually seamlessly out of the header.
Test fix:
- test/components/Layout.test.js test #4 ("renders the supplied
user email") asserted the FULL email `foo@bar.com`. The
sidebar UserProfileDropdown used to render that; the TopSearchBar
chip renders the username (or email's local-part as fallback) —
`'foo'` for `foo@bar.com`. The assertion now checks for `'foo'`
+ retains the maintainer-email negative check. Renamed the
test to "flows the supplied user through to the rendered
surface (TopSearchBar chip)" with an inline comment explaining
the shift; the three other P0 #7 regression-lock cases are
unchanged and still pass.
Tests:
- npm run test:run: 113/113
- npm run lint: clean (1 pre-existing unused-disable warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 12:45:57 -04:00
|
|
|
}
|
|
|
|
|
|
refactor(design-system): redesign-v2 — bolder hover + mouse-tracking spotlight + active gradient (#110)
Operator feedback after the first refinement (PR #109): "It's hard
to see which one you're hovering" + asked for a fun mouse-tracking
radial gradient AND a subtle gradient on the active state. Three
targeted refinements in one PR.
1. Active state: subtle ember linear-gradient added.
- Kept the 1px ember border + ember text from PR #109's first
refinement (operator approved the toned-down read).
- Added a 135deg linear-gradient at 14% → 6% alpha (light) /
18% → 8% alpha (dark) so the active item has visible warmth
without returning to the heavy saturated pill from sub-convoy
#2. Background shorthand replaces the prior transparent value.
2. Hover state: base tint bolder.
- rgba(216, 67, 21, 0.08) → rgba(216, 67, 21, 0.18) on light
(per operator: "hard to see which one you're hovering")
- rgba(255, 138, 80, 0.10) → rgba(255, 138, 80, 0.22) on dark
- Also expanded the selector list to include .nav-item-hover
consistently with .nav-item and .nav-item-bottom (the three
hoverable nav surface classes).
3. Hover state: mouse-tracking radial spotlight overlay.
- New ::before pseudo-element on .nav-item / .nav-item-bottom /
.nav-item-hover. Uses a radial-gradient positioned via two
CSS custom properties --mouse-x and --mouse-y (default 50%).
- Light theme: rgba(255, 140, 30, 0.35) center → rgba(216, 67,
21, 0.18) at 35% → transparent at 70%. 120px radius.
- Dark theme: rgba(255, 160, 70, 0.40) center → rgba(255, 110,
0, 0.22) → transparent. Brighter for visibility against navy.
- Opacity transitions in/out via --motion-duration-quick.
- pointer-events: none so it never blocks clicks or focus.
- Content z-index: 1 so icons + labels paint above the gradient.
4. Layout.js: single delegated mousemove listener at document level.
- Cheaper than per-item React onMouseMove handlers (the
NavigationContent function has 10+ different nav-item render
paths). closest() returns null for non-nav events so the
listener cost is negligible.
- Writes --mouse-x / --mouse-y as percentages onto the closest
matching nav element. CSS consumes those vars to position the
radial-gradient via inheritance through the ::before.
- prefers-reduced-motion: respected by the existing global
0.01ms transition sweep — the spotlight opacity fade is
collapsed but the static gradient position update is a
positional change (not an animation), so it stays per WCAG
SC 2.3.3 without special-casing.
Tests:
- npm run test:run: 113/113
- npm run lint: clean (1 pre-existing unused-disable warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 12:52:10 -04:00
|
|
|
/* Mouse-tracking radial spotlight on every hoverable nav surface.
|
|
|
|
|
The radial-gradient is positioned via the --mouse-x / --mouse-y
|
|
|
|
|
CSS variables that Layout's delegated mousemove handler writes
|
|
|
|
|
onto the currently-hovered element. Default position (50% 50%)
|
|
|
|
|
ensures the spotlight is centered before the first mouse event
|
|
|
|
|
fires (e.g. on focus-within from keyboard nav). The ::before is
|
|
|
|
|
non-interactive (pointer-events: none) so it never blocks
|
|
|
|
|
clicks or steals focus from the underlying Link/Button. The
|
|
|
|
|
`position: relative` + `overflow: hidden` on the host ensure the
|
|
|
|
|
gradient clips to the rounded-2xl border-radius. */
|
|
|
|
|
.nav-item,
|
|
|
|
|
.nav-item-bottom,
|
|
|
|
|
.nav-item-hover {
|
|
|
|
|
position: relative;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.nav-item::before,
|
|
|
|
|
.nav-item-bottom::before,
|
|
|
|
|
.nav-item-hover::before {
|
|
|
|
|
content: '';
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
border-radius: inherit;
|
|
|
|
|
background: radial-gradient(
|
|
|
|
|
circle 120px at var(--mouse-x, 50%) var(--mouse-y, 50%),
|
2026-06-04 12:57:10 -04:00
|
|
|
rgba(255, 140, 30, 0.22) 0%,
|
|
|
|
|
rgba(216, 67, 21, 0.10) 35%,
|
refactor(design-system): redesign-v2 — bolder hover + mouse-tracking spotlight + active gradient (#110)
Operator feedback after the first refinement (PR #109): "It's hard
to see which one you're hovering" + asked for a fun mouse-tracking
radial gradient AND a subtle gradient on the active state. Three
targeted refinements in one PR.
1. Active state: subtle ember linear-gradient added.
- Kept the 1px ember border + ember text from PR #109's first
refinement (operator approved the toned-down read).
- Added a 135deg linear-gradient at 14% → 6% alpha (light) /
18% → 8% alpha (dark) so the active item has visible warmth
without returning to the heavy saturated pill from sub-convoy
#2. Background shorthand replaces the prior transparent value.
2. Hover state: base tint bolder.
- rgba(216, 67, 21, 0.08) → rgba(216, 67, 21, 0.18) on light
(per operator: "hard to see which one you're hovering")
- rgba(255, 138, 80, 0.10) → rgba(255, 138, 80, 0.22) on dark
- Also expanded the selector list to include .nav-item-hover
consistently with .nav-item and .nav-item-bottom (the three
hoverable nav surface classes).
3. Hover state: mouse-tracking radial spotlight overlay.
- New ::before pseudo-element on .nav-item / .nav-item-bottom /
.nav-item-hover. Uses a radial-gradient positioned via two
CSS custom properties --mouse-x and --mouse-y (default 50%).
- Light theme: rgba(255, 140, 30, 0.35) center → rgba(216, 67,
21, 0.18) at 35% → transparent at 70%. 120px radius.
- Dark theme: rgba(255, 160, 70, 0.40) center → rgba(255, 110,
0, 0.22) → transparent. Brighter for visibility against navy.
- Opacity transitions in/out via --motion-duration-quick.
- pointer-events: none so it never blocks clicks or focus.
- Content z-index: 1 so icons + labels paint above the gradient.
4. Layout.js: single delegated mousemove listener at document level.
- Cheaper than per-item React onMouseMove handlers (the
NavigationContent function has 10+ different nav-item render
paths). closest() returns null for non-nav events so the
listener cost is negligible.
- Writes --mouse-x / --mouse-y as percentages onto the closest
matching nav element. CSS consumes those vars to position the
radial-gradient via inheritance through the ::before.
- prefers-reduced-motion: respected by the existing global
0.01ms transition sweep — the spotlight opacity fade is
collapsed but the static gradient position update is a
positional change (not an animation), so it stays per WCAG
SC 2.3.3 without special-casing.
Tests:
- npm run test:run: 113/113
- npm run lint: clean (1 pre-existing unused-disable warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 12:52:10 -04:00
|
|
|
transparent 70%
|
|
|
|
|
);
|
|
|
|
|
opacity: 0;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
transition: opacity var(--motion-duration-quick) var(--motion-ease-out);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.nav-item:hover::before,
|
|
|
|
|
.nav-item-bottom:hover::before,
|
|
|
|
|
.nav-item-hover:hover::before,
|
|
|
|
|
.nav-item:focus-within::before,
|
|
|
|
|
.nav-item-bottom:focus-within::before,
|
|
|
|
|
.nav-item-hover:focus-within::before {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[data-theme="dark"] .nav-item::before,
|
|
|
|
|
[data-theme="dark"] .nav-item-bottom::before,
|
|
|
|
|
[data-theme="dark"] .nav-item-hover::before {
|
|
|
|
|
background: radial-gradient(
|
|
|
|
|
circle 120px at var(--mouse-x, 50%) var(--mouse-y, 50%),
|
2026-06-04 12:57:10 -04:00
|
|
|
rgba(255, 160, 70, 0.26) 0%,
|
|
|
|
|
rgba(255, 110, 0, 0.12) 35%,
|
refactor(design-system): redesign-v2 — bolder hover + mouse-tracking spotlight + active gradient (#110)
Operator feedback after the first refinement (PR #109): "It's hard
to see which one you're hovering" + asked for a fun mouse-tracking
radial gradient AND a subtle gradient on the active state. Three
targeted refinements in one PR.
1. Active state: subtle ember linear-gradient added.
- Kept the 1px ember border + ember text from PR #109's first
refinement (operator approved the toned-down read).
- Added a 135deg linear-gradient at 14% → 6% alpha (light) /
18% → 8% alpha (dark) so the active item has visible warmth
without returning to the heavy saturated pill from sub-convoy
#2. Background shorthand replaces the prior transparent value.
2. Hover state: base tint bolder.
- rgba(216, 67, 21, 0.08) → rgba(216, 67, 21, 0.18) on light
(per operator: "hard to see which one you're hovering")
- rgba(255, 138, 80, 0.10) → rgba(255, 138, 80, 0.22) on dark
- Also expanded the selector list to include .nav-item-hover
consistently with .nav-item and .nav-item-bottom (the three
hoverable nav surface classes).
3. Hover state: mouse-tracking radial spotlight overlay.
- New ::before pseudo-element on .nav-item / .nav-item-bottom /
.nav-item-hover. Uses a radial-gradient positioned via two
CSS custom properties --mouse-x and --mouse-y (default 50%).
- Light theme: rgba(255, 140, 30, 0.35) center → rgba(216, 67,
21, 0.18) at 35% → transparent at 70%. 120px radius.
- Dark theme: rgba(255, 160, 70, 0.40) center → rgba(255, 110,
0, 0.22) → transparent. Brighter for visibility against navy.
- Opacity transitions in/out via --motion-duration-quick.
- pointer-events: none so it never blocks clicks or focus.
- Content z-index: 1 so icons + labels paint above the gradient.
4. Layout.js: single delegated mousemove listener at document level.
- Cheaper than per-item React onMouseMove handlers (the
NavigationContent function has 10+ different nav-item render
paths). closest() returns null for non-nav events so the
listener cost is negligible.
- Writes --mouse-x / --mouse-y as percentages onto the closest
matching nav element. CSS consumes those vars to position the
radial-gradient via inheritance through the ::before.
- prefers-reduced-motion: respected by the existing global
0.01ms transition sweep — the spotlight opacity fade is
collapsed but the static gradient position update is a
positional change (not an animation), so it stays per WCAG
SC 2.3.3 without special-casing.
Tests:
- npm run test:run: 113/113
- npm run lint: clean (1 pre-existing unused-disable warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 12:52:10 -04:00
|
|
|
transparent 70%
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Ensure the existing nav-item content sits above the ::before
|
|
|
|
|
spotlight overlay. Without this, the radial-gradient would paint
|
|
|
|
|
on top of the icon + label text. */
|
|
|
|
|
.nav-item > *,
|
|
|
|
|
.nav-item-bottom > *,
|
|
|
|
|
.nav-item-hover > * {
|
|
|
|
|
position: relative;
|
|
|
|
|
z-index: 1;
|
2025-07-26 10:38:18 -04:00
|
|
|
}
|
|
|
|
|
|
2026-06-04 12:14:53 -04:00
|
|
|
/* Card grid outer-glow (redesign-v2 sub-convoy #6, 2026-06-04).
|
|
|
|
|
The operator's mockup shows TCG card thumbnails in the Featured
|
|
|
|
|
Collection grid with a soft warm glow emanating around each card,
|
|
|
|
|
suggesting "light cast onto the cards" — the hearth metaphor at
|
|
|
|
|
the card level. Applied to the outermost CardItem container so it
|
|
|
|
|
sits OUTSIDE the rarity-color glow (which lives on the card's
|
|
|
|
|
inner chrome). Single cheap box-shadow per card; safe on long
|
|
|
|
|
scroll lists where stacked filters would jank.
|
|
|
|
|
AGENTS.md performance budget: "card grids may NOT use glass on
|
|
|
|
|
every card item" — this is a single-axis shadow, not a backdrop-
|
|
|
|
|
filter, so it stays well under that budget. */
|
|
|
|
|
.card-grid-outer-glow {
|
|
|
|
|
box-shadow:
|
|
|
|
|
0 0 24px -4px rgba(255, 128, 0, 0.18),
|
|
|
|
|
0 4px 12px -2px rgba(0, 0, 0, 0.10);
|
|
|
|
|
transition: box-shadow var(--motion-duration-default)
|
|
|
|
|
var(--motion-ease-out);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card-grid-outer-glow:hover {
|
|
|
|
|
box-shadow:
|
|
|
|
|
0 0 32px -2px rgba(255, 128, 0, 0.32),
|
|
|
|
|
0 8px 16px -4px rgba(0, 0, 0, 0.15);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[data-theme="dark"] .card-grid-outer-glow {
|
|
|
|
|
box-shadow:
|
|
|
|
|
0 0 28px -4px rgba(255, 110, 30, 0.28),
|
|
|
|
|
0 4px 12px -2px rgba(0, 0, 0, 0.40);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[data-theme="dark"] .card-grid-outer-glow:hover {
|
|
|
|
|
box-shadow:
|
|
|
|
|
0 0 40px -2px rgba(255, 110, 30, 0.50),
|
|
|
|
|
0 8px 16px -4px rgba(0, 0, 0, 0.50);
|
|
|
|
|
}
|
|
|
|
|
|
refactor(design-system): redesign-v2 — bolder hover + mouse-tracking spotlight + active gradient (#110)
Operator feedback after the first refinement (PR #109): "It's hard
to see which one you're hovering" + asked for a fun mouse-tracking
radial gradient AND a subtle gradient on the active state. Three
targeted refinements in one PR.
1. Active state: subtle ember linear-gradient added.
- Kept the 1px ember border + ember text from PR #109's first
refinement (operator approved the toned-down read).
- Added a 135deg linear-gradient at 14% → 6% alpha (light) /
18% → 8% alpha (dark) so the active item has visible warmth
without returning to the heavy saturated pill from sub-convoy
#2. Background shorthand replaces the prior transparent value.
2. Hover state: base tint bolder.
- rgba(216, 67, 21, 0.08) → rgba(216, 67, 21, 0.18) on light
(per operator: "hard to see which one you're hovering")
- rgba(255, 138, 80, 0.10) → rgba(255, 138, 80, 0.22) on dark
- Also expanded the selector list to include .nav-item-hover
consistently with .nav-item and .nav-item-bottom (the three
hoverable nav surface classes).
3. Hover state: mouse-tracking radial spotlight overlay.
- New ::before pseudo-element on .nav-item / .nav-item-bottom /
.nav-item-hover. Uses a radial-gradient positioned via two
CSS custom properties --mouse-x and --mouse-y (default 50%).
- Light theme: rgba(255, 140, 30, 0.35) center → rgba(216, 67,
21, 0.18) at 35% → transparent at 70%. 120px radius.
- Dark theme: rgba(255, 160, 70, 0.40) center → rgba(255, 110,
0, 0.22) → transparent. Brighter for visibility against navy.
- Opacity transitions in/out via --motion-duration-quick.
- pointer-events: none so it never blocks clicks or focus.
- Content z-index: 1 so icons + labels paint above the gradient.
4. Layout.js: single delegated mousemove listener at document level.
- Cheaper than per-item React onMouseMove handlers (the
NavigationContent function has 10+ different nav-item render
paths). closest() returns null for non-nav events so the
listener cost is negligible.
- Writes --mouse-x / --mouse-y as percentages onto the closest
matching nav element. CSS consumes those vars to position the
radial-gradient via inheritance through the ::before.
- prefers-reduced-motion: respected by the existing global
0.01ms transition sweep — the spotlight opacity fade is
collapsed but the static gradient position update is a
positional change (not an animation), so it stays per WCAG
SC 2.3.3 without special-casing.
Tests:
- npm run test:run: 113/113
- npm run lint: clean (1 pre-existing unused-disable warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 12:52:10 -04:00
|
|
|
/* Sidebar active-pill — refined again 2026-06-04 (spotlight-hover
|
|
|
|
|
refinement, second pass). Operator: "give the background of an
|
|
|
|
|
active state a subtle gradient." Kept the 1px ember border + ember
|
|
|
|
|
text from the first refinement; added a soft 135deg ember linear
|
|
|
|
|
gradient as the background fill so the active item has noticeable
|
|
|
|
|
warmth without returning to the heavy saturated pill we walked
|
|
|
|
|
back from. The gradient is ~12-15% peak alpha so AA contrast
|
|
|
|
|
remains intact (ember text on the ember-tinted ramp still measures
|
|
|
|
|
4.5:1+ against the visible substrate).
|
|
|
|
|
Note: `background` shorthand below intentionally REPLACES any
|
|
|
|
|
prior background-color/background-image (a half-shorthand-half-
|
|
|
|
|
longhand mix would lose the gradient under the alpha background
|
|
|
|
|
cascade). */
|
2025-07-26 10:38:18 -04:00
|
|
|
.nav-item-active {
|
refactor(design-system): redesign-v2 — bolder hover + mouse-tracking spotlight + active gradient (#110)
Operator feedback after the first refinement (PR #109): "It's hard
to see which one you're hovering" + asked for a fun mouse-tracking
radial gradient AND a subtle gradient on the active state. Three
targeted refinements in one PR.
1. Active state: subtle ember linear-gradient added.
- Kept the 1px ember border + ember text from PR #109's first
refinement (operator approved the toned-down read).
- Added a 135deg linear-gradient at 14% → 6% alpha (light) /
18% → 8% alpha (dark) so the active item has visible warmth
without returning to the heavy saturated pill from sub-convoy
#2. Background shorthand replaces the prior transparent value.
2. Hover state: base tint bolder.
- rgba(216, 67, 21, 0.08) → rgba(216, 67, 21, 0.18) on light
(per operator: "hard to see which one you're hovering")
- rgba(255, 138, 80, 0.10) → rgba(255, 138, 80, 0.22) on dark
- Also expanded the selector list to include .nav-item-hover
consistently with .nav-item and .nav-item-bottom (the three
hoverable nav surface classes).
3. Hover state: mouse-tracking radial spotlight overlay.
- New ::before pseudo-element on .nav-item / .nav-item-bottom /
.nav-item-hover. Uses a radial-gradient positioned via two
CSS custom properties --mouse-x and --mouse-y (default 50%).
- Light theme: rgba(255, 140, 30, 0.35) center → rgba(216, 67,
21, 0.18) at 35% → transparent at 70%. 120px radius.
- Dark theme: rgba(255, 160, 70, 0.40) center → rgba(255, 110,
0, 0.22) → transparent. Brighter for visibility against navy.
- Opacity transitions in/out via --motion-duration-quick.
- pointer-events: none so it never blocks clicks or focus.
- Content z-index: 1 so icons + labels paint above the gradient.
4. Layout.js: single delegated mousemove listener at document level.
- Cheaper than per-item React onMouseMove handlers (the
NavigationContent function has 10+ different nav-item render
paths). closest() returns null for non-nav events so the
listener cost is negligible.
- Writes --mouse-x / --mouse-y as percentages onto the closest
matching nav element. CSS consumes those vars to position the
radial-gradient via inheritance through the ::before.
- prefers-reduced-motion: respected by the existing global
0.01ms transition sweep — the spotlight opacity fade is
collapsed but the static gradient position update is a
positional change (not an animation), so it stays per WCAG
SC 2.3.3 without special-casing.
Tests:
- npm run test:run: 113/113
- npm run lint: clean (1 pre-existing unused-disable warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 12:52:10 -04:00
|
|
|
background: linear-gradient(
|
|
|
|
|
135deg,
|
|
|
|
|
rgba(216, 67, 21, 0.14) 0%,
|
|
|
|
|
rgba(255, 110, 0, 0.06) 100%
|
|
|
|
|
) !important;
|
refactor(design-system): redesign-v2 refinements — tone down active/hover states + dedupe profile + seamless header (#109)
Operator feedback after the redesign-v2 epic shipped (PRs #102-#108):
the bold ember-gradient pill, the left-shifting hover, the duplicate
profile dropdown, and the divider below the header all read too
heavy. Four targeted refinements in one PR.
1. Move profile from sidebar bottom → TopSearchBar user-menu chip
(top-right). The chip already existed (sub-convoy #3, PR #105);
the sidebar's UserProfileDropdown was redundant. Removed from
BOTH desktop sidebar and mobile drawer. Kept for logged-out
visitors only (the top bar renders null when user is null, so
the sidebar still surfaces the auth path via the existing
Sign-in CTA branch).
2. Active state: bold ember-gradient pill → 1px ember border on
transparent background.
- styles/globals.css .nav-item-active: dropped the
linear-gradient + 3-stop box-shadow glow. Now: transparent bg,
accent-ember text color, inset 0 0 0 1px var(--accent-ember).
- Dark theme variant uses a slightly hotter ember
(rgb(255,138,80)) for eye-perception correction against the
deep-navy substrate. AA contrast measured: 5.4:1 on dark
navy bg, 4.6:1 on light cream bg — both pass 4.5:1 normal-
text threshold.
3. Hover state: left-shifting border + transform → static
transparent ember-tinted background.
- Removed `border-left: 3px solid var(--accent-flame)` +
`padding-left: calc(1rem - 3px)` on .nav-item-hover:hover
(and focus-within). These were causing the 3px-width shift
the operator called "movement with the left align."
- Removed `transform: translateX(4px)` on .nav-item:hover and
.nav-item-bottom:hover — the horizontal-jitter the operator
also flagged.
- Both classes now apply a flat `background-color:
rgba(216, 67, 21, 0.08)` (light) / `rgba(255, 138, 80, 0.10)`
(dark) on hover/focus-within with zero geometry shift.
4. TopSearchBar bottom divider removed.
- styles change in components/ui/TopSearchBar.js: dropped the
`0 1px 0 var(--border)` segment from the box-shadow
composition. The rim-light-inner top highlight stays so the
bar still reads as elevated chrome against the gradient body,
but there's no longer a hairline below — page content flows
visually seamlessly out of the header.
Test fix:
- test/components/Layout.test.js test #4 ("renders the supplied
user email") asserted the FULL email `foo@bar.com`. The
sidebar UserProfileDropdown used to render that; the TopSearchBar
chip renders the username (or email's local-part as fallback) —
`'foo'` for `foo@bar.com`. The assertion now checks for `'foo'`
+ retains the maintainer-email negative check. Renamed the
test to "flows the supplied user through to the rendered
surface (TopSearchBar chip)" with an inline comment explaining
the shift; the three other P0 #7 regression-lock cases are
unchanged and still pass.
Tests:
- npm run test:run: 113/113
- npm run lint: clean (1 pre-existing unused-disable warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 12:45:57 -04:00
|
|
|
color: var(--accent-ember) !important;
|
|
|
|
|
box-shadow: inset 0 0 0 1px var(--accent-ember) !important;
|
2026-06-04 11:59:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[data-theme="dark"] .nav-item-active {
|
refactor(design-system): redesign-v2 — bolder hover + mouse-tracking spotlight + active gradient (#110)
Operator feedback after the first refinement (PR #109): "It's hard
to see which one you're hovering" + asked for a fun mouse-tracking
radial gradient AND a subtle gradient on the active state. Three
targeted refinements in one PR.
1. Active state: subtle ember linear-gradient added.
- Kept the 1px ember border + ember text from PR #109's first
refinement (operator approved the toned-down read).
- Added a 135deg linear-gradient at 14% → 6% alpha (light) /
18% → 8% alpha (dark) so the active item has visible warmth
without returning to the heavy saturated pill from sub-convoy
#2. Background shorthand replaces the prior transparent value.
2. Hover state: base tint bolder.
- rgba(216, 67, 21, 0.08) → rgba(216, 67, 21, 0.18) on light
(per operator: "hard to see which one you're hovering")
- rgba(255, 138, 80, 0.10) → rgba(255, 138, 80, 0.22) on dark
- Also expanded the selector list to include .nav-item-hover
consistently with .nav-item and .nav-item-bottom (the three
hoverable nav surface classes).
3. Hover state: mouse-tracking radial spotlight overlay.
- New ::before pseudo-element on .nav-item / .nav-item-bottom /
.nav-item-hover. Uses a radial-gradient positioned via two
CSS custom properties --mouse-x and --mouse-y (default 50%).
- Light theme: rgba(255, 140, 30, 0.35) center → rgba(216, 67,
21, 0.18) at 35% → transparent at 70%. 120px radius.
- Dark theme: rgba(255, 160, 70, 0.40) center → rgba(255, 110,
0, 0.22) → transparent. Brighter for visibility against navy.
- Opacity transitions in/out via --motion-duration-quick.
- pointer-events: none so it never blocks clicks or focus.
- Content z-index: 1 so icons + labels paint above the gradient.
4. Layout.js: single delegated mousemove listener at document level.
- Cheaper than per-item React onMouseMove handlers (the
NavigationContent function has 10+ different nav-item render
paths). closest() returns null for non-nav events so the
listener cost is negligible.
- Writes --mouse-x / --mouse-y as percentages onto the closest
matching nav element. CSS consumes those vars to position the
radial-gradient via inheritance through the ::before.
- prefers-reduced-motion: respected by the existing global
0.01ms transition sweep — the spotlight opacity fade is
collapsed but the static gradient position update is a
positional change (not an animation), so it stays per WCAG
SC 2.3.3 without special-casing.
Tests:
- npm run test:run: 113/113
- npm run lint: clean (1 pre-existing unused-disable warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 12:52:10 -04:00
|
|
|
background: linear-gradient(
|
|
|
|
|
135deg,
|
|
|
|
|
rgba(255, 138, 80, 0.18) 0%,
|
|
|
|
|
rgba(255, 110, 0, 0.08) 100%
|
|
|
|
|
) !important;
|
refactor(design-system): redesign-v2 refinements — tone down active/hover states + dedupe profile + seamless header (#109)
Operator feedback after the redesign-v2 epic shipped (PRs #102-#108):
the bold ember-gradient pill, the left-shifting hover, the duplicate
profile dropdown, and the divider below the header all read too
heavy. Four targeted refinements in one PR.
1. Move profile from sidebar bottom → TopSearchBar user-menu chip
(top-right). The chip already existed (sub-convoy #3, PR #105);
the sidebar's UserProfileDropdown was redundant. Removed from
BOTH desktop sidebar and mobile drawer. Kept for logged-out
visitors only (the top bar renders null when user is null, so
the sidebar still surfaces the auth path via the existing
Sign-in CTA branch).
2. Active state: bold ember-gradient pill → 1px ember border on
transparent background.
- styles/globals.css .nav-item-active: dropped the
linear-gradient + 3-stop box-shadow glow. Now: transparent bg,
accent-ember text color, inset 0 0 0 1px var(--accent-ember).
- Dark theme variant uses a slightly hotter ember
(rgb(255,138,80)) for eye-perception correction against the
deep-navy substrate. AA contrast measured: 5.4:1 on dark
navy bg, 4.6:1 on light cream bg — both pass 4.5:1 normal-
text threshold.
3. Hover state: left-shifting border + transform → static
transparent ember-tinted background.
- Removed `border-left: 3px solid var(--accent-flame)` +
`padding-left: calc(1rem - 3px)` on .nav-item-hover:hover
(and focus-within). These were causing the 3px-width shift
the operator called "movement with the left align."
- Removed `transform: translateX(4px)` on .nav-item:hover and
.nav-item-bottom:hover — the horizontal-jitter the operator
also flagged.
- Both classes now apply a flat `background-color:
rgba(216, 67, 21, 0.08)` (light) / `rgba(255, 138, 80, 0.10)`
(dark) on hover/focus-within with zero geometry shift.
4. TopSearchBar bottom divider removed.
- styles change in components/ui/TopSearchBar.js: dropped the
`0 1px 0 var(--border)` segment from the box-shadow
composition. The rim-light-inner top highlight stays so the
bar still reads as elevated chrome against the gradient body,
but there's no longer a hairline below — page content flows
visually seamlessly out of the header.
Test fix:
- test/components/Layout.test.js test #4 ("renders the supplied
user email") asserted the FULL email `foo@bar.com`. The
sidebar UserProfileDropdown used to render that; the TopSearchBar
chip renders the username (or email's local-part as fallback) —
`'foo'` for `foo@bar.com`. The assertion now checks for `'foo'`
+ retains the maintainer-email negative check. Renamed the
test to "flows the supplied user through to the rendered
surface (TopSearchBar chip)" with an inline comment explaining
the shift; the three other P0 #7 regression-lock cases are
unchanged and still pass.
Tests:
- npm run test:run: 113/113
- npm run lint: clean (1 pre-existing unused-disable warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 12:45:57 -04:00
|
|
|
color: rgb(255, 138, 80) !important;
|
|
|
|
|
box-shadow: inset 0 0 0 1px rgb(255, 138, 80) !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Hover state — refined 2026-06-04. Operator feedback: the prior
|
|
|
|
|
border-left + padding-shift caused visible layout movement on
|
|
|
|
|
hover which felt jittery. Replaced with a subtle transparent
|
|
|
|
|
bg-tint that keeps the element in place. Same treatment for
|
|
|
|
|
focus-within so keyboard nav matches mouse. */
|
|
|
|
|
.nav-item-hover:hover,
|
|
|
|
|
.nav-item-hover:focus-within {
|
|
|
|
|
background-color: rgba(216, 67, 21, 0.08);
|
2025-07-26 10:38:18 -04:00
|
|
|
}
|
|
|
|
|
|
refactor(design-system): redesign-v2 refinements — tone down active/hover states + dedupe profile + seamless header (#109)
Operator feedback after the redesign-v2 epic shipped (PRs #102-#108):
the bold ember-gradient pill, the left-shifting hover, the duplicate
profile dropdown, and the divider below the header all read too
heavy. Four targeted refinements in one PR.
1. Move profile from sidebar bottom → TopSearchBar user-menu chip
(top-right). The chip already existed (sub-convoy #3, PR #105);
the sidebar's UserProfileDropdown was redundant. Removed from
BOTH desktop sidebar and mobile drawer. Kept for logged-out
visitors only (the top bar renders null when user is null, so
the sidebar still surfaces the auth path via the existing
Sign-in CTA branch).
2. Active state: bold ember-gradient pill → 1px ember border on
transparent background.
- styles/globals.css .nav-item-active: dropped the
linear-gradient + 3-stop box-shadow glow. Now: transparent bg,
accent-ember text color, inset 0 0 0 1px var(--accent-ember).
- Dark theme variant uses a slightly hotter ember
(rgb(255,138,80)) for eye-perception correction against the
deep-navy substrate. AA contrast measured: 5.4:1 on dark
navy bg, 4.6:1 on light cream bg — both pass 4.5:1 normal-
text threshold.
3. Hover state: left-shifting border + transform → static
transparent ember-tinted background.
- Removed `border-left: 3px solid var(--accent-flame)` +
`padding-left: calc(1rem - 3px)` on .nav-item-hover:hover
(and focus-within). These were causing the 3px-width shift
the operator called "movement with the left align."
- Removed `transform: translateX(4px)` on .nav-item:hover and
.nav-item-bottom:hover — the horizontal-jitter the operator
also flagged.
- Both classes now apply a flat `background-color:
rgba(216, 67, 21, 0.08)` (light) / `rgba(255, 138, 80, 0.10)`
(dark) on hover/focus-within with zero geometry shift.
4. TopSearchBar bottom divider removed.
- styles change in components/ui/TopSearchBar.js: dropped the
`0 1px 0 var(--border)` segment from the box-shadow
composition. The rim-light-inner top highlight stays so the
bar still reads as elevated chrome against the gradient body,
but there's no longer a hairline below — page content flows
visually seamlessly out of the header.
Test fix:
- test/components/Layout.test.js test #4 ("renders the supplied
user email") asserted the FULL email `foo@bar.com`. The
sidebar UserProfileDropdown used to render that; the TopSearchBar
chip renders the username (or email's local-part as fallback) —
`'foo'` for `foo@bar.com`. The assertion now checks for `'foo'`
+ retains the maintainer-email negative check. Renamed the
test to "flows the supplied user through to the rendered
surface (TopSearchBar chip)" with an inline comment explaining
the shift; the three other P0 #7 regression-lock cases are
unchanged and still pass.
Tests:
- npm run test:run: 113/113
- npm run lint: clean (1 pre-existing unused-disable warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 12:45:57 -04:00
|
|
|
[data-theme="dark"] .nav-item-hover:hover,
|
|
|
|
|
[data-theme="dark"] .nav-item-hover:focus-within {
|
|
|
|
|
background-color: rgba(255, 138, 80, 0.10);
|
2025-07-26 10:38:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* High contrast mode support */
|
|
|
|
|
@media (prefers-contrast: high) {
|
|
|
|
|
.nav-item:hover,
|
|
|
|
|
.nav-item-bottom:hover,
|
|
|
|
|
.nav-item:focus-within,
|
|
|
|
|
.nav-item-bottom:focus-within {
|
|
|
|
|
outline: 2px solid var(--accent-ember);
|
|
|
|
|
outline-offset: 2px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
feat(design-system): Liquid Glass redesign portfolio — foundation + primitives + Layout (#95)
* feat(design-system): Liquid Glass redesign portfolio — foundation + primitive kit + Layout shell
Operator-requested epic to migrate the UI from the current "warm panel + side-highlight + heavy gradient" visual language to a Liquid Glass aesthetic that retains Deck Hearth's fireplace warmth as accent / gradient / motion (not as panel fill). This squash carries the full 8-convoy portfolio drive-through; 5 sub-convoys reach merged state, 3 land architecture-only and queue impl for follow-up turns gated on dedicated visual-diff baseline re-seeds.
Sub-convoy #1 (liquid-glass-design-tokens) — MERGED. 29 CSS custom properties: glass-surface {low,mid,high} alpha ramp + blur/saturate + rim-light (inner/outer) + ember-rim (subtle/pronounced; RGB triple) + 3-tier elevation + modal-scrim, both light + dark themes with eye-perception-corrected alphas; @supports not (backdrop-filter) fallback collapsing surfaces toward solid (preserves ramp ordering). Authored docs/DESIGN_TOKENS.md (270 LOC reference with WCAG AA contrast tables, composite recipes, when-NOT-to-use-glass guidance, per-card grid GPU budget). AGENTS.md gains a § Visual language section as the new agent-contract surface.
Sub-convoy #2 (liquid-glass-modal-and-surface-primitive) — Brief 1 MERGED. Adds <GlassSurface> (forwardRef composable; tint / rim / elevation / blur props) and <Modal> primitive (focus-trap, ESC + backdrop close, body-scroll lock, ARIA dialog shape, built-in close button) consuming the token surface. lib/use-focus-trap.js — homegrown hook (~60 LOC, no dep). 10 new vitest cases covering open/close render, ARIA, ESC + closeOnEsc gate, backdrop gate, hideCloseButton, body-scroll lock + restore. 4 reference modal migrations as proof-of-pattern: ShareModal, CollectionDeleteModal, CollectionsCreateModal, CardDetailQuantityModal. Brief 2 (11 remaining modals) queued; CI grandfather list locks the pattern in.
Sub-convoy #3 (liquid-glass-form-primitives) — Brief 1 MERGED. Adds <Button> (primary ember-gradient with ember-rim-pronounced; secondary glass-mid; danger; ghost), <Input> (glass-high with ember focus ring + label + helperText + error + aria-invalid + describedby wiring + leadingIcon decorative + trailingAction interactive), <SearchBar> (composes Input with leading search icon + conditional clear button). 10 new vitest cases. pages/login.js + pages/signup.js fully migrated — 2 submit buttons + 7 inputs total; existing test/pages/login.test.js assertion ("Sign in to Deck Hearth" button text) preserved. Brief 2 (profile/settings + deck-builder + scanner + card-editor + collection-cluster modal forms) queued.
Sub-convoy #4 (liquid-glass-layout-shell) — MERGED. 6 shell surfaces glass-migrated: desktop sidebar rail (glass-mid + rim + ambient elevation), mobile drawer (glass-mid + pronounced elevation), mobile overlay scrim (modal-scrim + blur-high — visually consistent with <Modal>), search header strip (glass-mid + rim), UserProfileDropdown popover (glass-high + ember-rim-subtle + ambient — matches popover recipe), MobileNavigation bottom bar (replaces legacy mobile-nav-backdrop class). The 5 Layout regression-lock tests (logged-out CTA, no maintainer-email default, "Sign in" link present, supplied email renders, no "Guest" placeholder) all still pass — every edit preserved the documented contract.
Sub-convoy #5 (liquid-glass-card-surfaces) — ARCHITECTURE RATIFIED; implementation queued. Pixel-sensitive (rarity-glow reconciliation) so wants a dedicated visual-diff baseline re-seed PR. Pre-blocked on a fix-card3d-state convoy (Card3D has pre-existing state-management bug: state setters used without useState declarations).
Sub-convoy #6 (liquid-glass-public-and-auth) — ARCHITECTURE RATIFIED; partial impl shipped via #3 (login + signup form primitives migrated). Landing page editorial + public collection/deck views + login/signup outer-wrapper sweep queued.
Sub-convoy #7 (motion-system-pass) — MERGED. 8 motion tokens (5-tier duration taxonomy: instant/quick/default/slow/deliberate; 3 easings: ease-out default, spring for delight, linear for progress) added to the token surface. prefers-reduced-motion upgraded from a narrow nav-item rule to a site-wide universal sweep collapsing animation-duration + transition-duration to 0.01ms (preserves end states, no flicker); .motion-essential class is the opt-in escape hatch for state-meaningful animation (loading spinners, scan reticles). Authored docs/MOTION_SYSTEM.md with WCAG SC 2.3.3 contract, composition recipes, audit of existing keyframes, and adding-new-animation checklist.
Sub-convoy #8 (cleanup-legacy-design-css) — Brief 1 MERGED. Two new CI jobs in .github/workflows/ci.yml: (1) forbidden-modal-shell-without-primitive (BLOCKING) — fails build if any new file outside the 9 grandfathered legacy modals uses the fixed inset-0 bg-black bg-opacity- shell pattern; locks in the discipline that every modal must compose <Modal> from components/ui. (2) forbidden-deprecated-color-aliases (WARN-only) — audits pre-Deck-Hearth blue/purple/pink aliases (gradient-text-purple/pink/blue, glow-purple/pink/blue, gradient-bg-purple/blue/pink) as a baseline; graduates to FAIL after #8 Brief 2 sweeps consumers. .cursor/rules/ui-and-theming.mdc updated to document the components/ui/ primitive kit and point at the new canonical reference modals.
Verification: lint 0 errors (2 pre-existing warnings in unrelated CardEditorForm.js + CollectionsPageView.js — out of scope); vitest 104/104 passing (was 84 — +20 from new primitive tests: 10 Modal + 10 ui-primitives); ci.yml valid YAML; both new CI gates locally exercised and pass on the current tree.
Operator follow-ups documented in .convoys/ship-readiness.md § "Design-system redesign portfolio":
- Re-seed Linux visual-diff baselines via Docker workflow (AGENTS.md § 6) after this merges.
- preview-smoke.yml runs against the preview; auth + scanner specs touch the migrated surfaces.
- Vercel promote to production once smoke + visual gates pass.
- Queued follow-up implementer turns: #2 Brief 2 (11 modals), #3 Brief 2 (other forms), #5 Brief 1 (cards, after fix-card3d-state), #6 Brief 1 (landing editorial), #8 Brief 2 (legacy CSS deletion + WARN→FAIL graduation).
The user-visible promise — "modern fireplace aesthetic; modals blur the page behind them; reusable components" — is delivered TODAY by the merged work.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(use-focus-trap): preserve named useFocusTrap export for ScannerPageView
The portfolio squash inadvertently overwrote the pre-existing
lib/use-focus-trap.js (named `export function useFocusTrap(active)`
returning a ref — used by ScannerPageView, line 21) with a default-
only export shaped for the new `<Modal>` primitive. Vercel build
failed: "Export useFocusTrap doesn't exist in target module".
Fix: the file now exports BOTH —
- `useFocusTrap(active)` (named, original) — returns a ref;
pre-Liquid-Glass call sites (ScannerPageView) keep working.
- `useFocusTrapContainer({ active, containerRef, ... })` (default,
new) — takes a caller-owned ref so panel refs can forward through
forwardRef chains (Modal.js consumes this shape).
Both hooks are commented to document which to use when. Modal.js
imports default already, so no change needed there.
Verified: npm run build passes (was failing in CI); lint 0 errors;
vitest 104/104 still green.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 21:12:33 -04:00
|
|
|
/* Reduced motion support — site-wide sweep (added by motion-system-pass
|
|
|
|
|
convoy, 2026-06-03). Per WCAG SC 2.3.3, users who set the OS
|
|
|
|
|
"Reduce motion" preference get decorative animations collapsed to a
|
|
|
|
|
no-op while keeping the end-state of state transitions. The 0.01ms
|
|
|
|
|
value (vs `animation: none`) is the well-known idiom for preserving
|
|
|
|
|
end-state without flicker. Essential motion (e.g. loading spinners
|
|
|
|
|
indicating in-flight work) is kept opt-in by individual components
|
|
|
|
|
via the `motion-essential` class. */
|
2025-07-26 10:38:18 -04:00
|
|
|
@media (prefers-reduced-motion: reduce) {
|
feat(design-system): Liquid Glass redesign portfolio — foundation + primitives + Layout (#95)
* feat(design-system): Liquid Glass redesign portfolio — foundation + primitive kit + Layout shell
Operator-requested epic to migrate the UI from the current "warm panel + side-highlight + heavy gradient" visual language to a Liquid Glass aesthetic that retains Deck Hearth's fireplace warmth as accent / gradient / motion (not as panel fill). This squash carries the full 8-convoy portfolio drive-through; 5 sub-convoys reach merged state, 3 land architecture-only and queue impl for follow-up turns gated on dedicated visual-diff baseline re-seeds.
Sub-convoy #1 (liquid-glass-design-tokens) — MERGED. 29 CSS custom properties: glass-surface {low,mid,high} alpha ramp + blur/saturate + rim-light (inner/outer) + ember-rim (subtle/pronounced; RGB triple) + 3-tier elevation + modal-scrim, both light + dark themes with eye-perception-corrected alphas; @supports not (backdrop-filter) fallback collapsing surfaces toward solid (preserves ramp ordering). Authored docs/DESIGN_TOKENS.md (270 LOC reference with WCAG AA contrast tables, composite recipes, when-NOT-to-use-glass guidance, per-card grid GPU budget). AGENTS.md gains a § Visual language section as the new agent-contract surface.
Sub-convoy #2 (liquid-glass-modal-and-surface-primitive) — Brief 1 MERGED. Adds <GlassSurface> (forwardRef composable; tint / rim / elevation / blur props) and <Modal> primitive (focus-trap, ESC + backdrop close, body-scroll lock, ARIA dialog shape, built-in close button) consuming the token surface. lib/use-focus-trap.js — homegrown hook (~60 LOC, no dep). 10 new vitest cases covering open/close render, ARIA, ESC + closeOnEsc gate, backdrop gate, hideCloseButton, body-scroll lock + restore. 4 reference modal migrations as proof-of-pattern: ShareModal, CollectionDeleteModal, CollectionsCreateModal, CardDetailQuantityModal. Brief 2 (11 remaining modals) queued; CI grandfather list locks the pattern in.
Sub-convoy #3 (liquid-glass-form-primitives) — Brief 1 MERGED. Adds <Button> (primary ember-gradient with ember-rim-pronounced; secondary glass-mid; danger; ghost), <Input> (glass-high with ember focus ring + label + helperText + error + aria-invalid + describedby wiring + leadingIcon decorative + trailingAction interactive), <SearchBar> (composes Input with leading search icon + conditional clear button). 10 new vitest cases. pages/login.js + pages/signup.js fully migrated — 2 submit buttons + 7 inputs total; existing test/pages/login.test.js assertion ("Sign in to Deck Hearth" button text) preserved. Brief 2 (profile/settings + deck-builder + scanner + card-editor + collection-cluster modal forms) queued.
Sub-convoy #4 (liquid-glass-layout-shell) — MERGED. 6 shell surfaces glass-migrated: desktop sidebar rail (glass-mid + rim + ambient elevation), mobile drawer (glass-mid + pronounced elevation), mobile overlay scrim (modal-scrim + blur-high — visually consistent with <Modal>), search header strip (glass-mid + rim), UserProfileDropdown popover (glass-high + ember-rim-subtle + ambient — matches popover recipe), MobileNavigation bottom bar (replaces legacy mobile-nav-backdrop class). The 5 Layout regression-lock tests (logged-out CTA, no maintainer-email default, "Sign in" link present, supplied email renders, no "Guest" placeholder) all still pass — every edit preserved the documented contract.
Sub-convoy #5 (liquid-glass-card-surfaces) — ARCHITECTURE RATIFIED; implementation queued. Pixel-sensitive (rarity-glow reconciliation) so wants a dedicated visual-diff baseline re-seed PR. Pre-blocked on a fix-card3d-state convoy (Card3D has pre-existing state-management bug: state setters used without useState declarations).
Sub-convoy #6 (liquid-glass-public-and-auth) — ARCHITECTURE RATIFIED; partial impl shipped via #3 (login + signup form primitives migrated). Landing page editorial + public collection/deck views + login/signup outer-wrapper sweep queued.
Sub-convoy #7 (motion-system-pass) — MERGED. 8 motion tokens (5-tier duration taxonomy: instant/quick/default/slow/deliberate; 3 easings: ease-out default, spring for delight, linear for progress) added to the token surface. prefers-reduced-motion upgraded from a narrow nav-item rule to a site-wide universal sweep collapsing animation-duration + transition-duration to 0.01ms (preserves end states, no flicker); .motion-essential class is the opt-in escape hatch for state-meaningful animation (loading spinners, scan reticles). Authored docs/MOTION_SYSTEM.md with WCAG SC 2.3.3 contract, composition recipes, audit of existing keyframes, and adding-new-animation checklist.
Sub-convoy #8 (cleanup-legacy-design-css) — Brief 1 MERGED. Two new CI jobs in .github/workflows/ci.yml: (1) forbidden-modal-shell-without-primitive (BLOCKING) — fails build if any new file outside the 9 grandfathered legacy modals uses the fixed inset-0 bg-black bg-opacity- shell pattern; locks in the discipline that every modal must compose <Modal> from components/ui. (2) forbidden-deprecated-color-aliases (WARN-only) — audits pre-Deck-Hearth blue/purple/pink aliases (gradient-text-purple/pink/blue, glow-purple/pink/blue, gradient-bg-purple/blue/pink) as a baseline; graduates to FAIL after #8 Brief 2 sweeps consumers. .cursor/rules/ui-and-theming.mdc updated to document the components/ui/ primitive kit and point at the new canonical reference modals.
Verification: lint 0 errors (2 pre-existing warnings in unrelated CardEditorForm.js + CollectionsPageView.js — out of scope); vitest 104/104 passing (was 84 — +20 from new primitive tests: 10 Modal + 10 ui-primitives); ci.yml valid YAML; both new CI gates locally exercised and pass on the current tree.
Operator follow-ups documented in .convoys/ship-readiness.md § "Design-system redesign portfolio":
- Re-seed Linux visual-diff baselines via Docker workflow (AGENTS.md § 6) after this merges.
- preview-smoke.yml runs against the preview; auth + scanner specs touch the migrated surfaces.
- Vercel promote to production once smoke + visual gates pass.
- Queued follow-up implementer turns: #2 Brief 2 (11 modals), #3 Brief 2 (other forms), #5 Brief 1 (cards, after fix-card3d-state), #6 Brief 1 (landing editorial), #8 Brief 2 (legacy CSS deletion + WARN→FAIL graduation).
The user-visible promise — "modern fireplace aesthetic; modals blur the page behind them; reusable components" — is delivered TODAY by the merged work.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(use-focus-trap): preserve named useFocusTrap export for ScannerPageView
The portfolio squash inadvertently overwrote the pre-existing
lib/use-focus-trap.js (named `export function useFocusTrap(active)`
returning a ref — used by ScannerPageView, line 21) with a default-
only export shaped for the new `<Modal>` primitive. Vercel build
failed: "Export useFocusTrap doesn't exist in target module".
Fix: the file now exports BOTH —
- `useFocusTrap(active)` (named, original) — returns a ref;
pre-Liquid-Glass call sites (ScannerPageView) keep working.
- `useFocusTrapContainer({ active, containerRef, ... })` (default,
new) — takes a caller-owned ref so panel refs can forward through
forwardRef chains (Modal.js consumes this shape).
Both hooks are commented to document which to use when. Modal.js
imports default already, so no change needed there.
Verified: npm run build passes (was failing in CI); lint 0 errors;
vitest 104/104 still green.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 21:12:33 -04:00
|
|
|
*,
|
|
|
|
|
*::before,
|
|
|
|
|
*::after {
|
|
|
|
|
animation-duration: 0.01ms !important;
|
|
|
|
|
animation-iteration-count: 1 !important;
|
|
|
|
|
transition-duration: 0.01ms !important;
|
|
|
|
|
scroll-behavior: auto !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.motion-essential,
|
|
|
|
|
.motion-essential * {
|
|
|
|
|
animation-duration: revert !important;
|
|
|
|
|
animation-iteration-count: revert !important;
|
|
|
|
|
transition-duration: revert !important;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-26 10:38:18 -04:00
|
|
|
.nav-item,
|
|
|
|
|
.nav-item-bottom {
|
|
|
|
|
transition: none;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-23 22:26:54 -04:00
|
|
|
.btn-secondary {
|
|
|
|
|
@apply font-bold py-3 px-6 rounded-2xl transition-all duration-300 shadow-lg hover:shadow-xl;
|
|
|
|
|
background: var(--gradient-secondary);
|
|
|
|
|
color: white;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.input-field {
|
|
|
|
|
@apply w-full px-4 py-3 border rounded-2xl shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-0 transition-all duration-200;
|
|
|
|
|
background-color: var(--input-bg);
|
|
|
|
|
border-color: var(--input-border);
|
|
|
|
|
color: var(--input-text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.input-field::placeholder {
|
|
|
|
|
color: var(--input-placeholder);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card {
|
|
|
|
|
@apply rounded-3xl shadow-lg p-6 transition-all duration-300;
|
|
|
|
|
background-color: var(--bg-primary);
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
}
|
|
|
|
|
|
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
|
|
|
/* ============================================================
|
|
|
|
|
.glass-panel — canonical "content card" utility for body
|
|
|
|
|
surfaces on authenticated pages. The Liquid Glass recipe in
|
|
|
|
|
class form so pages can apply it without importing the
|
|
|
|
|
<GlassSurface> primitive for every flat container. Added by
|
|
|
|
|
glass-body-panels PR after PR #96 left admin/settings/dashboard
|
|
|
|
|
body cards on solid --bg-secondary.
|
|
|
|
|
|
|
|
|
|
Composition matches <GlassSurface tint="mid" rim="subtle"
|
|
|
|
|
elevation="ambient" blur="mid">. Always pair with rounded-*
|
|
|
|
|
from Tailwind (the class itself doesn't set border-radius
|
|
|
|
|
because consumers vary between rounded-xl and rounded-2xl).
|
|
|
|
|
============================================================ */
|
fix(design-system): compose rim + elevation tokens as chained box-shadow (#99)
User reported after PR #98 ("backdrop-filter now applies") that the
dashboard glass panels still looked identical to solid cards. Browser
CDP diagnostic on production found the root cause: the .glass-panel
rule had backdrop-filter working, but border: 0px and box-shadow: none.
Why both were missing:
1) `border: 1px solid var(--rim-light-outer)` — the --rim-light-outer
token is a complete box-shadow declaration (`0 0 0 1px #2d181014`),
NOT a color value. When passed to `border: 1px solid <var>`, the
first whitespace-separated token of the shadow string ("0") parses
as border-width = 0px, yielding no border at all.
2) `box-shadow: var(--elevation-ambient), inset 0 1px 0
var(--rim-light-inner)` — the --rim-light-inner token already
contains its own `inset` keyword (`inset 0 1px 0 0 #ffffffa6`).
Wrapping it in another `inset 0 1px 0 ...` prefix produces
`inset 0 1px 0 inset 0 1px 0 0 #ffffffa6` which the CSS parser
rejects — and rejecting one layer drops the ENTIRE box-shadow
declaration. That's why `getComputedStyle().boxShadow === 'none'`.
Fix: the tokens are designed to be *chained* into a single
`box-shadow` declaration, exactly the way <GlassSurface>'s
`boxShadow: shadowParts.join(', ')` composes them (see
components/ui/GlassSurface.js lines 5-15). Drop the `border` line
entirely (rim-light-outer IS the visual border via 1px-spread shadow),
and chain rim-light-inner + rim-light-outer + elevation-* directly.
Verified by `npm run build` — compiled CSS now has all three:
- background
- backdrop-filter (+ -webkit- prefix from autoprefixer)
- box-shadow with all 4 layers correctly chained
This will produce a visible: top inset rim highlight, dark hairline
outer ring, soft ambient drop shadow — i.e. an actual glass card.
Long inline maintainer comment added above the rules so the next agent
doesn't reintroduce either mistake.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 23:49:05 -04:00
|
|
|
/* IMPORTANT — composing rim + elevation tokens correctly.
|
|
|
|
|
The --rim-light-outer / --rim-light-inner / --elevation-* tokens are
|
|
|
|
|
COMPLETE box-shadow declarations (each contains offset/spread/color or
|
|
|
|
|
the `inset` keyword), not raw color values. They are designed to be
|
|
|
|
|
*chained into a single box-shadow*, exactly the way <GlassSurface>'s
|
|
|
|
|
`boxShadow: shadowParts.join(', ')` composes them. Do NOT use
|
|
|
|
|
`border: 1px solid var(--rim-light-outer)` — that parses
|
|
|
|
|
`border-width` from the first token of the shadow string ("0"), which
|
|
|
|
|
yields a 0px border. And do NOT wrap --rim-light-inner in another
|
|
|
|
|
`inset 0 1px 0` prefix — the token already contains its own `inset`
|
|
|
|
|
keyword, so wrapping produces invalid syntax that the browser drops
|
|
|
|
|
the entire box-shadow declaration for. PR #98 had both mistakes,
|
|
|
|
|
which is why the panels in PR #97/#98 had neither visible border nor
|
|
|
|
|
drop shadow despite the backdrop-filter being correct.
|
|
|
|
|
|
|
|
|
|
Backdrop-filter quirks with Next.js 16's Lightning CSS (also relevant):
|
2026-06-03 22:38:38 -04:00
|
|
|
1) `backdrop-filter: blur(var(--X)) saturate(var(--Y))` is silently
|
fix(design-system): compose rim + elevation tokens as chained box-shadow (#99)
User reported after PR #98 ("backdrop-filter now applies") that the
dashboard glass panels still looked identical to solid cards. Browser
CDP diagnostic on production found the root cause: the .glass-panel
rule had backdrop-filter working, but border: 0px and box-shadow: none.
Why both were missing:
1) `border: 1px solid var(--rim-light-outer)` — the --rim-light-outer
token is a complete box-shadow declaration (`0 0 0 1px #2d181014`),
NOT a color value. When passed to `border: 1px solid <var>`, the
first whitespace-separated token of the shadow string ("0") parses
as border-width = 0px, yielding no border at all.
2) `box-shadow: var(--elevation-ambient), inset 0 1px 0
var(--rim-light-inner)` — the --rim-light-inner token already
contains its own `inset` keyword (`inset 0 1px 0 0 #ffffffa6`).
Wrapping it in another `inset 0 1px 0 ...` prefix produces
`inset 0 1px 0 inset 0 1px 0 0 #ffffffa6` which the CSS parser
rejects — and rejecting one layer drops the ENTIRE box-shadow
declaration. That's why `getComputedStyle().boxShadow === 'none'`.
Fix: the tokens are designed to be *chained* into a single
`box-shadow` declaration, exactly the way <GlassSurface>'s
`boxShadow: shadowParts.join(', ')` composes them (see
components/ui/GlassSurface.js lines 5-15). Drop the `border` line
entirely (rim-light-outer IS the visual border via 1px-spread shadow),
and chain rim-light-inner + rim-light-outer + elevation-* directly.
Verified by `npm run build` — compiled CSS now has all three:
- background
- backdrop-filter (+ -webkit- prefix from autoprefixer)
- box-shadow with all 4 layers correctly chained
This will produce a visible: top inset rim highlight, dark hairline
outer ring, soft ambient drop shadow — i.e. an actual glass card.
Long inline maintainer comment added above the rules so the next agent
doesn't reintroduce either mistake.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 23:49:05 -04:00
|
|
|
stripped (var() inside function args breaks the optimizer). Use
|
|
|
|
|
LITERAL values here. Tokens stay in :root for the <GlassSurface>
|
|
|
|
|
JSX primitive which sets backdropFilter via inline style.
|
|
|
|
|
2) Writing both `backdrop-filter` AND `-webkit-backdrop-filter` with
|
|
|
|
|
identical values triggers Lightning CSS's buggy de-dup; it keeps
|
|
|
|
|
only the `-webkit-` form which modern browsers ignore. Write only
|
|
|
|
|
the unprefixed form; Lightning CSS's autoprefixer adds the prefix
|
|
|
|
|
per browserslist (ios_saf <18 still needs it). */
|
feat(design): warm↔cool gradient + edge-light response on chips (#115)
Operator feedback 2026-06-04: "increase the gradient in the
background that goes from our nice warm red to a nice purpley
blue. And then let's have the corners or the edges of our sections
respond as if those two are light sources."
Two coordinated changes that together create the "lit by two light
sources" effect.
1) Body gradient pushed harder + diagonal warm↔cool story
(styles/globals.css body { ... } + [data-theme="dark"] body)
- Dark theme:
* Warm ember pool bottom-left: alpha 0.55 → 0.72, ellipse
enlarged from 75%/55% to 95%/70%.
* Top-right corner: switched from secondary ember + dim
purple-magenta (0.26) to a unified bright purpley-blue pool
(alpha 0.58, indigo→violet ramp, 90%/70% ellipse).
* Base linear-gradient: 180deg vertical wash → 45deg diagonal
(warm aubergine bottom-left → indigo top-right) so the whole
page reads as one continuous warm↔cool dialogue, not just
two corner pools.
- Light theme:
* Top-right gold accent replaced with purpley-blue (indigo
0.42 → violet 0.20 → light violet 0.08) so both themes share
the same diagonal story.
* Bottom-left ember boosted (0.42 → 0.55, ellipse enlarged).
* Base linear-gradient: 180deg → 45deg diagonal (warm-cream
→ cool-violet).
2) Edge-light tokens on every floating chip's shadow stack
(styles/globals.css + components/Layout.js + components/ui/TopSearchBar.js)
- New design tokens in :root and [data-theme="dark"]:
--edge-light-warm (offset -X +Y → glow on the chip's
bottom-left edge, matching the warm
ember light source)
--edge-light-cool (offset +X -Y → glow on the chip's
top-right edge, matching the cool
purpley-blue light source)
Light: 0.16 warm / 0.12 cool. Dark: 0.34 warm / 0.28 cool.
- .glass-panel and .glass-panel-strong shadow stacks updated to
prepend the two edge-light tokens. Every glass surface in the
app now picks up the directional rim glows from the body
gradient without per-component changes (DailyEmberWidget,
CommandPaletteModal popover, the logged-out sidebar Support/
Theme tray, UserMenu dropdown, etc).
- components/Layout.js nav-chip and components/ui/TopSearchBar.js
inline boxShadow strings prepended with the same two tokens
(they use inline styles rather than the .glass-panel class so
they need the explicit shadow stack).
Result: the body now visibly transitions from warm ember (bottom-
left) through a neutral midpoint to purpley-blue (top-right). Every
floating chip's bottom-left and top-right edges pick up subtle
directional glow from the matching corner, reading as light wrapping
around the chip's rim from the two off-screen-ish light sources.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:23:22 -04:00
|
|
|
/* Glass panels — the canonical floating-chrome surface. 2026-06-04
|
refactor(design): move light response onto chip borders; cut directional drop shadows (#116)
Operator feedback 2026-06-04 (after PR #115's two-light-source pass):
"A lot of gradients happening, though I want the gradient in the
background to be the focus, and then the border gets a highlight,
perhaps, in a corner that's closest to the background gradients...
the borders are reflecting the color, not necessarily the drop
shadows behind the cards. In fact, you could probably reduce those
box shadows quite a bit. At least they shouldn't span the entire
edge of the card. Just a subtle bit in the corner."
PR #115's outer directional drop-shadows (`--edge-light-warm` /
`--edge-light-cool`) DID make the chips respond to the two light
sources but spanned the entire bottom-left + top-right edges, which
competed visually with the body gradient. This PR moves the
warm/cool response onto the chip's 1px BORDER, concentrated in the
two corners closest to the matching light sources, and cuts the
directional drop-shadows entirely (just rim-light-inner + a single
neutral elevation-ambient remain).
Technique: the modern gradient-border pattern.
background:
/* surface fill, clipped to padding-box */
linear-gradient(surface, surface) padding-box,
/* warm catch-light at bottom-left, border-box-clipped */
radial-gradient(at 0% 100%, warm 0%, transparent 42%) border-box,
/* cool catch-light at top-right, border-box-clipped */
radial-gradient(at 100% 0%, cool 0%, transparent 42%) border-box,
/* faint diagonal white tint for the rest of the rim */
chip-border-base border-box;
border: 1px solid transparent;
The transparent border is what exposes the border-box-clipped
gradient layers as the visible rim.
Token changes (styles/globals.css):
- Retired: --edge-light-warm, --edge-light-cool (both themes).
These were complete box-shadow declarations with offsets spanning
the full bottom-left + top-right edges of every chip.
- Added: --corner-light-warm, --corner-light-cool — plain RGBA
colors for radial-gradient consumption.
Light: 0.50 warm / 0.38 cool.
Dark: 0.68 warm / 0.58 cool (bumped so catch-lights read
against the deep substrate).
- Added: --chip-border-base — faint 135deg white tint so the rest
of the rim isn't invisible (sensible chip edge far from either
light source). Lower alpha on dark.
Application sites:
- styles/globals.css `.glass-panel` and `.glass-panel-strong` —
updated to multi-layer background + transparent border. Picks
up DailyEmberWidget, CommandPaletteModal popover, UserMenu
dropdown, the logged-out sidebar Support/Theme tray, etc.
- components/Layout.js nav-chip inline style — same treatment.
- components/ui/TopSearchBar.js inline style — same treatment.
Shadow stacks across all four sites are now just:
var(--rim-light-inner), var(--elevation-ambient)
(or elevation-pronounced for .glass-panel-strong). The body
gradient stays unchanged from PR #115 — it remains the dominant
gradient story per operator's "I want the gradient in the
background to be the focus" framing.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:31:53 -04:00
|
|
|
corner-border pass (refines the prior two-light-source pass):
|
|
|
|
|
the warm + cool light response now paints on the chip's 1px
|
|
|
|
|
border using the multi-layer background + transparent-border
|
|
|
|
|
technique:
|
|
|
|
|
1. linear-gradient(surface, surface) padding-box — the chip's
|
|
|
|
|
translucent fill, clipped to the padding box so it doesn't
|
|
|
|
|
cover the 1px border ring.
|
|
|
|
|
2. radial-gradient(at 0% 100%, warm) border-box — warm corner
|
|
|
|
|
catch-light, anchored at the chip's bottom-left.
|
|
|
|
|
3. radial-gradient(at 100% 0%, cool) border-box — cool corner
|
|
|
|
|
catch-light, anchored at the chip's top-right.
|
|
|
|
|
4. --chip-border-base border-box — faint diagonal white tint
|
|
|
|
|
under the corner gradients so the rest of the rim isn't
|
|
|
|
|
invisible (gives the chip a sensible edge far from either
|
|
|
|
|
light source).
|
|
|
|
|
The `border: 1px solid transparent` is what exposes those
|
|
|
|
|
border-box-clipped gradient layers as the visible rim.
|
|
|
|
|
Drop-shadow stack is now just rim-light-inner + elevation —
|
|
|
|
|
the directional warm/cool outer glows are gone per operator
|
refactor(design): tone down card corner-light gradients to ~55% intensity (#118)
Operator feedback after the design-sweep PR went out: the corner-light
gradients on data cards (the `.glass-panel` / `.glass-panel-strong`
class) read too vibrant once the pattern was applied broadly to deck
cards, stat cards, list cards, modals, dropdowns, etc.
The same intensity worked fine on the floating chrome chips (sidebar
nav, top header) because there are only two of those per viewport and
they SHOULD anchor the page visually as the surfaces "responding" to
the background light sources. Data cards tile densely; at full
intensity they compete with the chrome and the catch-lights become
visual noise instead of an environmental cue.
Two intensity tiers now live in `styles/globals.css`:
1. `--corner-light-{warm,cool}` (full intensity, unchanged) — used
by the floating chrome chips via inline `boxShadow` in
`components/Layout.js` and `components/ui/TopSearchBar.js`.
2. `--corner-light-{warm,cool}-subtle` (NEW, ~55% of full alpha) —
used by `.glass-panel` and `.glass-panel-strong` for data cards.
Alpha values:
| Token | Light theme | Dark theme |
|----------------------------------|-------------|------------|
| `--corner-light-warm` | 0.50 | 0.68 |
| `--corner-light-cool` | 0.38 | 0.58 |
| `--corner-light-warm-subtle` | 0.28 | 0.38 |
| `--corner-light-cool-subtle` | 0.20 | 0.32 |
No other surfaces changed, no token names removed (so any custom
inline `boxShadow` styles in other components continue to compile).
The hearth body gradient + `--chip-border-base` faint white tint are
untouched.
Verified: build + lint + 113/113 vitest tests pass.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 15:06:25 -04:00
|
|
|
feedback ("reduce those box shadows quite a bit").
|
|
|
|
|
|
|
|
|
|
`.glass-panel*` uses the SUBTLE corner-light variants (about 55%
|
|
|
|
|
of the full-intensity alpha used by the floating chrome chips in
|
|
|
|
|
Layout / TopSearchBar). Data cards tile densely, so at full
|
|
|
|
|
intensity the catch lights compete with the chrome and overwhelm
|
|
|
|
|
the page (2026-06-04 operator feedback: "reduce the vibrancy of
|
|
|
|
|
the border gradients on the cards"). */
|
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
|
|
|
.glass-panel {
|
refactor(design): move light response onto chip borders; cut directional drop shadows (#116)
Operator feedback 2026-06-04 (after PR #115's two-light-source pass):
"A lot of gradients happening, though I want the gradient in the
background to be the focus, and then the border gets a highlight,
perhaps, in a corner that's closest to the background gradients...
the borders are reflecting the color, not necessarily the drop
shadows behind the cards. In fact, you could probably reduce those
box shadows quite a bit. At least they shouldn't span the entire
edge of the card. Just a subtle bit in the corner."
PR #115's outer directional drop-shadows (`--edge-light-warm` /
`--edge-light-cool`) DID make the chips respond to the two light
sources but spanned the entire bottom-left + top-right edges, which
competed visually with the body gradient. This PR moves the
warm/cool response onto the chip's 1px BORDER, concentrated in the
two corners closest to the matching light sources, and cuts the
directional drop-shadows entirely (just rim-light-inner + a single
neutral elevation-ambient remain).
Technique: the modern gradient-border pattern.
background:
/* surface fill, clipped to padding-box */
linear-gradient(surface, surface) padding-box,
/* warm catch-light at bottom-left, border-box-clipped */
radial-gradient(at 0% 100%, warm 0%, transparent 42%) border-box,
/* cool catch-light at top-right, border-box-clipped */
radial-gradient(at 100% 0%, cool 0%, transparent 42%) border-box,
/* faint diagonal white tint for the rest of the rim */
chip-border-base border-box;
border: 1px solid transparent;
The transparent border is what exposes the border-box-clipped
gradient layers as the visible rim.
Token changes (styles/globals.css):
- Retired: --edge-light-warm, --edge-light-cool (both themes).
These were complete box-shadow declarations with offsets spanning
the full bottom-left + top-right edges of every chip.
- Added: --corner-light-warm, --corner-light-cool — plain RGBA
colors for radial-gradient consumption.
Light: 0.50 warm / 0.38 cool.
Dark: 0.68 warm / 0.58 cool (bumped so catch-lights read
against the deep substrate).
- Added: --chip-border-base — faint 135deg white tint so the rest
of the rim isn't invisible (sensible chip edge far from either
light source). Lower alpha on dark.
Application sites:
- styles/globals.css `.glass-panel` and `.glass-panel-strong` —
updated to multi-layer background + transparent border. Picks
up DailyEmberWidget, CommandPaletteModal popover, UserMenu
dropdown, the logged-out sidebar Support/Theme tray, etc.
- components/Layout.js nav-chip inline style — same treatment.
- components/ui/TopSearchBar.js inline style — same treatment.
Shadow stacks across all four sites are now just:
var(--rim-light-inner), var(--elevation-ambient)
(or elevation-pronounced for .glass-panel-strong). The body
gradient stays unchanged from PR #115 — it remains the dominant
gradient story per operator's "I want the gradient in the
background to be the focus" framing.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:31:53 -04:00
|
|
|
background:
|
|
|
|
|
linear-gradient(var(--glass-surface-mid), var(--glass-surface-mid))
|
|
|
|
|
padding-box,
|
refactor(design): tone down card corner-light gradients to ~55% intensity (#118)
Operator feedback after the design-sweep PR went out: the corner-light
gradients on data cards (the `.glass-panel` / `.glass-panel-strong`
class) read too vibrant once the pattern was applied broadly to deck
cards, stat cards, list cards, modals, dropdowns, etc.
The same intensity worked fine on the floating chrome chips (sidebar
nav, top header) because there are only two of those per viewport and
they SHOULD anchor the page visually as the surfaces "responding" to
the background light sources. Data cards tile densely; at full
intensity they compete with the chrome and the catch-lights become
visual noise instead of an environmental cue.
Two intensity tiers now live in `styles/globals.css`:
1. `--corner-light-{warm,cool}` (full intensity, unchanged) — used
by the floating chrome chips via inline `boxShadow` in
`components/Layout.js` and `components/ui/TopSearchBar.js`.
2. `--corner-light-{warm,cool}-subtle` (NEW, ~55% of full alpha) —
used by `.glass-panel` and `.glass-panel-strong` for data cards.
Alpha values:
| Token | Light theme | Dark theme |
|----------------------------------|-------------|------------|
| `--corner-light-warm` | 0.50 | 0.68 |
| `--corner-light-cool` | 0.38 | 0.58 |
| `--corner-light-warm-subtle` | 0.28 | 0.38 |
| `--corner-light-cool-subtle` | 0.20 | 0.32 |
No other surfaces changed, no token names removed (so any custom
inline `boxShadow` styles in other components continue to compile).
The hearth body gradient + `--chip-border-base` faint white tint are
untouched.
Verified: build + lint + 113/113 vitest tests pass.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 15:06:25 -04:00
|
|
|
radial-gradient(at 0% 100%, var(--corner-light-warm-subtle) 0%, transparent 42%)
|
refactor(design): move light response onto chip borders; cut directional drop shadows (#116)
Operator feedback 2026-06-04 (after PR #115's two-light-source pass):
"A lot of gradients happening, though I want the gradient in the
background to be the focus, and then the border gets a highlight,
perhaps, in a corner that's closest to the background gradients...
the borders are reflecting the color, not necessarily the drop
shadows behind the cards. In fact, you could probably reduce those
box shadows quite a bit. At least they shouldn't span the entire
edge of the card. Just a subtle bit in the corner."
PR #115's outer directional drop-shadows (`--edge-light-warm` /
`--edge-light-cool`) DID make the chips respond to the two light
sources but spanned the entire bottom-left + top-right edges, which
competed visually with the body gradient. This PR moves the
warm/cool response onto the chip's 1px BORDER, concentrated in the
two corners closest to the matching light sources, and cuts the
directional drop-shadows entirely (just rim-light-inner + a single
neutral elevation-ambient remain).
Technique: the modern gradient-border pattern.
background:
/* surface fill, clipped to padding-box */
linear-gradient(surface, surface) padding-box,
/* warm catch-light at bottom-left, border-box-clipped */
radial-gradient(at 0% 100%, warm 0%, transparent 42%) border-box,
/* cool catch-light at top-right, border-box-clipped */
radial-gradient(at 100% 0%, cool 0%, transparent 42%) border-box,
/* faint diagonal white tint for the rest of the rim */
chip-border-base border-box;
border: 1px solid transparent;
The transparent border is what exposes the border-box-clipped
gradient layers as the visible rim.
Token changes (styles/globals.css):
- Retired: --edge-light-warm, --edge-light-cool (both themes).
These were complete box-shadow declarations with offsets spanning
the full bottom-left + top-right edges of every chip.
- Added: --corner-light-warm, --corner-light-cool — plain RGBA
colors for radial-gradient consumption.
Light: 0.50 warm / 0.38 cool.
Dark: 0.68 warm / 0.58 cool (bumped so catch-lights read
against the deep substrate).
- Added: --chip-border-base — faint 135deg white tint so the rest
of the rim isn't invisible (sensible chip edge far from either
light source). Lower alpha on dark.
Application sites:
- styles/globals.css `.glass-panel` and `.glass-panel-strong` —
updated to multi-layer background + transparent border. Picks
up DailyEmberWidget, CommandPaletteModal popover, UserMenu
dropdown, the logged-out sidebar Support/Theme tray, etc.
- components/Layout.js nav-chip inline style — same treatment.
- components/ui/TopSearchBar.js inline style — same treatment.
Shadow stacks across all four sites are now just:
var(--rim-light-inner), var(--elevation-ambient)
(or elevation-pronounced for .glass-panel-strong). The body
gradient stays unchanged from PR #115 — it remains the dominant
gradient story per operator's "I want the gradient in the
background to be the focus" framing.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:31:53 -04:00
|
|
|
border-box,
|
refactor(design): tone down card corner-light gradients to ~55% intensity (#118)
Operator feedback after the design-sweep PR went out: the corner-light
gradients on data cards (the `.glass-panel` / `.glass-panel-strong`
class) read too vibrant once the pattern was applied broadly to deck
cards, stat cards, list cards, modals, dropdowns, etc.
The same intensity worked fine on the floating chrome chips (sidebar
nav, top header) because there are only two of those per viewport and
they SHOULD anchor the page visually as the surfaces "responding" to
the background light sources. Data cards tile densely; at full
intensity they compete with the chrome and the catch-lights become
visual noise instead of an environmental cue.
Two intensity tiers now live in `styles/globals.css`:
1. `--corner-light-{warm,cool}` (full intensity, unchanged) — used
by the floating chrome chips via inline `boxShadow` in
`components/Layout.js` and `components/ui/TopSearchBar.js`.
2. `--corner-light-{warm,cool}-subtle` (NEW, ~55% of full alpha) —
used by `.glass-panel` and `.glass-panel-strong` for data cards.
Alpha values:
| Token | Light theme | Dark theme |
|----------------------------------|-------------|------------|
| `--corner-light-warm` | 0.50 | 0.68 |
| `--corner-light-cool` | 0.38 | 0.58 |
| `--corner-light-warm-subtle` | 0.28 | 0.38 |
| `--corner-light-cool-subtle` | 0.20 | 0.32 |
No other surfaces changed, no token names removed (so any custom
inline `boxShadow` styles in other components continue to compile).
The hearth body gradient + `--chip-border-base` faint white tint are
untouched.
Verified: build + lint + 113/113 vitest tests pass.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 15:06:25 -04:00
|
|
|
radial-gradient(at 100% 0%, var(--corner-light-cool-subtle) 0%, transparent 42%)
|
refactor(design): move light response onto chip borders; cut directional drop shadows (#116)
Operator feedback 2026-06-04 (after PR #115's two-light-source pass):
"A lot of gradients happening, though I want the gradient in the
background to be the focus, and then the border gets a highlight,
perhaps, in a corner that's closest to the background gradients...
the borders are reflecting the color, not necessarily the drop
shadows behind the cards. In fact, you could probably reduce those
box shadows quite a bit. At least they shouldn't span the entire
edge of the card. Just a subtle bit in the corner."
PR #115's outer directional drop-shadows (`--edge-light-warm` /
`--edge-light-cool`) DID make the chips respond to the two light
sources but spanned the entire bottom-left + top-right edges, which
competed visually with the body gradient. This PR moves the
warm/cool response onto the chip's 1px BORDER, concentrated in the
two corners closest to the matching light sources, and cuts the
directional drop-shadows entirely (just rim-light-inner + a single
neutral elevation-ambient remain).
Technique: the modern gradient-border pattern.
background:
/* surface fill, clipped to padding-box */
linear-gradient(surface, surface) padding-box,
/* warm catch-light at bottom-left, border-box-clipped */
radial-gradient(at 0% 100%, warm 0%, transparent 42%) border-box,
/* cool catch-light at top-right, border-box-clipped */
radial-gradient(at 100% 0%, cool 0%, transparent 42%) border-box,
/* faint diagonal white tint for the rest of the rim */
chip-border-base border-box;
border: 1px solid transparent;
The transparent border is what exposes the border-box-clipped
gradient layers as the visible rim.
Token changes (styles/globals.css):
- Retired: --edge-light-warm, --edge-light-cool (both themes).
These were complete box-shadow declarations with offsets spanning
the full bottom-left + top-right edges of every chip.
- Added: --corner-light-warm, --corner-light-cool — plain RGBA
colors for radial-gradient consumption.
Light: 0.50 warm / 0.38 cool.
Dark: 0.68 warm / 0.58 cool (bumped so catch-lights read
against the deep substrate).
- Added: --chip-border-base — faint 135deg white tint so the rest
of the rim isn't invisible (sensible chip edge far from either
light source). Lower alpha on dark.
Application sites:
- styles/globals.css `.glass-panel` and `.glass-panel-strong` —
updated to multi-layer background + transparent border. Picks
up DailyEmberWidget, CommandPaletteModal popover, UserMenu
dropdown, the logged-out sidebar Support/Theme tray, etc.
- components/Layout.js nav-chip inline style — same treatment.
- components/ui/TopSearchBar.js inline style — same treatment.
Shadow stacks across all four sites are now just:
var(--rim-light-inner), var(--elevation-ambient)
(or elevation-pronounced for .glass-panel-strong). The body
gradient stays unchanged from PR #115 — it remains the dominant
gradient story per operator's "I want the gradient in the
background to be the focus" framing.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:31:53 -04:00
|
|
|
border-box,
|
|
|
|
|
var(--chip-border-base) border-box;
|
|
|
|
|
border: 1px solid transparent;
|
2026-06-03 22:38:38 -04:00
|
|
|
backdrop-filter: blur(12px) saturate(180%);
|
fix(design-system): compose rim + elevation tokens as chained box-shadow (#99)
User reported after PR #98 ("backdrop-filter now applies") that the
dashboard glass panels still looked identical to solid cards. Browser
CDP diagnostic on production found the root cause: the .glass-panel
rule had backdrop-filter working, but border: 0px and box-shadow: none.
Why both were missing:
1) `border: 1px solid var(--rim-light-outer)` — the --rim-light-outer
token is a complete box-shadow declaration (`0 0 0 1px #2d181014`),
NOT a color value. When passed to `border: 1px solid <var>`, the
first whitespace-separated token of the shadow string ("0") parses
as border-width = 0px, yielding no border at all.
2) `box-shadow: var(--elevation-ambient), inset 0 1px 0
var(--rim-light-inner)` — the --rim-light-inner token already
contains its own `inset` keyword (`inset 0 1px 0 0 #ffffffa6`).
Wrapping it in another `inset 0 1px 0 ...` prefix produces
`inset 0 1px 0 inset 0 1px 0 0 #ffffffa6` which the CSS parser
rejects — and rejecting one layer drops the ENTIRE box-shadow
declaration. That's why `getComputedStyle().boxShadow === 'none'`.
Fix: the tokens are designed to be *chained* into a single
`box-shadow` declaration, exactly the way <GlassSurface>'s
`boxShadow: shadowParts.join(', ')` composes them (see
components/ui/GlassSurface.js lines 5-15). Drop the `border` line
entirely (rim-light-outer IS the visual border via 1px-spread shadow),
and chain rim-light-inner + rim-light-outer + elevation-* directly.
Verified by `npm run build` — compiled CSS now has all three:
- background
- backdrop-filter (+ -webkit- prefix from autoprefixer)
- box-shadow with all 4 layers correctly chained
This will produce a visible: top inset rim highlight, dark hairline
outer ring, soft ambient drop shadow — i.e. an actual glass card.
Long inline maintainer comment added above the rules so the next agent
doesn't reintroduce either mistake.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 23:49:05 -04:00
|
|
|
box-shadow:
|
|
|
|
|
var(--rim-light-inner),
|
|
|
|
|
var(--elevation-ambient);
|
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.glass-panel-strong {
|
refactor(design): move light response onto chip borders; cut directional drop shadows (#116)
Operator feedback 2026-06-04 (after PR #115's two-light-source pass):
"A lot of gradients happening, though I want the gradient in the
background to be the focus, and then the border gets a highlight,
perhaps, in a corner that's closest to the background gradients...
the borders are reflecting the color, not necessarily the drop
shadows behind the cards. In fact, you could probably reduce those
box shadows quite a bit. At least they shouldn't span the entire
edge of the card. Just a subtle bit in the corner."
PR #115's outer directional drop-shadows (`--edge-light-warm` /
`--edge-light-cool`) DID make the chips respond to the two light
sources but spanned the entire bottom-left + top-right edges, which
competed visually with the body gradient. This PR moves the
warm/cool response onto the chip's 1px BORDER, concentrated in the
two corners closest to the matching light sources, and cuts the
directional drop-shadows entirely (just rim-light-inner + a single
neutral elevation-ambient remain).
Technique: the modern gradient-border pattern.
background:
/* surface fill, clipped to padding-box */
linear-gradient(surface, surface) padding-box,
/* warm catch-light at bottom-left, border-box-clipped */
radial-gradient(at 0% 100%, warm 0%, transparent 42%) border-box,
/* cool catch-light at top-right, border-box-clipped */
radial-gradient(at 100% 0%, cool 0%, transparent 42%) border-box,
/* faint diagonal white tint for the rest of the rim */
chip-border-base border-box;
border: 1px solid transparent;
The transparent border is what exposes the border-box-clipped
gradient layers as the visible rim.
Token changes (styles/globals.css):
- Retired: --edge-light-warm, --edge-light-cool (both themes).
These were complete box-shadow declarations with offsets spanning
the full bottom-left + top-right edges of every chip.
- Added: --corner-light-warm, --corner-light-cool — plain RGBA
colors for radial-gradient consumption.
Light: 0.50 warm / 0.38 cool.
Dark: 0.68 warm / 0.58 cool (bumped so catch-lights read
against the deep substrate).
- Added: --chip-border-base — faint 135deg white tint so the rest
of the rim isn't invisible (sensible chip edge far from either
light source). Lower alpha on dark.
Application sites:
- styles/globals.css `.glass-panel` and `.glass-panel-strong` —
updated to multi-layer background + transparent border. Picks
up DailyEmberWidget, CommandPaletteModal popover, UserMenu
dropdown, the logged-out sidebar Support/Theme tray, etc.
- components/Layout.js nav-chip inline style — same treatment.
- components/ui/TopSearchBar.js inline style — same treatment.
Shadow stacks across all four sites are now just:
var(--rim-light-inner), var(--elevation-ambient)
(or elevation-pronounced for .glass-panel-strong). The body
gradient stays unchanged from PR #115 — it remains the dominant
gradient story per operator's "I want the gradient in the
background to be the focus" framing.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:31:53 -04:00
|
|
|
background:
|
|
|
|
|
linear-gradient(var(--glass-surface-high), var(--glass-surface-high))
|
|
|
|
|
padding-box,
|
refactor(design): tone down card corner-light gradients to ~55% intensity (#118)
Operator feedback after the design-sweep PR went out: the corner-light
gradients on data cards (the `.glass-panel` / `.glass-panel-strong`
class) read too vibrant once the pattern was applied broadly to deck
cards, stat cards, list cards, modals, dropdowns, etc.
The same intensity worked fine on the floating chrome chips (sidebar
nav, top header) because there are only two of those per viewport and
they SHOULD anchor the page visually as the surfaces "responding" to
the background light sources. Data cards tile densely; at full
intensity they compete with the chrome and the catch-lights become
visual noise instead of an environmental cue.
Two intensity tiers now live in `styles/globals.css`:
1. `--corner-light-{warm,cool}` (full intensity, unchanged) — used
by the floating chrome chips via inline `boxShadow` in
`components/Layout.js` and `components/ui/TopSearchBar.js`.
2. `--corner-light-{warm,cool}-subtle` (NEW, ~55% of full alpha) —
used by `.glass-panel` and `.glass-panel-strong` for data cards.
Alpha values:
| Token | Light theme | Dark theme |
|----------------------------------|-------------|------------|
| `--corner-light-warm` | 0.50 | 0.68 |
| `--corner-light-cool` | 0.38 | 0.58 |
| `--corner-light-warm-subtle` | 0.28 | 0.38 |
| `--corner-light-cool-subtle` | 0.20 | 0.32 |
No other surfaces changed, no token names removed (so any custom
inline `boxShadow` styles in other components continue to compile).
The hearth body gradient + `--chip-border-base` faint white tint are
untouched.
Verified: build + lint + 113/113 vitest tests pass.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 15:06:25 -04:00
|
|
|
radial-gradient(at 0% 100%, var(--corner-light-warm-subtle) 0%, transparent 42%)
|
refactor(design): move light response onto chip borders; cut directional drop shadows (#116)
Operator feedback 2026-06-04 (after PR #115's two-light-source pass):
"A lot of gradients happening, though I want the gradient in the
background to be the focus, and then the border gets a highlight,
perhaps, in a corner that's closest to the background gradients...
the borders are reflecting the color, not necessarily the drop
shadows behind the cards. In fact, you could probably reduce those
box shadows quite a bit. At least they shouldn't span the entire
edge of the card. Just a subtle bit in the corner."
PR #115's outer directional drop-shadows (`--edge-light-warm` /
`--edge-light-cool`) DID make the chips respond to the two light
sources but spanned the entire bottom-left + top-right edges, which
competed visually with the body gradient. This PR moves the
warm/cool response onto the chip's 1px BORDER, concentrated in the
two corners closest to the matching light sources, and cuts the
directional drop-shadows entirely (just rim-light-inner + a single
neutral elevation-ambient remain).
Technique: the modern gradient-border pattern.
background:
/* surface fill, clipped to padding-box */
linear-gradient(surface, surface) padding-box,
/* warm catch-light at bottom-left, border-box-clipped */
radial-gradient(at 0% 100%, warm 0%, transparent 42%) border-box,
/* cool catch-light at top-right, border-box-clipped */
radial-gradient(at 100% 0%, cool 0%, transparent 42%) border-box,
/* faint diagonal white tint for the rest of the rim */
chip-border-base border-box;
border: 1px solid transparent;
The transparent border is what exposes the border-box-clipped
gradient layers as the visible rim.
Token changes (styles/globals.css):
- Retired: --edge-light-warm, --edge-light-cool (both themes).
These were complete box-shadow declarations with offsets spanning
the full bottom-left + top-right edges of every chip.
- Added: --corner-light-warm, --corner-light-cool — plain RGBA
colors for radial-gradient consumption.
Light: 0.50 warm / 0.38 cool.
Dark: 0.68 warm / 0.58 cool (bumped so catch-lights read
against the deep substrate).
- Added: --chip-border-base — faint 135deg white tint so the rest
of the rim isn't invisible (sensible chip edge far from either
light source). Lower alpha on dark.
Application sites:
- styles/globals.css `.glass-panel` and `.glass-panel-strong` —
updated to multi-layer background + transparent border. Picks
up DailyEmberWidget, CommandPaletteModal popover, UserMenu
dropdown, the logged-out sidebar Support/Theme tray, etc.
- components/Layout.js nav-chip inline style — same treatment.
- components/ui/TopSearchBar.js inline style — same treatment.
Shadow stacks across all four sites are now just:
var(--rim-light-inner), var(--elevation-ambient)
(or elevation-pronounced for .glass-panel-strong). The body
gradient stays unchanged from PR #115 — it remains the dominant
gradient story per operator's "I want the gradient in the
background to be the focus" framing.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:31:53 -04:00
|
|
|
border-box,
|
refactor(design): tone down card corner-light gradients to ~55% intensity (#118)
Operator feedback after the design-sweep PR went out: the corner-light
gradients on data cards (the `.glass-panel` / `.glass-panel-strong`
class) read too vibrant once the pattern was applied broadly to deck
cards, stat cards, list cards, modals, dropdowns, etc.
The same intensity worked fine on the floating chrome chips (sidebar
nav, top header) because there are only two of those per viewport and
they SHOULD anchor the page visually as the surfaces "responding" to
the background light sources. Data cards tile densely; at full
intensity they compete with the chrome and the catch-lights become
visual noise instead of an environmental cue.
Two intensity tiers now live in `styles/globals.css`:
1. `--corner-light-{warm,cool}` (full intensity, unchanged) — used
by the floating chrome chips via inline `boxShadow` in
`components/Layout.js` and `components/ui/TopSearchBar.js`.
2. `--corner-light-{warm,cool}-subtle` (NEW, ~55% of full alpha) —
used by `.glass-panel` and `.glass-panel-strong` for data cards.
Alpha values:
| Token | Light theme | Dark theme |
|----------------------------------|-------------|------------|
| `--corner-light-warm` | 0.50 | 0.68 |
| `--corner-light-cool` | 0.38 | 0.58 |
| `--corner-light-warm-subtle` | 0.28 | 0.38 |
| `--corner-light-cool-subtle` | 0.20 | 0.32 |
No other surfaces changed, no token names removed (so any custom
inline `boxShadow` styles in other components continue to compile).
The hearth body gradient + `--chip-border-base` faint white tint are
untouched.
Verified: build + lint + 113/113 vitest tests pass.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 15:06:25 -04:00
|
|
|
radial-gradient(at 100% 0%, var(--corner-light-cool-subtle) 0%, transparent 42%)
|
refactor(design): move light response onto chip borders; cut directional drop shadows (#116)
Operator feedback 2026-06-04 (after PR #115's two-light-source pass):
"A lot of gradients happening, though I want the gradient in the
background to be the focus, and then the border gets a highlight,
perhaps, in a corner that's closest to the background gradients...
the borders are reflecting the color, not necessarily the drop
shadows behind the cards. In fact, you could probably reduce those
box shadows quite a bit. At least they shouldn't span the entire
edge of the card. Just a subtle bit in the corner."
PR #115's outer directional drop-shadows (`--edge-light-warm` /
`--edge-light-cool`) DID make the chips respond to the two light
sources but spanned the entire bottom-left + top-right edges, which
competed visually with the body gradient. This PR moves the
warm/cool response onto the chip's 1px BORDER, concentrated in the
two corners closest to the matching light sources, and cuts the
directional drop-shadows entirely (just rim-light-inner + a single
neutral elevation-ambient remain).
Technique: the modern gradient-border pattern.
background:
/* surface fill, clipped to padding-box */
linear-gradient(surface, surface) padding-box,
/* warm catch-light at bottom-left, border-box-clipped */
radial-gradient(at 0% 100%, warm 0%, transparent 42%) border-box,
/* cool catch-light at top-right, border-box-clipped */
radial-gradient(at 100% 0%, cool 0%, transparent 42%) border-box,
/* faint diagonal white tint for the rest of the rim */
chip-border-base border-box;
border: 1px solid transparent;
The transparent border is what exposes the border-box-clipped
gradient layers as the visible rim.
Token changes (styles/globals.css):
- Retired: --edge-light-warm, --edge-light-cool (both themes).
These were complete box-shadow declarations with offsets spanning
the full bottom-left + top-right edges of every chip.
- Added: --corner-light-warm, --corner-light-cool — plain RGBA
colors for radial-gradient consumption.
Light: 0.50 warm / 0.38 cool.
Dark: 0.68 warm / 0.58 cool (bumped so catch-lights read
against the deep substrate).
- Added: --chip-border-base — faint 135deg white tint so the rest
of the rim isn't invisible (sensible chip edge far from either
light source). Lower alpha on dark.
Application sites:
- styles/globals.css `.glass-panel` and `.glass-panel-strong` —
updated to multi-layer background + transparent border. Picks
up DailyEmberWidget, CommandPaletteModal popover, UserMenu
dropdown, the logged-out sidebar Support/Theme tray, etc.
- components/Layout.js nav-chip inline style — same treatment.
- components/ui/TopSearchBar.js inline style — same treatment.
Shadow stacks across all four sites are now just:
var(--rim-light-inner), var(--elevation-ambient)
(or elevation-pronounced for .glass-panel-strong). The body
gradient stays unchanged from PR #115 — it remains the dominant
gradient story per operator's "I want the gradient in the
background to be the focus" framing.
Tests:
- npm run test:run: 113/113
- npm run lint: 0 errors (1 pre-existing warning)
- npm run build: green
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 13:31:53 -04:00
|
|
|
border-box,
|
|
|
|
|
var(--chip-border-base) border-box;
|
|
|
|
|
border: 1px solid transparent;
|
2026-06-03 22:38:38 -04:00
|
|
|
backdrop-filter: blur(20px) saturate(180%);
|
fix(design-system): compose rim + elevation tokens as chained box-shadow (#99)
User reported after PR #98 ("backdrop-filter now applies") that the
dashboard glass panels still looked identical to solid cards. Browser
CDP diagnostic on production found the root cause: the .glass-panel
rule had backdrop-filter working, but border: 0px and box-shadow: none.
Why both were missing:
1) `border: 1px solid var(--rim-light-outer)` — the --rim-light-outer
token is a complete box-shadow declaration (`0 0 0 1px #2d181014`),
NOT a color value. When passed to `border: 1px solid <var>`, the
first whitespace-separated token of the shadow string ("0") parses
as border-width = 0px, yielding no border at all.
2) `box-shadow: var(--elevation-ambient), inset 0 1px 0
var(--rim-light-inner)` — the --rim-light-inner token already
contains its own `inset` keyword (`inset 0 1px 0 0 #ffffffa6`).
Wrapping it in another `inset 0 1px 0 ...` prefix produces
`inset 0 1px 0 inset 0 1px 0 0 #ffffffa6` which the CSS parser
rejects — and rejecting one layer drops the ENTIRE box-shadow
declaration. That's why `getComputedStyle().boxShadow === 'none'`.
Fix: the tokens are designed to be *chained* into a single
`box-shadow` declaration, exactly the way <GlassSurface>'s
`boxShadow: shadowParts.join(', ')` composes them (see
components/ui/GlassSurface.js lines 5-15). Drop the `border` line
entirely (rim-light-outer IS the visual border via 1px-spread shadow),
and chain rim-light-inner + rim-light-outer + elevation-* directly.
Verified by `npm run build` — compiled CSS now has all three:
- background
- backdrop-filter (+ -webkit- prefix from autoprefixer)
- box-shadow with all 4 layers correctly chained
This will produce a visible: top inset rim highlight, dark hairline
outer ring, soft ambient drop shadow — i.e. an actual glass card.
Long inline maintainer comment added above the rules so the next agent
doesn't reintroduce either mistake.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 23:49:05 -04:00
|
|
|
box-shadow:
|
|
|
|
|
var(--rim-light-inner),
|
|
|
|
|
var(--elevation-pronounced);
|
feat(design-system): sweep authenticated body-content panels to glass (#97)
PR #95/#96 shipped the Liquid Glass foundation (tokens, primitives, gates)
plus Layout shell, modals, landing, auth pages, and form CTAs — but body-
content panels on authenticated pages (admin Card Editor, admin Card
Import, admin Submissions, dashboard, my-cards, settings, scanner panels,
card detail price cards, popovers) were still rendering as flat
var(--bg-secondary) cards. Result: the admin Tools screen and several
core pages looked unchanged after the redesign.
This sweep adds a `.glass-panel` / `.glass-panel-strong` utility
(<GlassSurface tint=mid/high rim=subtle elevation=ambient/pronounced
blur=mid/high> in class form) and applies it across 18 surfaces:
* Admin Card Editor view, search panel, form (5 sections), preview
* Admin Card Import navigation + 3 body cards + sync panel
* Admin Card Submissions list items
* Dashboard stat cards + empty-state + grid items (5 surfaces)
* My-cards empty-state CTA card
* Settings panels (3)
* Scanner page settings + grid + queue + bulk toolbar + dialog
* Scanner destination picker + camera status banner + disambiguation
* Card detail price cards (Current / TCGPlayer / CardKingdom)
* Permission indicator tooltips
* Collections page header card
* Card detail view price cards
Also migrates the lingering admin Card Editor "Card Editor / Card Import"
nav buttons and the "Save Changes" / "Import Cards" / "Run catalog sync"
CTAs to the <Button> primitive (consistent loading + disabled states).
Page header bands (full-bleed strips with border-bottom on dashboard,
my-cards, cards, collections, community/collections, collection/[id])
are intentionally left solid — they're not card-shaped surfaces and
stacking glass-on-glass directly below the already-glass topbar would
muddy the hierarchy.
Tests: lint clean, vitest 104/104, build green. The visual diff
baseline will need refresh because the homepage spec is unaffected
(it targets the unauthenticated landing page) but the dashboard/
admin/scanner surfaces will diff if/when we add baselines for them.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 22:28:52 -04:00
|
|
|
}
|
|
|
|
|
|
feat(design-system): finish Liquid Glass — close all remaining sub-convoys (#96)
Follow-up PR to #95 (Liquid Glass foundation + primitives + Layout shell)
that closes out the remaining sub-convoy briefs in a single sweep.
Operator-instructed scope: "finish off the design changes."
After this PR, **all 8 Liquid Glass sub-convoys are MERGED to main**;
the deferred-from-#5 `fix-card3d-state` convoy is dropped (its target,
`components/Card3D.js`, turned out to be dead code).
## #2 Brief 2 — Remaining 8 modals migrated to <Modal> primitive
- `CollectionsSuccessModal.js` — wrap in <Modal hideCloseButton>; 2 Buttons.
- `CollectionsEditModal.js` — full <Modal> + <Input> + <Button> rewrite (4 fields, tag chip section, public-toggle preserved, 2 footer Buttons).
- `CollectionEditModal.js` — same pattern as above (4 fields + public-toggle + 2 Buttons).
- `CardDetailDeckModal.js` — <Modal> + native select (Select primitive not in scope) + 2 Buttons; sweep `gradient-bg-purple` → `<Button variant="primary">`.
- `UploadImageModal.js` — <Modal> + token-driven URL/file tab switcher + drag-drop using `--accent-ember` rim + 2 Buttons (one with `loading` prop).
- `CollectionSelectionModal.js` — largest of the set (header summary + SearchBar + scrollable list w/ checkbox toggles + footer); migrated to <Modal size="lg"> while preserving the per-collection card preview thumbnails.
- `OCRSettings.js` — trivial <Modal> wrap + single primary <Button>.
- `pages/decks.js` — both inline modals (Create Deck + Edit Deck) and `components/ScannerPageView.js` (Create List) migrated; ScannerPageView dropped its `useFocusTrap` named-import (Modal's internal focus trap owns the panel ref now).
- **`.github/workflows/ci.yml` `forbidden-modal-shell-without-primitive`** — grandfather list emptied to zero entries; gate is now strict.
## #3 Brief 2 — Forms migrated to <Button> / <SearchBar>
- `pages/dashboard.js` — 3 CTAs → <Button> (Create List with leadingIcon, Create Your First List, View All Lists).
- `pages/my-cards.js` — empty-state CTA → <Button variant="primary" size="lg">. View-mode toggle buttons intentionally left native (icon-only, doesn't match Button variants).
- `pages/community/collections.js` — Go to My Lists CTA → <Button>.
- `components/CollectionsPageView.js` — Discover Community + Create List header CTAs → <Button>; search input → <SearchBar>.
- Card-grid per-row icon buttons (CollectionsPageView, my-cards, CardsPageView) intentionally left native — tiny per-card actions whose styling doesn't match Button variants and would invalidate visual-diff baselines.
## #5 — scope revised + landed
`components/Card3D.js` deletion: surveyed every importer with grep — **zero consumers** in `pages/**` or `components/**`. Only references were in convoy docs. The "pre-existing state-management bug" (state setters used without useState declarations) never affected the running app because the component was never rendered. -505 LOC. The `fix-card3d-state` convoy is dropped from the roadmap as a result.
The actual card-grid component (`components/CardItem.js`) is intentionally **not** modified in this sweep — it has per-rarity glow tuning that the existing visual-diff baseline locks in, and the architect's #5 deferral note specifically called out the dedicated baseline re-seed cost. A future implementer turn can apply rim-light tokens to CardItem with its own baseline re-seed when an operator wants that polish.
## #6 Brief 1 — Landing + invite pages glass-migrated
- `pages/index.js` — top nav: `var(--glass-surface-mid)` + `--glass-blur-mid` + rim-light. 3 feature cards: `<GlassSurface tint="mid" rim="subtle" elevation="ambient">`. Featured-list cards (the public collection grid): same `<GlassSurface>` recipe with motion-token transitions. All 6 CTA buttons → <Button variant="primary"|"secondary"|"ghost"> with proper sizes. Pulse-loading placeholders tagged `.motion-essential` so reduced-motion users still see them animate (state-meaningful).
- `pages/invite/accept.js` + `pages/invite/decline.js` — both outcome panels wrapped in `<GlassSurface tint="mid" rim="subtle" elevation="pronounced">`. Loading spinner border colors corrected from `--text-accent` (which didn't exist) to `--accent-ember`. All 8 buttons → <Button>. `gradient-bg-ember` consumers retained (the canonical warm-palette utility class is fine).
## #8 Brief 2 — Legacy alias sweep + CI gate graduation
- Swept `gradient-bg-purple` → `gradient-bg-ember` across **8 files** / **13 occurrences**: `CardDetailQuantityModal`, `CardEditorView`, `CardEditorForm`, `AdminProtected`, `pages/card/[id]`, `pages/invite/{accept,decline}`, `pages/admin/card-import`. `gradient-bg-purple` was a dangling class name with no CSS definition (it was rendering no styling), so the sweep is also a bug fix — those buttons now actually get the ember gradient.
- Deleted the 5 dead CSS classes from `styles/globals.css`: `.gradient-text-blue`, `.gradient-text-purple`, `[data-theme="dark"] .glow-blue`, `[data-theme="dark"] .glow-purple`, `[data-theme="dark"] .glow-pink`. Each was zero-consumer post-sweep.
- **Graduated the `forbidden-deprecated-color-aliases` CI job from WARN to FAIL.** All 9 patterns (`gradient-text-{purple,pink,blue}`, `glow-{purple,pink,blue}`, `gradient-bg-{purple,blue,pink}`) now block the build if any consumer is reintroduced.
## Verification (local + CI gates locally exercised)
- Lint: 0 errors, 2 pre-existing warnings (`CardEditorForm.js` + `CollectionsPageView.js` carry-overs from before #95; out of scope).
- Vitest: 104/104 passing — unchanged from #95.
- Build: clean (Turbopack default; passes both light + dark theme prerender).
- `forbidden-modal-shell-without-primitive` gate: locally clear (`grep -lE 'fixed inset-0 bg-black bg-opacity-' pages components -r --include='*.js'` returns no matches).
- `forbidden-deprecated-color-aliases` gate: locally clear (all 9 patterns return no matches in `pages/` or `components/`).
## What still needs human action
- **Linux visual-diff baselines** must re-seed via the Docker workflow in `AGENTS.md` § 6. This PR's landing-page + invite-page changes will produce baseline drift on the homepage screenshot (which is currently the only baseline committed) AND additional baselines will be generated for the landing's glass-card sections once the visual spec is expanded. Recommended: run the Docker re-seed against this PR's Vercel preview, commit the result to this branch, push, verify CI green, then merge.
- Vercel auto-promotes the merge to production.
## Closes / supersedes
- Closes `.convoys/liquid-glass-modal-and-surface-primitive.md` Brief 2 (status → merged).
- Closes `.convoys/liquid-glass-form-primitives.md` Brief 2 (status → merged with explicit per-row-icon-button deferral note).
- Closes `.convoys/liquid-glass-public-and-auth.md` Brief 1 (status → merged).
- Closes `.convoys/cleanup-legacy-design-css.md` Brief 2 (status → merged + CI gate FAIL).
- Drops `.convoys/liquid-glass-card-surfaces.md` Brief 1 prerequisite (`fix-card3d-state` no longer needed; Card3D deleted).
- Drops the queued `fix-card3d-state` follow-up from the roadmap (target deleted).
- Updates `.convoys/ship-readiness.md` § "Design-system redesign portfolio" with a "Finish-portfolio sweep" subsection documenting final status of all 8 sub-convoys.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 21:34:06 -04:00
|
|
|
/* Gradient text effects — Deck Hearth warm palette only.
|
|
|
|
|
The pre-Deck-Hearth aliases (.glow-blue / .glow-purple / .glow-pink,
|
|
|
|
|
.gradient-text-blue / .gradient-text-purple, .gradient-bg-purple)
|
|
|
|
|
were removed by the finish-liquid-glass-design PR after sweeping
|
|
|
|
|
their last consumers to --accent-ember equivalents. */
|
2025-07-27 22:36:40 -04:00
|
|
|
.gradient-text-gold {
|
|
|
|
|
background: linear-gradient(135deg, #ffab40 0%, #ff6f00 100%);
|
|
|
|
|
-webkit-background-clip: text;
|
|
|
|
|
-webkit-text-fill-color: transparent;
|
|
|
|
|
background-clip: text;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.gradient-text-flame {
|
|
|
|
|
background: linear-gradient(135deg, #ff6f00 0%, #d84315 100%);
|
|
|
|
|
-webkit-background-clip: text;
|
|
|
|
|
-webkit-text-fill-color: transparent;
|
|
|
|
|
background-clip: text;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.gradient-text-ember {
|
|
|
|
|
background: linear-gradient(135deg, #d84315 0%, #bf360c 100%);
|
|
|
|
|
-webkit-background-clip: text;
|
|
|
|
|
-webkit-text-fill-color: transparent;
|
|
|
|
|
background-clip: text;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-23 22:26:54 -04:00
|
|
|
/* Theme toggle button */
|
|
|
|
|
.theme-toggle {
|
|
|
|
|
@apply p-2 rounded-2xl transition-all duration-300;
|
|
|
|
|
background: var(--bg-secondary);
|
|
|
|
|
color: var(--text-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Header icons */
|
|
|
|
|
.header-icon {
|
|
|
|
|
@apply p-2 rounded-xl transition-all duration-200;
|
|
|
|
|
color: var(--text-secondary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Logo styling */
|
|
|
|
|
.logo-container {
|
|
|
|
|
@apply rounded-2xl flex items-center justify-center transition-all duration-300;
|
|
|
|
|
background: var(--gradient-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Custom scrollbar */
|
|
|
|
|
::-webkit-scrollbar {
|
|
|
|
|
width: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
::-webkit-scrollbar-track {
|
|
|
|
|
background: var(--bg-secondary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
::-webkit-scrollbar-thumb {
|
|
|
|
|
background: var(--text-secondary);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
::-webkit-scrollbar-thumb:hover {
|
|
|
|
|
background: var(--text-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Search bar specific styling */
|
|
|
|
|
.search-bar {
|
|
|
|
|
@apply w-full px-4 py-3 border rounded-2xl shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-0 transition-all duration-200;
|
|
|
|
|
background-color: var(--input-bg);
|
|
|
|
|
border-color: var(--input-border);
|
|
|
|
|
color: var(--input-text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.search-bar::placeholder {
|
|
|
|
|
color: var(--input-placeholder);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Action buttons with proper gradients */
|
|
|
|
|
.action-btn-primary {
|
|
|
|
|
@apply font-bold py-3 px-6 rounded-2xl transition-all duration-300 shadow-lg hover:shadow-xl;
|
|
|
|
|
background: var(--gradient-primary);
|
|
|
|
|
color: white;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.action-btn-secondary {
|
|
|
|
|
@apply font-bold py-3 px-6 rounded-2xl transition-all duration-300 shadow-lg hover:shadow-xl;
|
|
|
|
|
background: var(--gradient-secondary);
|
|
|
|
|
color: white;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Particle Effects for High Rarity Cards */
|
|
|
|
|
@keyframes float {
|
|
|
|
|
0%, 100% {
|
|
|
|
|
transform: translateY(0px) translateX(0px);
|
|
|
|
|
opacity: 0.7;
|
|
|
|
|
}
|
|
|
|
|
25% {
|
|
|
|
|
transform: translateY(-8px) translateX(4px);
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
50% {
|
|
|
|
|
transform: translateY(-4px) translateX(-2px);
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
}
|
|
|
|
|
75% {
|
|
|
|
|
transform: translateY(-12px) translateX(6px);
|
|
|
|
|
opacity: 0.9;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes sparkle {
|
|
|
|
|
0%, 100% {
|
|
|
|
|
transform: scale(1) rotate(0deg);
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
}
|
|
|
|
|
25% {
|
|
|
|
|
transform: scale(1.5) rotate(90deg);
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
50% {
|
|
|
|
|
transform: scale(0.8) rotate(180deg);
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
}
|
|
|
|
|
75% {
|
|
|
|
|
transform: scale(1.2) rotate(270deg);
|
|
|
|
|
opacity: 0.9;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes aura {
|
|
|
|
|
0%, 100% {
|
|
|
|
|
opacity: 0.1;
|
|
|
|
|
transform: scale(1);
|
|
|
|
|
}
|
|
|
|
|
50% {
|
|
|
|
|
opacity: 0.2;
|
|
|
|
|
transform: scale(1.05);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes edgeFloat {
|
|
|
|
|
0%, 100% {
|
|
|
|
|
opacity: 0.7;
|
|
|
|
|
transform: translateY(0px) translateX(0px);
|
|
|
|
|
}
|
|
|
|
|
25% {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
transform: translateY(-3px) translateX(2px);
|
|
|
|
|
}
|
|
|
|
|
50% {
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
transform: translateY(-1px) translateX(-1px);
|
|
|
|
|
}
|
|
|
|
|
75% {
|
|
|
|
|
opacity: 0.9;
|
|
|
|
|
transform: translateY(-4px) translateX(3px);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes edgeGlow {
|
|
|
|
|
0%, 100% {
|
|
|
|
|
opacity: 0.4;
|
|
|
|
|
transform: scale(1);
|
|
|
|
|
}
|
|
|
|
|
25% {
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
transform: scale(1.2);
|
|
|
|
|
}
|
|
|
|
|
50% {
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
transform: scale(0.8);
|
|
|
|
|
}
|
|
|
|
|
75% {
|
|
|
|
|
opacity: 0.9;
|
|
|
|
|
transform: scale(1.1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Enhanced glow effects for mythic and enchanted cards */
|
|
|
|
|
.card-mythic-glow {
|
|
|
|
|
box-shadow:
|
|
|
|
|
0 0 20px rgba(255, 215, 0, 0.4),
|
|
|
|
|
0 0 40px rgba(255, 215, 0, 0.2),
|
|
|
|
|
0 0 60px rgba(255, 215, 0, 0.1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card-enchanted-glow {
|
|
|
|
|
box-shadow:
|
|
|
|
|
0 0 20px rgba(168, 85, 247, 0.4),
|
|
|
|
|
0 0 40px rgba(168, 85, 247, 0.2),
|
|
|
|
|
0 0 60px rgba(168, 85, 247, 0.1);
|
2025-07-26 00:44:03 -04:00
|
|
|
}
|
|
|
|
|
|
2025-07-26 00:55:54 -04:00
|
|
|
/* Card Hover Panel Styles */
|
2025-07-26 00:44:03 -04:00
|
|
|
.card-item-container {
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card-item-container:hover {
|
|
|
|
|
z-index: 50;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card-item-container.selected {
|
|
|
|
|
z-index: 60;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Ensure proper layering during animations */
|
|
|
|
|
.card-grid-container {
|
|
|
|
|
position: relative;
|
|
|
|
|
z-index: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Prevent text selection during card interactions */
|
|
|
|
|
.card-item-container * {
|
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-26 00:55:54 -04:00
|
|
|
/* Side panel specific styles */
|
|
|
|
|
.card-side-panel {
|
|
|
|
|
transform-origin: left center;
|
|
|
|
|
backdrop-filter: blur(8px);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Responsive adjustments for side panels */
|
|
|
|
|
@media (max-width: 1280px) {
|
|
|
|
|
.card-side-panel {
|
|
|
|
|
width: 16rem; /* w-64 */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media (max-width: 1024px) {
|
|
|
|
|
.card-side-panel {
|
|
|
|
|
display: none; /* Hide panels on smaller screens */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Dark mode improvements */
|
|
|
|
|
@media (prefers-color-scheme: dark) {
|
|
|
|
|
.card-side-panel {
|
|
|
|
|
backdrop-filter: blur(8px) brightness(0.8);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Smooth panel transitions */
|
|
|
|
|
.card-panel-enter {
|
|
|
|
|
opacity: 0;
|
|
|
|
|
transform: translateX(8px);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card-panel-enter-active {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
transform: translateX(0);
|
|
|
|
|
transition: all 300ms ease-out;
|
2025-07-26 01:01:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Rarity Effects */
|
|
|
|
|
/* Mythic/Legendary - Golden glow */
|
|
|
|
|
.rarity-glow-mythic {
|
|
|
|
|
box-shadow:
|
2025-07-26 10:17:22 -04:00
|
|
|
0 0 25px rgba(251, 191, 36, 0.5),
|
|
|
|
|
0 0 50px rgba(251, 191, 36, 0.35),
|
|
|
|
|
0 0 75px rgba(251, 191, 36, 0.25);
|
2025-07-26 01:01:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.rarity-particles-mythic::before {
|
|
|
|
|
content: '';
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
background: radial-gradient(circle at 20% 30%, rgba(251, 191, 36, 0.3) 0%, transparent 50%),
|
|
|
|
|
radial-gradient(circle at 80% 70%, rgba(251, 191, 36, 0.2) 0%, transparent 50%),
|
|
|
|
|
radial-gradient(circle at 60% 20%, rgba(251, 191, 36, 0.25) 0%, transparent 50%);
|
|
|
|
|
animation: mythic-sparkle 4s ease-in-out infinite;
|
|
|
|
|
border-radius: inherit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes mythic-sparkle {
|
|
|
|
|
0%, 100% { opacity: 0.3; }
|
|
|
|
|
50% { opacity: 0.7; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Rare - Purple glow */
|
|
|
|
|
.rarity-glow-rare {
|
|
|
|
|
box-shadow:
|
2025-07-26 10:17:22 -04:00
|
|
|
0 0 20px rgba(168, 85, 247, 0.45),
|
|
|
|
|
0 0 40px rgba(168, 85, 247, 0.35),
|
|
|
|
|
0 0 60px rgba(168, 85, 247, 0.25);
|
2025-07-26 01:01:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.rarity-particles-rare::before {
|
|
|
|
|
content: '';
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
background: radial-gradient(circle at 30% 40%, rgba(168, 85, 247, 0.2) 0%, transparent 40%),
|
|
|
|
|
radial-gradient(circle at 70% 60%, rgba(168, 85, 247, 0.15) 0%, transparent 40%);
|
|
|
|
|
animation: rare-shimmer 3s ease-in-out infinite;
|
|
|
|
|
border-radius: inherit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes rare-shimmer {
|
|
|
|
|
0%, 100% { opacity: 0.2; }
|
|
|
|
|
50% { opacity: 0.5; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Uncommon - Blue glow */
|
|
|
|
|
.rarity-glow-uncommon {
|
|
|
|
|
box-shadow:
|
2025-07-26 10:17:22 -04:00
|
|
|
0 0 12px rgba(96, 165, 250, 0.35),
|
|
|
|
|
0 0 25px rgba(96, 165, 250, 0.25);
|
2025-07-26 01:01:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.rarity-particles-uncommon::before {
|
|
|
|
|
content: '';
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
background: radial-gradient(circle at 40% 30%, rgba(96, 165, 250, 0.15) 0%, transparent 30%);
|
|
|
|
|
animation: uncommon-twinkle 2.5s ease-in-out infinite;
|
|
|
|
|
border-radius: inherit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes uncommon-twinkle {
|
|
|
|
|
0%, 100% { opacity: 0.1; }
|
|
|
|
|
50% { opacity: 0.3; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Enchanted/Secret - Pink/Rainbow glow */
|
|
|
|
|
.rarity-glow-enchanted {
|
|
|
|
|
box-shadow:
|
2025-07-26 10:17:22 -04:00
|
|
|
0 0 28px rgba(244, 114, 182, 0.5),
|
|
|
|
|
0 0 55px rgba(168, 85, 247, 0.35),
|
|
|
|
|
0 0 80px rgba(59, 130, 246, 0.25);
|
2025-07-26 01:01:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.rarity-particles-enchanted::before {
|
|
|
|
|
content: '';
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
|
|
|
|
background:
|
|
|
|
|
radial-gradient(circle at 25% 25%, rgba(244, 114, 182, 0.3) 0%, transparent 40%),
|
|
|
|
|
radial-gradient(circle at 75% 75%, rgba(168, 85, 247, 0.2) 0%, transparent 40%),
|
|
|
|
|
radial-gradient(circle at 50% 50%, rgba(59, 130, 246, 0.25) 0%, transparent 40%);
|
|
|
|
|
animation: enchanted-rainbow 3s ease-in-out infinite;
|
|
|
|
|
border-radius: inherit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes enchanted-rainbow {
|
|
|
|
|
0%, 100% {
|
|
|
|
|
opacity: 0.4;
|
|
|
|
|
transform: rotate(0deg);
|
|
|
|
|
}
|
|
|
|
|
33% {
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
transform: rotate(120deg);
|
|
|
|
|
}
|
|
|
|
|
66% {
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
transform: rotate(240deg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Hover enhancement for rarity effects */
|
|
|
|
|
.card-item-container:hover .rarity-glow-mythic {
|
|
|
|
|
box-shadow:
|
|
|
|
|
0 0 30px rgba(251, 191, 36, 0.6),
|
|
|
|
|
0 0 60px rgba(251, 191, 36, 0.5),
|
|
|
|
|
0 0 90px rgba(251, 191, 36, 0.4);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card-item-container:hover .rarity-glow-rare {
|
|
|
|
|
box-shadow:
|
|
|
|
|
0 0 25px rgba(168, 85, 247, 0.6),
|
|
|
|
|
0 0 50px rgba(168, 85, 247, 0.5),
|
|
|
|
|
0 0 75px rgba(168, 85, 247, 0.4);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card-item-container:hover .rarity-glow-uncommon {
|
|
|
|
|
box-shadow:
|
|
|
|
|
0 0 20px rgba(96, 165, 250, 0.5),
|
|
|
|
|
0 0 40px rgba(96, 165, 250, 0.4);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.card-item-container:hover .rarity-glow-enchanted {
|
|
|
|
|
box-shadow:
|
|
|
|
|
0 0 35px rgba(244, 114, 182, 0.7),
|
|
|
|
|
0 0 70px rgba(244, 114, 182, 0.6),
|
|
|
|
|
0 0 105px rgba(244, 114, 182, 0.5);
|
2025-07-26 22:51:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Utility classes */
|
|
|
|
|
.flex-2 {
|
|
|
|
|
flex: 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.line-clamp-2 {
|
|
|
|
|
display: -webkit-box;
|
|
|
|
|
-webkit-line-clamp: 2;
|
|
|
|
|
-webkit-box-orient: vertical;
|
|
|
|
|
overflow: hidden;
|
2025-07-27 22:46:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Floating particle animation */
|
|
|
|
|
@keyframes float {
|
|
|
|
|
0%, 100% {
|
|
|
|
|
transform: translateY(0px) translateX(0px) scale(1);
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
}
|
|
|
|
|
25% {
|
|
|
|
|
transform: translateY(-20px) translateX(10px) scale(1.1);
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
}
|
|
|
|
|
50% {
|
|
|
|
|
transform: translateY(-10px) translateX(-15px) scale(0.9);
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
75% {
|
|
|
|
|
transform: translateY(-25px) translateX(5px) scale(1.05);
|
|
|
|
|
opacity: 0.7;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.animate-float {
|
|
|
|
|
animation: float 4s ease-in-out infinite;
|
2025-07-28 10:51:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Fire Glow Background Animation */
|
|
|
|
|
@keyframes fire-glow {
|
|
|
|
|
0%, 100% {
|
|
|
|
|
filter: hue-rotate(0deg) brightness(1) saturate(1);
|
|
|
|
|
transform: scale(1);
|
|
|
|
|
}
|
|
|
|
|
25% {
|
|
|
|
|
filter: hue-rotate(5deg) brightness(1.05) saturate(1.1);
|
|
|
|
|
transform: scale(1.02);
|
|
|
|
|
}
|
|
|
|
|
50% {
|
|
|
|
|
filter: hue-rotate(-3deg) brightness(0.98) saturate(1.05);
|
|
|
|
|
transform: scale(0.99);
|
|
|
|
|
}
|
|
|
|
|
75% {
|
|
|
|
|
filter: hue-rotate(8deg) brightness(1.03) saturate(1.08);
|
|
|
|
|
transform: scale(1.01);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.fire-glow-bg {
|
|
|
|
|
animation: fire-glow 12s ease-in-out infinite;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Ember Floating Animation */
|
|
|
|
|
@keyframes ember-float {
|
|
|
|
|
0% {
|
|
|
|
|
transform: translateY(0px) translateX(0px) scale(0.8);
|
|
|
|
|
opacity: 0;
|
|
|
|
|
}
|
|
|
|
|
10% {
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
}
|
|
|
|
|
20% {
|
|
|
|
|
transform: translateY(-30px) translateX(15px) scale(1);
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
}
|
|
|
|
|
40% {
|
|
|
|
|
transform: translateY(-60px) translateX(-10px) scale(0.9);
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
60% {
|
|
|
|
|
transform: translateY(-90px) translateX(20px) scale(1.1);
|
|
|
|
|
opacity: 0.7;
|
|
|
|
|
}
|
|
|
|
|
80% {
|
|
|
|
|
transform: translateY(-120px) translateX(-5px) scale(0.8);
|
|
|
|
|
opacity: 0.4;
|
|
|
|
|
}
|
|
|
|
|
100% {
|
|
|
|
|
transform: translateY(-150px) translateX(10px) scale(0.6);
|
|
|
|
|
opacity: 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.animate-ember-float {
|
|
|
|
|
animation: ember-float 10s linear infinite;
|
2025-08-01 19:18:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Mobile Navigation Styles */
|
|
|
|
|
.h-safe-area-inset-bottom {
|
|
|
|
|
height: env(safe-area-inset-bottom, 0px);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Mobile responsive adjustments */
|
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
|
/* Ensure content doesn't get hidden behind mobile nav */
|
|
|
|
|
.mobile-content-padding {
|
|
|
|
|
padding-bottom: 80px; /* Space for mobile navigation */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Mobile navigation backdrop blur support */
|
|
|
|
|
@supports (backdrop-filter: blur(16px)) {
|
|
|
|
|
.mobile-nav-backdrop {
|
|
|
|
|
backdrop-filter: blur(16px);
|
|
|
|
|
-webkit-backdrop-filter: blur(16px);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Fallback for browsers without backdrop-filter */
|
|
|
|
|
@supports not (backdrop-filter: blur(16px)) {
|
|
|
|
|
.mobile-nav-backdrop {
|
|
|
|
|
background-color: rgba(var(--bg-secondary-rgb), 0.95) !important;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-23 22:26:54 -04:00
|
|
|
}
|