A devlog by Neeraj Kotwani, who builds every game on Elipar.
I was running an accessibility sweep across every game here: focus each mute button, press Enter, assert the mute actually toggles. Not just that the element renders as a button — that the thing happens.
Forty-three games passed. Ski Slalom did not.
And nothing in its mute code was wrong. The button was a real <button> with a proper label, the handler was correctly bound, and clicking it with a mouse worked perfectly.
The browser turns Enter into a click, and something ate it
The game had a window-level key handler for steering. It listed the arrow keys, A and D for left and right, and — reasonably — Space and Enter for "start" and "restart". Then it called preventDefault() on all of them, unconditionally, to stop the page scrolling when you press Space mid-run.
Here is the part that is easy to miss. When a <button> has focus and you press Enter or Space, the browser generates a synthetic click event. That is the entire mechanism by which keyboard activation works. It is the default action of that keypress.
So calling preventDefault() on Enter at the window level, during the capture of a key that was aimed at a focused button, suppresses the click the browser was about to synthesise. The handler ran, the default was cancelled, and the button never heard anything.
Which means it was not the mute button. It was every button in the game — mute, start, play again. All of them keyboard-dead, for anyone navigating by keyboard. A real accessibility failure with no visual symptom whatsoever.
The fix, and why its shape matters
The handler now returns early when the event target is an interactive control — a button, an input, a select, a textarea, a link, or anything contenteditable. Outside those, it behaves exactly as before.
Note what the fix is not. It is not "stop preventing the default on Enter", because then Space scrolls the page mid-run, which is the thing the line was there for. Both behaviours are correct; they just apply to different targets. A global keyboard handler in a game needs to know when the keyboard is not talking to the game.
I grepped the other games for the same pattern and found only this one. But grep is the wrong instrument here, which is the real lesson:
There is no pattern to search for. The mute code was fine, the button markup was fine, and the bug lived in an unrelated function three hundred lines away. The only thing that finds this is a test that focuses the control, presses the key, and then asserts the game's own state changed — not that the element is focusable, not that it is a button, not that the handler exists. All of those were true.
The sweep that came out of it covers all forty-four games and asserts the button's own class or label actually changed after the keypress. Forty-four of forty-four now pass.
The other half of that sweep: twenty-three dead mute buttons
While I was there, the same sweep checked something different and found a bigger problem: twenty-three of forty-four mute buttons were painted but unclickable.
Every one of them rendered a mute button that the game's own full-screen title card was covering. It looked live and did nothing — on the exact screen where somebody silences a game before it has made any sound. Mute worked normally once you were playing, which is why it survived: anyone testing mid-run sees nothing wrong.
Checked by asking the browser what element is actually at the button's own centre point, rather than whether the button is visible. Those are different questions and only one of them is the question.
The detector itself had to be validated before the count could be trusted, because a hidden element still reports a bounding box and the hit test then returns whatever is behind it — so "covered by the title card" and "deliberately not shown yet" produce an identical reading. A second pass walking each element's ancestors for visibility separated them: all twenty-three were genuinely painted, and three more were correctly hidden and not bugs at all.
Nineteen were a stacking-order bump. Four could not be fixed that way and are the interesting case: their button lives inside a HUD element that has both a position and a stacking order, which creates a stacking context — so no value on the button can lift it past a sibling overlay, however large. Those needed the HUD itself raised with its readouts hidden behind the card, which is a visual improvement rather than a trade.
Ski Slalom itself got the full audio stack in the same pass. It had effects and a mute flag but no music, so it got a sixteen-step alpine pulse — scheduled with a separate note function taking an absolute clock time, because the existing effects helper takes a relative delay and that goes negative the moment the loop falls behind, silently dropping notes into the past.
Ski Slalom is free in a browser, no account, no download. Every button works from the keyboard.