Skip to content

Which booking widgets are covered ​

The Hospitality Booking test is a specialised scan for the interactive widgets that drive hotel booking flows — search, dates, guests and room selection.

Most accessibility scanners only see the page as it first loads. The interesting parts of a booking form are hidden until you interact with them: the destination list, the calendar, the guests panel. This test opens each one the way a visitor would, and runs the accessibility checks inside the panel once it is open.


How a run works ​

When the test is pointed at a booking-flow URL, it works through this loop automatically, with no manual interaction:

StepWhat happens
1. Page loadA browser opens the URL with the configured viewport, cookies and zoom level, and scopes the audit to the booking form rather than the whole page.
2. Clear the wayCookie banners and any modal dialogs already on screen are dismissed, so they do not pollute the audit.
3. Widget discoveryThe page is scanned for the fields and buttons that open booking panels, through nine layered detection passes (see below).
4. Per-widget loopFor each one: open it (click or press Enter / Space / arrow keys), navigate through the panel with Tab and arrow keys recording every element that receives focus, check each of those elements, then close the panel before moving on.
5. Checks per elementThe same checkpoints the Detector runs elsewhere, applied inside the open panel: keyboard reachability, keyboard operability, focus visibility, text contrast (normal, hover and focus states), non-text contrast, and accessible name & role.
6. AggregationFindings are deduplicated — the same failure across several widgets is collapsed into one — sorted in page order, and enriched with a suggested fix.

Each finding carries a title, a description, the location on the page, the HTML involved, and the WCAG criterion it falls under.


Test variants ​

The same engine ships with several entry points, so a review can isolate one concern at a time:

TestScope
hospitality_bookingFull audit.
hospitality_booking_navigation_onlyKeyboard reachability only — no contrast.
hospitality_booking_contrast_onlyText and non-text contrast inside widgets — no navigation findings.
hospitality_booking_states_onlyHover and focus state contrast only.
hospitality_booking_formCombines the page-wide text contrast check with the full booking audit.

What counts as a widget ​

An element is treated as a widget trigger when it matches at least one of the signals below. Detection runs in priority order — specific ARIA signals first, fallbacks last — and an element matched by an earlier pass is skipped by later ones, so proper semantic markup is always preferred over keyword guesses.

Primary signals (ARIA first)

SignalWhat is looked for
aria-haspopupThe element declares that it opens a popup (menu, listbox, tree, grid, dialog).
aria-expandedThe element controls a region that can expand and collapse.
role="combobox"An input that opens a listbox of options, optionally supported by aria-controls, aria-owns, aria-activedescendant or aria-autocomplete.

Fallbacks, for custom components and pre-ARIA widgets

SignalPattern
Native selectA <select> element (single-select, without multiple or size).
Pressable field[data-role="field"][tabindex], used by some Polymer-based booking engines.
Input with placeholderAn <input> with a meaningful placeholder of three characters or more — common in Angular and other single-page forms.
Autocomplete inputjQuery-UI or custom autocomplete inputs, recognised by class name or data-source attribute.
Field buttonA <div role="button"> or <span tabindex="0"> wrapper with a destination-like class or label.
Keyword fieldA catch-all: any remaining input whose label, placeholder, id, name or text mentions a booking keyword — destination, hotel, check-in, dates, guests, rooms, personas, huéspedes, quartos.

Once a trigger is found, the specialised handlers for destination, date picker and guests-and-rooms each get a chance to claim it. If none does, a generic open / navigate / close routine takes over.

Outside hospitality, the same philosophy backs general-purpose navigators for comboboxes, accordions, disclosure widgets and tab lists.


Widget types and how well each is covered ​

WidgetSupportHow it is detected
Date picker (check-in / check-out calendar)✅ Dedicated handleraria-haspopup or aria-expanded on a date input or its wrapper; class-based fallback (.datepicker, .daterangepicker, jQuery-UI); keyword fallback. Past and disabled cells are excluded from the audit.
Dropdown selectors (destination, room type, rate plan)✅ Several pathsNative <select>; a <div role="button" aria-haspopup> wrapper; an ARIA combobox; Select2-enhanced selects, where the hidden native element is recognised so it is not audited twice.
Guest / room counter✅ Dedicated handlerKeyword match on guest, room, persona, huésped, quarto. Opens the popup and tabs through the increment, decrement and quantity controls.
Room cards / carousel⚠️ PartialCards with aria-expanded or aria-haspopup are treated as triggers and walked with Tab. There is no carousel-specific handler yet — no rotation-speed or skip-link checks — but keyboard reachability and contrast still run on each focusable child.
Step indicator / progress bar⚠️ Generic coverageSteps built as buttons with aria-expanded are walked; otherwise the generic tab navigator covers them. No dedicated step-indicator semantics.
Modal dialogs (room details, cancellation policies, rate breakdowns)✅ CoveredDetected via role="dialog". Modals already open when the run starts are closed first so they do not shadow the booking form; modals opened during the audit are walked by the generic navigator.
Form fields (personal info, payment, special requests)✅ Base coverageThe standard name-and-role, keyboard-operability, focus-visibility and text-contrast checks. Form error association is covered by its own check.
Autocomplete / search-suggest (destination search)✅ Dedicated handleraria-autocomplete="list" or "both" plus a popup role; class-based and data-source fallbacks. The handler types a seed query, walks the suggestion list, and records contrast and focus on each option.
Accordion panels (rate details, inclusions, policies)✅ General-purpose navigatoraria-expanded (required) plus a valid header element (button, <h1>–<h6>, or role="button") plus a panel with role="region" or aria-labelledby. At least two of these indicators are required, to suppress false positives.
Tab panels (room views, amenities, reviews)✅ General-purpose navigatorThe native ARIA pattern: role="tablist" containing role="tab" elements with aria-selected. Tab moves between groups, arrow keys cycle within one.

Why a general-purpose scanner misses this ​

This test introduces no new WCAG checks. It applies the existing ones — contrast, name and role, keyboard operability, focus visibility, non-text contrast — inside live popup contexts.

That is the whole point. Traditional automated scanners audit the static initial DOM, so everything behind a closed calendar or a collapsed guests panel is invisible to them. On a booking form, that is most of the interface a traveller actually has to operate.


Scoping ​

The audit is always scoped to the booking widget rather than the whole page. A root selector covering the entire document is rejected, so that the run stays focused on the booking form and does not drift into site chrome — headers, footers and navigation, which the page-wide checks already cover.