htmx
htmx copied to clipboard
Accessibility in docs: tabs examples use inaccessible <a> tags
The tabs examples are inaccessible to keyboard users since the tab "links" are not keyboard focusable. To continue using an anchor element, the issue can be resolved by adding an href attribute, or applying a tabindex attribute and any relevant aria attributes to notify screen readers that these should function like a link (eg. aria role=link). See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#properties
If the tabs are not actually links then I highly recommend using a button element instead. Check this page for how create markup for tabs using buttons: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/tab_role
- https://github.com/bigskysoftware/htmx/blob/6775f8bf7dee0f521e03e05048615f1427c90648/www/examples/tabs-hateoas.md?plain=1#L20
- https://github.com/bigskysoftware/htmx/blob/6775f8bf7dee0f521e03e05048615f1427c90648/www/examples/tabs-hyperscript.md?plain=1#L17
Note: I've seen other markup in the examples/docs that demonstrate inaccessible coding practices. I can continue to open issues as I come across them if it's helpful. Generally speaking, if it's clickable it should either be an a
with an href
, or a button
element. Never use a div
, span
, etc. for clickable elements unless you've added the appropriate attributes (and even then you may want to reconsider the approach since you are now responsible for manually defining the semantics of the element instead of using the browser defaults)
@dz4k do you mind taking this one?
One accessible way to do this would probably be:
<div role="tablist" hx-target="tab-content">
<button role=tab hx-post="/tab1" aria-selected="true" aria-controls="tab-content">Tab 1</a>
<button role=tab hx-post="/tab1" aria-controls="tab-content">Tab 2</a>
...
</div>
<div role="tabpanel" class="tab-content">
...
</div>
Unfortunately, you'd need manual JavaScript to make the keyboard interactions work. You might also need to use idiomorph to preserve focus.
Another approach is fully avoiding making it look like tabs altogether:
<div>
<a hx-post="/tab1" href="/tab1" id="tab-1-link">Tab 1</a>
<a hx-post="/tab1" href="/tab1" id="tab-2-link">Tab 2</a>
...
</div>
<div class="tab-content" aria-labelledby="tab-1-link">
...
</div>
This approach also supports a no-JS fallback to having the tabs' content as separate pages.
I haven't tested either of these approaches yet though. There's a lot of work that needs to be done wrt. the relationship between htmx, aria and keyboard interactions.
FWIW, if I were building tabs in a website, I'd use a JS tabs library (perhaps https://missing.style/docs/js/#tabs :wink:) and hook it up to htmx with events to load the tabs' contents.
It's not just the tabs example, also the examples in the README file and introduction section (https://htmx.org/docs/#introduction) both use this example: <a data-hx-post="/click">Click Me!</a>
.
Perhaps the could be updated to have an href for GET requests, for example: <a href="/some-page" data-hx-get="/click">Click Me!</a>
?
For POST requests I struggle to think of a case where it would be good in terms of accessibility/semantics to use an anchor link. Perhaps the docs should suggest using a button in those cases?