Noise-driven YES, sticky hero with frosted cards, full light theme
Deploy / deploy (push) Successful in 1m0s

Three changes that belong together, all verified against a real
mounted page in headless WebKit:

- yes.js's two behavior dials (containment/wiggle) now drift on a
  smooth two-octave value-noise field at cloud pace (~25s per weather
  change) instead of following the mouse. Includes a real bug fix
  found in verification: the hash's final XOR yields a SIGNED 32-bit
  value in JS, so the "0..1" noise dipped to -0.36 without a
  reinterpreting >>> 0.

- The landing hero is position: sticky, so the piece keeps animating
  behind the whole page. Cards go translucent with backdrop blur and
  a soft shadow so the piece reads faintly through and around them
  (near-opaque fallback where backdrop-filter is unsupported). The
  hero copy fades out over the first half-screen of scroll - pinned,
  it ghosted through the cards. The bottom fade gradient is gone: its
  hard-cut reason disappeared with the canvas behind everything.

- Full prefers-color-scheme light theme: warm paper, near-black ink,
  accent deepened from dusty cyan to teal ink (#8ec2c0 washes out on
  white), wordmark inverted via filter. yes.js mirrors the palette
  itself (canvas can't read CSS vars): CMYK process-ink strokes dark
  enough to carry on paper, raster ghost repainted as multiply-blended
  gray on white, live re-theme on scheme flip with a trail clear.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Bendik Aagaard Lynghaug
2026-08-16 15:53:55 +02:00
co-authored by Claude Fable 5
parent ee91b092d1
commit 1b7ffe0f43
2 changed files with 228 additions and 74 deletions
+88 -22
View File
@@ -47,11 +47,22 @@
--accent-soft: rgba(142, 194, 192, 0.16);
--paper: #0a0a0a;
--paper-raised: #161616;
--ink: #ededed;
--ink-dim: #9a9a9a;
--line: #2a2a2a;
/* Cards float over the sticky YES canvas - translucent so the piece
reads faintly through them (paired with backdrop-filter on
.alt-card), shadowed so their edge against the animated backdrop
is a soft lift rather than a hard contrast line. */
--card-bg: rgba(22, 22, 22, 0.55);
--card-bg-opaque: rgba(22, 22, 22, 0.94);
--card-shadow: rgba(0, 0, 0, 0.5);
--error: #ff6b6b;
/* Behind .hero-copy: dark halo on dark stock, paper halo on light. */
--hero-glow: rgba(0, 0, 0, 0.8);
--heading: "Quicksand", var(--sans);
--sans: -apple-system, "SF Pro Text", ui-sans-serif, "Segoe UI", system-ui, sans-serif;
@@ -64,6 +75,44 @@
font-size: max(12px, calc(2.1lvmin + 3pt));
}
/* Light theme: the same muted-press idea on white stock - warm paper,
near-black ink, and the accent deepened from dusty cyan to a teal
ink that actually carries as text on light paper (the dark theme's
#8ec2c0 washes out there). yes.js mirrors this palette on its own
(matchMedia in setupTheme) since canvas paint can't read CSS vars. */
@media (prefers-color-scheme: light) {
:root {
--accent: #47807e;
--accent-soft: rgba(71, 128, 126, 0.16);
--paper: #f6f5f1;
--ink: #1d1d1b;
--ink-dim: #6d6c66;
--line: #d9d7cf;
--card-bg: rgba(252, 251, 248, 0.55);
--card-bg-opaque: rgba(252, 251, 248, 0.94);
--card-shadow: rgba(50, 48, 42, 0.18);
--error: #a83a32;
--hero-glow: rgba(246, 245, 241, 0.85);
}
/* The wordmark SVG is a hardcoded white stroke (also used raw in
dark contexts elsewhere) - flip it to ink here rather than fork
the asset. */
.wordmark img {
filter: invert(0.92);
}
/* overlay against near-white paper resolves to ~white and the
raster ghost vanishes; multiply lets the light theme's gray YES
(see rasterizeText's themed repaint in yes.js) show as ink. */
.hero-canvas #rasterCanvas {
mix-blend-mode: multiply;
}
}
* {
box-sizing: border-box;
}
@@ -157,7 +206,14 @@ main.not-found {
than interrupting the canvas. */
.hero-yes {
position: relative;
/* Sticky at the viewport top for the whole scroll (its containing
block is the page itself), so the piece stays animating behind
everything that follows - the translucent cards scroll over it
and it shows through them and in the gaps around them. z-index 0
so positioned content below can stack above with z-index 1. */
position: sticky;
top: 0;
z-index: 0;
max-width: none;
width: 100%;
/* svh here is only the pre-JS/no-JS fallback (and first paint before
@@ -203,28 +259,17 @@ main.not-found {
z-index: 1;
padding: 0 1.5rem 3.5rem;
gap: 0.6rem;
text-shadow: 0 0.12em 1.4em rgba(0, 0, 0, 0.8);
}
/* Softens the otherwise hard cut where the full-bleed canvas meets the
page background right below it - fades to the same --paper color
over the last few ems instead of stopping dead. Non-interactive so
it never steals clicks meant for the canvas underneath. */
.hero-yes::after {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 8rem;
background: linear-gradient(to bottom, transparent, var(--paper));
pointer-events: none;
z-index: 1;
text-shadow: 0 0.12em 1.4em var(--hero-glow);
}
/* alternatives */
/* position + z-index on these three: everything that scrolls over the
sticky canvas must be a positioned box above the hero's z-index 0,
or the (positioned) hero would paint over it. */
.alternatives {
position: relative;
z-index: 1;
max-width: 75ch;
margin: 0 auto 5rem;
padding: 0 1.5rem;
@@ -232,6 +277,12 @@ main.not-found {
gap: 1.25rem;
}
.question-nav,
.question-responsible {
position: relative;
z-index: 1;
}
@media (max-width: 43.75rem) {
.alternatives {
padding: 0;
@@ -295,10 +346,25 @@ main.not-found {
}
.alt-card {
background: var(--paper-raised);
background: var(--card-bg);
/* Frosted glass over the animated canvas behind: the blur is what
keeps text on a translucent card readable while the piece still
reads through it in color and motion. */
-webkit-backdrop-filter: blur(0.8rem);
backdrop-filter: blur(0.8rem);
border: 0.06rem solid var(--line);
border-radius: var(--radius);
padding: 1.75rem 1.9rem;
box-shadow: 0 1.1rem 2.4rem var(--card-shadow);
}
/* Without backdrop blur, 0.55-alpha cards put text straight over the
moving canvas - illegible. Engines lacking the filter get a
near-opaque card instead. */
@supports not ((backdrop-filter: blur(1rem)) or (-webkit-backdrop-filter: blur(1rem))) {
.alt-card {
background: var(--card-bg-opaque);
}
}
.alt-image {
@@ -570,7 +636,7 @@ textarea:focus {
font-family: var(--sans);
font-weight: 600;
font-size: 0.95rem;
color: #0a0a0a;
color: var(--paper);
background: var(--accent);
border: none;
border-radius: 0.7rem;
@@ -609,7 +675,7 @@ textarea:focus {
}
.resource-error {
color: #ff6b6b;
color: var(--error);
font-size: 0.9rem;
}