- Shipped
- September 6, 2026 at 1:56 PM UTC
- Author
- Kamo
- Commit
- 56dd454
On ~84 days a year the marketing site now decorates itself, automatically, from the reader's own calendar. Snow falls through December and settles on the cards it lands on. Two beams of light stand still on September 11. Easter hides eggs out and hands you a lantern. Eighteen scenes, one per occasion, each a different mechanic rather than the same particle field in a different colour. WHY THE CLIENT DECIDES THE DATE Two independent reasons, both of which look fine in development. These routes are statically prerendered — check-static-routes.mjs exists to keep them that way — so a page built in August carries August's answer forever, and Christmas would arrive at the next deploy rather than on December 1st. And "is it Christmas" is a question about the READER's calendar: a server in UTC tells someone in Auckland it is still Christmas Eve for most of their Christmas Day. HolidayLayer reads new Date() after mount, re-resolves at the reader's own local midnight, and nothing in the tree touches cookies() or headers(). All 187 routes stay prerendered. WHAT IT COSTS ON AN ORDINARY TUESDAY Nothing eager. The resolver rides in ChromeBundle, which was already fetched at idle on every page, for the reason written on that file: two lazy chunks that always load together are one request too many. Past a further dynamic() sit the greeting and eighteen separately-split scenes, ~8-20 KB each, so a visitor on Halloween downloads the pumpkins and not the snow. Verified: no holiday chunk is referenced anywhere in the prerendered HTML. THE WINDOWS DO NOT COLLIDE, AND THAT IS NOT AN ASSUMPTION Four windows are pinned to moving feasts. Easter slides 35 days dragging Mardi Gras with it; Thanksgiving slides 7 dragging Black Friday and Cyber Monday, which can land inside the Christmas window; and the football championship is the second Sunday in February, which IS February 14 in 2027 and 2038. check-holiday-calendar.ts walks 2026-2040 and asserts the resolution. It earned its place immediately: it found three years where a scene vanished for the WHOLE year — the Big Game in 2027 and 2038, Mardi Gras in 2040 — each swallowed by Valentine's. The fix is not cosmetic. With one shared day-of boost the constraints are unsatisfiable: for the game to hold its Saturday its base must exceed Valentine's, and for Valentine's to hold the 14th its base must exceed the game's. A shared boost cancels and the two contradict. So the boost belongs to the holiday, and the number now says something true — how hard a day insists on being itself. The two remembrance days sit at priority 95 and the guard asserts separately that nothing ever covers them, because a priority is easy to edit without noticing what it was protecting. TONE IS ENFORCED, NOT IMPLIED patriot-day and veterans-day are tone: "solemn": no bursts, no confetti, no sparkle, no click reward, and copy with no exclamation mark. The greeting drops its glow and its coloured title for them. The site marks those days; it does not sell them. LICENSING Every scene is drawn at runtime from canvas paths and gradients authored here. No third-party artwork, fonts, logos, photographs, audio or character likenesses. Two names are deliberate: "The Big Game" never reaches for the championship's actual trademarked name, and Black Friday's price tags carry no number or percentage, because a discount on screen is a claim and this is decoration. THE FOOTER PICKER IS TEMPORARY Seventeen of the eighteen are unreachable on any given day, so the footer carries a "Seasonal scene" control, and ?holiday=<id> makes a dressed page shareable. HolidayPicker.tsx lists the three deletions that remove it. Defects found and fixed on the way, recorded because each was invisible to the compiler: reduced-motion still frames drew before useShelves had sampled (child effects run before the parent's); quality resolved in an effect AFTER onResize had sized the particle field, so a desktop drew a phone's worth of snow for the life of the page; and the overlay layers originally sat above the cookie consent banner and the toast stack, which meant Halloween's dusk sheet was dimming a visitor's consent choices by 34%. Dictionary first: the greeting copy landed in kamo-translation-dictionary as 52974da5 before this commit, because messages/ is gitignored here and copied in at image-build time. copy.ts carries an English fallback for that window anyway — the greeting asks t.has() before t(), so a dictionary one deploy behind can never put a key path on screen. tsc clean, all 14 build guards green, next build succeeds, 187 routes prerendered.