Keyboard tab order is supposed to follow reading order, but it actually follows render order - which breaks the moment a modal, a nested dialog, or an out-of-order component enters the mix.tab-indexer is a tiny, zero-dependency counter keyed by named "contexts" (header,form, modal, ...), each with its own start value and step, plus a region API to activate/deactivate a whole context - e.g. handing tab order over to a modal while it's open and back to the page once it closes.
@tab-indexer/react wraps the same core in useTabIndex(), a<TabScope> component that activates/deactivates its region in an effect (no render-purity problems), and a <TabIndexProvider> for when a counter needs to be reactive to nested activation. The interesting part was everything React breaks that a plain counter doesn't have to worry about: a re-render calling the counter again and drifting it forward, StrictMode's double-invoke, and SSR sharing one module-level singleton across requests.
npm i tab-indexer
npm i @tab-indexer/reactLive playground (React example)