TabNav from @prepared911/ui-core renders a route-backed tab strip built on Radix TabNav. Each item is a real <a>, not a tab in the WAI-ARIA sense. The primitive hands off browser-native link behavior; your job is to label the navigation region and signal the current page.
What the component handles
- Link semantics. Items render as anchors, preserving middle-click, right-click, and modifier-key behaviors.
- Active state. Radix marks the current link with a selected visual and
aria-current="page" when routed correctly.
- Focus ring. Visible focus rings follow the browser's default link behavior.
- Accessible name for the nav. Wrap
TabNav in a landmark (or aria-label the root) so screen readers announce "Navigation: Incident subviews".
- Router integration. Ensure the
asChild pattern wires the active state to the router's current pathname so aria-current reflects reality.
- Link text. Avoid "Tab 1"; use meaningful labels that also make sense as bookmarkable URLs.
- Tab / Shift+Tab move between links.
- Enter activates the focused link.
- Modifier + click and Middle-click open in a new tab; respect these as you would any other link.
- Each item renders as
<a> with aria-current="page" on the active link.
- The component is not a tablist; it does not expose
role="tab" or manage roving focus.
- Nested landmarks. When
TabNav sits inside another named navigation such as Navbar, set landmark={false} so the inner Radix root is not a second navigation landmark. NavbarTabs does this automatically.
- In-page filters. If selecting a tab does not change the URL or page identity, use
TabMenu instead.
- Disabled destinations. Locking a route behind a feature flag requires an alternative affordance (tooltip or callout) that explains the locked state; don't render a disabled anchor silently.