Bundled Themes: A11y: Dismiss submenus with esc in Twenty Twenty.

Allow submenus in Twenty Twenty to be dismissed using the `esc` key to meet WCAG 1.4.13. Set focus to previous submenu parent on `esc`, and ensure that after escaping the last submenu, tab moves to the next parent item, not back into the submenu.

Props lcarevic, rcreators, pratiklondhe, poena, karmatosed, chaion07, audrasjb, mehdi01, mohonchandra, najmulsaju, saurabhdhariwal, ugyensupport, shailu25.
Fixes #49950.

git-svn-id: https://develop.svn.wordpress.org/trunk@60040 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Joe Dolson 2025-03-18 14:39:38 +00:00
parent 10e18b614b
commit 19c08e3bd9
3 changed files with 85 additions and 15 deletions

View File

@ -438,29 +438,91 @@ twentytwenty.primaryMenu = {
links = menu.getElementsByTagName( 'a' );
// Each time a menu link is focused or blurred, toggle focus.
// Each time a menu link is focused, update focus.
for ( i = 0, len = links.length; i < len; i++ ) {
links[i].addEventListener( 'focus', toggleFocus, true );
links[i].addEventListener( 'blur', toggleFocus, true );
links[i].addEventListener( 'focus', updateFocus, true );
}
//Sets or removes the .focus class on an element.
function toggleFocus() {
menu.addEventListener( 'focusout', removeFocus, true );
// Remove focus classes from menu.
function removeFocus(e){
const leavingMenu = ! menu.contains( e.relatedTarget );
if ( leavingMenu ) {
// Remove focus from all li elements of primary-menu.
menu.querySelectorAll( 'li' ).forEach( function( el ) {
if ( el.classList.contains( 'focus' ) ) {
el.classList.remove( 'focus', 'closed' );
}
});
}
}
// Update focus class on an element.
function updateFocus() {
var self = this;
// Move up through the ancestors of the current link until we hit .primary-menu.
while ( -1 === self.className.indexOf( 'primary-menu' ) ) {
// On li elements toggle the class .focus.
if ( 'li' === self.tagName.toLowerCase() ) {
if ( -1 !== self.className.indexOf( 'focus' ) ) {
self.className = self.className.replace( ' focus', '' );
} else {
self.className += ' focus';
}
// Remove focus from all li elements of primary-menu.
menu.querySelectorAll( 'li' ).forEach( function( el ){
if ( el.classList.contains( 'closed' ) ) {
el.classList.remove( 'closed' );
}
self = self.parentElement;
if ( el.classList.contains( 'focus' ) ) {
el.classList.remove( 'focus' );
}
});
// Set focus on current `a` element's parent `li`.
self.parentElement.classList.add( 'focus' );
// If current element is inside sub-menu find main parent li and add focus.
if ( self.closest( '.menu-item-has-children' ) ) {
twentytwentyFindParents( self, 'li.menu-item-has-children' ).forEach( function( element ) {
element.classList.add( 'focus' );
});
}
}
// When the `esc` key is pressed while in menu, move focus up one level.
menu.addEventListener( 'keydown', removeFocusEsc, true );
// Remove focus when `esc` key pressed.
function removeFocusEsc( e ) {
e = e || window.event;
var isEscape = false,
focusedElement = e.target;
// Find if pressed key is `esc`.
if ( 'key' in e ) {
isEscape = ( e.key === 'Escape' || e.key === 'Esc' );
} else {
isEscape = ( e.keyCode === 27 );
}
// If pressed key is esc, remove focus class from parent menu li.
if ( isEscape ) {
var parentLi = focusedElement.closest( 'li' ),
nestedParent = closestExcludingSelf( parentLi, 'li.menu-item-has-children' ),
focusPosition = nestedParent ? nestedParent.querySelector('a') : false;
if ( null !== nestedParent ) {
nestedParent.classList.add( 'focus' );
focusPosition.focus();
} else {
parentLi.classList.remove( 'focus' );
parentLi.classList.add( 'closed' );
}
}
}
function closestExcludingSelf(element, selector) {
if ( ! element || ! selector ) {
return null;
}
const parent = element.parentElement;
return parent ? parent.closest(selector) : null;
}
}
}; // twentytwenty.primaryMenu

View File

@ -1676,6 +1676,10 @@ ul.primary-menu {
z-index: 1;
}
.primary-menu .closed ul {
display: none;
}
.primary-menu li.menu-item-has-children:hover > ul,
.primary-menu li.menu-item-has-children:focus > ul,
.primary-menu li.menu-item-has-children.focus > ul {

View File

@ -1682,6 +1682,10 @@ ul.primary-menu {
z-index: 1;
}
.primary-menu .closed ul {
display: none;
}
.primary-menu li.menu-item-has-children:hover > ul,
.primary-menu li.menu-item-has-children:focus > ul,
.primary-menu li.menu-item-has-children.focus > ul {