Tooltip Components Should Not Exist
— ReactJs, Design System, API Design — 3 min read
- No translations available.
- Add translation
I have to admit: bad tooltips in web apps are one of my pet peeves. And to be fair, it's quite easy to get tooltips wrong. There are lots of concerns that need to be thought of:
- Accessibility
- Keyboard interactivity
- Least surprise for all users
- Not hiding critical information behind them
Over the years, I've seen many design systems try to implement a <Tooltip> component for consumers to use, and imo the one thing they have in common is that they will not be used as intended. From an API design perspective, that likely means that the <Tooltip> component is an abstraction that is too low level, which leads me to today's hot take 🌶:
I'm not saying tooltips themselves should not exist. They are a valuable tool when applied "correctly" - whatever that means for your use-cases. Sure, you might get there with education, but let's be honest: Very few people read docs and AI only reproduces what it already sees, so chances are it will amplify the anti-patterns we have in our codebase.
So let's dig into the potential problems you can get when using tooltips the wrong way before I'm trying to outline what an alternative world could look like.
Keyboard Interactivity
This one should be obvious: We ought to build web-apps for everyone, which means we have to be aware that not all users will be using a mouse. Nevertheless, I've often come across implementations that only show tooltips on hover in situations when the element that triggers the tooltip is not interactive.
We don't have to look any further than Material UI, one of the most popular component libraries. Their basic tooltip example from the docs looks like this:
1<Tooltip title="Delete">2 <IconButton>3 <DeleteIcon />4 </IconButton>5</Tooltip>That's easy and works, but what happens if we only add a <Tooltip> around an Icon, or around another non-interactive element like a Badge:
1<Tooltip title="Home">2 <IconHome />3</Tooltip>4
5<Tooltip title="Unread Mails">6 <Badge badgeContent={4} color="primary">7 <IconMail color="action" />8 </Badge>9</Tooltip>That's right - the tooltip will still show up on hover, but not on focus because tabbing will skip right over it. The element isn't keyboard-interactive, so it never receives focus, meaning the tooltip will never appear when navigating with the keyboard alone.
There's also no way to make this type-safe, as we can't control the type of children that get passed to the <Tooltip> component. React Aria's approach is a bit better, as it won't show the tooltip at all for non-interactive elements, making it easier to spot for developers who are primarily using their mouse. The fix is then to wrap the custom trigger with the <Focusable> component. This is miles better than other approaches, but still suffers from other problems, specifically:
Least Surprise For All Users
If a <Tooltip> can be added to any element, someone will try. I've seen tooltips in the weirdest of places, like on a certain text in the middle of a sentence - without any indication that there is additional information behind that.
But I've also seen the opposite. It's super annoying to me to see a button that only consists of an icon that I don't understand, but I'm not getting any information about what's going to happen when I click that button.
You're basically guaranteed to get those inconsistencies in your app as soon as you have a critical mass of developers and designers working on it.
So, if a standalone <Tooltip> component isn’t ideal, what other approaches can a design system use to expose tooltip behavior? My take is:
that enforce consistent and accessible tooltip usage
Some things that I've seen work really well in the past are:
- Interactive components like
<Button>or<Link>get an optionaltitleprop.
This allows us to show additional information if we want to. Interactive elements are usually easily discoverable in the UI, and they will also be found with keyboard tabbing. <IconButton>gets a requiredtitleprop.
This ensures icons are explained to users and additionally gives us a way to properly label the Button for accessibility.- Expose an additional
<InfoIcon>component to render an info or question mark icon + a tooltip.
This makes sure that users know where to find additional information, and we can internally ensure that it's focusable. - Create an
<InfoText>to allow giving more context to text.
This component should be visually distinct from other texts, e.g. with a dashed underline, and obviously also keyboard interactive.
I'm sure there are other things I might be missing, but if that's the case, adding another pattern component is better than giving everyone access to the low-level <Tooltip> component. Don't get me wrong, flexibility is great for a lot of cases, like e.g. layouting, but providing consistent UX and being inclusive are way more important for tooltips, which is why being restrictive is better.
Restriction also breeds creativity, so if we can't just stick information behind a tooltip just because we don't have enough space on our screen, maybe that helps us re-think some ideas from the ground up.
So if you're building a design system for your organization, try to resist the urge to add a <Tooltip> Component to your public interface.
That's it for today. Feel free to reach out to me on bluesky if you have any questions, or just leave a comment below. ⬇️
