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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tooltip accessibility: aria-labeledby vs aria-describedby #2228

Closed
brookewp opened this issue Mar 24, 2023 · 4 comments 路 Fixed by #2279
Closed

Tooltip accessibility: aria-labeledby vs aria-describedby #2228

brookewp opened this issue Mar 24, 2023 · 4 comments 路 Fixed by #2279

Comments

@brookewp
Copy link

Current behavior

Hi there! 馃憢

Although the current behaviour is intentional, I've marked this as a potential bug; by default, the Tooltip component is not entirely accessible to screen readers.

Currently, Tooltip uses aria-labeledby instead of aria-describedby but has the option to change that with described:

* Determines wether the tooltip anchor is described or labelled by the
* tooltip. If `true`, the tooltip id will be set as the `aria-describedby`
* attribute on the anchor element, and not as the `aria-labelledby`
* attribute.
* @default false
* @example
* ```jsx
* const tooltip = useTooltipState();
* <TooltipAnchor state={tooltip} described>
* This is an element with a visible label.
* </TooltipAnchor>
* <Tooltip state={tooltip}>Description</Tooltip>
* ```
*/
described?: boolean;

Steps to reproduce the bug

  1. Visit the Tooltip documentation/example: https://ariakit.org/components/tooltip
  2. Enable a screen reader
  3. Tab to the Tooltip anchor
  4. Hear that only the Tooltip text is announced

Screenshot of all that VoiceOver reads:

Screenshot 2023-03-24 at 9 49 15 AM

Expected behavior

From the guidelines in WAI-ARIA Tooltip Pattern, it states:

The element that triggers the tooltip references the tooltip element with aria-describedby.

Since this isn't enabled by default, the anchor element is not announced, only the tooltip. Of course, with described={ true } we get the expected behaviour:

example

A sandbox with a described example demonstrated in the gif above:
https://codesandbox.io/embed/tooltip-with-described-8dzfew?fontsize=14&hidenavigation=1&theme=dark


The question:


Why is aria-labelledby the default instead of aria-describedby ? Is there a scenario where a developer might want to use aria-labelledby instead?

Workaround

No response

Possible solutions

No response

@diegohaz
Copy link
Member

diegohaz commented Mar 24, 2023

Hi @brookewp! Thank you for bringing this up.

After analyzing how sites were using tooltips, we've introduced aria-labelledby on Tooltip in v2 because, in most cases, the tooltip was being used as a label (for example, for buttons with an icon and no visible label). Even though that's not mentioned in the ARIA docs, screen readers, in general, would provide more consistent and meaningful announcements with aria-labelledby in those scenarios.

Consider, for example, a button with an unlabelled (or aria-hidden) <svg> and a tooltip with the text Undo. We can talk about the correctness of this example, but this is a widespread use case. The tooltip is clearly being used as a label in this case. For a non-sighted screen reader user, though, the tooltip doesn't exist. They can only perceive a label or description.

In that case, if aria-describedby was used, the button would have no accessible name. Most combinations of browsers and screen readers would announce something like button, Undo. On VoiceOver, it would be Undo, button, Undo. If the button already has Undo as its accessible name (let's say the <svg> is labeled), VoiceOver would still announce Undo, button, Undo. In contrast, some other screen readers, like NVDA, would be smart enough to announce only the label: Undo, button.

On the other hand, if we use aria-labelledby to reference the tooltip, it will always be Undo, button.

But it's definitely a bug in our tooltip example. We should either use an icon button representing most use cases on existing web apps or use the described prop. Or we can keep aria-describedby the default, like in v1, and add a labelled prop or something, that would also remove the role="tooltip" attribute.

@brookewp
Copy link
Author

Thank you for the detailed explanation!

But it's definitely a bug in our tooltip example. We should either use an icon button representing most use cases on existing web apps or use the described prop. Or we can keep aria-describedby the default, like in v1, and add a labelled prop or something, that would also remove the role="tooltip" attribute.

All of these options would help to clarify but I am partial to the last suggestion; because of ARIA documentation, I would assume that aria-describedby would be the default. But now I understand why aria-labeledby is also an option. 馃檪

@diegohaz
Copy link
Member

diegohaz commented Mar 28, 2023

So here's my idea:

  1. We add a type option to useTooltipState/useTooltipStore (see RFC: Component stores聽#1875). Accepted values are "label" and "description".
  2. The type will be automatically inferred based on whether the tooltip anchor already has an accessible name. So in most cases, it wouldn't need to be set.
  3. If type is description (default), the tooltip will have role="tooltip" and be referenced on the anchor element by the aria-describedby attribute.
  4. If type is label, the tooltip will have no role attribute and will be referenced by aria-labelledby.

@diegohaz
Copy link
Member

diegohaz commented Apr 19, 2023

2. The type will be automatically inferred based on whether the tooltip anchor already has an accessible name. So in most cases, it wouldn't need to be set.

It turns out that identifying if the element has an accessible name isn't a trivial task. We could use dom-accessible-api, which is used by testing-library, but I don't think it's worth it just for this feature.

For now, users will have to explicitly define type if the tooltip is intended to be a label.

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

Successfully merging a pull request may close this issue.

2 participants