Problem/Motivation
Vertical tabs have the many accessibility issues. At the most basic level, tablist/tab/tabpanel aria roles are not in use. More specific issues are described below.
To see these issues on a plain Drupal 9 site, install Drupal with the standard or Umami install profile, and check a text format settings form (i.e. /admin/config/content/formats/manage/filtered_html), or a block layout config form (i.e. /admin/structure/block/manage/claro_page_title).
Drupal.verticalTabdoesn't take care of the right aria attributes. Whatever happens, every details summary element (of a single VerticalTabs) are described witharia-expanded="false"andaria-pressed="false". This is expected behavior. When content is displayed as vertical tabs, there are hidden details/summary elements. The pressed/expanded will not change as they are hidden. On narrow viewports these details/summary elements are visible (it must be a narrow viewport when the page loads for them to appear), and the aria attributes update properly.Drupal.verticalTabmarks active vertical tab menu items with an element that text is active tab. The element has a non-unique CSS id#active-vertical-tab. Sadly, this will be very wrong on the filter format and editor configuration form where multiple vertical tabs may appear (e.g./admin/config/content/formats/manage/basic_html).Fearing the duplicated id, if the user changes the active tab, this marker will be removed from every other vertical tab's of the page as well
- May 9, 2022, alisonjo315: I don't see this issue on a plain Drupal 9 site, probably it's been fixed since September 2019? -- but, I'll leave it here for now, until a second person confirms what I'm seeing.
- Mar 29, 2023, bnjmnm: this is still happening. Currently the contents of
#active-vertical-tabare how the active tab is conveyed to AT. If there are multiple sets of vertical tabs on a page, only one of those sets can have#active-vertical-tabat a given time. For example, on a text format page, select a tab in plugin settings, and note that#active-vertical-tabis added to that tab. Then choose a tab in filter settings (a different set of vertical tabs). Those tabs now have#active-vertical-tabto indicate the just-pressed tab, but that same indicator is no longer present in the plugin settings tabs.This is because any time a vertical tab is pressed this code runs:
$('#active-vertical-tab').remove();, which removes all "active tab" elements, when the intent is to only remove it within the vertical tabs group where the new tab was pressed.
- Auto-focus bug: When a vertical tab is activated (triggered to be shown) by pressing Enter on the tab menu link
Drupal.verticalTabtries to focus the first visible:inputelement in a vertical tab content. But the implementation is wrong. The effects are most obvious on pages with multiple vertical tab groups.
For example, on the Filter format and editor form (/admin/config/content/formats/manage/basic_html), if you press the Enter key to select a tab under Filter settings (the last vertical tabs element on the page), the focus should be on the tab you just selected, but instead, the focus is moved to the first vertical tabs further up on the page (CKEditor plugin settings).
This is because the focus trigger happens globally; it isn't limited to the current vertical tabs. - The label of a vertical tabs element has invalid
forattribute value.From #3023310-85: Vertical Tabs style update.2:
(...) the for attribute is looking for an ID that is not present on the page. The value it is looking for does appear in a div's data-drupal-selector attribute, an element that can't receive focus.
Added May 9, 2022:
- Should be able to press enter or spacebar to activate a vertical tab.
- Attribute aria-selected is missing from tab elements -- it should be present and have a value of true/false based on whether the tab is active.
- Each tab should have attribute
aria-controls, pointing to the related tabpanel ID. - Attribute tabindex="-1" must be present on all non-active tabs (and tabindex="0" on active tabs).
- When I tab through the form, it should take me into the "active" tabpanel and then out of the tablist -- right now, it takes me through the tablist, then into the tabpanel. (If I press spacebar/enter on a tab, it does move focus to the active tabpanel then out of the tablist; it should behave like this with tabs that are open by default/on page load, too.)
- Using up/down arrow keys while focused on a tab should take user to the next or previous tab.
Proposed resolution
Many of the above issues can be addressed by converting the tabs to use the recommended pattern from MDN/W3C
https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/tab_role
https://www.w3.org/WAI/ARIA/apg/example-index/tabs/tabs-automatic
Remaining tasks
User interface changes
API changes
Data model changes
Release notes snippet
@todo
Issue fork drupal-3081500
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
id.aleks commentedComment #3
andrewmacpherson commentedThanks for filing this @id.aleks
Drupal's vertical tabs started out in D6 contrib, and hasn't changed a great deal since. I think we should actually replace it with an implementation of the WAI-ARIA tabs pattern, which has good support now. That's a bigger plan forming at #2893640: Modernize ARIA usage, in line with ARIA 1.1 and the ARIA Authoring Practices guide..
The fact that our vertical tabs is a special case of a responsive wrapper around details groups makes this a bit awkward.
The individual issues you describe here are things we could address separately in the meantime. Maybe we'll split this into separate issues.
Comment #4
huzookaComment #5
huzooka@id.aleks: nothing happened since you assigned this issue to yourself. Are you working on this?
Comment #6
id.aleks commented@huzooka Yep, I'm working on it.
Comment #7
huzookaAdded point 4.
Comment #8
andrewmacpherson commentedComment #11
bnjmnmComment #12
bnjmnmThis patch covers the four reported bugs. It may ultimately be necessary to split these into individual issues, but they're pretty easy to isolate if that need arises. Going to try the 4-in-1 for now since this is already a child issue.
Item 1:
The two changes that fix this are in vertical-tabs.es6.js and are both accompanied by this comment:
Item 2:
Changed this to use a data-attribute and make the scope more precise instead of targeting the whole page
Item 3:
Made the scope more precise instead of targeting the whole page
Item 4:
The solution is largely explained in the additions to VerticalTabs.php
The solution also includes some changes to the initialization in vertical-tabs.es6.js when the group must be expanded to include newly added tabs.
Comment #13
nod_The aria attributes are changed on the summary element, but this summary element is in "display:none", is it useful then? shouldn't the aria labels be on the
<li>?Comment #14
bnjmnmYou're right. I've changed that item in the issue summary to a more generic "
Drupal.verticalTabdoesn't properly communicate its functionality to assistive technology."To address this, I refactored this to as an aria tablist/tabpanel, using the modified approach described in https://www.deque.com/blog/a11y-support-series-part-1-aria-tab-panel-acc... which permits tabs to be accessed via tab navigation.
This indirectly solves item 2 as well. Adding "active tab" to the tab text is not necessary when the tablist has the correct aria attributes, so this has been removed entirely.
Comment #15
lauriiiComment #16
bnjmnmAdding "Needs accessibility review" since this has been refactored to follow WAI-ARIA tab panel practices (with slight changes suggested by the Deque article in #14)
Comment #17
wellme commentedComment #18
wellme commentedComment #19
wellme commentedReviewed the patch and found that the aria attribute and id thing working.
Comment #20
bnjmnmRevisited #14 with a screenreader and it isn't quite doing what it ought to be doing. Fixing that now.
Comment #21
bnjmnmI reviewed this against some examples again:
https://www.w3.org/TR/wai-aria-practices-1.1/examples/tabs/tabs-1/tabs.html
and
https://www.deque.com/blog/a11y-support-series-part-1-aria-tab-panel-acc... (which deviates from the pattern a bit to allow tabs to be reached via the tab key, something implemented here as well).
Some of the changes needed to get this working like the examples:
handleFragmentLinkClickOrHashChangefrom responding to hash events.Here's a recording of this patch in use with voiceover.
https://youtu.be/qLTikOfosYg
Comment #23
bnjmnmHad to change a few tests since the href is removed from the "tabs" to prevent issues related to the hashchange event. The
clickLink()testing method requires an href attribute, not just an<a>.Note that yhe
<a>"tabs" should actually be<button>elements, but I believe that would be a BC break.Comment #24
quietone commentedJust a few points in the comments.
Should be a single quite "'" and not a back tick.
> 80 chars and back ticks instead of single quotes.
> 80 chars and back ticks instead of single quote.
Maybe there is a reason for back ticks instead of single quotes?
Comment #25
nod_Backticks are to output code tags when the doc is generated (or displayed in PhpStorm for example). See the wildly outdated: https://read.theodoreb.net/drupal-jsapi/Drupal.html#.attachBehaviors
In this case they are all used in line comments, so they won't ever get displayed in the generated JSDoc. I'd keep them for consistency with the rest of the comments though.
Comment #26
bnjmnmRegarding the backtick feedback in #24, I've been using backticks as an equivalent to
<code>in JS comments based on it being used that way throughout much of core. However, I could not find documentation that would confirm this is or isn't proper usage. Based on the JS maintainer feedback in #25 and many existing examples of this kind of backtick use in core JS, I believe these can stay as-is unless there is documentation of the standard indicating otherwise.If there are still concerns with backtick usage, this would be a good opportunity to open a followup issue to seek clarity on how this should be standardized.
Comment #28
mgiffordI can confirm #1 in the description by looking at /admin/config/content/formats/manage/restricted_html in simplytest.me
You should be able to see that here:

Both the highlighted & hidden text has:
<details data-drupal-selector="edit-filters-filter-html-settings" id="edit-filters-filter-html-settings" class="js-form-wrapper form-wrapper seven-details vertical-tabs__pane" open="open" role="tabpanel" aria-labelledby="edit-filters-filter-html-settings-tab" tabindex="0" style=""> <summary role="button" aria-controls="edit-filters-filter-html-settings" aria-expanded="true" aria-pressed="true" class="seven-details__summary"><details data-drupal-selector="edit-filters-filter-url-settings" id="edit-filters-filter-url-settings" class="js-form-wrapper form-wrapper seven-details vertical-tabs__pane" open="open" role="tabpanel" aria-labelledby="edit-filters-filter-url-settings-tab" tabindex="0" style="display: none;"> <summary role="button" aria-controls="edit-filters-filter-url-settings" aria-expanded="true" aria-pressed="true" class="seven-details__summary">semantically, why are we making the summary a button? If anything is semantically a button it would be the vertical tabs menu:
I'm still trying to wrap my head around this again. It's been a while since I've looked at this.
Comment #29
bnjmnmGood catch! I tracked this down to an issue from ~8 years back, created by a nice fellow with the username @mgifford #1848684: Add ARIA role to DETAILS. My hunch is assigning
role="button"was something advised before browsers had agreement ondetails/summary. Unless I'm misremembering, button is the implicit role of summary, so removing this should be fine but it's presence is doing no harm either. I recommend removing that in a separate issue as it doesn't directly impact the bugs reported here and is a straightforward solution that can likely committed without much fuss.Comment #31
volkswagenchickTagging for DrupalCamp Colorado
DCCO2021which is Sunday August 8.https://2021.drupalcampcolorado.org/contrib-day
This patch can be re-rolled into a Merge Request to make maintainer review easier, thanks!
Comment #32
ckrinaI was trying to understand what needs to be done/reviewed here but from the comments I’m not able to get clear instructions. Tagging this as Needs issue summary update to see if someone could help making it easier to know where to help of if there are things that are still being discussed.
Comment #35
ankithashettyJust re-rolled into a Merge Request as requested in #31 and addressed #24.2 & #24.3. Rest of the points in #24 are discussed in #25 & #26.
Thank you!
Comment #38
kristen polBack to needs work as merge request is not usable.
Comment #39
alison(DrupalCon contrib room)
Trying changing version back to 9.3.x because of dependency/version constraint issues.
Comment #40
alisonNo composer errors when I used 9.3.x-dev, so I think that means we need a reroll so that the MR works with 9.4.x, ya? -- as I write this, I think what I'm saying is basically the same as the "not currently mergeable" message!
Adding "needs reroll" -- I hope to do that this week at DrupalCon, but if someone else gets to it first, awesome!
Comment #41
alisonMR does need reroll, but the files with rebase conflicts are JavaScript, and the conflicts *seem* substantive as far as I can tell -- and I don't have the JS expertise to work on a reroll!
If anyone wants to take a stab at it, maybe this will help:
core/misc/vertical-tabs.es6.jsandcore/misc/vertical-tabs.js(Also, sorry for the issue notification noise!)
Comment #42
gauravvvv commentedUpdated attribution
Comment #43
bnjmnmComment #46
immaculatexavier commentedRerolled patch against 9.4.x
Comment #47
immaculatexavier commentedComment #48
karishmaamin commentedTried to fix custom code error at #46
Comment #49
ranjith_kumar_k_u commentedComment #51
alisonI'm so glad people are working on this issue, thank you, everyone!
Unfortunately, we have a mix of a MR and patches, and I'm not sure the best way to move forward. I'll post on Slack > #contribute channel for advice. Meanwhile! --
-------
I can see the MR has rebase conflicts:
- There's an error when I try the rebase button on GitLab: https://git.drupalcode.org/project/drupal/-/merge_requests/2169
- Also when I try to rebase via command line, there are conflicts: https://gitpod.io#snapshot/dc078644-d6cb-47be-b7c8-958e29760109
-------
@immaculatexavier (#46) Which patch did you reroll? -- please link to the comment or patch file, or if you rerolled a patch based on a MR, please link to that.
@ranjith_kumar_k_u (#49) Could you please summarize what your changes did?
Comment #52
alisonContinuing to look at our MR/patch situation.
@bnjmnm I know you just created a new MR, but the branch you were working on isn't new, and also, there are a lot of commits not meant to be kept around, I'm sure? (file permissions, mainly)
How about creating a new branch for the issue, straight from 9.4.x (or new fork + branch, whichever is easier -- as long as it's created from the latest 9.4.x), and cherry-picking your commits, if possible? -- it might not work, the rebase conflicts might mean you need to redo the changes, but I think a fresh branch from 9.4.x (and closing MR 2169) is the best next step.
(I'm trying to compare the changes in #46 with MR 2169 -- maybe a place to start on a new branch is with the patch reroll in #46? -- depending on what @immaculatexavier says they rerolled "from," so to speak.)
Comment #53
bnjmnmRe #52 I think we may be more aligned than it seems:
These were temporary changes to address merge conflicts, they're part of the commit history but not part of the current diff as these changes were reverted after they were no longer necessary.
The branch I'm working on 3081500-vertical-tab-94 was created last week to do exactly that 🙂
It does not look like the patches in #46 - #49 do anything additional that hadn't already been present in the MR, but they do remove
handleFragmentLinkClickOrHashChange, which shouldn't be removed.Comment #54
alison@bnjmnm Thanks for all the updates and explanations!
I am very excited to try the latest MR tomorrow, and I should be able to get a screen reader + keyboard nav review soon thereafter.
Thank you!
Comment #55
alisonShoot sorry, "accidental status change via refreshed old browser tab" -- back to Needs review.
Comment #56
alisonHi, thank you again for working on this issue! I have some preliminary feedback. Also, I updated the issue summary for content and overall clarity (hopefully).
About me: I'm reviewing the MR on a plain Drupal 9.4 site with the Umami install profile. Specifically, I'm reviewing the vertical tabs elements on a text format settings form (i.e. /admin/config/content/formats/manage/filtered_html) and a block layout config form (i.e. /admin/structure/block/manage/claro_page_title).
Fixed:
aria-expanded="false"andaria-pressed="false"; use of labelforattribute).#active-vertical-tab)Also fixed:
aria-selectedwas missing from tab elements -- now it's there, and properly true/false based on whether the tab is active ⭐aria-controls(on each tab) points to the related tabpanel's ID ⭐Question + remaining issues:
-1on all non-active tabs / on all tab elements that arearia-selected="false".New issue / side effect
I ran into trouble with vertical tab field groups. I don't know if this fix should/could fix the same accessibility issues on field group vertical tabs, but it actually made things a little broken: Basically, vertical tab field groups look like details fieldsets (maybe accordion groups), not vertical tabs.
Steps to reproduce:
Screenshots + YML attached -- screenshots show vertical tabs on a plain Drupal 9.4.0-alpha1 site, and then with the patch/MR applied to the same site. (I have DrupalPod snapshots, but gitpod snapshots are a little under the weather at the moment, so hopefully the screenshots and YML are sufficient -- if a full config export would be helpful, I can provide that!)
That's all for now, I hope I'm making enough sense to make sense -- please lmk if you have any questions or if I can provide more info on anything. Thank you!
Comment #59
chi commentedWith all this complexity don't you think that Vertical Tabs component needs a brand-new render element made from scratch?
Comment #60
alisonMmmm that might make sense! -- I probably don't have the best skill-set to have a strong opinion about it, but my gut feeling is that seems worth strong consideration.
Comment #61
bnjmnmIt doesn't seems like any of the remaining points are any more difficult to implement because they're on an existing render element/JavaScript class. Starting from scratch would certainly create productive-feeling work, but we'd eventually hit the same implementation details that this was waiting on, but instead of fixing an existing element, we'd need to require people migrate to something entirely new to enjoy the benefits. There would also need to be a backwards compatibility layer for anything changed in core.
I believe this current patch addresses all the current concerns, including the change to arrow navigation. That could potentially be moved to a followup if this gets stalled on details, but I think it may be worth bundling to minimize the number of reviews/commits needed to get our Vertical Tabs current with modern A11y standards.
Comment #62
bnjmnmComment #63
chi commentedThe outlined area does not match the tab container in Claro.

Comment #64
chi commentedComment #65
mgiffordWCAG 1.3.1 SC
Comment #66
bnjmnmIt's worth noting that comment #63 is not pointing out anything wrong with the changes being made in this issue. It's an unrelated pre-existing issue that was not caused by any of the accessibility fixes here and has its own issue. Patch #61 is still ready for review.
Comment #67
chi commented@bnjmnm, That's correct, but patch #61 made the problem outlined in #3327848: Claro: Wrong background for active vertical tab much more noticeable. I propose we postpone this issue till #3327848: Claro: Wrong background for active vertical tab is resolved.
Comment #68
gauravvvv commentedComment #69
smustgrave commentedTesting patch #61 on /admin/structure/block/manage/claro_page_title
Notice the aria-expanded and aria-pressed still remain false.
Comment #70
bnjmnm#69 is expected behavior. When vertical tabs are visible, the details and summary elements are hidden and inert and the attributes should not change. I updated the issue summary to clarify this.
Also, #3327848: Claro: Wrong background for active vertical tab is complete.
Comment #71
smustgrave commentedFor accessibility rule
Tested on /admin/structure/block by adding a block
I can tab to vertical tabs and using my up/down arrows cycle through them.
Pressing enter opens the tab and moves focus to the first focusable element.
aria-selected attribute correctly updates.
The "a" tag for each tab aria-controls correctly points to the aria-own attributes with matching values.
The "a" tag that is active currently has tabindex="0" while the rest are -1
Also tested on /admin/config/content/formats/manage/full_html where 2 tab groups exists and had no issues of focus going to the wrong group,
Going to mark this and ask #accessibility channel if my review was good enough.
@bnjmnm you tagged for follow up 2 years ago that still needed?
Comment #72
benjifisherWe discussed this issue briefly at #3350284: Drupal Usability Meeting 2023-04-07. That issue will have a link to a recording of the meeting.
For the record, the attendees at today's usability meeting were @benjifisher, @mgifford, @rkoller, @simohell, and @swirt.
We were not sure exactly what was supposed to change, since we did not follow the links in the "Proposed resolution" section of the issue summary. But we did notice one problem with vertical tabs in the block configuration page (same as the screenshot from Issue #63).
In Step 4, we expected the focus to return to Roles, not Vocabulary. We saw the same behavior with or without the patch from Comment #61.
I am setting the issue status to NW to consider this behavior. If the actual behavior is what we want, or if it is out of scope for this issue, then feel free to change the status back to RTBC.