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.
Built from https://develop.svn.wordpress.org/trunk@60040


git-svn-id: http://core.svn.wordpress.org/trunk@59376 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
joedolson
2025-03-18 14:41:27 +00:00
parent 2f1611e273
commit d4330af271
4 changed files with 86 additions and 16 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 {