question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Accessible `IconButton` with label viewable as `Tooltip`

See original GitHub issue

Summary

There is no documented example of how to render an icon-only button with an accessible tooltip in a way that would meet all of the following standard requirements:

  • Render as an icon-only button with a single focus target
  • Read the label one time in a manner that ties it to the button, for screen reader users
  • Show the label on hover, for mouse users
  • Show the label on focus, for keyboard users

If this is a supported behavior, then some documentation is needed to show how to do it. If this is not supported, it would be very useful functionality to add to Primer.

The IconButton documentation provides an example of how to label the component in an accessible manner:

<IconButton aria-label="Search" icon={SearchIcon} />

And the Tooltip documentation has the same:

<Tooltip aria-label="Hello, Tooltip!">Text with a tooltip</Tooltip>

Both of these examples use aria-label however, which provides no obvious way to create an icon button that shows its accessible label on hover as a tooltip.

Use Cases

This is a very common requirement. See for example these instances on github.com:

Notifications bell: Screenshot of the GitHub notifications bell button showing a tooltip that says 'you have unread notifications'

Markdown editor toolbar: Screenshot of the GitHub markdown editor 'bold' button showing a tooltip

Code view: Screenshot of the GitHub pull request code view, showing the 'Expand all' button with a tooltip

This is recommended as a best practice by Primer’s own tooltip guide:

Do: Use a tooltip to give a text label to an icon button

Attempts

I’ve tried all of the following examples but I haven’t been able to find a satisfying way to do this:

Label on tooltip: Tooltip does not appear on button focus and the button appears as unlabelled in the accessibility tree.

<Tooltip aria-label="Bold">
  <IconButton icon={BoldIcon} />
</Tooltip>

Label on button only: Tooltip does not work at all.

<Tooltip>
  <IconButton icon={BoldIcon} aria-label="Bold" />
</Tooltip>

Label on both: The label will be read twice to screen readers (something like “Tooltip: Bold, Button: Bold”) which is unnecessarily redundant. Also the tooltip still doesn’t appear on focus.

<Tooltip aria-label="Bold">
  <IconButton icon={BoldIcon} aria-label="Bold" />
</Tooltip>

Tooltip as button: Seems like a promising idea but Tooltip does not support the as prop.

<Tooltip aria-label="Bold" as={IconButton} icon={BoldIcon} />

Button as tooltip: Renders an unfocuseable span instead of a button.

<IconButton icon={BoldIcon} as={Tooltip} aria-label="Bold" />

Tooltip in button children: Does not work because the IconButton ignores children.

<IconButton icon={BoldIcon} aria-label="Bold">
  <Tooltip aria-label="Bold" role="presentation">Bold</Tooltip>
</IconButton>

Tooltip as button icon: This is horribly inelegant, but it does generate a good accessibility tree and still shows the tooltip on hover. It unfortunately still doesn’t show the tooltip on focus, but it’s the best I have for now.

<IconButton
  icon={() => <Tooltip aria-label="Bold" role="presentation"><BoldIcon /></Tooltip>}
  aria-label="Bold"
/>

Proposed Solution

My ideal solution would be to just show the IconButton’s aria-label as a tooltip by default.

To configure this, I propose adding two new props to the IconButton component:

  • disableTooltip: Turn off the tooltip.
  • tooltipProps: Configure the tooltip by passing props to the underlying Tooltip component.

Then I could achieve my goal simply with:

<IconButton icon={BoldIcon} aria-label="Bold" />

Issue Analytics

  • State:open
  • Created a year ago
  • Comments:24 (20 by maintainers)

github_iconTop GitHub Comments

3reactions
khiga8commented, Apr 5, 2022

Hi @lesliecdubs!

@hectahertz can speak more to the work on IconButton in PVC! I believe we were leaning towards having tooltip by default for all IconButton for accessibility purposes. Discussion can be found in: https://github.com/primer/view_components/pull/1062.

As a side note, I notice in all the examples listed in this issue, the tooltip is always semantically associated as a label. I’m not sure if this is out of scope, but in the long run, the tooltip should also be allowed to be associated to the icon button as a supplementary description.

You can check out the markup of the markdown toolbar in dotcom as an example. These were recently updated to use tooltips that are associated semantically using aria-describedby with aria-label directly set on the button with a brief accessible name. This was done (at least in dotcom) because the tooltip content is kind of long and contains keyboard shortcuts which makes the tooltip inappropriate as an accessible name. Accessible names should be brief. Therefore you’ll notice we have a label like Bold directly on the button and a tooltip associated as description that contains, Add bold text, <Cmd+b>.

We don’t have this in dotcom, but similarly in the Notifications button example, it may be appropriate to set aria-label as Notifications, and set a tooltip as supplementary description with You have unread notifications.

We’ve recently worked on a bunch of accessibility doc improvements on the PVC and Primer Design side to encourage proper tooltip usage with valid tooltip use examples. Here are some references that may be helpful as Primer React team works on tooltip:

2reactions
siddharthkpcommented, Apr 13, 2022

hideTooltip:

@mperotti: Am I supposed to pass tooltip={false} to prevent two tooltips from being shown? Instead of tooltip={false} to override the default behavior, I’d suggest we phrase it like hideTooltip.

Yep, I agree with you. tooltip=false only made sense while there was a tooltip="some text" prop on the IconButton.

We should disable it automatically. Both context and children magic would work here.

@pksjce: We want to use something like hideTooltip sparingly but with this pattern it might become a norm than an escape hatch.

Yes! 💯

This should be a super rare use case. We could even start by not adding it to the public API at all and only using it internally, for example in TreeGrid.


Automatic tooltip:

@pksjce: My vote is for the Tooltip component to intentionally wrap around the IconButton when a tooltip is required. This can then become a consistent pattern for all Tooltip usage. I agree with @khiga8 that the label and the tooltip content must be separate entities. They both have different functions.

Vote recorded!

You’re right, ideally tooltip content should be a description instead of simple label. But, I fear folks would often not use one 😢

Personal opinion: anything we can do by default, without getting in the way of them doing better is a step in the right direction.

longer personal opinion from above:

If we look at the library as a whole and not just IconButton, it makes more sense to go with Option 1, re-use Tooltip and ask folks to wrap IconButton with a Tooltip. It’s easy to miss, but we can enforce this with help of our linter or/and by adding warnings in dev mode. example of warning, sandbox

However, I think being accessible out of the box makes Option 2 very attractive despite the awkwardness around customising tooltip position. If we know developers would always have to wrap an IconButton with a tooltip, we should do it for them.


Rich text Tooltips:

@mperrotti: Maybe we change it to something more generic like trailingContent or a child component like Tooltip.TrailingContent?

@pksjce: Further, we should have additional API for Tooltip to separate the trigger and rendered content to support more complex usecases like components in tooltips and dynamically rendered tooltips.

I agree with both of you. But, I think we should exclude shortcuts and other rich text for this conversation and come back to it after along with other components that use shortcuts. There is a broader pattern there.


Summary

Leaning towards these decisions:

  1. No additions to the IconButton API
  2. Make aria-label compulsory on IconButton
  3. Automatic add tooltip for IconButton if it’s not wrapped in a Tooltip. (tooltip text = aria-label)
  4. Avoid multiple tooltips automatically, don’t add hideTooltip to public API just yet.
Read more comments on GitHub >

github_iconTop Results From Across the Web

Accessible Icon Buttons - Sara Soueidan
I hope this article has given you a clear overview of many possible ways to provide an accessible label to an icon button....
Read more >
IconButton | Primer ViewComponents
Use icon button as an accessible button component with no text and only icon. ... IconButton will always render with a tooltip unless...
Read more >
Tooltips in the time of WCAG 2.1 | Sarah Higley
The main caveat for the name use case is that UI controls should always have some sort of label visible.
Read more >
IconButton | Workbench by Gusto
IconButton is an accessible solution for turning an Icon without a visible text label into an interactive element. Figma logo.
Read more >
Tooltip class - material library - Flutter - Dart API docs
Tooltips provide text labels which help explain the function of a button or other user interface action. Wrap the button in a Tooltip...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found