Video examples

iOS Voiceover Safari

Android Talkback Chrome

MacOS Voiceover Safari

Code examples

Use semantic HTML

This semantic HTML contains all accessibility features by default.

<fieldset>
  <legend>
    Choose your favorite 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" checked>
  <label for="charlieRadio">Charlie</label>
</fieldset>
Choose your favorite NATO letter:

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>

Speciality use cases

Radio with interactive elements

When a UI calls for interactive elements inbetween radio buttons, this can be very difficult.

  • 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.

This hack must be used very carefully on a case by case basis.

<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:

Developer notes

Name

  • label text should describe the radio input.
  • Use aria-describedby="hint-id" for hints or additional descriptions
  • aria-label="Radio input purpose" can also be used (as a last resort)

Role

  • By default, semantic HTML radio inputs identify as radio button
  • Use role="radio" for custom elements

Group

  • Semantic HTML
    • <fieldset> should wrap the radio group
    • <legend> should describe the group’s purpose
    • Each <label> must include for="input-id" to be associated with its input
  • Custom elements
    • Use role="radiogroup" to take the palace of fieldset
    • Use aria-labelledby="label-id" to associate an element as a label
    • aria-label="Group purpose" can also be used if there’s no label with an ID

State

  • Semantic HTML
    • checked (will be read as “selected” by screen reader)
    • Use the disabled state for inactive buttons
  • Custom element
    • Use aria-checked="true/false" to express state
    • Use aria-disabled="true" to declare inactive elements

Focus

  • Focus must be visible
  • Custom elements will require keyboard event listeners and roving tabindex
  • DO NOT put interactive elements inbetween radio inputs.