instructure-ui icon indicating copy to clipboard operation
instructure-ui copied to clipboard

Missing aria-live and aria-errormessage attributes in form field components

Open sebastianchristopher opened this issue 9 months ago • 5 comments

Background Information

Package Version(s):

  • ui-form-field v10.14.0
  • ui-select v10.14.0
  • ui-simple-select v10.14.0
  • ui-text-input v10.14.0

Browser: Chrome (observed), potentially all

Screen Reader: Mac VoiceOver (tested), potentially all

Component: FormFieldLayout, Select, SimpleSelect, TextInput

Describe the Issue

Accessibility issues in form field components:

  1. Error messages are not automatically announced by screen readers due to missing aria-live and role="alert" attributes

  2. The aria-errormessage attribute is not being propagated from FormFieldLayout down to input elements

  3. The aria-errormessage attribute does not match the ID of the error message container

Steps To Reproduce

  1. Render a Select or SimpleSelect with error messages
  2. Verify that input elements have aria-invalid but are missing aria-errormessage
  3. Trigger form validation errors
  4. Error messages are not automatically announced (unless focus is on the invalid input)

Expected Behavior

  1. Error message container should:

    • Have aria-live="assertive" and role="alert" for error messages
    • Be persistently in the DOM
  2. Input elements should have both aria-invalid="true" and aria-errormessage pointing to message container ID

  3. Error message container should have an ID matching aria-errormessage

Additional Information

Current Workaround(s): At the moment we are duplicating error messages in a ScreenReaderContent with aria-live="assertive" and role="alert" to ensure they are announced

Products Affected: All form field components

Are you willing to submit a PR to fix?

  • [x] Yes, I'm willing to submit a PR
  • (I may have a speculative fix...)

WCAG Guidelines

  • 1.3.1 Info and Relationships (Level A)
  • 4.1.2 Name, Role, Value (Level A)
  • 3.3.1 Error Identification (Level A)
  • 4.1.3 Status Messages (Level AA)

sebastianchristopher avatar Mar 21 '25 09:03 sebastianchristopher

hi!

Thanks for the detailed report! We are looking into these issues

matyasf avatar Mar 24 '25 11:03 matyasf

Where have you seen this behavior? "The aria-errormessage attribute does not match the ID of the error message container"

I could not reproduce this with aSelect or SimpleSelect, e.g.

<SimpleSelect renderLabel="Uncontrolled Select"
  messages={[{type: 'newError', text: 'Password have to be at least 6 characters long!'}]}>
  <SimpleSelect.Option id="foo" value="foo"
                       renderBeforeLabel={(props) => {
                         console.log(props)
                         return <IconCheckSolid />
                       }}>
    Foo
  </SimpleSelect.Option>
  <SimpleSelect.Option id="bar" value="bar">
    Bar
  </SimpleSelect.Option>
  <SimpleSelect.Option id="baz" value="baz">
    Baz
  </SimpleSelect.Option>
</SimpleSelect>

afaik we are only using aria-errormessage with RadioInputGroup at the moment

matyasf avatar Mar 24 '25 11:03 matyasf

Also about aria-errormessage: I've tried adding it to our TextInput component's first example, VoiceOver reads Password Password have to be at least 6 characters long! invalid data, secure edit text NVDA reads then edit protected invalid entry Password have to be at least 6 characters long! JAWS: Password Password has to be at least 6 characters long! password edit, invalid entry

Without it: VoiceOver: Password Password have to be at least 6 characters long! invalid data, secure edit text (same) NVDA: Password Password have to be at least 6 characters long! edit protected invalid entry JAWS: Password Password has to be at least 6 characters long! password edit, invalid entry (same)

Is there some case where screenreaders perform better if we add this attribute? Thanks!

matyasf avatar Mar 24 '25 12:03 matyasf

Hi,

Thanks for getting back to me - to answer your questions:

WRT "The aria-errormessage attribute does not match the ID of the error message container" - aria-errormessage isn't present at all on these components (Select & SimpleSelect), but aria-invalid is - it's my understanding that any error messages associated with the invalid input should be linked programmatically (e.g. aria-errormessage on SimpleSelect should be the id of Password have to be at least 6 characters long!). (this is a "may" rather than "must in WAI-ARIA 1.1 specification.) The presence of aria-errormessage onFormFieldLayout led me to believe it should be being propogated down to Select (but that might not be the case).

As to the screenreader, the issue we're having not that the screenreader doesn't read messages, but that it doesn't announce them when the messages are rendered - the user would have to navigate back to that element to find that it was marked as invalid (see this simplified example https://stackblitz.com/edit/vitejs-vite-jgbvpolx?file=src%2FApp.jsx).

sebastianchristopher avatar Mar 24 '25 18:03 sebastianchristopher

OK, so as I see you would like that form errors are read as soon as a component enters the error state (even when the page was just rendered and the component with the error state is offscreen?). We will investigate how tricky is to add this, and if we add it, it will be very likely not a default behaviour, rather some prop that toggles this mode.

matyasf avatar Mar 25 '25 15:03 matyasf