Would this Navigation menu pass an accessibility test?

I am trying to improve my skills in developing accessible elements for my WordPress theme since the new European accessibility act will soon come into effect.

I’d like to show the HTML that my custom main menu walker generates and the javascript that goes with it, and it would be great if people could comment on whether this would pass an accessibility check.

Why am I asking this?

Online, you can find a lot of accessibility checkers, guides, and standards on what’s expected from an accessible main menu navigation. Yet, the information either contradicts each other or is a bit open-ended. Thus, I have a hard time understanding if what I am doing is a step in the right direction.

Bellow is the HTML that is generated by my custom walker class.

<nav id="main-menu" class="main-menu" aria-label="Main Navigation">
    <section class="menu-toggle-section">
        <button id="menu-toggle" class="menu-toggle" aria-label="Button for showing or hiding the Main Menu from view.">
            Menu
        </button>
    </section>
    <ul class="main-menu-list">
        <li id="menu-item-2469" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-home current-menu-item page_item page-item-2206 current_page_item menu-item-2469">
            <a href="https://darkfolklore.local/" aria-current="page" role="menuitem">Home</a>
        </li>
        <li id="menu-item-2588" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-2588">
            <a href="https://darkfolklore.local/services/" role="menuitem">Services</a>
        </li>
        <li id="menu-item-2470" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-has-children menu-item-2470">
            <a href="https://darkfolklore.local/blog/" role="menuitem">Blog</a>
            <button class="dropdown-button" aria-controls="dropdown-0" aria-haspopup="true" aria-expanded="false" aria-label="Toggle button for Blog submenu">
                <span class="dropdown-button-content" aria-hidden="true">+</span>
            </button>
            <ul class="sub-menu" role="menu" id="dropdown-0" aria-hidden="true">
                <li id="menu-item-2613" class="menu-item menu-item-type-post_type menu-item-object-post menu-item-2613">
                    <a href="https://darkfolklore.local/block-image/" role="menuitem">Block: Image</a>
                </li>
                <li id="menu-item-2610" class="menu-item menu-item-type-post_type menu-item-object-post menu-item-has-children menu-item-2610">
                    <a href="https://darkfolklore.local/birds-and-more-birds/" role="menuitem">Birds and More Birds</a>
                    <button class="dropdown-button" aria-controls="dropdown-1" aria-haspopup="true" aria-expanded="false" aria-label="Toggle button for Birds and More Birds submenu">
                        <span class="dropdown-button-content" aria-hidden="true">+</span>
                    </button>
                    <ul class="sub-menu" role="menu" id="dropdown-1" aria-hidden="true">
                        <li id="menu-item-2611" class="menu-item menu-item-type-post_type menu-item-object-post menu-item-has-children menu-item-2611">
                            <a href="https://darkfolklore.local/wp-6-1-font-size-scale/" role="menuitem">WP 6.1 Font size scale</a>
                            <button class="dropdown-button" aria-controls="dropdown-2" aria-haspopup="true" aria-expanded="false" aria-label="Toggle button for WP 6.1 Font size scale submenu">
                                <span class="dropdown-button-content" aria-hidden="true">+</span>
                            </button>
                            <ul class="sub-menu" role="menu" id="dropdown-2" aria-hidden="true">
                                <li id="menu-item-2616" class="menu-item menu-item-type-post_type menu-item-object-post menu-item-2616">
                                    <a href="https://darkfolklore.local/block-gallery/" role="menuitem">Block: Gallery</a>
                                </li>
                            </ul>
                        </li>
                        <li id="menu-item-2614" class="menu-item menu-item-type-post_type menu-item-object-post menu-item-2614">
                            <a href="https://darkfolklore.local/block-button/" role="menuitem">Block: Button</a>
                        </li>
                    </ul>
                </li>
                <li id="menu-item-2615" class="menu-item menu-item-type-post_type menu-item-object-post menu-item-2615">
                    <a href="https://darkfolklore.local/block-cover/" role="menuitem">Block: Cover</a>
                </li>
            </ul>
        </li>
        <li id="menu-item-2471" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-2471">
            <a href="https://darkfolklore.local/contacts/" role="menuitem">Contacts</a>
        </li>
        <li class="menu-item">
            <a role="menuitem" aria-haspopup="false" aria-expanded="false" href="https://darkfolklore.local/wp-login.php?redirect_to=https%3A%2F%2Fdarkfolklore.local%2F">Sign-in</a>
        </li>
        <li class="menu-item">
            <a role="menuitem" aria-haspopup="false" aria-expanded="false" href="https://darkfolklore.local/wp-login.php?action=register">Register</a>
        </li>
    </ul>
</nav>

The javascript for my menu.

document.addEventListener('DOMContentLoaded', function() {

    //* Clicking on menu item button.
    // Get all dropdown buttons
    const dropdownButtons = document.querySelectorAll('.dropdown-button');
    // Add to each button...
    dropdownButtons.forEach(function (button) {
        // If the button iss clicked...
        button.addEventListener('click', function () {
            // Get the dropdown menu.
            const submenu = document.getElementById(button.getAttribute('aria-controls'));
            const openChildren = submenu.getElementsByClassName('sub-menu visible');

            if(openChildren) {
                for (const childMenu of openChildren) {
                    childMenu.classList.remove('visible');
                    childMenu.classList.add('hidden');

                    childMenu.setAttribute('aria-hidden', 'true');
                
                    const childButton = childMenu.previousElementSibling;
                    childButton.setAttribute('aria-expanded', 'false');
                
                    const buttonText = childButton.querySelector('.dropdown-button-content');
                    if (buttonText) {
                        buttonText.textContent = '+';
                    }
                }
            }

            if (submenu.classList.contains('visible')) {
                submenu.classList.remove('visible');
                submenu.classList.add('hidden');

                submenu.setAttribute('aria-hidden', 'true');

                button.setAttribute('aria-expanded', 'false');

                const buttonText = button.querySelector(".dropdown-button-content");
                buttonText.textContent = '+';
            } else {
                submenu.classList.remove('hidden');
                submenu.classList.add('visible');

                submenu.setAttribute('aria-hidden', 'false');

                button.setAttribute('aria-expanded', 'true');

                const buttonText = button.querySelector(".dropdown-button-content");
                buttonText.textContent = '-';
            }
        });
    });

    //* If the menu item looses focus while Tab or Tab + Shift is pressed.
    document.addEventListener('focusout', function (event) {
        const focusedElement = event.target;

        // Check if the focused element is the last menu item in a submenu
        if (focusedElement.getAttribute('role') === 'menuitem') {
            const subMenu = focusedElement.closest('.sub-menu');
            if (subMenu) {
                const button = subMenu.previousElementSibling;
                // Delay the check to allow the next focused element to be determined
                setTimeout(() => {
                    // Check if focus is still inside the submenu
                    if (!subMenu.contains(document.activeElement)) {
                        // Remove the .visible class from the submenu
                        button.focus();
                        button.setAttribute('aria-expanded', 'false');
                        subMenu.classList.add('hidden');
                        subMenu.classList.remove('visible');

                        subMenu.setAttribute('aria-hidden', 'true');

                        const buttonText = button.querySelector('.dropdown-button-content');
                        if (buttonText) {
                            buttonText.textContent = '+';
                        }
                    }
                }, 0);
            }
        }
    });

    //* Enable keyboard buttons.
    document.addEventListener('keydown', function (event) {
        const focusedElement = document.activeElement;

        //* Escape: Close the menu that contains focus and return focus to the element or context, e.g., menu button or parent menuitem, from which the menu was opened.
        if (event.key === 'Escape') { 
            const subMenu = focusedElement.closest('.sub-menu.visible');

            // Check if the focused element is inside .sub-menu
            if (subMenu) {
                const button = subMenu.previousElementSibling;
                button.focus();
                // Close the submenu
                button.setAttribute('aria-expanded', 'false');
                subMenu.classList.add('hidden');
                subMenu.classList.remove('visible');

                subMenu.setAttribute('aria-hidden', 'true');

                const buttonText = button.querySelector('.dropdown-button-content');
                if (buttonText) {
                    buttonText.textContent = '+';
                }
            }
        }

        //* Enter
        //* When focus is on a menuitem that has a submenu, opens the submenu and places focus on its first item.
        //* Otherwise, activates the item and closes the menu.
        if (event.key === 'Enter' ) {
            // Check if the focused element has a submenu
            if (focusedElement.classList.contains('dropdown-button')) {
                const ariaExpanded = focusedElement.getAttribute('aria-expanded');
                const ariaControls = focusedElement.getAttribute('aria-controls');
                const submenu = document.getElementById(ariaControls);

                if (submenu) {
                    if (ariaExpanded === 'false') {
                        // Open the submenu
                        focusedElement.setAttribute('aria-expanded', 'true');
                        submenu.classList.add('visible');
                        submenu.classList.remove('hidden');

                        submenu.setAttribute('aria-hidden', 'false');

                        const buttonText = focusedElement.querySelector('.dropdown-button-content');
                        if (buttonText) {
                            buttonText.textContent = '-';
                        }

                        // Move focus to the first item in the submenu
                        const firstSubmenuItem = submenu.querySelector('[role="menuitem"]');
                        if (firstSubmenuItem) {
                            firstSubmenuItem.focus();
                        }
                    } else {
                        // Close the submenu
                        focusedElement.setAttribute('aria-expanded', 'false');
                        submenu.classList.add('hidden');
                        submenu.classList.remove('visible');

                        submenu.setAttribute('aria-hidden', 'true');

                        const buttonText = focusedElement.querySelector('.dropdown-button-content');
                        if (buttonText) {
                            buttonText.textContent = '+';
                        }
                    }
                }
            }
        }

        //* Home: If arrow key wrapping is not supported, moves focus to the first item in the current menu or menubar.
        //* End: If arrow key wrapping is not supported, moves focus to the last item in the current menu or menubar.
        if (focusedElement.closest('.sub-menu')) {
            const menuItems = Array.from(focusedElement.closest('.sub-menu').querySelectorAll('[role="menuitem"]'));
    
            if (event.key === 'Home') {
                event.preventDefault();
                menuItems[0].focus();
            }
    
            if (event.key === 'End') {
                event.preventDefault();
                menuItems[menuItems.length - 1].focus();
            }
        }
    });
});
  1. I have used some Chrome extensions like “Accessibility Insights for Web” and “WAVE”.
  2. I have used some articles from https://www.w3.org/WAI/ARIA/apg/ as a guideline.
  3. Manual testing by using TAB and other keys to test the implementation.
  4. I have not tried automated testing websites since my website is being developed locally and is not on a server.