Take the basic tabs and give them the ARIA tabs contract so assistive technology understands the pattern: a tablist containing tabs, each linked to the tabpanel it controls, with the selected tab marked. Visually unchanged — but now a screen reader announces "tab, 1 of 3, selected" and knows which panel a tab reveals.
// A self-contained component. No props.
function App(): JSX.Element;
The same tabs, now with role="tablist", role="tab", role="tabpanel", aria-selected, and the aria-controls/aria-labelledby links.
<div role="tablist" aria-label="Product information">
<button role="tab" id="tab-pricing"
aria-selected="true" aria-controls="panel-pricing">Pricing</button>
…
</div>
<div role="tabpanel" id="panel-pricing" aria-labelledby="tab-pricing" tabindex="0">…</div>
aria-selected="true" on the active tab; aria-controls links tab → panel;
aria-labelledby links panel → tab.
role="tablist" wraps the tabs; each tab button is role="tab"; the content area is role="tabpanel".aria-selected="true" on the active tab and false on the others (a class isn't enough for AT).aria-controls is the panel's id; the panel's aria-labelledby is the active tab's id (so the panel is named by its tab).tabIndex={0} so keyboard users can move into its content.