Home

Awesome

Accessible Tabbed Interfaces

A script to progressively enhance sectioned content into an accessible tabbed interface.

How to use

To help facilitate the simplest integration with your code base, the necessary markup has been boiled down to a wrapping element with a data-atabs attribute to serve as the Tab Widget container. Additionally, tabs, and tabpanels can be designated via different markup patterns to help suit your needs.

Example Setup

<div data-atabs> <!-- necessary wrapping element -->
  <!-- Panel method 1 -->
  <div data-atabs-panel 
    data-atabs-tab-label="Tab label goes here">
    <!-- all panel content goes here -->
  </div>

  <!-- Panel method 2 -->
  <section data-atabs-panel>
    <h# data-atabs-heading>
      <!-- 
        The text/markup injected the panel's 
        associated role="tab" element.
      -->
    </h#>
  </section>
  <!-- repeat as necessary -->
</div>
<!-- ... -->
<script src="index.js"></script>
<script>
  var widget = '[data-atabs]';
  var els = document.querySelectorAll(widget);

  // Generate all Tab Widget instances
  for ( var i = 0; i < els.length; i++ ) {
    var nTabs = new ARIAtabs( els[i] );
  }
</script>

Injecting content into the Tab Widget

Once a new instance of a Tab Widget has been created, the addTab function can be called from outside the script. Using this function, you can reference elements in the DOM (by id) that are not in the data-atabs wrapping element, and move them into the Tab Widget, creating a tab and tabpanel for the content. This may be useful if you need to create a Tab Widget, but you may not have full control over the HTML of your document.

For instance:

<script src="index.js"></script>
<script>
  var tabInstance = '[data-atabs]';
  var els = document.querySelectorAll(tabInstance);
  var injectContent = document.getElementById('inject-content');
  var cloneContent = injectContent.cloneNode(true);
  var allTabs = [];

  // Generate all tab instances
  for ( var i = 0; i < els.length; i++ ) {
    var nTabs = new ARIAtabs( els[i] );

    allTabs.push(nTabs);
  }

  // remove the original instance of the external content from the document.
  injectContent.parentNode.removeChild(injectContent);

  // Inject the external content into a particular
  // tab, captured in the allTabs var.
  allTabs[1].addTab(cloneContent, 'Tab label', 'add-a-class');
</script>

Tab Widget attributes & options

The Tab Widget script runs through the markup looking for specific data-atabs-* attributes to use as hooks to modify the original markup and generate the final component. Here are the data attributes that are recognized by this script.

User Experience

Tab Widgets are a type of show/hide component, and the manner in which you interact with a Tab Widget will dictate the experience.

For mouse and touch users, the show/hide functionality is initiated by interacting with the tabs within the tablist. Activating a tab will reveal the tabpanel its associated with, while also deactivating the previously active tab, and hiding its tabpanel.

For keyboard users, and screen reader users in forms mode, a Tab Widget will automatically select and reveal the contents of the focused tab as a user navigates the tablist with arrow keys.

A option is available (data-atabs-manual) to require manual activation of tabs. This is not necessarily preferred behavior of a Tab Widget, but there may be instances where it's required due to performance issues.

The ARIA Authoring Practices notes:

It is recommended that tabs activate automatically when they receive focus as long as their associated tab panels are displayed without noticeable latency. This typically requires tab panel content to be preloaded. Otherwise, automatic activation slows focus movement, which significantly hampers users' ability to navigate efficiently across the tab list.

This script has been tested to work with mouse, touch, and keyboard devices. Each input device has also been re-tested while running various browser and screen reader pairings.

FYI: More information concerning expected functionality and screen reader announcements will be linked to from here.

Dependencies and known issues

There are no dependencies for this script. Any necessary polyfill (for IE11) is included in the JavaScript.

There are some issues with how screen readers interact with Tab Widgets.

Additional Reading

License, Thank yous & Such

This script was written by Scott O'Hara: Website, Twitter.

It has an MIT license.

Special thanks to Josh Drumm and Chris Ferdinandi for helping me with some JavaScript refactoring and helper functions.

Thanks to Léonie Watson, Adam Campfield, Ryan Adams, and many others who provided excellent user feedback.

Use it, modify it, contribute to it to help make your project more accessible :)