Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Listbox with label isn't read properly by VoiceOver #2817

Closed
robsquires opened this issue Oct 27, 2023 · 1 comment · Fixed by #2824
Closed

Listbox with label isn't read properly by VoiceOver #2817

robsquires opened this issue Oct 27, 2023 · 1 comment · Fixed by #2824
Assignees

Comments

@robsquires
Copy link

What package within Headless UI are you using?

@headlessui/react

What version of that package are you using?

For example: v1.7.17

What browser are you using?

Chrome, Safari, Firefox, MacOSX

Reproduction URL

Github repo: https://github.com/robsquires/polished-cookies-mqyxtz
Codesandbox: https://codesandbox.io/p/github/robsquires/polished-cookies-mqyxtz/main

Describe your issue
I'm seeing an issue with the Listbox component not being read correctly by Voiceover. I believe the correct behaviour is that when the listbox is opened, Voiceover should read the text {Text for Option 1}, not selected, (1 of {X}). Eg:

Screenshot 2023-10-27 at 16 23 01

This can be observed when using the ListBox in single selection mode - <Listbox multiple={false} />.

This issue I'm seeing is when the Listbox is used in multiple selection mode - <Listbox multiple /> and used in-conjuntion with a Listbox.Label. In this case Voiceover reads the text from the label, instead the details outlined previously:

Screenshot 2023-10-27 at 16 29 16

Once an item is selected and the listbox is re-opened, VoiceOver reads the options correctly.

The codesandbox outlines which combinations are exhibiting issues:

  • Listbox single selection, no label ✅
  • Listbox single selection, label ✅
  • Listbox multiple selection, no label ✅
  • Listbox multiple selection, label ❌

I've done some digging and it looks to be a combination of aria-labelledby and aria-activedescendant which are applied differently depending on how the component is configured.

aria-labelledby aria-activedescendant Outcome
not present present
not present not present
present present
present not present

When the Listbox is in multiple mode it doesn't look like aria-activedescendant unless an item is selected, which is why the addition of a label breaks things.

There's a v. similar issue raised for the comobox #2736

Feels like this is a bug but I'd be happy to know if I'm configuring the component wrong too!

Thanks!

@RobinMalfait RobinMalfait self-assigned this Oct 31, 2023
RobinMalfait added a commit that referenced this issue Nov 2, 2023
Chrome currently has a bug if you use a `Listbox` with a `Label` and use
the `aria-multiselectable` attribute. This combination will cause
VoiceOver to _not_ announce the `role="option"` elements when
interacting with them.

If we drop the `aria-multiselectable` OR the `aria-labelledby` it starts
working. Alternatively replacing `aria-labelledby` with `aria-label`
won't work either.

I filed a Chrome bug report about this here: https://bugs.chromium.org/p/chromium/issues/detail?id=1498261

---

Luckily there is a workaround in our `Listbox` implementation. Right now
we always require the `Listbox.Button` to be there. The
`Listbox.Options` component doesn't work on its own in our
implementation.

This means that whenever we open the `Listbox` that we have to go via
the `Listbox.Button`. This `Listbox.Button` is already labelled by the
`Listbox.Label` if there is one.

This also means that we can safely drop the `id` of the label inside the
`aria-labelledby` from the `Listbox.Options`.

This wouldn't have worked if our `Listbox.Options` could be used in a
standalone way without the `Listbox.Button`.

At the end of the day the hierarchy looks like this:

- Options is labelled by the Button
   - Button is labelled by the Label
      - Label

Fixes: #2817
RobinMalfait added a commit that referenced this issue Nov 2, 2023
* fix VoiceOver bug for Listbox in Chrome

Chrome currently has a bug if you use a `Listbox` with a `Label` and use
the `aria-multiselectable` attribute. This combination will cause
VoiceOver to _not_ announce the `role="option"` elements when
interacting with them.

If we drop the `aria-multiselectable` OR the `aria-labelledby` it starts
working. Alternatively replacing `aria-labelledby` with `aria-label`
won't work either.

I filed a Chrome bug report about this here: https://bugs.chromium.org/p/chromium/issues/detail?id=1498261

---

Luckily there is a workaround in our `Listbox` implementation. Right now
we always require the `Listbox.Button` to be there. The
`Listbox.Options` component doesn't work on its own in our
implementation.

This means that whenever we open the `Listbox` that we have to go via
the `Listbox.Button`. This `Listbox.Button` is already labelled by the
`Listbox.Label` if there is one.

This also means that we can safely drop the `id` of the label inside the
`aria-labelledby` from the `Listbox.Options`.

This wouldn't have worked if our `Listbox.Options` could be used in a
standalone way without the `Listbox.Button`.

At the end of the day the hierarchy looks like this:

- Options is labelled by the Button
   - Button is labelled by the Label
      - Label

Fixes: #2817

* update changelog
@RobinMalfait
Copy link
Collaborator

Hey!

There is currently a bug in Chrome + VoiceOver for this use case. I filed a bug report for this. Luckily there was a workaround we could apply in Headless UI itself. (More info about that: #2824).

This should be fixed by #2824, and will be available in the next release.

You can already try it using:

  • npm install @headlessui/react@insiders.
  • npm install @headlessui/vue@insiders.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants