Building a fully-accessible help tooltip

...is harder than I thought.

Today is one of those days that started out with a Google search for yet another accessibility question/concern. I’m working on a new project for my client Provata and part of that project is to build a sweet and seemingly simple help tooltip which explains to the reader/user what the Framingham calculator is.

The tooltip is triggered by a small help icon like the one shown in the top right corner of this screenshot:

The tooltip trigger as provided in the project mockup

As with every project, starting out by thinking about what HTML element to use to mark up a component was the first thing I did. But it turned out there is no HTML element to mark up a tooltip like this. And when we have no semantic elements to mark up our components, we are faced with the challenge to make sure that assistive technologies (ATs)—which are usually capable of understanding and conveying meaning via semantics—are able to understand what our elements really are and what they do.

ARIA attributes to the rescue?

Making elements more readable by ATs when HTML isn’t enough is possible using ARIA attributes. For example, you can get by creating a progress bar without actually using the not-too-well-supported HTML <progess> element. Making that bar look and behave like a bar is straightforward using some CSS, but if you’re using divs and spans to build it, you need to give ATs something more to make something of those non-semantic elements. This is where ARIA attributes like role="progressbar" and its companions, the helpful aria-valuenow, aria-valuemin and aria-valuemax (among others), come in handy. Sprinkling in some simple JavaScript, you upate the value inside aria-valuenow as the user progresses through whatever steps you provided them with and you’re generally good to go. You can read more about this on MDN if you’re curious or unfamilir with this.

Great.

We have an ARIA attribute value to help us indicate that a certain element is a tooltip: tooltip. Using role="tooltip" ATs know that this element is indeed a tooltip.

But tooltips are usually triggered by an action performed on another element.

Let’s first get back to the basics. According to Wikipedia:

The tooltip or infotip or a hint is a common graphical user interface element. It is used in conjunction with a cursor, usually a pointer. The user hovers the pointer over an item, without clicking it, and a tooltip may appear—a small “hover box” with information about the item being hovered over. Tooltips do not usually appear on mobile operating systems, because there is no cursor (though tooltips may be displayed when using a mouse).

I have a problem with the “Tooltips do not usually appear on mobile operating systems, because there is no cursor” part because mobile users have just as much right to get information on a page as non-mobile users. Whether you’re using a mouse or your finger, you should be able to understand what the Framingham score is, so this tooltip should be accessible in all contexts. And this is exactly where the fun starts.

Triggering the opening/closing of the tooltip

I knew I couldn’t rely on hover alone (even though my client only requested hover) because, unless you have a touch screen with a fancy digital pen which allows for hover to be triggered, you won’t be able to see the hint inside the tooltip, and that is unacceptable.

I set out to make the tooltip show when the help icon is both hovered and clicked.

It is worth noting at this point that sometimes tooltips work on both touch and pointer screens without having to do any extra work, such as those that are shown as help labels on input fields, like this example by Heydon Pickering. In scenarios like this one, role="tooltip" in combination with aria-describedby are enough to indicate to ATs that this piece of text is indeed a tooltip which describes the content or functionality of the input field. Marking the tooltip up in this case is very easy because it’s already clear enough what it is. All that remains is for you to show/hide the text when the input field is focused, and this can be done using a couple of lines of CSS alone. The experience is great on mouse and touch screens, and everyone gets to experience it the same way.

However, things aren’t so straightforward when you have an example like the one in my project. And apparently I’m not the only one who’s been here and has been just as confused as to how to approach this.

Here are a few of the ideas that crossed my mind as I was thinking about the implementation of this:

Usually, when I’m torn between two solutions and when the JS-no-JS thoughts start floating in my mind, I look for second opinions. My first source of a second opinion is Google. I thought: “someone else must have built one of these before, and so must have somebody else too, so let’s see how they approached it and solved these issues”.

At this point I was still on the “I want to do this without JS if possible” boat, but believe it or not I love JS and was completely open to using it if the JS-based solution is better than all of the others.

Googling got me to land on a question that was exactly the same as mine. I found a lot of similar questions but all of them were more like the tooltip-on-input example. I found a very old thread started by Zoe Gillenwater who had the same question back in 2011 that I had today. I am aware that some technical details in there would/could be invalid today but the general principles are still valid. I highly recommend you read through that thread before continuing to read this article because everything else in here is based on some of the insights I got from that thread. Main points mentioned in the thread included:

Every single no-JS solution came with a very bad downside that negatively affected the user experience. JavaScript is the way to make this work properly for every user in every context. It solves every one of those problems mentioned, except for the “this link doesn’t link anywhere” problem which can only be solved by, well, not using a link that links nowhere. :D (Some may disagree that a link not linking anywhere is not particularly wrong or invalid, but it’s not something I’d personally do.) I ended up choosing a solution that works, using JavaScript, and has a not-too-bad fallback as well. This pretty much covers the main thoughts and considerations I had throughout this.

Conclusion(s)?

JavaScript is imperative to make fully-accessible interactive components. Sure, you can get away without using it in some cases, but a lot of accessibility requires JavaScript. Some ARIA roles and attributes are absolutely necessary to make components accessible, and many of those will simply not behave as they need to unless you make them work with JavaScript. Since my client is a health company, it is more likely that their users have one form of disability or the other than it is that they have JS disabled, so it’s safe to worry about not implementing JavaScript than about it not running.

If you’re interested in learning more about which attributes require what, I highly recommend checking out the ARIA Role Matrices from WhatSock. It provides and easy-to-scan and read overview.

Paul J Adam has also created a demo to show the different ways to show tooltips on hover. His examples still use JavaScript to make the components more accessible by toggling ARIA attributes as the tooltips open/close. This one is also definitely worth checking out.

Did I miss something? Let me know on in a tweet.



Level up your accessibility knowledge with the Practical Accessibility course!

I created a self-paced, get-right-down-to-it online video course for web designers and developers who want to start creating more accessible Web user interfaces and digital products today.

The course is now open for enrollment!

Real. Simple. Syndication.

Get my latest content in your favorite RSS reader. (What is RSS?)

Follow me on X (formerly Twitter)