Tailwind CSS cookbook · Intermediate
Make tabs with radio inputs and no JavaScript
Switch between panels of content with tab buttons, without writing JavaScript.
Approach
Each tab is a label for a visually hidden radio input. Style the selected label with has-checked:, and show
the matching panel with peer-checked:block on a sibling that follows its radio.
Solution
<div class="m-6 max-w-md">
<div class="grid">
<input type="radio" name="tabs" id="t1" class="peer/t1 sr-only" checked>
<input type="radio" name="tabs" id="t2" class="peer/t2 sr-only">
<div class="flex gap-1 rounded-lg bg-slate-100 p-1 text-sm font-medium">
<label for="t1" class="flex-1 cursor-pointer rounded-md px-3 py-2 text-center text-slate-600 peer-checked/t1:bg-white peer-checked/t1:text-slate-900 peer-checked/t1:shadow-sm">Account</label>
<label for="t2" class="flex-1 cursor-pointer rounded-md px-3 py-2 text-center text-slate-600 peer-checked/t2:bg-white peer-checked/t2:text-slate-900 peer-checked/t2:shadow-sm">Billing</label>
</div>
<div class="mt-4 hidden rounded-lg border border-slate-200 p-4 peer-checked/t1:block">Account settings go here.</div>
<div class="mt-4 hidden rounded-lg border border-slate-200 p-4 peer-checked/t2:block">Billing details go here.</div>
</div>
</div>
Pitfalls
- Named peers (
peer/t1) are needed when several peers share a parent. - This pattern isn't a full ARIA tabs widget (no arrow-key navigation); for app UIs prefer Headless UI or Radix Tabs.
Variations
- Use links with
aria-current="page"andaria-[current=page]:styles for navigation tabs that change pages.
Further reading: Tailwind CSS docs
Reference: https://tailwindcss.com/docs/hover-focus-and-other-states#differentiating-peers