Description
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:
This is recommended as a best practice by Primer's own tooltip guide:
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 underlyingTooltip
component.
Then I could achieve my goal simply with:
<IconButton icon={BoldIcon} aria-label="Bold" />