Segmented Control

Billing period
@use 'sprucecss/scss/spruce' as *;

.segmented-control {
  background-color: color('surface-muted');
  border: config('border-width-base', $display) solid color('border');
  border-radius: radius('md');
  display: inline-flex;
  gap: spacer('xxs');
  margin: 0;
  padding: spacer('xxs');

  > * + * {
    margin-block-start: 0;
  }

  &__option {
    border-radius: calc(#{radius('md')} - #{spacer('xxs')});
    color: color('text');
    cursor: pointer;
    font-weight: 600;
    padding: spacer('xs') spacer('s');
    @include transition($property: 'background-color, box-shadow, color');

    &:hover {
      color: color('heading');
    }

    &:has(:checked) {
      background-color: color('background');
      box-shadow: shadow('sm');
      color: color('heading');
    }

    &:has(:focus-visible) {
      outline: 2px solid color('focus-ring');
      outline-offset: 2px;
    }
  }

  &__input {
    @include visually-hidden;
  }

  &__hint {
    color: color('success', 'alert');
    font-weight: 700;
  }
}
<fieldset class="segmented-control">
    <legend class="sr-only">Billing period</legend>
    <label class="segmented-control__option">
        <input class="segmented-control__input" type="radio" name="billing" value="monthly" checked>
        <span>Monthly</span>
    </label>
    <label class="segmented-control__option">
        <input class="segmented-control__input" type="radio" name="billing" value="yearly">
        <span>Yearly <small class="segmented-control__hint">−20%</small></span>
    </label>
    <label class="segmented-control__option">
        <input class="segmented-control__input" type="radio" name="billing" value="lifetime">
        <span>Lifetime</span>
    </label>
</fieldset>

Segmented Control

On this page

A segmented control (button group style toggle) for switching between a few mutually exclusive options.

Technical Details

  • It is a group of native radio buttons in a fieldset, so the arrow keys move the selection and screen readers announce "1 of 3".
  • The selected option is styled with :has(:checked), and the focus ring with :has(:focus-visible); no JavaScript needed.
  • The legend is visually hidden; keep it so the group has a name.
  • Use it for 2–5 short options. For more options, use a select.

Documentation

Learn about Spruce CSS through our extensive documentation.

Components

Explore our UI library of accessible components built with Spruce CSS.

Blocks and Pages

Full-width page blocks and complete page templates, ready to copy.

Find us on GitHub

Did you find a bug? Have an idea or a question? Please open an issue to help us develop the project.