Code examples

Adding hint/help text

<label for="best-nato-letter">
  The best NATO letter is:
<input type="text" 

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

Adding an error

Note: The alert must be structured as below to function properly in VoiceOver, with the alert text nested inside the role="alert" element.

<label for="favorite-nato-letter">
  What is your favorite NATO letter?

<input type="text"
       aria-describedby="favorite-nato-error favorite-nato-hint"

<div role="alert" 
     class="alert inert">
  <!--- Do not reference this alert element
        directly with aria-describedby -->
  <div id="favorite-nato-error">
    <!--- Use JS to inject the alert here -->

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

<button id="show-error">
  Toggle error
Example: Alpha, Bravo, Charlie

When there is no hint or alert

Using aria-describedby with a uniqueID that doesn’t exist on page yet will generate errors in automated syntax checking tools.

If it’s not possible to remove the attribute, there are ways to avoid the error flag.

Option 1: Leave aria-describedby="" empty until the hint exists (preferred)

This is preferred because the DOM is cleaner.

<label for="favorite-pickle">
  What is your favorite pickle?
<input type="text"
       <!-- Leave aria-describedby attribute empty -->

Option 2: Leave the empty hint element in the DOM

This technique shouldn’t have any significant side effects, but does leave surplus elements in the DOM which is gross.

<label for="favorite-snack">
  What is your favorite healthy snack?
<input type="text"
<div class="hint" id="hint-favorite-snack">
  <!-- Leave the hint element empty -->

Developer notes

Browser + screenreader quirks

  • Screenreaders do not implement alerts uniformly and must be tested
    • Just because an alert pattern works in one screenreader doesn’t mean it will work in all three
  • The element referenced by the aria-describedby attribute cannot use the role="alert" attribute (see example above for workaround).
  • NVDA will read the alert twice if it appears while the input is in focus: once from the role="alert" being injected and from the aria-describedby association.
  • NVDA needs a fraction of a second to catch up with changes in the DOM, use a setTimeout to delay displaying the alert