Accessible Styled Form Controls

Styled Radio Button Pills

Published:

Last updated:

Pattern to restyle a radio button group into a selection pill.

Pattern Demo

Group Title
Show:
Show (no group wrapper):

Pattern Details

Pattern Markup
<fieldset class="r-pill">
  <legend>Group Title</legend>

  <div class="r-pill__group"> <!-- optional styling wrapper -->
  	<span class="r-pill__item">
	    <input type="radio" id="r1" name="radios" checked>
	    <label for="r1">Option 1</label>
	  </span>
	  <span class="r-pill__item">
	    <input type="radio" id="r2" name="radios">
	    <label for="r2">Option 2</label>
	  </span>
  </div>
</fieldset>

As with the styled radio buttons pattern, the radio button pills also utilize visually hidden native HTML radio buttons as the cornerstone for providing the necessary semantics and user experience for keyboard users, and the accessibility API.

The radio buttons are wrapped in a fieldset element. The presence of the fieldset element groups the radio buttons and programmatically communicates that they are functionally interdependent. The legend element provides a description of the grouped radio buttons' purpose.

If it makes sense for the grouping of radio buttons to be called out in the document outline, then the legend can incorporate an appropriately ranked heading element (e.g. h3).

The form control with the checked attribute applied to it will trigger a CSS selector that visually updates the appearance of the sibling label associated with it, indicating that it is the selected option. As there is no way to deselect a radio button, without also resetting an entire form or including a radio button option specifically to indicate a "no selection", a radio button pill should always have a radio button set with a default checked attribute.

A selector targeting the last label's :after pseudo-element will create an outline around the entire component to indicate it has keyboard focus and can be interacted with.

Affects on Screen Reader Announcements?

Since the native radio buttons remain in the DOM, and are used as the focusable elements that control the state of their custom visualization, there are no alterations to how a screen reader announces keyboard focused styled radio buttons.

Using standard visually hidden CSS to hide the native radio button off screen, or as a single pixel in the document, will mean that certain screen reader users might be unable to locate the radio button. For instance, those searching by touch, or using a mouse while a screen reader announces what is being hovered. To mitigate this, the radio buttons have been positioned on top of their styled labels, yet still styled to be visually hidden. Note that NVDA does not announce the radio button role when hovered by mouse, regardless of it being styled or not, unless default NVDA settings are changed.

When a radio button is receives keyboard focus, screen readers like JAWS and NVDA may automatically go into forms mode, or require a user to hit Enter or Space to focus into the radio button, depending on the user's settings. Then arrow keys can be used to select the different radio buttons, as expected in forms mode.

VoiceOver on macOS may begin to focus the radio button labels when using arrow keys to change the selected radio button. This issue does not exist in VoiceOver + Chrome and is not necessarily unique to this particular styling of radio buttons.

Usage note

Radio buttons are meant to indicate a group of single choice options, typically part of a larger form area of a document. It may be acceptable to a pattern like this outside of the context of a form, as long as reverting state is a simple process for the user, and the user is aware of the effect the state change will have on the UI. Be mindful of WCAG 3.2.2, On Input.

Alternatively, a grouping of button elements that are aware of each other's state, and utilize aria-pressed="true/false" to indicate their own state, may be more appropriate and understandable to users.