A11Y: Improve semantics of visual tab panels that are links#3107
A11Y: Improve semantics of visual tab panels that are links#3107
Conversation
…r active tab. Created generic JS tabs keyboard event handler, applied it to the alerts tabs.
…ab keyboard event handler
…the keyboard navigation handler.
joshlarson
left a comment
There was a problem hiding this comment.
The homepage behavior still doesn't look quite right - if that's an easy fix, I think it's probably worth doing.
Otherwise this looks great! I always found the space-bar-scrolls-down-a-bit behavior so annoying!
…when the tab is selected by the tab key.
thecristen
left a comment
There was a problem hiding this comment.
So work for two tickets got combined here, but those two tickets were separate for a reason I will call out -- the homepage tabs exhibit tab behavior (switching content views without navigating away) while all the other "tabs" in MBTA.com behave like lists of links (supporting navigation, right-click to open, loading new pages), thus they should have different semantics.
- This PR does a great job at adding the necessary semantics and keyboard navigation to the homepage tabs, with the exception of the incomplete behavior of pressing
Tabfrom thetablist(noted in a comment) - Navigation links don't need
aria-selected, but settingaria-current="page"IS helpful - Using a
<nav>is appropriate for the navigation links, so thanks for that - The navigation links don't need the fancy keyboard behavior expected for tabbed interfaces -- likely the default HTML behavior is fine.
Hoping this makes sense. Happy to discuss if you have any questions, this was interesting to ponder over back when I was reading NCAM's feedback and writing these tickets.
| slug = slug(title) | ||
| %> | ||
| <%= PhoenixHTMLHelpers.Link.link to: "#{href}\##{slug}", data: [scroll: "false"], id: slug, class: "btn #{selected_class}" do %> | ||
| <%= PhoenixHTMLHelpers.Link.link to: "#{href}\##{slug}", data: [scroll: "false"], id: slug, aria_selected: args.selected, aria_current: (if args.selected, do: "page"), class: "btn #{selected_class} tabnav" do %> |
There was a problem hiding this comment.
I think this might not be working as intended, given what I see in the generated markup on /stops/ (all three links have the same aria-current and aria-selected values)
|
|
||
| item.addEventListener("click", callback); | ||
| item.addEventListener("keyup", e => { | ||
| item.addEventListener("keydown", e => { |
There was a problem hiding this comment.
suggestion(blocking): I think we need one more clause here -- according to the ARIA authoring guidance on manual tabs, when pressing Tab the focus should move from the tab to the next element in the page tab sequence outside the tablist. e.g. if I'm focused on the "Trip Planner" tab, I'd expect Tab to take me to the "From" input, instead of to the next tab button.
There was a problem hiding this comment.
I've added some JS so that [tab] no longer focuses the next tab, but instead focuses the next focusable item. [shift]+[tab] does the same, but in the other direction. I'm not sure if I like it though, it seems sorta janky. Part of me feels like completely remaking these tabs so we don't have to rely on so much JS.
| tab.removeAttribute("aria-current"); | ||
| if (clickedTab) { | ||
| if (updateContent) { | ||
| tab.setAttribute("aria-current", "page"); |
There was a problem hiding this comment.
These tabs aren't pages, and I think we don't need aria-current on the homepage tabs at all. Excerpt from the WAI-ARIA doc (https://w3c.github.io/aria/#aria-current):
Authors SHOULD NOT use the aria-current attribute as a substitute for aria-selected in widgets where aria-selected has the same meaning. For example, in a tablist, aria-selected is used on a tab to indicate the currently-displayed tabpanel
…ter a tab is activated and [tab] is pressed.
|
Closing this PR to break it out into two different ones. Here's the first: #3114 |
Scope
Asana Ticket: ♿ Improve semantics of visual tab panels that are links
Implementation
Two different implementations for two different behaviors:
For the tabs on the main page (Routes, Trip Planner, Alerts) I updated the existing javascript that currently hides the appropriate content and sets the css classes to also set the aria attributes and respect keyboard navigation.
For the tabs on the other pages that are links to new pages I created a generic keyboard event hander to enabled keyboard navigation and to update the aria-selected attribute accordingly.
Users should be able to use the arrow keys to switch which tab is focused, and then either Space or Enter to activate that tab.
Screenshots
MBTA.com index page tabs
Screen.Recording.2026-04-13.at.3.19.14.PM.mov
Alerts page mode tabs
Screen.Recording.2026-04-13.at.3.20.01.PM.mov
Schedule/Line page tabs
Screen.Recording.2026-04-13.at.3.20.48.PM.mov
Stops page tabs
Screen.Recording.2026-04-13.at.3.21.20.PM.mov
How to test
http://localhost:4001/
http://localhost:4001/alerts
http://localhost:4001/schedules/Orange/line
http://localhost:4001/stops/subway