Make WordPress Core

Changeset 60040


Ignore:
Timestamp:
03/18/2025 02:39:38 PM (18 months ago)
Author:
joedolson
Message:

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.

Location:
trunk/src/wp-content/themes/twentytwenty
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-content/themes/twentytwenty/assets/js/index.js

    r57987 r60040  
    439439                links = menu.getElementsByTagName( 'a' );
    440440
    441                 // Each time a menu link is focused or blurred, toggle focus.
     441                // Each time a menu link is focused, update focus.
    442442                for ( i = 0, len = links.length; i < len; i++ ) {
    443                         links[i].addEventListener( 'focus', toggleFocus, true );
    444                         links[i].addEventListener( 'blur', toggleFocus, true );
    445                 }
    446 
    447                 //Sets or removes the .focus class on an element.
    448                 function toggleFocus() {
     443                        links[i].addEventListener( 'focus', updateFocus, true );
     444                }
     445
     446                menu.addEventListener( 'focusout', removeFocus, true );
     447
     448                // Remove focus classes from menu.
     449                function removeFocus(e){
     450                        const leavingMenu = ! menu.contains( e.relatedTarget );
     451
     452                        if ( leavingMenu ) {
     453                                // Remove focus from all li elements of primary-menu.
     454                                menu.querySelectorAll( 'li' ).forEach( function( el ) {
     455                                        if ( el.classList.contains( 'focus' ) ) {
     456                                                el.classList.remove( 'focus', 'closed' );
     457                                        }
     458                                });
     459                        }
     460                }
     461
     462                // Update focus class on an element.
     463                function updateFocus() {
    449464                        var self = this;
    450465
    451                         // Move up through the ancestors of the current link until we hit .primary-menu.
    452                         while ( -1 === self.className.indexOf( 'primary-menu' ) ) {
    453                                 // On li elements toggle the class .focus.
    454                                 if ( 'li' === self.tagName.toLowerCase() ) {
    455                                         if ( -1 !== self.className.indexOf( 'focus' ) ) {
    456                                                 self.className = self.className.replace( ' focus', '' );
     466                        // Remove focus from all li elements of primary-menu.
     467                        menu.querySelectorAll( 'li' ).forEach( function( el ){
     468                                if ( el.classList.contains( 'closed' ) ) {
     469                                        el.classList.remove( 'closed' );
     470                                }
     471                                if ( el.classList.contains( 'focus' ) ) {
     472                                        el.classList.remove( 'focus' );
     473                                }
     474                        });
     475                       
     476                        // Set focus on current `a` element's parent `li`.
     477                        self.parentElement.classList.add( 'focus' );
     478                        // If current element is inside sub-menu find main parent li and add focus.
     479                        if ( self.closest( '.menu-item-has-children' ) ) {
     480                                twentytwentyFindParents( self, 'li.menu-item-has-children' ).forEach( function( element ) {
     481                                        element.classList.add( 'focus' );
     482                                });
     483                        }
     484                }
     485
     486                // When the `esc` key is pressed while in menu, move focus up one level.
     487                menu.addEventListener( 'keydown', removeFocusEsc, true );
     488
     489                // Remove focus when `esc` key pressed.
     490                function removeFocusEsc( e ) {
     491                        e = e || window.event;
     492                        var isEscape = false,
     493                                focusedElement = e.target;
     494
     495                        // Find if pressed key is `esc`.
     496                        if ( 'key' in e ) {
     497                                isEscape = ( e.key === 'Escape' || e.key === 'Esc' );
     498                        } else {
     499                                isEscape = ( e.keyCode === 27 );
     500                        }
     501
     502                        // If pressed key is esc, remove focus class from parent menu li.
     503                        if ( isEscape ) {
     504                                var parentLi = focusedElement.closest( 'li' ),
     505                                        nestedParent = closestExcludingSelf( parentLi, 'li.menu-item-has-children' ),
     506                                        focusPosition = nestedParent ? nestedParent.querySelector('a') : false;
     507
     508                                        if ( null !== nestedParent ) {
     509                                                nestedParent.classList.add( 'focus' );
     510                                                focusPosition.focus();
    457511                                        } else {
    458                                                 self.className += ' focus';
    459                                         }
    460                                 }
    461                                 self = self.parentElement;
    462                         }
     512                                                parentLi.classList.remove( 'focus' );
     513                                                parentLi.classList.add( 'closed' );
     514                                        }
     515                        }
     516                }
     517
     518                function closestExcludingSelf(element, selector) {
     519                        if ( ! element || ! selector ) {
     520                                return null;
     521                        }
     522                        const parent = element.parentElement;
     523
     524                        return parent ? parent.closest(selector) : null;
    463525                }
    464526        }
  • trunk/src/wp-content/themes/twentytwenty/style-rtl.css

    r59832 r60040  
    16771677}
    16781678
     1679.primary-menu .closed ul {
     1680        display: none;
     1681}
     1682
    16791683.primary-menu li.menu-item-has-children:hover > ul,
    16801684.primary-menu li.menu-item-has-children:focus > ul,
  • trunk/src/wp-content/themes/twentytwenty/style.css

    r59832 r60040  
    16831683}
    16841684
     1685.primary-menu .closed ul {
     1686        display: none;
     1687}
     1688
    16851689.primary-menu li.menu-item-has-children:hover > ul,
    16861690.primary-menu li.menu-item-has-children:focus > ul,
Note: See TracChangeset for help on using the changeset viewer.