web,prototype: dust motes go light-on-dark in dark themes (#360)
The loader's stage dust used --muted for every speck — a mid-grey that all but vanishes on a dark background. The dust now contrasts with the floor it sits on: dark specks (--muted) on a light background, LIGHT specks (--ink) on a dark one. The product loader judges darkness by the RESOLVED body background luminance (the user's theme + color-scheme decide it, not the OS media query alone); the prototype uses the OS query (it has no theme picker) and re-seeds on scheme change.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2 files changed,
+12
−2
prototypes/space-lever.html+5 −1web/static/shell.js+7 −1
diff --git a/prototypes/space-lever.html b/prototypes/space-lever.html| index b517029..393810b 100644 |
| --- a/prototypes/space-lever.html |
| +++ b/prototypes/space-lever.html |
| @@ -239,7 +239,10 @@ function initDust() { |
| const g = canvas.getContext("2d"); |
| g.scale(dpr, dpr); |
| const css = getComputedStyle(document.documentElement); |
| - const ink = css.getPropertyValue("--muted").trim(); |
| + // Dust must contrast with the floor it sits on: dark specks (muted) on a light |
| + // background, LIGHT specks (ink) on a dark one — muted-on-dark all but vanishes. |
| + const darkMode = matchMedia("(prefers-color-scheme: dark)").matches; |
| + const ink = css.getPropertyValue(darkMode ? "--ink" : "--muted").trim(); |
| const accent = css.getPropertyValue("--accent").trim(); |
| const parts = Array.from({ length: P.motes }, () => ({ |
| x: Math.random() * w, y: h - 2 - Math.random() * 2, vx: 0, vy: 0, |
| @@ -539,6 +542,7 @@ bind("hold", "hold"); bind("shake", "shake"); bind("crank", "crank"); |
| bind("kick", "kick", (v) => v.toFixed(2)); bind("motes", "motes"); bind("build", "build"); |
| addEventListener("resize", initDust); |
| +matchMedia("(prefers-color-scheme: dark)").addEventListener("change", initDust); |
| initDust(); |
| rafId = requestAnimationFrame(tick); |
| </script> |
diff --git a/web/static/shell.js b/web/static/shell.js| index 484d9d6..4cf51fb 100644 |
| --- a/web/static/shell.js |
| +++ b/web/static/shell.js |
| @@ -507,7 +507,13 @@ function initDust() { |
| if (!g) return; |
| g.scale(dpr, dpr); |
| const css = getComputedStyle(document.documentElement); |
| - const ink = (css.getPropertyValue("--muted") || "#888").trim(); |
| + // Dust must contrast with the floor it sits on: dark specks (muted) on a light |
| + // background, LIGHT specks (ink) on a dark one — muted-on-dark all but |
| + // vanishes. Judge dark by the RESOLVED page background (the user's theme + |
| + // color-scheme decide it, not the OS media query alone). |
| + const bgRGB = ((getComputedStyle(document.body).backgroundColor || "").match(/\d+/g) || []).map(Number); |
| + const darkMode = bgRGB.length >= 3 && 0.299 * bgRGB[0] + 0.587 * bgRGB[1] + 0.114 * bgRGB[2] < 128; |
| + const ink = (css.getPropertyValue(darkMode ? "--ink" : "--muted") || "#888").trim(); |
| const accent = (css.getPropertyValue("--accent") || "#7c7cf4").trim(); |
| const parts = Array.from({ length: Math.min(110, Math.max(60, Math.round(w / 14))) }, () => ({ |
| x: Math.random() * w, |