The pre-paint script in site/index.html writes ?theme= / localStorage straight onto data-theme without checking the registry. site/js/main.js then resolves:
const initial = THEMES[queried] ? queried
: THEMES[stored] ? stored
: (root.dataset.theme || "hum");
The last fallback reads back the attribute the pre-paint script just set, so an unrecognised value passes through and is then written to localStorage — the bad state persists on every later visit, with no query param needed.
Repro: open /?theme=totally-not-a-theme, then reload /.
htmlDataTheme: "totally-not-a-theme"
persisted: "totally-not-a-theme"
bannerLabel: "Specimen"
ordinal: "01 / 20"
pressedDots: []
The page renders :root tokens while the banner claims Specimen, no dot is pressed, and the ordinal is meaningless. This also bites organically: any theme renamed or dropped in a future release leaves returning visitors stuck until they press T or R.
The pre-paint script in
site/index.htmlwrites?theme=/ localStorage straight ontodata-themewithout checking the registry.site/js/main.jsthen resolves:The last fallback reads back the attribute the pre-paint script just set, so an unrecognised value passes through and is then written to localStorage — the bad state persists on every later visit, with no query param needed.
Repro: open
/?theme=totally-not-a-theme, then reload/.The page renders
:roottokens while the banner claims Specimen, no dot is pressed, and the ordinal is meaningless. This also bites organically: any theme renamed or dropped in a future release leaves returning visitors stuck until they press T or R.