Video examples

iOS Voiceover Safari

Android Talkback Chrome

Windows Jaws Chrome

Windows NVDA Chrome

MacOS Voiceover Safari

Code examples

Use semantic HTML

This semantic HTML contains all accessibility features by default.

<label for="best-nato-letter">
  The best NATO letter is:
</label>
<input type="text" 
       id="best-nato-letter" 
       aria-describedby="best-nato-letter-hint">

<div class="hint" id="best-nato-letter-hint">
  Example: Alpha, Bravo, Charlie
</div>
Example: Alpha, Bravo, Charlie

Required input

<label for="second-nato-letter">
 The second NATO letter is: <span>Required</span>
</label>
<input type="text"
       id="second-nato-letter"
       aria-required="true"
       required
       value="Bravo">

Disabled but focusable input

  • Use JS to preventDefault()
  • There may be times that it is advantageous for the input to be disabled but still focusable
  • Fully disabled inputs are not focusable and may not be as discoverable in a form
<label for="first-nato-letter">
  The first NATO letter is:
</label>
<input type="text" 
       id="first-nato-letter" 
       aria-disabled="true"
       value="Alpha">

Fully disabled input

  • Fully disabled inputs are not focusable so may not be as discoverable in a form
<label for="suavest-nato-letter">
  The suavest NATO letter is:
</label>
<input type="text" 
       id="suavest-nato-letter"
       value="Romeo"
       disabled>

readonly input

  • Only use readonly when presenting already submitted information.
  • readonly inputs are focusable but not editable
  • VoiceOver does not describe readonly attribute, so aria-disabled was added to reinforce that it’s not editable
<label for="fourth-nato-letter">
  The fourth NATO letter is:
</label>
<input type="text"
       id="fourth-nato-letter"
       aria-readonly="true"
       aria-disabled="true"
       readonly
       value="Delta">

Email input

  • Setting type=”email” changes the keyboard for mobile app users
<label for="email">
  Email address
</label>
<input id="email"
       type="email"
       autocomplete="email"
       spellcheck="false"
       aria-describedby="hint-email">
<div class="hint" id="hint-email">
  We’ll never sell or share your information
</div>
    
We’ll never sell or share your information

Output

  • output can be used for a dynamic content that changes based on user inputs (example: a calculator).
  • Screenreader support varies, so placing the label inside the output seems to work best
  • Alternatively, using a custom element with role=”status” will achieve more predictable results
<output tabindex="0">
  The answer to the question of life,
  the universe and everything is: 
  42
</output>
The answer to the question of life, the universe and everything is: 42

Group of inputs

After the screenreader focuses on each input, it will read the group name “Enter your personal information” after the input.

<fieldset>
  <legend>
    Enter your personal information
  </legend>

  <label for="first-name">
    First name
  </label>
  <input type="text" id="first-name">

  <label for="last-name">
    Last name
  </label>
  <input type="text" id="last-name">

  <label for="username">
    Username
  </label>
  <input type="text" id="username">
</fieldset>
Enter your personal information

Developer notes

Name

  • Include for="input-id in each <label> label to associate it with the input
  • Use aria-label="Input name" as a last resort if a <label> can’t be used
  • Don’t hide the label on focus

Role

  • Identifies as a text input

Group

  • Include for="input-id in each <label> label to associate it with the input
  • Use <fieldset> and <legend> to name a group of inputs.

Focus

  • Focus must be visible