Video demos

iOS Voiceover

Android Talkback

MacOS Voiceover Safari

Code examples

Use semantic HTML

  • This semantic HTML contains all accessibility features by default.
  • It uses CSS pseudo attributes to create the radio indicator, no Javascript.
<fieldset>
  <legend>
    Choose the best NATO letter
  </legend>

  <input type="radio" name="nato" id="alphaRadio">
  <label for="alphaRadio">Alpha</label>

  <input type="radio" name="nato" id="bravoRadio">
  <label for="bravoRadio">Bravo</label>

  <input type="radio" name="nato" id="charlieRadio" aria-describedby="description-charlie" checked>
  <label for="charlieRadio">Charlie</label>
  <div class="description" id="description-charlie">The best at everything</div>
</fieldset>
Choose the best NATO letter
The best at everything

Fully disabled radio inputs

<fieldset>
  <legend>
    Choose your favorite coffee chain
  </legend>

  <input type="radio" name="coffee" id="dunkinRadio">
  <label for="dunkinRadio">Dunkin'</label>

  <input type="radio" name="coffee" id="dutchRadio" checked>
  <label for="dutchRadio">Dutch Brothers</label>

  <input type="radio" name="coffee" id="starbucksRadio" disabled>
  <label for="starbucksRadio">Starbucks</label>
</fieldset>
Choose your favorite coffee chain

Disabled and focusable radio inputs

It’s rare, but there are times when it’s desirable UX to increase the discoverability of disabled radio buttons. When specific radio inputs are conditionally enabled/disabled by other controls in the page this method can make ensure all radio input options and consequences of previous actions are discoverable.

It’s possible to use aria-disabled="true" allowing screen reader users to focus the radio button with the arrow keys. Using JS preventDefault() will prevent the checkbox from being checked.

<fieldset>
  <legend>
    Choose your favorite dance
  </legend>

  <input type="radio" name="dance" id="carltonRadio" aria-disabled="true">
  <label for="carltonRadio">Carlton</label>

  <input type="radio" name="dance" id="foxtrotRadio">
  <label for="foxtrotRadio">Foxtrot</label>

  <input type="radio" name="dance" id="tangoRadio" checked>
  <label for="tangoRadio">Tango</label>
</fieldset>
Choose your favorite dance

Required radio inputs

Ensuring all screenreaders indicate radio inputs as being required requires some aria and reinforcement.

  • Use aria-required="true" to indicate the group is required
  • Use aria-invalid="true/false" to indicate an error state
  • Add role="radiogroup" to the <fieldset> to make the aria-required attribute valid
  • Add “Required” as text to the <legend> to ensure compliance across all platforms
<fieldset aria-required="true" 
          aria-invalid="true" 
          role="radiogroup"
          aria-describedby="description-alert">

  <legend>
    Choose your extended warranty options <span>Required</span>
  </legend>

  <input type="radio" name="natoReq" id="deltaRadioReq">
  <label for="deltaRadioReq">Full replacement coverage</label>

  <input type="radio" name="natoReq" id="echoRadioReq">
  <label for="echoRadioReq">Conditional repair</label>

  <input type="radio" name="natoReq" id="foxtrotRadioReq" aria-describedby="description-foxtrotRadioReq">
  <label for="foxtrotRadioReq">None</label>
  <div class="description" id="description-foxtrotRadioReq">Warranty will expire after 30 days</div>
</fieldset>
Choose your extended warranty options Required
Warranty will expire after 30 days

Radio button cards

<ul class="cards">
  <li class="card interactive">
    <input type="radio"
           name="radioCards"
           id="deltaRadioCard"
           aria-describedby="description-deltaRadioCard" >
    <label for="deltaRadioCard">
      Delta
    </label>
    <div class="extended-description"
         id="description-deltaRadioCard">
      Delta (prounounced: <strong>dell</strong>-tah)
      is the fourth letter of the NATO alphabet.
    </div>
  </li>
  <li class="card interactive">
    <input type="radio"
           name="radioCards"
           id="echoRadioCard"
           aria-describedby="description-echoRadioCard">
    <label for="echoRadioCard">Echo</label>
    <div class="extended-description"
         id="description-echoRadioCard">
      Echo (prounounced: <strong>eck</strong>-oh)
      is the fifth letter of the NATO alphabet.
    </div>
  </li>
</ul>
  • Delta (prounounced: dell-tah) is the fourth letter of the NATO alphabet.
  • Echo (prounounced: eck-oh) is the fifth letter of the NATO alphabet.

When you can’t use semantic HTML

This custom button requires extra scripting work for roving tabindex and event listeners.

<custom-label id="labelId">
    Which is your favorite NATO letter:
</custom-label>
<div role="radiogroup" aria-labelledby="labelId">
  <custom-element role="radio" tabindex="-1">
    Alpha
  </custom-element>
  <custom-element role="radio" tabindex="-1">
    Bravo
  </custom-element>
  <custom-element role="radio" tabindex="-1">
    Charlie
  </custom-element>  
</div>

Specialty use cases

Radio mixed with interactive elements

Avoid placing interactive elements between radio buttons.

  • Radio button focus order is not what you think it is.
  • When nothing is selected, tab order moves through as expected.
  • However, as soon as a radio button is selected, the selected radio input receives focus first from the group.

Checkbox radio hack

  • This hack must be used very carefully on a case by case basis.
  • With great power comes great responsibility.
<fieldset class="checkbox-radio-group">
  <legend>Choose your payment method:</legend>
  <input class="radio"
         type="checkbox"
         role="radio"
         name="checkboxRadioGroup"
         id="checkboxRadioAlpha"
         aria-describedby="editAlpha"
         checked>
  <label for="checkboxRadioAlpha">
    Alpha
  </label>
  <button type="button"
          class="tertiary"
          id="editAlpha">
    Edit
    <span class="hidden">
      payment method alpha
    </span>
  </button>

  <input  class="radio"
          type="checkbox"
          role="radio"
          name="checkboxRadioGroup"
          id="checkboxRadioBravo"
          aria-describedby="editBravo">
  <label for="checkboxRadioBravo">
    Bravo
  </label>
  <button type="button"
          class="tertiary"
          id="editBravo">
    Edit
    <span class="hidden">
      payment method Bravo
    </span>
  </button>

  <input class="radio"
         type="checkbox"
         role="radio"
         name="checkboxRadioGroup"
         id="checkboxRadioCharlie"
         aria-describedby="editCharlie">
  <label for="checkboxRadioCharlie">
    Charlie
  </label>
  <button type="button"
          class="tertiary"
          id="editCharlie">
    Edit
    <span class="hidden">
      payment method Charlie
    </span>
  </button>
</fieldset>
Choose your payment method:

Thanks

Related radio button entries