Tailwind CSS cookbook · Intermediate

Make tabs with radio inputs and no JavaScript

Switch between panels of content with tab buttons, without writing JavaScript.

Create a free account to save this page, take notes and track your progress. Sign in

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

Make tabs with radio inputs and no JavaScriptTry it yourself
<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" and aria-[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

More Tailwind CSS recipes

Was this page helpful?