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

Tooltip has some accessibility issues #219

Closed
StommePoes opened this issue Sep 24, 2020 · 9 comments
Closed

Tooltip has some accessibility issues #219

StommePoes opened this issue Sep 24, 2020 · 9 comments
Assignees
Labels
a11y Anything relating to accessibility. bug Things that aren't working right in the library.

Comments

@StommePoes
Copy link

The tooltip component has some accessibility issues. I thought it was easier to lump them all together for the single component rather than separate issues for each.

I threw together a test page https://stommepoes.nl/work/shoelace/shoelace_demo.html and, on Windows only, ran 2 screen readers (NVDA latest, JAWS 2019) and two screen magnifiers (Windows Screen Mag, ZoomText) on 3 browsers (Edge latest, Chrome latest, Firefox latest... as of 23 September 2020). I don't own any iThings or I would have run VoiceOver on Safari (this is the combination for MacOS that is preferred for testing) as well.

See the notes below: tooltips are difficult components to actually make accessible. The best you can do is just get close to the current recommendations :(

The tooltips:

  • do not act as an accessible description for the control (the "Hover me" button) in the example.
  • are not themselves hoverable.
  • cannot be removed without moving the pointer.

To Reproduce
Steps to reproduce the behavior 1:

  1. With a screen reader running, go to either the tooltip demo page (https://shoelace.style/components/tooltip) or the test page listed above
  2. With the keyboard, navigate to the button called "Hover me".
  3. Instead of hearing the tooltip text as the accessible description (normally announced after the accessible name which in this case is "Hover me"), nothing is heard.
  4. Use the Down Arrow to move the screen reader's reading cursor to the next element. The tooltip text is heard then.

Steps to reproduce the behavior 2:

  1. Go to either the tooltip demo page (https://shoelace.style/components/tooltip) or the test page listed above
  2. Hover the button called "Hover me".
  3. When the tooltip appears, attempt to move the mouse pointer to the tooltip itself.
  4. Instead of remaining onscreen, the tooltip vanishes.

Steps to reproduce behaviour 3:

  1. Go to either the tooltip demo page (https://shoelace.style/components/tooltip) or the test page listed above
  2. With the keyboard, Tab to the button called "Hover me".
  3. When the tooltip appears, press the Escape key.
  4. Instead of focus remaining on the button and the tooltip vanishing, on the Shoelace demo page the tooltip remains onscreen; on the test page, keyboard focus is moved to one of the select components.
    ** Focus moving to a select was rather intermitant. Most of the time focus moved to the select directly above the "Hover me" button on my test page. Once I had Chrome always setting it on the first select set near the top of the page. Once in Firefox, focus did not move and the page acted like the demo test page—until I chose an option in that select, then Tabbed to the "Hover me" button. Then focus was placed on that select.

Expected behavior
Accessible descriptions (such as text linked to a control using aria-describedby) are announced when the controls receive focus.
Tooltips are hoverable, persistent and dismissable (as per https://www.w3.org/TR/WCAG21/#content-on-hover-or-focus)

Screenshots
A screenshot taken in ZoomText on Chrome browser at 7x magnification, showing most of the tooltip itself is outside the viewport. In order to move the viewport to see the tooltip, the mouse would clearly need to move off the "Hover me" button.

Desktop:

  • OS: Windows 10
  • Browsers: Edge latest, Chrome latest, Firefox latest (as per 23 September 2020)
  • AT: Screen readers NVDA latest, JAWS 2019. Windows Screen Magnification. ZoomText 2019.

Notes: tooltips are a poor UX pattern. This is an example where any fixes you do are solely to get your component closer to checking boxes on some "accessibility list", because they cannot truly be fixed.
For the "tooltip not read as an accessible description" (which, I am personally against throwing tooltip text at people simply because they focussed on a control which keyboard-only users cannot help, while sighted mouse users get to choose whether they want to view that tooltip text), I believe the reason it's not reading out is because the tooltip has aria-hidden="true" on it until the control is focussed, and I believe the removal of the aria-hidden state happens AFTER the browser has communicated to the assistive technology/screen reader that the acc. description does not exist. So it seems the only solution when following the recommended aria-describedby pattern is to leave the tooltip exposed (and simply visually hidden). To be honest, I feel it's more user-friendly that the text comes afterwards, but then completely blind users may not know that the tooltip text is being offered as a "tip" in the context of the control rather than just plain text. Also, I dunno what Macs do.

For the "hoverable, dismissable" parts: this is because when using screen magnification, tooltips have 2 problems: either they pop up unwanted and cover screen content and I may want to be able to see the screen without moving my viewport (moving the mouse is how one moves the viewport, if you can use a mouse), but conversely if I want to READ the tooltip, I need to be able to move my mouse over the tooltip, since the tooltip may be too big to fit in my viewport when I'm hovering the button.
These two things are fixable (the latter with CSS alone even), but the current suggestion on the WCAG is to offer the Escape key to close tooltips, because the Escape key is the typical pattern of removing stuff that has popped up or open.
However, this can get in the way of screen reader users who are unaware there is a tooltip, and can conflict if the tooltip'd element is inside a component which is already listening to the Escape key (such as a modal dialog).

Watch this and cry with me: https://www.youtube.com/watch?v=7s_sjl4bEP8

I'm making this ticket simply because I think you should be aware of the current issues and making the tooltips themselves hoverable is a doable fix for at least one of these issues. There might be a known way to deal with the accessible-description part somewhere on the web, but since I hate tooltips for the reasons listed above, I didn't look. Paul J Adam has created several examples in the past.

@StommePoes StommePoes added the bug Things that aren't working right in the library. label Sep 24, 2020
@claviska claviska added the a11y Anything relating to accessibility. label Sep 24, 2020
@claviska
Copy link
Member

This is an example where any fixes you do are solely to get your component closer to checking boxes on some "accessibility list", because they cannot truly be fixed.

Fair enough. Let's try to get them close then. 😄

I fixed a bug where aria-describedby was never set in 6b5c5aa, so that's a start. I'm wondering if pressing escape would qualify as being "dismissable."

Per this section:

Dismissable
A mechanism is available to dismiss the additional content without moving pointer hover or keyboard focus, unless the additional content communicates an input error or does not obscure or replace other content;

It seems as though pressing escape would be acceptable, as long as it doesn't result in focus being lost from the tooltip target, but I wanted to ask before making that change.

Note that all the accessibility updates I made tonight are in next and won't appear on shoelace.style until the next beta, but you can preview them at next.shoelace.style.

Of course, there's still more to be done. 😅

@StommePoes
Copy link
Author

the Escape key is the closest you'll get for something meant as a component for J Random Devs to use-- were you building your own app/website, you could try some of the suggestions in the video I pasted earlier (such as a dedicated close control inside the persisitent tip). The Escape key isn't terrible for general tooltips, but the moment your tooltip'd element is inside something already listening for then Escape key, keyboard users can no longer predict what will happen (will the tooltip simply vanish, or will the (example) modal dialog close on me?) and screen reader users who are fully blind can't see that there's a tooltip and may be expecting Escape to do something else.

So, TL;DR Escape key listener is probably your best option for now.

@smndhm
Copy link

smndhm commented Oct 5, 2021

Hello, I've been trying the tooltip with NVDA screen reader (updated) and it still does not work for me, it only read the element content.
If I press the down I'll have it but it's because it's the next element in the DOM.
Could it be because of the shadowdow with slot ?

@claviska
Copy link
Member

claviska commented Oct 5, 2021

I'm unfortunately not able to test on NVDA at the moment, so contributions are welcome to help make tooltips perform better with it.

@StommePoes
Copy link
Author

StommePoes commented Oct 11, 2021

Hi,
I got an email alert.
I opened this page (https://shoelace.style/components/tooltip) just now in Chrome/Windows and when I hover over the trigger (Hover Me button), and the tooltip appears, I still cannot move my mouse to the tooltip itself (the "hoverable" part of the original issue). I think probably this needs to be addressed in the JavaScript instead of CSS, since it looks like JavaScript is being used to determine whether the triggered is hovered (adding and removing the "open" attribute on the sl-tooltip custom element). Since the tooltip content is a child, normally this would work out naturally (hovering a child counts as hovering the parent), but I think once again something to do with the child being in a Shadow Dom is preventing this. So additional code addressing "if the tooltip itself is hovered, the tooltip remains and the sl-tooltip element remains in an 'open' state" is needed. I should be able to not only hover the button to make the tooltip appear, but then also move my mouse to the visible tooltip as well. Yes, even though there's another version which uses click rather than hover to expose/remove the tooltips (developers who choose the first tooltip type still need to be able to allow the tips to be hoverable).

I can confirm the posted behaviour of NVDA not saying anything particularly special about the button (Tabbing to the button merely presents that it's a button and called "Hover Me"), and that one must move forward with their browsing caret to hear the tooltip as if it were normal content which happens to follow the button.

That is, it sounds as if we have this HTML:

<button ...>Hover Me</button>
<p>This is a tooltip</p>

Just plain content which happens to be after the button, and no indication that it's intended to be an accessible description of the button itself.

I see that currently we've got this resultant HTML inside a shadow DOM:

<div class="tooltip-positioner" style="position: absolute;...">
    <div role="tooltip" id="tooltip-1" aria-hidden="false" ...>
        <slot name="content">This is a tooltip</slot>
    </div>
</div>

and a following sibling element (inside a nested shadow DOM) has this

<sl-button aria-describedby="tooltip-1" ...>Hover Me</sl-button>

I'm thinking the reason aria-describedby isn't doing anything is because this attribute is being set on an element (sl-button) which has no role. Setting ARIA-* attributes on stuff without a role (like spans and divs and custom <foo-bar> elements) generally causes nothing to happen: browsers don't pass this on. It's ignored.

Normally we'd say, set aria-describedby on the parent element (which would be sl-tooltip) but since that's outside the Shadow DOM that wouldn't work. We're hearing (correctly) that this is a "button" and that its name is "Hover Me" so my next thought would be placing aria-describedby on that inner <button> element, which naturally has the "button" role, but... that's inside another shadow DOM (they're nested). I don't think we can ARIA-anything even across nested shadows.

I'm not sure what would make this work. There's also the possibility that display: contents is also interfering (w3c/csswg-drafts#3040) but I don't believe it's the main cause at this point. Instead, it's something to test once we have a role'd element with aria-describedby set on it pointing to an element which can be reached within that role'd element's own DOM level.

The clickable example (second tooltip button) can also be confusing: it's basically using click to change states (showing and not-showing the tooltip). But then two questions come up:

  1. This change in state isn't offered semantically (like with aria-expanded or something), but...
  2. ...would that be a good thing? This is a button which when clicked shows a tooltip. Does that mean the button doesn't actually Do Something Else? If developers choosing to use this version restrict its use to something like an "?" info-type thing, where the button's sole function is to expose a tooltip with perhaps a chunk of info, then this is fine (and it would make sense for this control to have aria-expanded being set to true or false depending on the state, and not using any "tooltip" stuff like aria-describedby... it's not an accessible description of a control, it's just a disclosure button and the content it hides or discloses). However if the button is a control that Does Something, then users clicking it may get both a tooltip AND whatever clicking the button does. Developers may have a button which changes a setting on the page for example, but also want a tooltip. I guess the docs could just warn them not to do this. One click, one result.

@claviska
Copy link
Member

Reopening for further evaluation. Thank you for the detailed info, @StommePoes!

@claviska claviska reopened this Oct 11, 2021
@StommePoes
Copy link
Author

StommePoes commented Oct 14, 2021

Sorry I can't give any useful kind of solution :(

One idea is to rearrange the nested things so that the tooltip chunk and the button are inside a single shadow DOM and then the tooltip can easily aria-describe the button with aria-describedby set on that button (by button I mean the real <button> element inside that nested shadow). Problem probably is then that it's some kind of pain in the butt to offer all the positioning or something.

Then with any screen reader what users should encounter is:
if Tabbing to the button (at least for Windows and Linux SRs that switch modes) should offer that

  • it's a button
  • its name is Hover Me
  • its description is This is a tooltip

Those'll all read out together in whichever order the particular screenreader does or however the user may have configured it.

A user browsing (just reading content and not focussing on anything) with a screenreader that uses modes may only get the Hover Me name and that it's a button but I think it differs between SRs whether other control semantics get offered if the user isn't in a "focus" mode... I can't remember anymore, my brain has become cheese with those carbon-dioxide-gas holes, lol. So anyway any testing is only:

  • turn on a screen reader
  • Tab to the button
  • hear all the things

and not worry about what's announced if users are just reading stuff.

@maggiewachs
Copy link

@StommePoes @claviska I just came across this thread because I noticed the tooltips are still not reading the content (I'm using VoiceOver), and I have a suggestion that may be helpful. I recently worked on a project where we came up against the shadow DOM interfering with accessibility, specifically, with ARIA labelling attributes not finding their mates because they exist in different DOMs.

As @StommePoes mentioned, one fix is to have the button and content exist in the same shadow DOM, which has its own challenges (thinking of styling in particular). That didn't work for our implementation because we wanted to let users pass in whatever trigger element worked for them (button, inline link, some text), while retaining control over the content rendering.

So, we turned the content block into an aria-live region and populated it on hover/focus. This feels like a cheat, but it actually worked really well, and with the added bonus that we could configure it to read the tooltip content once or every time it's shown.

claviska added a commit that referenced this issue Apr 13, 2022
@claviska
Copy link
Member

one fix is to have the button and content exist in the same shadow DOM, which has its own challenges (thinking of styling in particular). That didn't work for our implementation because we wanted to let users pass in whatever trigger element worked for them

This was exactly the problem. I want to be able to wrap the trigger with a tooltip element so they can use whatever trigger they want.

So, we turned the content block into an aria-live region and populated it on hover/focus. This feels like a cheat, but it actually worked really well, and with the added bonus that we could configure it to read the tooltip content once or every time it's shown.

This is a brilliant suggestion! I just tried it and it's reading well now on hover and focus. You can try it out here:

https://next.shoelace.style/components/tooltip

I realize tooltips aren't the easiest to get right in terms of accessibility, but hopefully this improvement makes them more useful for everyone. Since this thread is getting long and I believe the main issues have been resolved, I'd like to close this thread and identify any new issues or suggestions in a new one.

Thanks for the detailed feedback, @StommePoes. And thanks for the great aria-live suggestion, @maggiewachs!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a11y Anything relating to accessibility. bug Things that aren't working right in the library.
Projects
None yet
Development

No branches or pull requests

4 participants