Snake Classic Game is designed to be fully operable by keyboard and usable with screen readers. It implements WAI-ARIA 1.2 patterns including live regions, modal dialogs withDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/ac-unefm/snake-game/llms.txt
Use this file to discover all available pages before exploring further.
aria-modal, focus management, and a skip link. Every interactive element meets the WCAG 2.5.5 minimum touch target size of 44×44 px (the help button is explicitly sized to 2.75rem × 2.75rem ≈ 44 px).
Skip link
The very first element in<body> is a visually hidden skip link:
.skip-link class positions the element off-screen (top: -100%) until it receives focus, at which point it slides into view at the top of the viewport. This allows keyboard and screen-reader users to bypass the header and jump directly to the game canvas region (#game-region) without tabbing through every preceding element.
ARIA live regions
Dynamic content is surfaced to assistive technologies through dedicated live region elements:| Element | Role / Attribute | Purpose |
|---|---|---|
#scoreEl | aria-live="polite", aria-atomic="true" | Announces score changes |
#levelEl | aria-live="polite", aria-atomic="true" | Announces level changes |
#session-bar | role="status" | Session progress dot indicator |
#message | role="status", aria-live="polite", aria-atomic="true" | Game status messages (game over, next attempt prompt) |
#game-alert | role="alert", aria-live="assertive", aria-atomic="true" | Urgent announcements (level-up, game over, session end) |
aria-atomic="true" on score and level elements tells screen readers to announce the entire element content when any part changes, rather than only the changed text node.
announceAlert(txt)
Level-up and game-over events use #game-alert (role="alert", aria-live="assertive") to interrupt the screen reader immediately. However, assistive technologies often ignore a live region update if the new text is identical to the previous content. announceAlert() works around this by clearing the element first, then setting the new text after a 50 ms delay:
textContent to '' forces the DOM mutation event to fire even when the same message is announced twice in a row (e.g., two consecutive GAME OVER announcements). The 50 ms gap gives the browser time to process the empty-string mutation before the new text is set.
Modal dialogs
Both#help-overlay and #results-overlay follow the WAI-ARIA dialog pattern:
aria-hidden="true"when closed — the overlay is present in the DOM but invisible to assistive technologies. The attribute is removed (removeAttribute('aria-hidden')) when the dialog opens and restored (setAttribute('aria-hidden', 'true')) when it closes.aria-labelledby— points to the dialog’s<h2>element, which screen readers announce as the dialog’s accessible name when focus enters it.- Focus management —
openHelp()moves focus tobtnCloseHelpwhen the help dialog opens;showSessionResults()moves focus tobtnNewSessionwhen the results dialog opens. - Focus restoration —
closeHelp()returns focus to the canvas (canvas.focus()) so keyboard navigation resumes exactly where the player left off. - Focus trap in the help modal — keyboard handling in the
keydownlistener ignores all game keys whilehelpOverlay.classList.contains('visible')is true, effectively trapping interaction within the modal until it is dismissed.
Keyboard-only operation
Every interactive element is reachable by Tab in document order. The canvas itself hastabindex="0" so it is a valid focus target and can receive keydown events without requiring a wrapper:
focus-visible to show outlines only during keyboard navigation (not on mouse click):
| Key | Action |
|---|---|
↑ ↓ ← → | Move the snake (also starts the game if idle) |
Enter / Space | Start the game or advance to the next attempt |
H / ? | Open the help dialog |
Escape | Close the active modal (help or results) |
prefers-reduced-motion
The When This ensures the game is comfortable for users who experience motion sickness or vestibular disorders from animated content.
reducedMotion constant is evaluated once at startup:true:- The food pulse animation is replaced with a static radius and fixed
shadowBlur. - All CSS
transitiondeclarations on interactive elements (skip link, session dots, buttons, footer links) are removed via:
Content Security Policy
Anti-XSS DOM construction
All dynamic HTML generated at runtime — session results rows, stat grid items, and session labels — is built withdocument.createElement and textContent assignments. Game-derived data is never written via innerHTML. The only uses of innerHTML in the codebase are two container-clearing assignments (resultsTbody.innerHTML = '' and statsGrid.innerHTML = '') that empty the containers before they are repopulated with safe createElement calls — no untrusted data is involved: