Public: anyone can read this; the crew can change it.
forked from trevato/cs
JavaScript off is a sentence, not a dead Play button: one noscript line in the shell, on all three games #249
Check · Review · Land · Live done — Landed and live in production.
With JavaScript off, every game page now says so in one sentence instead of showing a Play button that will never enable. The shell takes one new optional parameter, `noscript`: a game hands in its own sentence and gets a `<noscript>` line at the top of `<main>`, directly above the stage the dead control sits in; a page that runs nothing — the 404 and the 500 — hands in none and gets no element at…
shipped production is serving it deploy ae07c2d
Sign in to join this conversation.
new above
offline
With JavaScript off, every game page now says so in one sentence instead of showing a Play button that will never enable. The shell takes one new optional parameter,
noscript: a game hands in its own sentence and gets a<noscript>line at the top of<main>, directly above the stage the dead control sits in; a page that runs nothing — the 404 and the 500 — hands in none and gets no element at all, the same bargainscriptalready is. The sentence lives insrc/games.tsbeside the blurb and is read back throughnoscriptFor(here)insrc/pages/games.ts, so it is that game's own prose and a fourth game grows one by being written down.I chose to explain the disabled Play button rather than stop rendering it: the only way to hide a control from a scriptless browser and no other is a second inline
<style>inside<noscript>, which would need its own hash instyle-src— and the issue asks the CSP not to change. So#playand#prepare untouched (they are true in the common case), and the line above the stage is what covers the other case. It is styled byp.noscriptin the shell's one style block thatsrc/policy.tsalready hashes: no inline style, no second style element, no second request, and no layout shift, because a browser that runs scripts renders none of it. The leaderboard, the recent matches, the prose and the terminal line are unchanged.Tests:
src/pages/shell.test.tschecks placement (inside<main>, before the stage, above the footer), escaping, and the absence of the element for a page with no sentence; a loop over the registry holds every game's line to being one sentence that names that game and no other.src/server.test.tsasserts the same against the served pages and that the 404 and the 500 carry no such line. To look at it in the preview, load/,/rangeand/examplewith scripting disabled — or justcurlthem and read the top of<main>.One thing outside the issue:
scripts/train.test.tsnow importspacked.tsbeforetrain.ts. Entered the other way it hits thepacked.ts→observe.ts→world.ts→bots.ts→packed.tscycle and throwsCannot access 'OBS_SIZE' before initialization, which made that file fail run on its own and madebun run testpass or fail by which file bun loaded first — I saw the same tree do both. The reorder is a workaround with a comment saying so; #248 is the real fix, and it is not this pull's.🤖 Generated with Claude Code
Closes #161