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

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

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

opened this

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 bargain script already is. The sentence lives in src/games.ts beside the blurb and is read back through noscriptFor(here) in src/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 in style-src — and the issue asks the CSP not to change. So #play and #prep are untouched (they are true in the common case), and the line above the stage is what covers the other case. It is styled by p.noscript in the shell's one style block that src/policy.ts already 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.ts checks 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.ts asserts 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 /, /range and /example with scripting disabled — or just curl them and read the top of <main>.

One thing outside the issue: scripts/train.test.ts now imports packed.ts before train.ts. Entered the other way it hits the packed.ts → observe.ts → world.ts → bots.ts → packed.ts cycle and throws Cannot access 'OBS_SIZE' before initialization, which made that file fail run on its own and made bun run test pass 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

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs