GitHub

Roving Tab Index

Roving tab index (RTI) manages keyboard focus across groups of interactive elements.

Overview

It automatically handles <a> and <button> elements, supports arbitrarily nested groups with boundary-based scoping, and provides full keyboard navigation support.

Targets

All <a> and <button> elements are automatically treated as RTI targets. Other elements can become targets by using the rti directive.

Groups

RTI has the concept of groups delineated by boundaries. There is a default "root" group under which all RTI targets and other groups reside. This group is automatically managed and does not require a declared boundary. Specific groups can be created using the rtiBoundary. These groups can be arbitrarily deeply nested. A group boundary is automatically treated as an RTI target.

Boundaries are given unique anonymous IDs unless a value is passed to rtiBoundary. Targets are considered in the group of the nearest ancestor that is an rtiBoundary. This can be overridden by passing a group ID to rti but this is only valid if the boundary has also been given a name.

I get tabbed to first
I get tabbed to second
I only get tabbed to if space is hit on this group
Use arrow keys to navigate around in the group
I'm an anchor that defaults to the outer group
You have to hit space again to get to me
You can hit escape to return to the previous group
Everything in here uses autogenerated groups
Does it work?
Let's goooooo
<div rti>I get tabbed to first</div>
<div rti>I get tabbed to second</div>
<button>I'm a button that defaults to the root group</button>
<div rtiBoundary="group">
  <div rti="group">I only get tabbed to if space is hit on this group</div>
  <div rti="group">Use arrow keys to navigate around in the group</div>
	<a>I'm an anchor that defaults to the outer group</a>
  <div rtiBoundary="nested">
    <div rti="nested">You have to hit space again to get to me</div>
		<button>I'm a button that defaults to the inner group</button>
    <div rti="nested">You can hit escape to return to the previous group</div>
  </div>
	<div rtiBoundary>
    <div rti>Everything in here uses autogenerated groups</div>
    <div rti>Does it work?</div>
		<button>It does!</button>
		<a>Let's goooooo</a>
  </div>
</div>

Keyboard navigation

Key Action
Tab / Shift+Tab Moves focus between RTI targets
/ Moves focus between targets within a group
Space Enters the focused group
ESC Leaves the current group

Note: the parent group isn't necessarily the containing group in the DOM. Overriding boundary and target values can produce an RTI hierarchy that differs from the DOM tree.