-
Notifications
You must be signed in to change notification settings - Fork 536
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
IconButton
should include Tooltip
by default
#2008
Comments
+1 to that. I like the idea of having a tooltip by default.
We'd need to workshop |
I figured there might be a case where the built-in tooltip conflicts with another tooltip or hover effect, but I can't think of a concrete example. Maybe it's actually not necessary to support disabling it. |
I'm going to run this idea of having a tooltip by default at tomorrow's Primer Patterns working group session to get any feedback or concerns before we run with that idea. In the meantime - @khiga8 could you please take a quick look at this issue and the proposal to adding a tooltip-by-default on |
Hi @lesliecdubs! @hectahertz can speak more to the work on 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 We don't have this in dotcom, but similarly in the 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: |
Thank you for weighing in @khiga8, this is very helpful context to inform our next steps! It does indeed look like PVC came to the same decision about always showing a Tooltip on Icon Button in primer/view_components#1062:
@hectahertz can you weigh in on any other API decisions that have been made in PVC that may be relevant? Perhaps @hectahertz and @siddharthkp can sync in these comments on whether we want to provide support for props |
From: primer/view_components#1062, IconButton has an argument called @hectahertz @khiga8 Are there plans on expanding this list of arguments?
Thanks for the example, this is really helpful! It was one of the questions I had :) |
tl;dr: we should copy what @hectahertz did in primer/view_components#1062
long version: The tooltip from view_components only has one relevant attribute for the conversation:
Here are 2 use cases:
Personal opinion 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. Having 2 ways of doing one thing from the markdown example feels wrong, but is an okay tradeoff to make as long as the output for both approaches is accessible. |
@lesliecdubs thank you for bringing this issue to the Primer Patterns working group attention 👋 @siddharthkp said:
I agree we should provide components that are accessible by default. A few considerations:
|
Oops, I should have used aria-description, not aria-describedby. My bad, fixed in the code now.
I mostly agree with using descriptive names over html attributes here. <IconButton
icon={BoldIcon}
- aria-label="Bold"
+ label="Bold"
- aria-description="Add bold text"
+ tooltip="Add bold text"
/>
I agree with the latter but not with the first part. API based on an config object work well when we have tight control over the contents in the library, but not when the text is passed by the application layer. I'd prefer to build a tiny reusable import {Key} from '@primer/react'
<Tooltip
text={<>Add bold text, <Key meta>B</Key></>}
direction="sw"
>
<IconButton icon={BoldIcon} label="Bold" />
</Tooltip> |
I've slept over this and have a slightly different approach: opt-in composition But first, Revisiting option 1 and option 2 again after @vdepizzol's comments: Option 1 - Composition 1. IconButton with tooltip// option 1
// note: as label and tooltip text are same, we would add only one of them.
<Tooltip text="More options" direction="sw">
<IconButton icon={KebabIcon} label="More options" />
</Tooltip>
// option 2
<IconButton icon={KebabIcon} label="More options" tooltipDirection="sw" />
// note: tooltipDirection is an optional customisation option
// ⭐️ we like option 2 more because it has the tooltip baked in. 2. IconButton with tooltip text that's different than label// option 1: to add a different tooltip text than label,
// wrap the iconbutton in a tooltip.
// note: label is still required
<Tooltip text="You have no unread notifications" direction="sw" >
<IconButton icon={BellIcon} label="Notifications" />
</Tooltip>
// option 2: to add a different tooltip text than label,
// pass a tooltip prop. predictable API smell of having tooltip text + tooltip prop on anchor component.
<IconButton
icon={BellIcon}
label="Notifications"
tooltip="You have no unread notifications"
tooltipDirection="sw"
/>
// option 2 isn't ideal for this use case, but the tradeoff isn't too bad to optimise for use case 1 above. 3. IconButton with no tooltip// option 1: nothing to do here, because there was no tooltip
<IconButton icon={ChevronRightIcon} label="Expand folder" />
// option 2: disable tooltip by passing false instead of text
// note: label is still required
<IconButton icon={ChevronRightIcon} label="Expand folder" tooltip={false} />
// both options are pretty good. 4. IconButton with rich content tooltipimport {Key} from '@primer/react}
// option 1:
<Tooltip
text={<>Add bold text, <Key meta>B</Key></>}
direction="sw"
>
<IconButton icon={BoldIcon} label="Bold" />
</Tooltip>
// option 2:
<IconButton
icon={BoldIcon}
label="Bold"
tooltip={<>Add bold text, <Key meta>B</Key></>}
tooltipDirection="sw"
/>
// both options are pretty similar. option 1 feels a tiny bit better because the long form content is separated out.
Option 3: Opt-in compositionBest of both worlds 1. IconButton with tooltip// by default, we use label to add tooltip.
// ⭐️ accessible by default
<IconButton icon={KebabIcon} label="More options" /> 2. IconButton with tooltip text that's different than label// if you want to change the tooltip text or direction,
// opt-in into composition and wrap the IconButton in a tooltip component
<Tooltip text="You have no unread notifications" direction="sw" >
<IconButton icon={BellIcon} label="Notifications" />
</Tooltip>
// good things: Tooltip props are on the Tooltip. If you've used the Tooltip before, this is very intuitive. 3. IconButton with no tooltip// silly little prop to disable default behavior 😅
<IconButton icon={KebabIcon} label="More options" tooltip={false} /> 4. IconButton with rich content tooltipimport {Key} from '@primer/react'
// opt-into composition
<Tooltip
text={<>Add bold text, <Key meta>B</Key></>}
direction="sw"
>
<IconButton icon={BoldIcon} label="Bold" />
</Tooltip>
// note: only Tooltip supports rich text, IconButton label is still boring old string. |
I like 'Option 3: Opt-in composition' - it provides significant power for complex scenarios. Having I can see the last example generating a structure akin to: <div>
<button aria-label="Bold" aria-describedby="tooltipId"><!--bold icon--></button>
<span id="tooltipId">Add bold text, <kbd>⌘B</kbd></span>
</div> From a CSS perspective, it wouldn't be too hard to have a containing |
Next step on Primer side: let's have a working session to align on the component API. @siddharthkp can you organize? |
@siddharthkp love where this is going! Option 3 makes a lot of sense. Just a comment on shortcut placement. I believe we'd still benefit from a specific slot for shortcuts instead of placing them inline with the tooltip text. I think it'd be a more future-proof solution, and it'd give us more flexibility on how to handle their rendering... Example: <Tooltip
text="Add bold text"
shortcutPreview={<Key meta>B</Key>}
direction="sw"
>
<IconButton icon={BoldIcon} label="Bold" />
</Tooltip> |
@siddharthkp - I agree that 'Option 3: Opt-in composition' is the right way forward. Just a few observations: 1. Since // This will show a tooltip that reads "Notifications"
<IconButton icon={BellIcon} label="Notifications" />
// Am I supposed to pass `tooltip={false}` to prevent two tooltips from being shown?
<Tooltip text="You have no unread notifications" direction="sw">
<IconButton icon={BellIcon} label="Notifications" tooltip={false} />
</Tooltip> Did you have an idea for how we could suppress the 2. Instead of 3. I think @vdepizzol's If we want to keep <Tooltip
text="Add bold text"
shortcutPreview={['meta', 'B']}
direction="sw"
>
<IconButton icon={BoldIcon} label="Bold" />
</Tooltip> |
Love this discussion. First off, I agree with @khiga8 that the label and the tooltip content must be separate entities. They both have different functions. Label is an immediate quick recognition of the button's functionality while the tooltip is an elaborate context driven description. If the tooltip text is going to be the same as the button label text, that is not an appropriate use of the tooltip. Additionally, providing a default tooltip with the button is not a great idea. This leads to unnecessary API overload on the Button like My vote is for the So in the simple use case we have <Tooltip
text="Add bold text"
direction="sw"
>
<IconButton icon={BoldIcon} label="Bold" />
</Tooltip> 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. import {Key} from '@primer/react'
import * from '@primer/react/tooltip'
<Tooltip direction="sw">
<TooltipTrigger>
<IconButton icon={BoldIcon} label="Bold" />
</TooltipTrigger>
<TooltipContent>
<>Add bold text, <Key meta>B</Key></>
</TooltipContent>
</Tooltip> |
hideTooltip:
Yep, I agree with you. We should disable it automatically. Both context and children magic would work here.
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:
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:
Rich text 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. SummaryLeaning towards these decisions:
|
I really like where this is going. One more small thing I noticed today with respect to keyboard shortcuts - there is an Maybe the default tooltip content should be something like (rough pseudocode): <>
{ariaLabel} {ariaKeyshortcuts.split(' ').map(
shortcut => shortcut.split('+').map(key => <Key>{key}</Key>).join(' + ')
).join(' / ')}
</> In other words, maybe we should render the |
This comment was marked as resolved.
This comment was marked as resolved.
Turns out our Tooltip component isn't really accessible, so that's a slight increase in scope, updated size to |
Hi! I moved this issue to context: In #2006, I tried to do 3 things,
There were many breaking changes in memex (some because there's a version of IconButton + Tooltip component in memex, others because useAnchoredPosition didn't always work), so I had to roll this back to attempt again in the future. |
This also affects the icon-only SegmentedControl buttons because they use a tooltip to show the hidden label. Instead of creating a new a11y issue for SegmentedControl, I'm including this info here. Let me know if a new issue specific to SegmentedControl would be more helpful, and I'll create it. |
I've moved this from Inbox back to Unprioritized Backlog for now. @siddharthkp I think everyone else is heads down right now, but if you had space to take a look at this again this quarter, we could consider moving it up next. If not, we can reassess before the next planning cycle at the end of September. |
Cross posting: https://github.com/github/primer/issues/1244 |
IconButton
with label viewable as Tooltip
IconButton
should include Tooltip
by default
Hi everyone 👋🏻 I am working on remediating Tooltip accessibility issues and IconButton is a part of it. Please see the batch issue https://github.com/github/primer/issues/2137 if you are curious about the details of the overall remediations and the release strategy. I just caught up with all the comments here and as far as I understand, the suggested PRC IconButton API for label and description type tooltip looks like below (copy-paste from earlier) IconButton with tooltip// by default, we use label to add tooltip.
// ⭐️ accessible by default
<IconButton icon={KebabIcon} label="More options" /> IconButton with tooltip text that's different than label (Supplementary text)// if you want to change the tooltip text or direction,
// opt-in into composition and wrap the IconButton in a tooltip component
<Tooltip text="You have no unread notifications" direction="sw" >
<IconButton icon={BellIcon} label="Notifications" />
</Tooltip>
// good things: Tooltip props are on the Tooltip. If you've used the Tooltip before, this is very intuitive. However in PVC, all comes within the box. So the equivalent of PVC API would be IconButton with tooltip// Same except the prop name PVC uses `aria-label` versus `label`
// ⭐️ accessible by default
<IconButton icon={KebabIcon} aria-label="More options" /> IconButton with tooltip text that's different than label (Supplementary text) <IconButton icon={BellIcon} aria-label="Notifications" aria-description="You have no unread notifications"/> I am very curious to hear what you think. Feel free to add your questions and concerns and hopefully we will be resolving this issue soon ⭐ |
Definitely down to match the existing PVC work if that helps with consistency 👍 (meaning that we match the |
Disclaimer: I did some work on Tooltip before and went down this same road, so I have a lot of thoughts. Sorry!
perfect default case. love it, ship it
Obviously biased, so feel free to ignore, I like my suggestion of wrapping Tooltip explicitly when you want any customisations for tooltip. I think bringing the API from PVC has some issues: <IconButton
icon={BellIcon}
aria-label="Notifications"
aria-description="You have no unread notifications"
/>
Suggestion: Opt-in composition1. Default happy case: By default, we use label to add tooltip. It's perfect, no notes! <IconButton icon={KebabIcon} label="More options" /> 2. IconButton with tooltip text that's different than label (Supplementary text): If you want to customise the tooltip text, direction or styles, you can opt-in to composition and wrap the IconButton in a tooltip component <Tooltip text="You have no unread notifications" direction="sw" className="icon-button-tooltip">
<IconButton icon={BellIcon} label="Notifications" />
</Tooltip> 👍 Tooltip props are on the Tooltip. Also works for rich content (if we want to allow it): <Tooltip
text={<>Add bold text, <Key meta>B</Key></>}
direction="sw"
>
<IconButton icon={BoldIcon} label="Bold" />
</Tooltip> Different API for PVC and PRC?I think we can bring these suggestions to PVC, if we like this API more as a group. Personally, I think it's also okay to have some API differences between PVC and PRC based on what feels intuitive in the mental model of Rails and React. For me, It's always great when we can have the same API, but not compulsory when there are tradeoffs. |
Regarding rich content, I find it hard to come up with another valid example outside of keyboard shortcuts. I do think the keyboard shortcuts example is important though - the comment box solution of putting the shortcut in angle brackets ( In Memex we use the shared With that in mind, perhaps it would be better to add an explicit keyboard shortcut prop that we then render using Now how can we represent that concept in the API? To render this, we probably need to add a However, that doesn't solve the happy path - what if I have a button with a simple label and keyboard shortcut? We could add <IconButton icon={BoldIcon} label="Bold" keyboardShortcut="CommandOrControl+B" />
But then what happens in the case where we define it in both places? <Tooltip text="Add bold text" keyboardShortcut="Alt+B">
<IconButton icon={BoldIcon} label="Bold" keyboardShortcut="CommandOrControl+B" />
</IconButton> Personally I prefer the approach of forwarding the More importantly, it would then be obvious how to override the tooltip without having to refer to documentation examples to realize that wrapping in <IconButton
icon={BoldIcon}
label="Bold"
keyboardShortcut="CommandOrControl+B"
tooltipProps={{text: "Add bold text"}}
/> |
I feel like a great point that you bring up @iansan5653 is that it'd be great if we can make illegal (or non-ideal) states unrepresentable. I think you had a great example with: <Tooltip text="Add bold text" keyboardShortcut="Alt+B">
<IconButton icon={BoldIcon} label="Bold" keyboardShortcut="CommandOrControl+B" />
</IconButton> To me this demonstrates a challenge that can come up where you have ambiguity as to what will happen in the composition case (specifically what prop value "wins" here). I do think that over time you can infer that this is a merge between the two and that the inner one wins out, ultimately. I think an advantage of Either way, curious what you would recommend @broccolinisoup with all the work that you've been doing to see what would help make this the easiest change on your end 😅 lol |
👋🏻 Here is the API proposal for the IconButton 🎉 https://github.com/github/primer/pull/2339 We left the keyboard shortcut stuff to deal with later and tried addressing more prominent issues in the first iteration. Other than that, I think we addressed all issues we talked here. Let me know if I miss anything and I am looking forward to hearing your feedback on the PR 🙌🏻 |
Hi! This issue has been marked as stale because it has been open with no activity for 180 days. You can comment on the issue or remove the stale label to keep it open. If you do nothing, this issue will be closed in 7 days. |
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:
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:And the
Tooltip
documentation has the same: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:
Markdown editor toolbar:
Code view:
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.
Label on button only: Tooltip does not work at all.
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 as button: Seems like a promising idea but
Tooltip
does not support theas
prop.Button as tooltip: Renders an unfocuseable
span
instead of abutton
.Tooltip in button children: Does not work because the
IconButton
ignores children.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.
Proposed Solution
My ideal solution would be to just show the
IconButton
'saria-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:
The text was updated successfully, but these errors were encountered: