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>
This commit is contained in:
varutasu 2026-06-04 12:23:22 -05:00 committed by GitHub
parent f3faeab788
commit c5c31ba813
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 121 additions and 61 deletions

View file

@ -845,14 +845,24 @@ export default function Layout({ children, user = null, showSearch = false }) {
className="hidden md:flex md:flex-col md:gap-4 w-64" className="hidden md:flex md:flex-col md:gap-4 w-64"
aria-label="Sidebar" aria-label="Sidebar"
> >
{/* Nav chip */} {/* Nav chip. 2026-06-04 two-light-source pass: prepended the
two edge-light tokens so the nav chip's bottom-left and
top-right edges pick up warm + cool directional glows from
the matching corners of the body gradient the chip
visually "responds" to the two light sources the operator
asked for. The chip is positioned at the top-left of the
viewport so it sits closer to the warm light source; that's
a coincidence of layout, not something the token reacts to,
but the warm glow does naturally read stronger on this chip
because the warm source is right there. */}
<div <div
className="flex-1 flex flex-col md:rounded-3xl md:overflow-hidden min-h-0" className="flex-1 flex flex-col md:rounded-3xl md:overflow-hidden min-h-0"
style={{ style={{
background: 'var(--glass-surface-mid)', background: 'var(--glass-surface-mid)',
backdropFilter: 'blur(var(--glass-blur-mid)) saturate(var(--glass-saturate))', backdropFilter: 'blur(var(--glass-blur-mid)) saturate(var(--glass-saturate))',
WebkitBackdropFilter: 'blur(var(--glass-blur-mid)) saturate(var(--glass-saturate))', WebkitBackdropFilter: 'blur(var(--glass-blur-mid)) saturate(var(--glass-saturate))',
boxShadow: 'var(--rim-light-inner), var(--elevation-ambient)', boxShadow:
'var(--edge-light-warm), var(--edge-light-cool), var(--rim-light-inner), var(--elevation-ambient)',
}} }}
> >
<div className="p-4 h-full flex flex-col w-full"> <div className="p-4 h-full flex flex-col w-full">

View file

@ -315,7 +315,15 @@ export default function TopSearchBar({
background: 'var(--glass-surface-mid)', background: 'var(--glass-surface-mid)',
backdropFilter: 'blur(12px) saturate(180%)', backdropFilter: 'blur(12px) saturate(180%)',
WebkitBackdropFilter: 'blur(12px) saturate(180%)', WebkitBackdropFilter: 'blur(12px) saturate(180%)',
boxShadow: 'var(--rim-light-inner), var(--elevation-ambient)', // 2026-06-04 two-light-source pass: prepended edge-light
// tokens so the header's bottom-left and top-right edges
// pick up warm + cool directional glows. The header sits
// near the top-right of the viewport so the cool source
// reads stronger on it (the purple light is right above);
// the warm rim still appears on the bottom-left edge of the
// chip, just dimmer at this distance from the ember pool.
boxShadow:
'var(--edge-light-warm), var(--edge-light-cool), var(--rim-light-inner), var(--elevation-ambient)',
}} }}
> >
{/* Search affordance read-only-ish input that opens the {/* Search affordance read-only-ish input that opens the

View file

@ -200,47 +200,52 @@ body {
} }
/* Apply theme colors. /* Apply theme colors.
The hearth gradient: a soft warm wash that gives glass surfaces a real The hearth gradient: two diagonal light sources that the floating
substrate to blur and distort. Without this the page bg is flat warm- chrome chips visually "respond" to via the --edge-light-warm /
white and the blur is functionally invisible (the chief reason the --edge-light-cool design tokens (defined further down in :root /
first 3 redesign PRs landed but the user said "still looks the same"). [data-theme="dark"]).
Two layers: - Warm ember light source: bottom-left of the viewport.
- A radial ember glow biased to the bottom-left (the seat of a fire) - Cool purpley-blue light source: top-right of the viewport.
- A soft vertical gradient from cooler-top to warmer-bottom The base linear-gradient runs along the same 45deg diagonal between
Fixed attachment so scrolling content slides over the gradient rather the two sources so the page itself reads as a single continuous
than pulling it along that's what gives glass surfaces their wash, not just two corner pools on a flat substrate. Pushed harder
parallax-blur behavior. */ 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. */
body { body {
background-color: var(--bg-primary-light); background-color: var(--bg-primary-light);
background-image: background-image:
/* Bright amber ember pool, bottom-left — "seat of the fire". */ /* Warm ember pool, bottom-left — primary light source. */
radial-gradient( radial-gradient(
ellipse 75% 55% at 8% 108%, ellipse 90% 65% at 5% 110%,
rgba(255, 110, 0, 0.42) 0%, rgba(255, 95, 0, 0.55) 0%,
rgba(255, 140, 30, 0.22) 28%, rgba(255, 130, 25, 0.28) 28%,
rgba(255, 171, 64, 0.08) 55%, rgba(255, 171, 64, 0.10) 55%,
transparent 78% transparent 80%
), ),
/* Secondary amber, bottom-right — balances wide displays. */ /* Purpley-blue pool, top-right secondary light source (replaces
the prior gold accent). Indigoviolet so the chip rims that
respond to this source via --edge-light-cool read as cool. */
radial-gradient( radial-gradient(
ellipse 60% 50% at 98% 105%, ellipse 85% 65% at 100% -8%,
rgba(255, 130, 20, 0.30) 0%, rgba(99, 102, 241, 0.42) 0%,
rgba(255, 171, 64, 0.10) 45%, rgba(124, 58, 237, 0.20) 30%,
transparent 72% rgba(167, 139, 250, 0.08) 58%,
transparent 80%
), ),
/* Gold accent, top-right — "smoke catching last light". */ /* Base diagonal wash: warm-cream bottom-left cool-violet top-right.
radial-gradient( 45deg = bottom-left top-right (matches the two light sources). */
ellipse 60% 45% at 95% -8%,
rgba(255, 171, 64, 0.24) 0%,
rgba(255, 200, 110, 0.08) 40%,
transparent 68%
),
/* Base wash: cooler-cream top → warmer-amber-tinted bottom. */
linear-gradient( linear-gradient(
180deg, 45deg,
rgba(254, 252, 248, 1) 0%, rgba(255, 240, 220, 1) 0%,
rgba(252, 244, 230, 1) 55%, rgba(252, 244, 230, 1) 50%,
rgba(250, 233, 210, 1) 100% rgba(230, 232, 252, 1) 100%
); );
background-attachment: fixed; background-attachment: fixed;
color: var(--text-primary-light); color: var(--text-primary-light);
@ -249,36 +254,36 @@ body {
[data-theme="dark"] body { [data-theme="dark"] body {
background-color: var(--bg-primary-dark); background-color: var(--bg-primary-dark);
background-image: background-image:
/* Bright ember-red pool, bottom-left the operator mockup's /* Warm ember-red pool, bottom-left primary light source.
defining visual. High alpha against deep navy makes it glow. */ Pushed to 0.72 (was 0.55) so the operator's "nice warm red"
reads as a real glow against deep navy. */
radial-gradient( radial-gradient(
ellipse 75% 55% at 8% 108%, ellipse 95% 70% at 5% 110%,
rgba(255, 75, 30, 0.55) 0%, rgba(255, 65, 25, 0.72) 0%,
rgba(255, 110, 0, 0.30) 25%, rgba(255, 95, 0, 0.42) 24%,
rgba(255, 140, 30, 0.10) 55%, rgba(255, 130, 30, 0.16) 52%,
transparent 78% transparent 80%
), ),
/* Secondary ember, bottom-right. */ /* Purpley-blue pool, top-right secondary light source. Pushed
to 0.58 (was 0.26) + a more saturated indigoviolet ramp so
the corner reads unambiguously as the second light source the
chip rims respond to. */
radial-gradient( radial-gradient(
ellipse 60% 50% at 98% 105%, ellipse 90% 70% at 100% -10%,
rgba(255, 110, 30, 0.36) 0%, rgba(139, 92, 246, 0.58) 0%,
rgba(255, 140, 50, 0.12) 45%, rgba(99, 102, 241, 0.32) 28%,
transparent 72% rgba(168, 85, 247, 0.12) 55%,
transparent 82%
), ),
/* Purple-magenta accent, top-right the "cosmic" highlight from /* Base diagonal wash: warm-tinted deep aubergine bottom-left
the mockup's dark variant. Brings TCG/celestial brand energy. */ cool-tinted deep indigo top-right. 45deg matches the light
radial-gradient( sources. The midpoint stays near the previous deep-navy so
ellipse 65% 50% at 95% -8%, chrome surfaces still have a calm substrate to blur against. */
rgba(168, 85, 247, 0.26) 0%,
rgba(124, 58, 237, 0.10) 40%,
transparent 68%
),
/* Subtle vertical wash: pure deep navy → slightly purpler bottom. */
linear-gradient( linear-gradient(
180deg, 45deg,
rgba(13, 14, 26, 1) 0%, rgba(40, 16, 22, 1) 0%,
rgba(17, 18, 38, 1) 55%, rgba(17, 18, 38, 1) 50%,
rgba(22, 18, 45, 1) 100% rgba(28, 22, 58, 1) 100%
); );
background-attachment: fixed; background-attachment: fixed;
color: var(--text-primary-dark); color: var(--text-primary-dark);
@ -340,6 +345,21 @@ body {
--bg-primary-rgb: 254, 252, 248; --bg-primary-rgb: 254, 252, 248;
--bg-secondary-rgb: 247, 243, 237; --bg-secondary-rgb: 247, 243, 237;
--bg-tertiary-rgb: 240, 230, 214; --bg-tertiary-rgb: 240, 230, 214;
/* Two-light-source edge glow tokens (2026-06-04 operator request:
"let's have the corners or the edges of our sections respond as
if those two are light sources").
Each token is a complete box-shadow declaration with the offset
pointing toward the matching light source the colored shadow
bleeds outward from the chip's bottom-left edge (warm) and top-
right edge (cool), reading as light catching on the rim.
Light theme alphas are softer; the dark theme override below
bumps them since the deeper substrate can absorb more saturation
before the effect over-reads. Compose into chip shadow stacks
ahead of rim-light-inner + elevation tokens so the directional
glow is the dominant edge cue. */
--edge-light-warm: -6px 6px 18px rgba(255, 110, 0, 0.16);
--edge-light-cool: 6px -6px 18px rgba(99, 102, 241, 0.12);
} }
/* Dark theme variables */ /* Dark theme variables */
@ -366,6 +386,13 @@ body {
--input-border: var(--input-border-dark); --input-border: var(--input-border-dark);
--input-text: var(--input-text-dark); --input-text: var(--input-text-dark);
--input-placeholder: var(--input-placeholder-dark); --input-placeholder: var(--input-placeholder-dark);
/* Dark-theme edge glows alphas bumped since the deeper substrate
can take more saturation before reading as garish, and so the
"lit by ember + lit by purple" effect carries on dark theme (the
default for first-time visitors). See :root for full rationale. */
--edge-light-warm: -8px 8px 26px rgba(255, 110, 30, 0.34);
--edge-light-cool: 8px -8px 26px rgba(139, 92, 246, 0.28);
} }
/* Custom component styles - Fire-themed */ /* Custom component styles - Fire-themed */
@ -724,10 +751,23 @@ body {
only the `-webkit-` form which modern browsers ignore. Write only only the `-webkit-` form which modern browsers ignore. Write only
the unprefixed form; Lightning CSS's autoprefixer adds the prefix the unprefixed form; Lightning CSS's autoprefixer adds the prefix
per browserslist (ios_saf <18 still needs it). */ per browserslist (ios_saf <18 still needs it). */
/* Glass panels the canonical floating-chrome surface. 2026-06-04
two-light-source pass: `--edge-light-warm` (offset toward bottom-
left) and `--edge-light-cool` (offset toward top-right) lead the
shadow stack so every glass surface picks up the directional rim
glows from the matching corners of the body gradient. Order
matters directional glows must paint OUTSIDE the structural
elevation shadow, so they sit at the top of the stack (CSS paints
the LAST listed shadow lowest visually, but for box-shadow the
list order doesn't matter for compositing what matters is each
shadow's geometry. They're listed warmcoolrimelevation as a
readability convention). */
.glass-panel { .glass-panel {
background: var(--glass-surface-mid); background: var(--glass-surface-mid);
backdrop-filter: blur(12px) saturate(180%); backdrop-filter: blur(12px) saturate(180%);
box-shadow: box-shadow:
var(--edge-light-warm),
var(--edge-light-cool),
var(--rim-light-inner), var(--rim-light-inner),
var(--rim-light-outer), var(--rim-light-outer),
var(--elevation-ambient); var(--elevation-ambient);
@ -737,6 +777,8 @@ body {
background: var(--glass-surface-high); background: var(--glass-surface-high);
backdrop-filter: blur(20px) saturate(180%); backdrop-filter: blur(20px) saturate(180%);
box-shadow: box-shadow:
var(--edge-light-warm),
var(--edge-light-cool),
var(--rim-light-inner), var(--rim-light-inner),
var(--rim-light-outer), var(--rim-light-outer),
var(--elevation-pronounced); var(--elevation-pronounced);