Search topics
Checkout timers that release held seats
A ticket checkout holds the seats for 10 minutes while people type their details, then releases them and empties the form, however far they have got.
seats.hold(seats, { minutes: 10 }) The problem
People who type, read or decide more slowly lose their seats and everything they typed when the checkout's timer runs out
A small theatre sells its tickets online. Choosing two seats on the seat plan puts them on hold, so nobody else can buy them while the customer pays, and a bar at the top of the checkout says "We're holding your seats for 10:00", counting down every second. Below it, the customer types their name, an email address, any access requirements and their card. When the bar reaches zero, the page says "Sorry, your time is up. Your seats have been released." The seats are back on sale, and choosing them again brings back an empty form.
This is how the big platforms work by default. Eventbrite gives attendees 20 minutes to complete an order unless the organiser changes it, and keeps the selected tickets from everyone else until then. Ticketmaster's help pages describe a timer at the top of every checkout page, and tickets released for others to buy when it runs out. Shop plugins add the same countdown to an ordinary basket, telling customers their cart is reserved, and can empty the basket at zero.
Success Criterion 2.2.3 No Timing, at Level AAA, asks that timing is not an essential part of the event or activity, except for non-interactive synchronized media and real-time events. Unlike 2.2.1 Timing Adjustable at Level A, it has no exception for a limit that is essential, none for a limit longer than 20 hours, and no way to pass by letting people extend the limit. W3C's one sufficient technique, G5, is to let people complete the activity without any time limit.
A checkout is an activity, not media. W3C doesn't say whether a busy sale is a real-time event in WCAG's sense, one that happens as it is watched and isn't wholly generated by the content. The example 2.2.1 gives of one is an auction, and W3C files its own ticket hold under 2.2.1's exception for essential limits, not under real-time events, so this guide doesn't treat a checkout as one either. The hold puts every step after the seat plan inside a 10-minute window, so the checkout depends on timing. W3C's own examples for 2.2.1 treat a two-minute hold on tickets that sell out quickly as essential, which Level A allows. 2.2.3 doesn't.
The timer comes from when the seats are reserved. A hold made the moment someone chooses seats has to end by itself, or every checkout abandoned halfway would keep its seats off sale for good. Reserve the seats at the moment of payment instead, and there is nothing to time.
Ten minutes is enough for someone who types quickly and has their card in hand. Anyone who types, reads or decides more slowly loses the race, and loses it at the end, when most of the form is done: they have to choose seats again, and the seats they wanted may have been sold. On a phone, the on-screen keyboard covers half the screen and the countdown scrolls out of view while people type.
5 of 10 groups affected
- No vision (affected)
- Low vision (affected)
- Colour vision (not affected)
- No hearing (affected)
- Hard of hearing (not affected)
- No speech (not affected)
- Motor (affected)
- Reach, strength (not affected)
- Cognitive (affected)
- Seizures (not affected)
- Limited manipulation Typing a name, an email address, access requirements and a card number with a switch, a head pointer or voice control can take longer than 10 minutes on its own, so the seats go before the card is in.
- Limited language, cognitive and learning abilities People who read slowly, check each field twice or need to find their card meet the end of the timer while they are still working, and a number that changes every second beside the form makes it harder to think about the form itself.
- Without vision Moving through the form field by field with a screen reader takes longer, and the countdown, which is not announced as it changes, only shows how much time is gone when they go back to look for it.
- Limited vision Screen magnifier users zoomed in on the form don't see the countdown at the top of the page, so the first they know of the limit is the message that it is over.
- Without hearing Deaf people who sign may read written language as a second language, and W3C notes they can need more time to read text, so the seat details and the form can take them longer than the hold allows.
The release happens in a script's timer or in the server's reservation expiry, and neither shows in the page a scan reads. The audit sends a page to a person, to check against 2.2.1, when it has a timer role or a countdown beside words such as "held", "reserved" or "time left", but it has no check for 2.2.3: a timer that can be extended passes 2.2.1, and only reading the checkout's code shows whether any step still depends on time. An AI assistant asked how to stop two people buying the same seat may suggest the same expiring hold, the pattern the ticketing platforms document, and nothing in that request asks who the timer leaves out. Only someone who fills in the checkout at their own pace, or reads the reservation code, finds the failure.
Try it
Book the two seats, then skip ahead as if the form took you longer, and see what is left of your order
The demo clock above the window stands in for the minutes; it is not part of the theatre. Press Continue to start the checkout, then skip ahead. When the hold runs out, choose the seats again and look at the form.
Apply the fix, skip ahead an hour and press Pay.
Broken Can you fix this?
The Lighthouse Keeper
Sat 14 Nov, 7:30 pm
Row F, seats 7 and 8 · $48.00
The fix
Reserve the seats when people pay, not while they type
<p id="hold" data-expires="2026-11-14T18:40:00Z">We're holding your seats for <span id="hold-time">10:00</span></p><form method="post" action="/orders"> <input type="hidden" name="hold" value="h_81f2"> <label for="name">Full name</label> <input id="name" name="name" autocomplete="name"> <label for="email">Email</label> <input id="email" name="email" type="email" autocomplete="email"> <label for="access">Access requirements (optional)</label> <textarea id="access" name="access"></textarea> … <button>Pay $48.00</button></form>What is wrong3 points
- The seats are held for 10 minutes from the moment they are chosen, however long the form takes.
- At zero, the page goes back to the seat plan and the seats go back on sale.
- Everything typed into the form is lost with the page.
<p>Your seats are booked when you pay.</p><form method="post" action="/orders"> <input type="hidden" name="seats" value="F7,F8"> <label for="name">Full name</label> <input id="name" name="name" autocomplete="name"> <label for="email">Email</label> <input id="email" name="email" type="email" autocomplete="email"> <label for="access">Access requirements (optional)</label> <textarea id="access" name="access"></textarea> … <p id="seats-gone" tabindex="-1" hidden></p> <button>Pay $48.00</button></form>How it is fixed4 steps
- Delete the hold, its expiry and the countdown. Choosing seats now only fills in the form's
seatsfield, so nothing on the page or the server runs out. - Check and reserve the seats in the same request that takes the payment, so the only moment that matters is the moment people press Pay.
- Send the form with
fetch()and stay on the page. If the seats have gone, no payment is taken and every field still holds what was typed. - Write what happened beside the Pay button, with the nearest free seats, and move focus to it, so people hear it, see it and only have to press Pay again.
This fixes the Before and passes 2.2.3: no step of the checkout depends on time. The seats can still be sold to someone else while people type, and the message is how the checkout says so without costing them the form.
Why this fix: Change the page only when people ask
A sale that sells out in minutes. Without a hold, seats can go while someone types, and the slowest typist is the likeliest to find them gone. 2.2.3 takes the clock away, not the competition. For a sale that busy, W3C's own 2.2.1 example keeps a short hold and treats it as essential, which passes Level A and not 2.2.3. A sale that wants to pass AAA can take requests over days and allocate the seats afterwards, a ballot, so that nobody's place depends on how fast they type. W3C's technique G5 gives the same answer for an auction: one bid each, instead of bids raced against each other. W3C does not recommend requiring AAA for a whole site, because some content can't meet every AAA criterion; this is where a ticketing site may decide not to.
Shop baskets. A countdown on an ordinary shop's basket is a time limit too, whether or not it really holds the stock, and the plugins that add it present it as a way to create urgency and cut abandoned baskets. Delete it, and check the stock in the payment request, as the fix does for seats.
Card fields. A card typed into a payment provider's frame stays in that frame while the page stays open, so sending the form with fetch() keeps it as well. A form that posts and renders a new page can put back the name and the access requirements, but not the card number, which a site should never write back into a page.
The message. Write it in short sentences that say what went, what is offered and what to do, beside the button people pressed. If nothing is left, say that instead, and still take no payment. Moving focus to it reads it out and brings it into a magnifier's view; how form results are announced in general is the topic of form success and error messages that are never announced.
Not a fix. A "Need more time?" button that extends the hold passes 2.2.1, which has its own guide, session timeouts with no warning or way to extend, but people still have to finish inside a limit, so 2.2.3 still fails. So does moving the details and the card ahead of the seat plan, as W3C's 2.2.1 example suggests: the timed part gets shorter, but the seat choice and the Pay button are still inside it. A longer timer, such as 30 minutes instead of 10, is still a timer.
Verify the fix
5 checks, no mouse.
Test with: Keyboard, Screen reader, Zoom
Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the reservation code or the checkout changes.
Limits
This covers a checkout that holds seats or stock on a timer, the common form of 2.2.3 on shops and booking sites. A quiz or a test with a timer is the other: W3C's answer there is to score it without a time limit, calibrated on how people do when nobody is timed. A session that signs people out, and a warning that lets them extend it, are 2.2.1: session timeouts with no warning or way to extend. Live events, such as an auction with people bidding at the same time, and video or audio that only plays, are outside 2.2.3. Whether a ticket sale can go without a hold is for the people who run it to decide, with what they know about how fast it sells.
This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.
References
- WCAG Understanding: no-timing (opens in a new tab)
- WCAG Technique G5 (opens in a new tab)
- WCAG Understanding: timing-adjustable (opens in a new tab)
- WCAG Understanding: conformance (opens in a new tab)
- eventbrite.com: how-to-increase-or-decrease-the-amount-of-time-to-complete-an-order (opens in a new tab)
EaseWeb service
Fixed it? Keep it from coming back.
I turn guides like this one into rules your coding agents follow and checks your CI runs. Then I retest by hand with a keyboard and screen readers, periodically.