Accessible Styled Form Controls

Styled Radio Buttons

Last updated:

Cross-browser styling for HTML radio buttons.

Pattern Demo

Visually Hidden Radios

Radio Element Restyles
Visual design note

-moz-appearance: none; does not completely remove the styling from the radio button in Firefox ESR. (works as expected in Firefox 60+).

The styled radio button utilizes box-shadowes to visually indicate state. However box-shadow does not render as expected in IE11. The radio button looks as intended in Edge.

Pattern Details

These custom radio button styles follow one of the two following markup patterns:

1. Visually Hidden Radios

Pattern Markup: Visually Hidden Style
<div class="c-radio">
  <input type="radio" name="my_radios" id="unique_id">
  <label for="unique_id">
    Label text here

The div class="c-radio" container serves as a styling hook, utilizing position: relative; to keep absolutely positioned child elements, and pseudo elements, relative to the block container box.

While the actual radio button is visually hidden from view, it remains essential to screen readers and keyboard user focus. Hiding it completely, with display: none;, for example, would require all the native radio button functionality to be rewritten in JavaScript. Allowing the native radio button to continue to receive focus ensures that the form control can lean on its native semantics.

The label's :before and :after pseudo elements are used to recreate the radio button in the new custom style. With the :before acting as the outer border of the radio button, and the :after as the visual indicator for the checked state.

The styling of the custom radio button is relayed from the state of the native form control. The :checked, disabled, and :focus radio button states will all be reflected in the custom style by the use of CSS sibling selectors. For example input:checked ~ label:before {...}

2. Radio Element Restyles

Pattern Markup: Restyled Radio
<label class="n-radio-label">
  <input type="radio" name="n_radio" id="native_r2">

With this markup pattern the radio button is restyled, having its native styling completely removed by CSS appearance: none, and then restyled via use of box-shadows and borders.

The span element is used to wrap the radio button's text label so that a sibling selector can be used to modify the text's styling if the radio button is disabled.

Note, regarding the visual design note, there are issues with this styling technique in IE11 and Firefox ESR. If you need to support those browsers, then this pattern may not be sufficient, and the visually hidden technique should be considered instead.

Affects on Screen Reader Announcements?

Since the native radio buttons remains 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 styled radio buttons.

Continue reading

For additional information about creating minimal markup patterns for radio button and checkboxes, you should check out Under-Engineered Custom Radio Buttons and Checkboxen, by Adrian Roselli.