Save the date — 21–23 January 2027 IIT Bombay

Swapped Out From Under You: Accessible HTMX in Drupal 11.3

HTMX is now fully integrated in Drupal 11.3, and it makes dynamic interfaces dramatically cheaper to build. It also makes them dramatically easier to break for keyboard and screen reader users. This session demonstrates five real accessibility failures in

Drupal 11.2 added HTMX as a core dependency under the Replace AJAX API with HTMX community initiative. Drupal 11.3 shipped the full integration: a factory class exposing every HTMX attribute and response header to render arrays, and a FormBuilder that makes all form values available when a form is rebuilt from an HTMX request. The core blog reports up to 71% less JavaScript for browser-server interactions. Contrib is following — views_htmx already replaces the legacy Ajax framework for exposed filters, pagination, and table sorting.

This session works through five failure modes on a live Drupal 11.3 site, each demonstrated with a screen reader running before and after the fix:

  1. Focus destruction. hx-swap="outerHTML" On a target containing the focused element drops focus to <body>. Restoration strategies, hx-preserve, and swap/settle timing.
  2. Silent updates. HTMX ships no live-region semantics. Why the aria-live region must live outside the swap target, how aria-busy communicates in-flight state, and why hx-indicator is visual-only.
  3. Auto-submitting exposed filters. Debouncing, polite result-count announcements, and not stealing focus from the control the user is still operating.
  4. Non-semantic triggers. <div hx-get> has no role, no focusability, and no keyboard affordance. Progressive enhancement from a real <a href> or form action, so the interface degrades instead of disappearing.
  5. hx-push-url and route announcement. Back-button behaviour, document title updates, and the SPA-era problem of navigation happening with nobody told — now reachable from a server-rendered Drupal site.

The session then covers HTMX-rebuilt forms specifically: moving focus to an error summary, keeping aria-describedby wiring intact across a swap, and reattaching Drupal.behaviors with once() when custom JavaScript doesn't survive the replacement.