Are you a GenerateCustomer?

Do you have an active GP Premium or GenerateBlocks Pro license key?

Create a GenerateSupport account for GeneratePress and GenerateBlocks support in one dedicated place.

Create an account
Already have a GenerateSupport account? Login

Just browsing?

Feel free to browse the forums. Support for our free versions is provided on WordPress.org (GeneratePress, GenerateBlocks).

Want to become a premium user? Learn more below.

Keyboard accessible navigation and page scrolling

  • Hi I want to create a barebones theme based on GeneratePress which is totally accessible. The site is at https://barebones.pepped.app/

    For this I need the site to be navigable with a keyboard only. To do this I need the following elements to behave in a particular way.

    • Pressing tab causes the focus to move to the next focusable element; except
      1. pressing tab highlights the parent navigational items only and reveals any children/dropdown items
      2. using an arrow key allows me to scroll through the children/dropdown; otherwise
      3. tab will jump to the next parent navigational item
    • once the navigation is navigated or skipped (using skip to content button) pressing tab will take us to the next focusable element within the body of the page
    • and arrow keys simply scroll the page.

    My site looks, on first impressions, like this is what’s happening, except the tab and arrow currently both focus on the next focussable element, and the arrows won’t scroll the page.

    Does anyone know how I can tweak the theme to do this?

    Dominic

  • Hi Dominic,

    You’ll need Javascript to alter keyboard behaviors. Unfortunately, this would not be within our scope. It would be best to reach out to a developer regarding this. I would suggest asking for recommendations on our Facebook Group as well: https://web.facebook.com/groups/generatepress

  • Hi thanks – I’d argue that the Theme is not accessible as it stands and it should be. It has Javascript that included that renders it inaccessible. As a paid theme and support GP should feel obliged to try and make the product accessible.

  • OK, all – I have sorted this issue independently, and here is the solution. Put this code into your functions.php or use it as a PHP code snippet.

    //Navigation-fix
    
    function tab_though_menu_skipping_sub_menu_items_unless_down_or_up_arrow_keys_are_used() { ?>
     <script>
    document.addEventListener('DOMContentLoaded', function () {
       const menuItems = document.querySelectorAll('.menu-item-has-children'); // Adjust this selector to match your menu structure
    
       // Initially set tabIndex to -1 for all sub-menu items to skip them in tab navigation
       menuItems.forEach(item => {
           let subMenuItems = item.querySelectorAll('.sub-menu a'); // Adjust this selector for your sub-menus
           subMenuItems.forEach(subMenuItem => {
               subMenuItem.tabIndex = -1;
           });
    
           item.addEventListener('keydown', function (e) {
               // Only proceed if the event target is a menu item that contains a sub-menu
               let subMenu;
               if (e.target.classList.contains('menu-item-has-children')) {
                   subMenu = e.target.querySelector('.sub-menu'); // Adjust this selector for your sub-menus
               } else {
                   // This ensures we get the sub-menu even if a child of the menu item was focused
                   subMenu = e.target.closest('.menu-item-has-children').querySelector('.sub-menu');
               }
    
               let subMenuItems = subMenu.querySelectorAll('a'); // Assumes sub-menu items are anchor tags
               let focusedElement = document.activeElement;
               let currentIndex = Array.from(subMenuItems).indexOf(focusedElement);
    
               switch(e.key) {
                   case 'ArrowDown':
                       // Move focus to the next sub-menu item
                       if (currentIndex < subMenuItems.length - 1) {
                           subMenuItems[currentIndex + 1].tabIndex = 0;
                           subMenuItems[currentIndex + 1].focus();
                           e.preventDefault(); // Prevent scrolling the page
                       } else {
                           // Optionally wrap around to the first item
                           subMenuItems[0].tabIndex = 0;
                           subMenuItems[0].focus();
                           e.preventDefault(); // Prevent scrolling the page
                       }
                       break;
                   case 'ArrowUp':
                       // Move focus to the previous sub-menu item
                       if (currentIndex > 0) {
                           subMenuItems[currentIndex - 1].tabIndex = 0;
                           subMenuItems[currentIndex - 1].focus();
                           e.preventDefault(); // Prevent scrolling the page
                       } else {
                           // Optionally wrap around to the last item
                           subMenuItems[subMenuItems.length - 1].tabIndex = 0;
                           subMenuItems[subMenuItems.length - 1].focus();
                           e.preventDefault(); // Prevent scrolling the page
                       }
                       break;
               }
    
               // Reset tabIndex to -1 for items not focused to ensure they're skipped during tab navigation
               subMenuItems.forEach(subMenuItem => {
                   if (subMenuItem !== document.activeElement) {
                       subMenuItem.tabIndex = -1;
                   }
               });
           });
       });
    });
    </script>
     <?php
    }
    add_action( 'wp_footer', 'tab_though_menu_skipping_sub_menu_items_unless_down_or_up_arrow_keys_are_used', 99 );
    
    
  • Hi there,

    we take the themes accessibility very seriously so if there is anything we can do to improve it we will definitely do so. So we appreciate your feedback on anything we can do to make things better.

    If i understand correctly the issue is; an inability to use Arrow keys to navigate the focused menu.

    To the best of my understanding the requirement for Arrow keys is when non-sequential navigation is a requirement:

    https://www.w3.org/TR/UNDERSTANDING-WCAG20/navigation-mechanisms-focus-order.html

    But not having Arrow keys support, is not a WCAG failure IF, to paraphrase; the Navigation is sequentially and logically ordered and can be navigated using conventional methods eg. keyboard tabbing.

    But I will add this Arrow requirement to our development review.
    If there is any documentation or other information that may contradict our understanding please let me know.

  • BTW you’ll also need this to change the aria-expanded attributes on activation –

    document.addEventListener("DOMContentLoaded", function() {
        var menuItem = document.querySelector(".menu-item-has-children");
        var subMenu = menuItem.querySelector(".sub-menu");
        var hasMouseEnter = false;
        var clickedAfterMouseEnter = false;
    
        menuItem.addEventListener("mouseenter", function() {
            this.setAttribute("aria-expanded", "true");
            hasMouseEnter = true;
        });
    
        menuItem.addEventListener("mouseleave", function() {
            if (!clickedAfterMouseEnter && hasMouseEnter) {
                this.setAttribute("aria-expanded", "false");
            }
            clickedAfterMouseEnter = false; // Reset the flag
        });
    
        menuItem.addEventListener("focusin", function() {
            this.setAttribute("aria-expanded", "true");
            hasMouseEnter = true;
        });
    
        subMenu.addEventListener("transitionend", function(event) {
            if (event.propertyName === "max-height" && !subMenu.classList.contains("expanded")) {
                menuItem.setAttribute("aria-expanded", "false");
                hasMouseEnter = false;
            }
        });
    
        menuItem.addEventListener("click", function(event) {
            if (!subMenu.classList.contains("expanded")) {
                this.setAttribute("aria-expanded", "true");
            } else {
                this.setAttribute("aria-expanded", "false");
            }
            clickedAfterMouseEnter = hasMouseEnter;
            event.preventDefault(); // Prevent default behavior of click event
            event.stopPropagation(); // Stop propagation to prevent the mouseleave event
        });
    
        menuItem.addEventListener("focusout", function() {
            this.setAttribute("aria-expanded", "false");
        });
    });
  • Thanks – can you let me know if and when this is incorporated into the theme? Then I can update and remove my snippets!

  • It has been raised for review with our development team.

    Thanks for sharing your scripts.

Viewing 8 posts - 1 through 8 (of 8 total)
  • You must be logged in to reply to this topic.