Open demo

CSS code

HTML
<fieldset class="seg-field">
  <legend>Billing period</legend>
  <div class="seg">
    <input type="radio" name="bill" id="bill-monthly" checked><label for="bill-monthly">Monthly</label>
    <input type="radio" name="bill" id="bill-yearly"><label for="bill-yearly">Yearly</label>
    <input type="radio" name="bill" id="bill-lifetime"><label for="bill-lifetime">Lifetime</label>
  </div>
</fieldset>

<fieldset class="seg-field">
  <legend>Theme</legend>
  <div class="seg seg-dark">
    <input type="radio" name="theme" id="theme-light"><label for="theme-light">Light</label>
    <input type="radio" name="theme" id="theme-dark" checked><label for="theme-dark">Dark</label>
    <input type="radio" name="theme" id="theme-system"><label for="theme-system">System</label>
  </div>
</fieldset>

<fieldset class="seg-field">
  <legend>Project status</legend>
  <div class="seg seg-wide">
    <input type="radio" name="status" id="status-all" checked><label for="status-all">All</label>
    <input type="radio" name="status" id="status-active"><label for="status-active">Active</label>
    <input type="radio" name="status" id="status-archived"><label for="status-archived">Archived</label>
  </div>
</fieldset>
CSS
/* Segmented control: a fieldset of visually hidden radios, each followed by the <label> that draws its segment. */
.seg-field{border:0;margin:0;padding:0;min-width:0;max-width:100%}
.seg-field legend{padding:0;margin-bottom:11px;font-size:.78rem;font-weight:700;letter-spacing:.08em;
  text-transform:uppercase;color:#667085}
.seg{position:relative;display:inline-flex;gap:2px;padding:4px;border-radius:11px;background:#eef0f4}
.seg input{position:absolute;opacity:0;width:1px;height:1px;margin:0;pointer-events:none}
.seg label{padding:9px 20px;border-radius:8px;font-size:.88rem;font-weight:600;color:#475467;cursor:pointer;
  transition:background-color .18s,color .18s,box-shadow .18s}
.seg label:hover{color:#101828}
.seg input:checked+label{background:#fff;color:#101828;box-shadow:0 1px 3px rgba(16,24,40,.14)}
.seg input:focus-visible+label{outline:2px solid #4f46e5;outline-offset:2px}
/* dark */
.seg-dark{background:#101828}
.seg-dark label{color:#98a2b3}
.seg-dark label:hover{color:#fff}
.seg-dark input:checked+label{background:#344054;color:#fff}
.seg-dark input:focus-visible+label{outline-color:#a5b4fc}
/* full width */
.seg-wide{display:flex;width:340px;max-width:100%}
.seg-wide label{flex:1;text-align:center;padding-left:0;padding-right:0}
@media (prefers-reduced-motion:reduce){.seg label{transition:none}}

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<fieldset class="seg-field">
  <legend>Billing period</legend>
  <div class="btn-group seg">
    <input type="radio" class="btn-check" name="bill" id="bill-monthly" checked><label class="btn" for="bill-monthly">Monthly</label>
    <input type="radio" class="btn-check" name="bill" id="bill-yearly"><label class="btn" for="bill-yearly">Yearly</label>
    <input type="radio" class="btn-check" name="bill" id="bill-lifetime"><label class="btn" for="bill-lifetime">Lifetime</label>
  </div>
</fieldset>

<fieldset class="seg-field">
  <legend>Theme</legend>
  <div class="btn-group seg seg-dark">
    <input type="radio" class="btn-check" name="theme" id="theme-light"><label class="btn" for="theme-light">Light</label>
    <input type="radio" class="btn-check" name="theme" id="theme-dark" checked><label class="btn" for="theme-dark">Dark</label>
    <input type="radio" class="btn-check" name="theme" id="theme-system"><label class="btn" for="theme-system">System</label>
  </div>
</fieldset>

<fieldset class="seg-field">
  <legend>Project status</legend>
  <div class="btn-group seg seg-wide">
    <input type="radio" class="btn-check" name="status" id="status-all" checked><label class="btn" for="status-all">All</label>
    <input type="radio" class="btn-check" name="status" id="status-active"><label class="btn" for="status-active">Active</label>
    <input type="radio" class="btn-check" name="status" id="status-archived"><label class="btn" for="status-archived">Archived</label>
  </div>
</fieldset>
CSS
/* Segmented control: a Bootstrap .btn-group of .btn-check radios, restyled through the --bs-btn-* variables. */
.seg-field{min-width:0;max-width:100%}
.seg-field legend{float:none;margin-bottom:11px;font-size:.78rem;font-weight:700;letter-spacing:.08em;
  text-transform:uppercase;color:#667085;line-height:normal}
.seg.btn-group{gap:2px;padding:4px;border-radius:11px;background:#eef0f4}
.seg .btn{--bs-btn-padding-x:20px;--bs-btn-padding-y:9px;--bs-btn-font-size:.88rem;--bs-btn-font-weight:600;
  --bs-btn-line-height:normal;--bs-btn-border-width:0;--bs-btn-border-radius:8px;
  --bs-btn-color:#475467;--bs-btn-bg:transparent;--bs-btn-hover-color:#101828;--bs-btn-hover-bg:transparent;
  --bs-btn-active-color:#101828;--bs-btn-active-bg:#fff;--bs-btn-focus-box-shadow:none;
  transition:background-color .18s,color .18s,box-shadow .18s}
.btn-group.seg>.btn.btn{border-radius:8px;margin-left:0}
.seg .btn-check:not(:checked)+.btn:hover{color:var(--bs-btn-hover-color)}
.seg .btn-check:checked+.btn{box-shadow:0 1px 3px rgba(16,24,40,.14)}
.seg .btn-check:focus-visible+.btn{outline:2px solid #4f46e5;outline-offset:2px}
/* dark */
.seg-dark.btn-group{background:#101828}
.seg-dark .btn{--bs-btn-color:#98a2b3;--bs-btn-hover-color:#fff;--bs-btn-active-color:#fff;--bs-btn-active-bg:#344054}
.seg-dark .btn-check:focus-visible+.btn{outline-color:#a5b4fc}
/* full width */
.seg-wide.btn-group{display:flex;width:340px;max-width:100%}
.seg-wide .btn{--bs-btn-padding-x:0}
@media (prefers-reduced-motion:reduce){.seg .btn{transition:none}}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<fieldset class="min-w-0 max-w-full">
  <legend class="mb-[11px] text-[.78rem] font-bold tracking-[.08em] text-[#667085] uppercase">Billing period</legend>
  <div class="relative inline-flex gap-0.5 rounded-[11px] bg-[#eef0f4] p-1">
    <input type="radio" name="bill" id="bill-monthly" class="peer/a sr-only" checked><label for="bill-monthly" class="cursor-pointer rounded-lg px-5 py-[9px] text-[.88rem] font-semibold text-[#475467] transition-[background-color,color,box-shadow] duration-[180ms] hover:text-[#101828] peer-checked/a:bg-white peer-checked/a:text-[#101828] peer-checked/a:shadow-[0_1px_3px_rgba(16,24,40,.14)] peer-focus-visible/a:outline-2 peer-focus-visible/a:outline-offset-2 peer-focus-visible/a:outline-[#4f46e5] motion-reduce:transition-none">Monthly</label>
    <input type="radio" name="bill" id="bill-yearly" class="peer/b sr-only"><label for="bill-yearly" class="cursor-pointer rounded-lg px-5 py-[9px] text-[.88rem] font-semibold text-[#475467] transition-[background-color,color,box-shadow] duration-[180ms] hover:text-[#101828] peer-checked/b:bg-white peer-checked/b:text-[#101828] peer-checked/b:shadow-[0_1px_3px_rgba(16,24,40,.14)] peer-focus-visible/b:outline-2 peer-focus-visible/b:outline-offset-2 peer-focus-visible/b:outline-[#4f46e5] motion-reduce:transition-none">Yearly</label>
    <input type="radio" name="bill" id="bill-lifetime" class="peer/c sr-only"><label for="bill-lifetime" class="cursor-pointer rounded-lg px-5 py-[9px] text-[.88rem] font-semibold text-[#475467] transition-[background-color,color,box-shadow] duration-[180ms] hover:text-[#101828] peer-checked/c:bg-white peer-checked/c:text-[#101828] peer-checked/c:shadow-[0_1px_3px_rgba(16,24,40,.14)] peer-focus-visible/c:outline-2 peer-focus-visible/c:outline-offset-2 peer-focus-visible/c:outline-[#4f46e5] motion-reduce:transition-none">Lifetime</label>
  </div>
</fieldset>

<fieldset class="min-w-0 max-w-full">
  <legend class="mb-[11px] text-[.78rem] font-bold tracking-[.08em] text-[#667085] uppercase">Theme</legend>
  <div class="relative inline-flex gap-0.5 rounded-[11px] bg-[#101828] p-1">
    <input type="radio" name="theme" id="theme-light" class="peer/a sr-only"><label for="theme-light" class="cursor-pointer rounded-lg px-5 py-[9px] text-[.88rem] font-semibold text-[#98a2b3] transition-[background-color,color,box-shadow] duration-[180ms] hover:text-white peer-checked/a:bg-[#344054] peer-checked/a:text-white peer-checked/a:shadow-[0_1px_3px_rgba(16,24,40,.14)] peer-focus-visible/a:outline-2 peer-focus-visible/a:outline-offset-2 peer-focus-visible/a:outline-[#a5b4fc] motion-reduce:transition-none">Light</label>
    <input type="radio" name="theme" id="theme-dark" class="peer/b sr-only" checked><label for="theme-dark" class="cursor-pointer rounded-lg px-5 py-[9px] text-[.88rem] font-semibold text-[#98a2b3] transition-[background-color,color,box-shadow] duration-[180ms] hover:text-white peer-checked/b:bg-[#344054] peer-checked/b:text-white peer-checked/b:shadow-[0_1px_3px_rgba(16,24,40,.14)] peer-focus-visible/b:outline-2 peer-focus-visible/b:outline-offset-2 peer-focus-visible/b:outline-[#a5b4fc] motion-reduce:transition-none">Dark</label>
    <input type="radio" name="theme" id="theme-system" class="peer/c sr-only"><label for="theme-system" class="cursor-pointer rounded-lg px-5 py-[9px] text-[.88rem] font-semibold text-[#98a2b3] transition-[background-color,color,box-shadow] duration-[180ms] hover:text-white peer-checked/c:bg-[#344054] peer-checked/c:text-white peer-checked/c:shadow-[0_1px_3px_rgba(16,24,40,.14)] peer-focus-visible/c:outline-2 peer-focus-visible/c:outline-offset-2 peer-focus-visible/c:outline-[#a5b4fc] motion-reduce:transition-none">System</label>
  </div>
</fieldset>

<fieldset class="min-w-0 max-w-full">
  <legend class="mb-[11px] text-[.78rem] font-bold tracking-[.08em] text-[#667085] uppercase">Project status</legend>
  <div class="relative flex w-[340px] max-w-full gap-0.5 rounded-[11px] bg-[#eef0f4] p-1">
    <input type="radio" name="status" id="status-all" class="peer/a sr-only" checked><label for="status-all" class="cursor-pointer rounded-lg flex-1 px-0 text-center py-[9px] text-[.88rem] font-semibold text-[#475467] transition-[background-color,color,box-shadow] duration-[180ms] hover:text-[#101828] peer-checked/a:bg-white peer-checked/a:text-[#101828] peer-checked/a:shadow-[0_1px_3px_rgba(16,24,40,.14)] peer-focus-visible/a:outline-2 peer-focus-visible/a:outline-offset-2 peer-focus-visible/a:outline-[#4f46e5] motion-reduce:transition-none">All</label>
    <input type="radio" name="status" id="status-active" class="peer/b sr-only"><label for="status-active" class="cursor-pointer rounded-lg flex-1 px-0 text-center py-[9px] text-[.88rem] font-semibold text-[#475467] transition-[background-color,color,box-shadow] duration-[180ms] hover:text-[#101828] peer-checked/b:bg-white peer-checked/b:text-[#101828] peer-checked/b:shadow-[0_1px_3px_rgba(16,24,40,.14)] peer-focus-visible/b:outline-2 peer-focus-visible/b:outline-offset-2 peer-focus-visible/b:outline-[#4f46e5] motion-reduce:transition-none">Active</label>
    <input type="radio" name="status" id="status-archived" class="peer/c sr-only"><label for="status-archived" class="cursor-pointer rounded-lg flex-1 px-0 text-center py-[9px] text-[.88rem] font-semibold text-[#475467] transition-[background-color,color,box-shadow] duration-[180ms] hover:text-[#101828] peer-checked/c:bg-white peer-checked/c:text-[#101828] peer-checked/c:shadow-[0_1px_3px_rgba(16,24,40,.14)] peer-focus-visible/c:outline-2 peer-focus-visible/c:outline-offset-2 peer-focus-visible/c:outline-[#4f46e5] motion-reduce:transition-none">Archived</label>
  </div>
</fieldset>

About this set

Three segmented controls for choosing one option from a short list: a light Billing period control with Monthly, Yearly and Lifetime, a dark Theme control with Light, Dark and System, and a full-width Project status control whose three segments share a 340 pixel track equally. The selected segment lifts onto a white (or slate, in the dark variant) tile with a soft shadow. Use the light version inside forms and pricing tables, the dark one on dark toolbars, and the full-width one as a filter above a list or in a narrow sidebar. Each control is a fieldset with a visible legend, and each segment is a real radio input, visually hidden, followed by the label that draws the segment. Because they are native radios, the browser handles selection, arrow-key movement inside the group and form submission, and the CSS only reacts to :checked and :focus-visible. The Bootstrap version is a .btn-group of .btn-check radios restyled through the –bs-btn-* variables, and the Tailwind version uses named peer-checked variants on each label.

What’s included

  • Light, dark and full-width variants
  • Native radio inputs grouped in a fieldset with a legend
  • Arrow keys move the selection, as in any radio group
  • Focus outline drawn on the visible segment
  • Colour transitions switch off under reduced motion

Accessibility

Each control is a fieldset whose legend names the group, and every segment is a labelled radio input, so screen readers announce the option, its position and whether it is selected. Tab moves into the group and the arrow keys change the selection. Keyboard focus draws a 2px indigo outline around the visible segment (a light indigo outline on the dark variant). The label colour transitions are removed when prefers-reduced-motion is set.

Customise it

Change the track colour on .seg (#eef0f4, or #101828 on .seg-dark) and the selected colours in the input:checked + label rule. In Bootstrap edit the --bs-btn-active-bg and --bs-btn-color variables on .seg .btn; in Tailwind edit the bg- and peer-checked/ utilities on each label.