Tabs
Radio inputs wearing tab clothes. Arrow-key switching comes free — it's the native radio group behavior.
plain CSS — works everywhere
How it works
- Three
sr-onlyradios, three labels as triggers, three panels — all siblings, because Tailwind’speer-checked/name:needs the radio to precede what it styles. flex flex-wrap+basis-fullon the panels puts triggers on one row and the active panel below, no wrapper divs needed.peer-focus-visible/name:draws the focus ring on the label when the (visually hidden) radio has keyboard focus.
The honest accessibility trade-off
This announces as a radio group, not a tablist — because it is a radio group. Don’t
bolt role="tab" onto it: mixing ARIA tab semantics with radio behavior confuses screen
readers more than either alone. Arrow keys move between options and Tab leaves the group,
which is exactly what ARIA tabs require — you’re trading the “tab” announcement for zero JavaScript.
If your accessibility review demands true tablist semantics, that’s a legitimate reason to
reach for a JS implementation.
When the panels hold heavy content
hidden removes inactive panels from the accessibility tree and find-in-page. If that matters,
consider separate pages — tabs hiding important content is usually an information-architecture
smell anyway.