Skip to content

Fix: Escape key dismisses hover-opened submenus (WCAG 2.1 SC 1.4.13)#968

Open
Contributolo wants to merge 4 commits into
stellarwp:masterfrom
Contributolo:fix/esc-dismiss-hover-submenus
Open

Fix: Escape key dismisses hover-opened submenus (WCAG 2.1 SC 1.4.13)#968
Contributolo wants to merge 4 commits into
stellarwp:masterfrom
Contributolo:fix/esc-dismiss-hover-submenus

Conversation

@Contributolo
Copy link
Copy Markdown

Problem

The Navigation (Adv) Block already handles Escape for submenus opened via toggle button click (keyCode === 27 in kb-navigation-block.js). However, submenus opened via CSS :hover cannot be dismissed with Escape.

The CSS rule responsible:

.wp-block-kadence-navigation.navigation-desktop-orientation-horizontal
    .menu-item:not(.kb-nav-link-sub-click):hover > .sub-menu {
    opacity: 1;
    visibility: visible;
}

Since :hover is a CSS pseudo-class that cannot be removed via JavaScript, pressing Escape while hovering a submenu has no effect.

This violates:

  • WCAG 2.1 SC 1.4.13 (Content on Hover or Focus): "A mechanism is available to dismiss the additional content without moving pointer hover or keyboard focus."
  • WCAG 2.1 SC 2.1.1 (Keyboard): All functionality must be operable through a keyboard interface.

Since June 2025, the European Accessibility Act (EAA) makes WCAG 2.1 AA compliance a legal requirement for commercial websites in the EU.

Solution

  • Track hovered .menu-item-has-children elements via mouseenter/mouseleave.
  • Add a document-level keydown listener for Escape that adds a kb-nav-esc-close class to the deepest hovered menu item.
  • A CSS rule .menu-item.kb-nav-esc-close > ul.sub-menu { display: none !important } overrides the :hover visibility. The !important is necessary because CSS :hover cannot be programmatically disabled.
  • The class is automatically removed on mouseenter (re-hover) or mouseleave (pointer exits), restoring normal behavior.
  • The existing Escape handler for toggle-opened submenus (keyboard focus inside submenu) is unchanged; the new global handler skips if focus is already inside a sub-menu.

Changes

  • src/assets/js/kb-navigation-block.js: Added hoveredItems tracking, initHoverEscDismiss(), and handleHoverEscDismiss().
  • src/blocks/navigation/style.scss: Added .kb-nav-esc-close override rule.

Testing

  1. Open a page with Navigation (Adv) Block containing submenus.
  2. Hover test: Hover a menu item with children -- submenu appears. Press Escape -- submenu closes. Move pointer away and back -- submenu reopens normally.
  3. Keyboard test: Tab into a submenu, press Escape -- submenu closes, focus returns to toggle button (existing behavior, unchanged).
  4. Nested hover: Hover a top-level item, then hover a nested child with its own submenu. Press Escape -- only the deepest submenu closes.
  5. Vertical navigation: Verify vertical orientation is unaffected (hover submenus are not used in vertical mode).

Add functionality to dismiss hover-opened submenus with Escape key.
Add styles for submenu dismissal on Escape key press.
Comment thread src/assets/js/kb-navigation-block.js Outdated
(function () {
const focusableElementsString =
'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex="0"], [contenteditable]';
const hoveredItems = new Set();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please fix the indentation. there is an extra tab.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review! All three points addressed:

  1. Fixed the extra tab on const hoveredItems.
  2. Added the vertical orientation guard at the top of initHoverEscDismiss.
  3. Scoped the kb-nav-esc-close rule to .navigation-desktop-orientation-horizontal, matching the existing hover rule scope.

// Escape key dismissal for hover-opened submenus (WCAG 2.1 SC 1.4.13).
.menu-item.kb-nav-esc-close > ul.sub-menu {
display: none !important;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new CSS is global but should be limited to the horizontal orientation as hover based submenus only exist on horizontal navigations. It should look more like:

.navigation-desktop-orientation-horizontal .menu-item.kb-nav-esc-close > ul.sub-menu {
      display: none !important;                                                         
 }

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review! All three points addressed:

  1. Fixed the extra tab on const hoveredItems.
  2. Added the vertical orientation guard at the top of initHoverEscDismiss.
  3. Scoped the kb-nav-esc-close rule to .navigation-desktop-orientation-horizontal, matching the existing hover rule scope.

* CSS :hover cannot be removed programmatically, so a class-based override
* is applied and cleaned up when the user re-engages with the menu item.
*/
const initHoverEscDismiss = function (nav) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also add a guard here to make sure you don't add the listeners to the vertical orientation.

// Hover submenus only exist on horizontal orientation.                           
if (nav.classList.contains('is-vertical')) {
      return;                                                                       
} 

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review! All three points addressed:

  1. Fixed the extra tab on const hoveredItems.
  2. Added the vertical orientation guard at the top of initHoverEscDismiss.
  3. Scoped the kb-nav-esc-close rule to .navigation-desktop-orientation-horizontal, matching the existing hover rule scope.

…uard

- Fix extra tab on hoveredItems declaration
- Skip listeners on vertical navigation orientation
Hover-opened submenus only exist in horizontal orientation, matching the existing :hover rule scope.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants