Concepts 
Why Tabster exists
Web applications contain many actionable elements in various combinations. From the accessibility perspective, the application should be usable with the keyboard alone.
Some HTML elements like <button> are focusable by default (reachable with
the Tab key); for other elements the tabindex attribute can make them
focusable. Making an element focusable is not the same as making it reachable
in a sensible order. Consider an infinite news feed: it would be impossible
to Tab past it to reach whatever comes after, because new focusable items keep
appearing as you tab through it.
Browsers also provide higher-level focus-management primitives, but real
applications often exceed their models. The native <dialog> element has
evolved for more than a decade, yet it still cannot represent a modal region
composed of multiple DOM containers. The emerging focusgroup API handles
roving focus, arrow-key navigation, and focus memory, but not hierarchical
Enter/Escape navigation, custom Tab behavior or trapping, or application
selection and activation state.
Tabster was created as a practical focus-navigation core for a large,
complex application. It provides a small set of relatively low-level concepts,
but because they are aware of one another and compose as a single system, they
remain powerful in deeply nested and dynamic application structures. Tabster
handles these keyboard-navigation scenarios declaratively, mostly by adding a
data-tabster attribute to DOM elements.
It lets you group focusable elements so they behave as a single entity
(Groupper), move focus with arrow keys instead of just Tab
(Mover), restore focus when the focused element disappears from
the DOM (Deloser, Restorer), manage focus and
accessibility boundaries for modal regions and popups
(Modalizer), find and wait for elements that aren't mounted yet
(Observed Element), draw a robust focus outline
(Outline), and traverse focusable elements programmatically
(Focusable).
Browsers continue to add native alternatives for some of these capabilities, but application-level composition remains necessary. See Browser APIs and Tabster for a detailed comparison.
How it works
Tabster supports two application-level models for Tab navigation. Choose the
model that fits the application when calling createTabster(); Tabster does
not switch between them for different areas of the DOM. Neither model is
universally better—the right choice depends on how much of the focusable DOM
the application owns and how it integrates third-party components.
controlTab is independent of the
uncontrolled attribute. controlTab chooses how Tab is
handled application-wide. uncontrolled marks a particular subtree whose
internal focus behavior Tabster should leave to native browser behavior or
another component; it can be used with either controlTab value.
Controlling Tab programmatically
When controlTab is true (the default), Tabster intercepts Tab presses
under the root, calculates the next focus target, prevents the browser's
default Tab behavior, and focuses that target programmatically. This model is
best suited to relatively simple applications that own their focusable DOM
and do not need to coordinate with third-party components that manage focus
independently.
Letting the browser Tab and redirecting when necessary
When controlTab is false, Tabster does not choose the next element for
ordinary Tab presses. The browser follows its native tab order instead.
Whenever Tabster must alter that result—for example at a
Mover, Groupper, or
Modalizer boundary—it uses invisible "dummy" <input>
elements to receive the browser's focus and redirect it.
This model is generally a better fit for complex applications and
applications that integrate focus-managing third-party components, because
the browser remains responsible for finding the next element. The tradeoff is
that dummy inputs are real DOM nodes. Although invisible, they can affect DOM
assumptions, selectors, mutation observers, and tests, so applications should
account for and test them. Some features also use dummy inputs when
controlTab is true; with controlTab: false, they are fundamental to how
Tabster changes native navigation without taking over every Tab press. Use
getDummyInputContainer() if you
ever need to detect one.
IFrames
Applications that use <iframe> (especially cross-origin ones) add extra
complexity: iframes are isolated from each other for eventing purposes — a
Tab press in one iframe is invisible to the others. Tabster's
Cross-Origin module forwards a limited set of concerns
between iframes, most notably keyboard-navigation state and the Observed
Element API (so you can focus, by name, an element that lives
in a different frame). Cross-origin scenarios are advanced and comparatively
rare; see the Cross-Origin page for setup and caveats.
Summary
Tabster aims to make keyboard navigation work the way the browser would, if
the browser supported these scenarios natively. Everything is opt-in and
declarative — set a data-tabster attribute (similar to setting tabindex)
and enable the corresponding feature once via its get*() function. See
Getting Started for the practical walkthrough.