Vanilla Breeze

data-segmented

Group radio inputs into a compact segmented control with sunken track and raised active segment. CSS-only pattern over native radios with vertical and compact variants.

Overview

Add data-segmented to a <fieldset> wrapping radio inputs to render a segmented control. Pure CSS — no JavaScript required. The native radios still submit with the form, support keyboard navigation, and participate in validation.

How It Works

  • The fieldset becomes a flex row with a tinted track background.
  • Each <label> is a segment; its radio is visually hidden but still focusable.
  • label:has(input:checked) paints the active segment with the surface color and a small drop shadow.
  • label:has(input:focus-visible) draws the focus ring on the segment, not the hidden radio.
  • Give the <legend> the .visually-hidden class to keep the group accessible without showing a visible heading.

Attributes

Attribute Values Description
data-segmented boolean Opts the fieldset in to the segmented-control layout.
data-segmented-vertical boolean Stack segments in a column instead of a row.
data-segmented-compact boolean Tighter padding and a smaller font size for dense toolbars.

Icons in Segments

Place <icon-wc> alongside the label text. Both inline and icon-only segments work — for icon-only, set aria-label on the input.

Variants

Combine data-segmented-compact for tight toolbar usage or data-segmented-vertical to stack segments. Native disabled on any radio greys out its segment.

Accessibility

  • The fieldset already announces as a group; the legend names it. Use .visually-hidden on the legend when a visible heading is redundant.
  • Radios keep native arrow-key navigation and form submission — nothing to re-implement.
  • Focus-visible draws an outline on the segment label, which remains visible in high-contrast modes.
  • For icon-only segments, put the accessible name on input[aria-label], not on the icon.

When Not to Use

  • For two mutually exclusive states that read as on/off, prefer data-switch on a single checkbox.
  • For more than 5–6 options, use a native <select> — segments become cramped and lose their quick-scan benefit.
  • For action buttons rather than mutually exclusive selection, use the menu / toolbar pattern.