Search code examples
javascripthtmlvue.jsvuejs3accessibility

Setting tabindex dynamically in a navigation drawer


I'm trying to ignore the focus when the drawer is closed, but I still can tab through links, even when it's closed (when showDrawer ref is set to false). I can even still tab if I set tabindex="-1" on the <nav>. The only solution I found is to add :tabindex="showDrawer ? 0 : -1" directly to each of the <router-link> or a <button>, not even the <li>, but the actual element inside of it, otherwise it won't work. I feel like it's not the best way to do it. I need to somehow manage to do it using tabindex, because removing the drawer from the DOM when it's closed is going to add performance implications.

<template>
    <nav class="drawer" :tabindex="showDrawer ? 0 : -1">
        <ul class="drawer__list">
            <li class="drawer__item">
                <router-link
                    to="/"
                    >Home</router-link>
            </li>
            <li class="drawer__item">
                <button
                    About
                </button>
            </li>
            <li class="drawer__item">
                <button
                    Log In
                </button>
            </li>
        </ul>
    </nav>
    <ItemOverlay :class="{ active: showDrawer }" />
</template>

Looking for a possible solution.


Solution

  • It’s great that you are starting to consider keyboard interaction for accessibility! I hope I can clarify two misunderstandings that you currently seem to have.

    1. tabindex is not inherited in the DOM, it only applies to the element itself
    2. Focus order is not the only thing to worry about when hiding a (navigation) drawer, general access by assistive technology must also be prohibited

    This can be done with the inert attribute:

    <nav class="drawer" :inert="!showDrawer">
    

    That is if you can’t use CSS that reliably hides the element from interaction and assistive technology like display: none or visibility: hidden.

    <p><a href="#">First tab stop</a></p>
    
    <nav inert>
      <ul>
        <li><a href="#" current="page">Active Page</a></li>
        <li><a href="#">Another Page</a></li>
      </ul>
    </nav>
    
    <p><a href="#">Last tab stop</a></p>

    The alternative would be to hide the navigation from assistive technology

    <nav class="drawer" :aria-hidden="!showDrawer">
    

    and avoid focus for each interactive element

    <router-link :tabindex="showDrawer ? 0 : -1"…
    

    inert does both of these.