Accessible Styled Form Controls

Styled Radio Buttons


Last updated:

Cross-browser styling for HTML radio buttons.

Pattern Demo

Visually Hidden Radio Buttons

Restyled Radio Buttons
Visual design note

-moz-appearance: none; does not completely remove the styling from the radio button in Firefox prior to version 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.

Using standard visually hidden CSS to hide the native radio button off screen, or as a single pixel in the document, will mean that some 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 pseudo element styled counterparts, yet remain 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.

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. Restyled Radio Elements

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

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 pre-v60. If you need to support those browsers, then this pattern may not be sufficient, and the visually hidden technique should be considered instead.

IE11 and Edge can use ::-ms-check to restyle both radio buttons and checkboxes. If using this vendor prefixed selector, make sure the checkbox and radio button styles don't clash.

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.