Movie theater seat map: broken, then fixed
The same 42 seats twice. First as the ticketing platform ships them, then rebuilt. Identical data, identical layout. Turn on a screen reader and move through both.
RoadHouse Cinemas Colorado Springs, COYOTE VS ACME, session RS498145064779-087974000008. Markup captured live on August 24 2026 and reproduced verbatim below, minus per-seat tooltip boilerplate that is hidden from the accessibility tree. 42 seats, none sold in this showing.
As shipped
This section was rebuilt on August 24 2026. Between the original audit in July and now, RoadHouse fixed part of what the audit found, so what follows is the current live markup rather than the version that was first reported.
What they fixed
- Every seat cell now carries an
aria-label, so seat identity and availability reach the accessibility tree:aria-label="Seat A10 is available". - Seat type is exposed too, which it previously was not:
aria-label="Seat B6 is an available wheelchair space". - The grid is now marked up with
role="grid",role="row"androle="gridcell", plusaria-selectedandaria-disabledon each cell.
What is still broken
- All 42 seat checkboxes still have no accessible name. No
<label>, noaria-label, noid, every naming attribute empty at the platform layer. The name landed on the cell, not the control inside it. - Eight cells are named "Seat noSeat is available." The aisle gaps have no seat and no checkbox, and a template variable leaks straight into the accessible name. A screen reader user crossing the grid hears it eight times.
- Row letters are still
<span>, not headings, so reaching row D still means passing every seat before it. - The legend is loose static text. Nothing binds "Wheelchair Space" or "Unavailable" to any seat that is one.
The part that disappears below 900 pixels
Because the checkbox has no name of its own, VoiceOver falls back to reading text sitting next to it, and the only text there is the seat number. That number is governed by one line in the ticketing platform's stylesheet:
@media only screen and (max-width:900px){ .seatGrid__colLabel{ display:none } }
Below 900 pixels of viewport width the seat number is removed from the page. Not hidden visually, removed, so it leaves the accessibility tree entirely, in every browser and for every screen reader. Measured on the live page in Safari: 67 text nodes at 1500 pixels wide, 7 at 800. The 42 seat numbers are simply gone.
Widen your window past 900 pixels and the seats announce as "10, unchecked checkbox." Narrow it
and the same seats announce as "unchecked checkbox," 42 times, with nothing to tell them apart.
Arrowing still gives you the cell's aria-label, but the form-control path, which is
how you would actually pick a seat, has no per-seat identity left.
That is the phone case, and most people buy movie tickets on a phone.
You can hear it in this reproduction: the media query above is included, so resize this window across 900 pixels and the numbers come and go. In Safari, Develop > Enter Responsive Design Mode (Control-Command-R) does the same thing.
The point is not the breakpoint. The point is that a control with no accessible name is riding on a visual detail, so a purely visual decision can delete its only identity, and nothing fails, warns, or gets noticed.
Front of theater
Legend, exactly as the page supplies it: loose text, tied to no seat.
Rebuilt
- Each seat is named from data already in the page: "Row C, seat 7, love seat, sold, not available."
- Sold seats carry
aria-disabledand refuse selection from any input method, not just the mouse. - Row letters are headings, so heading navigation jumps straight to a row.
- Purchase Tickets and Deselect All are real
<button>elements. - Selections are announced in a status region.
- No visual change to the grid.
Screen
Row A, 10 of 10 available
Row B, 8 of 8 available
Row C, 5 of 8 available
Row D, 3 of 8 available
Row E, 2 of 8 available
No seats selected.
WCAG 2.2
1.3.1 Info and Relationships, 1.4.1 Use of Color, 2.1.1 Keyboard, 2.4.6 Headings and Labels, 4.1.2 Name, Role, Value.