HTML’s tabindex attribute may be used to modify whether an element can receive keyboard focus, or not. It’s an attribute that can be quite beneficial in creating custom widgets, and ensuring elements that should be able to receive keyboard focus in some way are keyboard accessible. It can also be easily abused by people who are often trying to do the right thing, but don’t quite understand the undesired impact the attribute can have on accessibility when misused.

Quick summary of tabindex usage

One of the things people have to be incredibly careful about when using tabindex, is knowing exactly when to use it. Accepting a value of 0, or positive or negative integers, the attribute will produce the following effects:

Negative values

tabindex="-1" can be used to remove an otherwise focusable element from the document’s tab order.

It can also be used to allow focus to be programmatically set to an element that would otherwise not be able to receive focus, nor should be in the document’s tab order. For example, a heading with is the target of a skip link.

The value of the negative integer makes no difference when declared. e.g. tabindex="-1" and tabindex="-100" work the same.

Zero value

tabindex="0" will allow an element to become focusable and become part of the document’s sequential tab order. A HTML element that natively receives keyboard focus (button, a href="...", summary, form controls, etc.) does not require a tabindex="0" to be declared.

Where a tabindex="0" would be appropriate is if creating a custom element or widget using ARIA attributes. For instance, <span role="button">. A span is not natively focusable, but ARIA’s role="button" has rewritten the semantics of the span so that it is exposed to the browser’s accessibility tree as a “button”. Buttons, unless disabled, should be able to receive keyboard focus, and thus a tabindex="0" is necessary:

<span role="button" tabindex="0">
  Don't forget to write JavaScript
  that allows me to be activated by 
  Space and Enter keys as well!
</span>

Positive value

Outside of very specific corner cases, a tabindex should not be given a positive integer value. People who use keyboards to interact with the web expect a web document to be navigable in sequential order, starting at the top left (or right depending on language of the document) and going in order from there. Messing around with this expectation can land you in trouble with WCAG 2.4.3: Focus Order pretty quickly.

Consider the following markup:

<nav>
  <ul>
    <li><a href="...">Page 1</a>
    <!-- ... -->
    <li><a href="...">Page 6</a>
  </ul>
</nav>
<main>
  <!-- ... -->
  <p>
    ... 
    <a href="..." tabindex="1">go somewhere</a>
    ...
  </p>
</main>

A user navigating by Tab key from the browser’s address bar into the document would expect to go to the “Page 1” link. However, due to the tabindex="1" on the link within the main, keyboard focus would instead unexpectedly shoot past all the focusable elements prior to that link. Upon subsequent Tab key presses, focus would move to any other element with a positive tabindex in the document, before finally returning to the top of the document to begin focusing natively focusable elements, or elements with tabindex="0".

Managing positive tabindex values in a single document, let alone across an entire website or application, would be a ridiculous level of effort.

A corner case in the shadows?

While a positive tabindex should typically be avoided, there may be some use for it when building custom component and using the Shadow DOM. Exactly what that use is, I can only speculate. However, the reason I even mention this is because positive tabindex values on elements within the Shadow DOM, do not have an impact on the light DOM’s document tabbing order.

For example, the first and last of the following four buttons are in the standard document (light DOM) and the two in between are in the Shadow DOM. One of the Shadow DOM buttons has a tabindex="1". If you keyboard tab through the document, keyboard focus will not immediately go to the tabindex="1" button until keyboard focus navigates into the Shadow DOM.

Shadow Buttons:

See the code

Here is the code for the quick and dirty custom element that contains the two buttons.

<p>
  <button>In the light</button>
</p>

<p>
  Shadow Buttons:<br>
  <positive-btns></positive-btns>
</p>

<p>
  <button>In the light again</button>
</p>


<script>
  class pBtns extends HTMLElement {
    constructor () {
      super();

      const doc = document;
      // Create a shadow root
      const shadow = this.attachShadow({
        mode: 'open'
      });

      const styles = document.createElement('style');
      defineStyles( styles );
      shadow.appendChild(styles);

      const btn = document.createElement('button');
      const btn2 = document.createElement('button');

      btn2.tabIndex = '1';

      btn.textContent = 'First in DOM';
      btn2.textContent = 'First in Tab Order';


      // Append elements to the shadow root.
      shadow.appendChild(btn);
      shadow.appendChild(btn2);
    }
  }


  /**
   * Just wanted to move this out of the
   * constructor...
   */
  const defineStyles = function ( styles ) {
    styles.textContent = ''+
    'button {' +
      '-webkit-appearance: button;' +
      'font-family: inherit;' +
      'font-size: 100%;' +
      'line-height: 1.15;' +
      'margin: 0;' +
      'overflow: visible;' +
      'text-transform: none;' +
    '}' +
    'button::-moz-focus-inner {' +
      'border-style: none;' +
      'padding: 0;' +
    '}' +
    'button:-moz-focusring {' +
      'outline: 1px dotted ButtonText;' +
    '}'
  }

  // define the custom element
  customElements.define('positive-btns', pBtns);
</script>

Again, what is the benefit of using a positive tabindex here, rather than appending the buttons in the correct document order? Really none outside of showing that the primary document’s tab order doesn’t get negatively affected by positive tabindex values in the Shadow DOM.

But maybe knowing this will help you out in some situation I haven’t thought of yet. Or maybe (likely) this is still just a horrible no good very bad idea.

In which case, ignore please.