Tabs: initTabs(node)
Create a tab UI that switches panels when the tabs are clicked, packaged as initTabs(node) — a jQuery-plugin-style initializer — so the two tab sets already on the page work independently. (Prefer React? Switch the template — the same exercise is there as a Tabs component rendered twice.)
Requirements
- Exactly one panel per tab set is visible; clicking a tab shows its panel and moves the active styling.
- The first tab is selected on load.
initTabs(root)wires up one[data-tabs]element and returns a small API (activate(index)). Initializing the second set must not touch the first.- Use "the right ID pattern": each tab's
aria-controlspoints at its panel's id and each panel'saria-labelledbypoints back — unique across instances. - Keep
aria-selectedin sync; hide inactive panels with thehiddenattribute. - Left/Right arrow keys move between tabs and wrap around, with a roving
tabindexso only the active tab is in the tab order.
Hints
Hint 1
Scope every query to the root: root.querySelector(':scope > [role="tablist"]') and :scope > [role="tabpanel"] — that's what stops nested or sibling tab sets from hijacking each other.
Hint 2
An instance counter (tabs-1, tabs-2, …) gives each set unique ids for aria-controls / aria-labelledby; one delegated click listener on the tablist replaces one listener per tab. In React, useId() is that counter.
Hint 3
Write a single activate(index) that sets aria-selected, tabIndex, the active class, and hidden for every tab/panel pair — click, keyboard, and the initial state all call it.