Design pattern for custom tooltips

by Jan Hellbusch published on

Should we use tooltips to convey information? Hints and descriptions are often included on web pages through tooltips – but not everyone has access to them.

A tooltip is a short text that usually appears as a popup when a user hovers a mouse pointer over an element. Tooltips can be attached to any HTML element using the title attribute. There are only a few exceptions when a tooltip created with a title attribute will not be displayed (e.g., a title attribute on an iframe element).

In this article, we’ll first take a look at the title attribute in HTML and point out to a few accessibility issues. Then we’ll make a few general suggestions about how to use tooltips. In the third part, we’ll build two accessible custom tooltips with HTML and ARIA, then look at two different CSS solutions and finally add an event handler in accordance with accessibility guidelines. Both tooltips can be previewed:

  • In example 1, you’ll find a nested tooltip with a basic design using the z-index property.
  • In example 2, you’ll find a tooltip using the new anchor positioning technique.

Accessibility issues for the title attribute

The title attribute is not particularly accessible. As Steve Faulkner puts it:

If you want to hide content from mobile and tablet users as well as assistive tech users and keyboard-only users, use the title attribute.

https://www.tpgi.com/using-the-html-title-attribute-updated

Assistive technologies include screen readers and screen magnifiers. In terms of accessibility, title attributes lack support for the following user groups:

  • Keyboard users.

    Because keyboard users are only able to focus active elements such as links or forms, browsers can only reveal tooltips for focusable elements. Even then, browsers do not display the content of title attributes on keyboard focus (there were some exceptions for forms on Internet Explorer some time ago).

  • Screen reader users.

    For active elements, browsers will process the content of a title attribute as an “accessible description” when an active element such as a link is focused with the tab key. Usually, the accessible description is suffixed to the link name. That can be helpful in certain situations, but screen reader users won’t necessarily navigate by tab key, because non-focusable content will be skipped. A title attribute can nevertheless be discovered on links, forms and other active elements in a screen reader.

  • Screen magnification users.

    Using a screen magnifier can make tooltips impossible to read. Tooltips included with a title attribute will only be displayed when the mouse pointer is hovering over the triggering element. Using screen magnification, the enlarged portion of the viewport that is actually visible can reduced significantly. In this situation, users will have to move the pointer to pan the tooltip to read the full text. However, as soon as the pointer hovers over the tooltip, browsers will hide the tooltip.

Also take a look at the HTML Standard. HTML discourages the use of the title attribute:

Relying on the title attribute is currently discouraged as many user agents do not expose the attribute in an accessible manner as required by this specification (e.g., requiring a pointing device such as a mouse to cause a tooltip to appear, which excludes keyboard-only users and touch-only users, such as anyone with a modern phone or tablet).

https://html.spec.whatwg.org/multipage/dom.html#the-title-attribute

Best practice: do not use the title attribute to create a tooltip.

Reconsidering using tooltips?

If you are still considering using tooltips on your web pages, be restrictive when using them. There are plenty of guidelines on the web about when to use tooltips and when not, for example:

  1. Do not include crucial information in a tooltip. Information required to finish a task must always be accessible to all users.
  2. Do not include repetitive or obvious text in a tooltip. At best, those kinds of tooltips are distracting.
  3. Keep tooltips short. Tooltips can be a reminder or a brief rewording to a short label.
  4. Tooltips should not cover other relevant content. For example, the tooltip should not cover the label in a form field.

Most importantly, tooltips should only provide descriptive and non-essential text, giving slightly more detailed text for active elements such as links and form controls. Ultimately, they provide expendable text which is already on the web page.

The following code shows a form field with an additional text to be shown as a tooltip:

<p>
<label for="foo">Full name</label>
<input type="text" autocomplete="name" id="foo">
<span class="tooltip" hidden>Enter your first name followed by your surname</span>
</p>

The hidden text is intended to be displayed when the form field is focused. The hidden text is the first step in creating a custom, accessible tooltip as an alternative to the title attribute. The CSS properties to disclose and hide the tooltip will be discussed in Step 2 below.

Best practice: provide only expendable information in a tooltip.

Tooltip accessibility

Make sure your tooltips are accessible. That will include keyboard users and users of assistive technology. An accessible tooltip will have to fulfil the following requirements:

  1. Use correct semantics (HTML and ARIA).
  2. Make sure the tooltip can be displayed by keyboard.
  3. Ensure all users can lightly dismiss the tooltip.

Note that there is more to accessible custom tooltips than just these three aspects. For example, tooltips must also satisfy contrast requirements or adapt to text resizing.

Step 1: HTML and ARIA

If you’re starting from scratch, it is always best to begin with HTML. A tooltip for a link can be set up as follows:

<p>
<a href="https://www.example.com">
link text
<span class="tooltip" hidden>helpful, but non-essential tooltip</span>
</a>
</p>

The tooltip does not have to be nested within the active element. What you need to consider in HTML is:

  • Assistive technology must be able to determine the correct reading sequence. The tooltip must immediately follow the triggering component or its label.
  • You will need to position the tooltip. Nesting the tooltip within the link simplifies CSS positioning.

Note: Users should never be able to focus something within a tooltip. If, for whatever reason, the popup requires focus, then you should be using a dialog element with the showModal() or show() methods. Alternatively, you might need a popovertarget attribute for the triggering element.

Best practice:

  • When building a custom tooltip, first make sure the HTML makes sense in a screen reader.
  • Ensure the triggering element can be focused by keyboard.

In our code example, the relationship between link and tooltip seems obvious, but the conveyed relationship must also be determinable by assistive technology. The places to check possible requirements are the ARIA Authoring Practices Guide (APG) or the ARIA specification, if the APG does not provide guidance for whatever you are coding. For tooltips, the APG suggests the following:

  1. The element that serves as the tooltip container has a tooltip role.
  2. The element that triggers the tooltip references the tooltip element with aria-describedby. To this end, the element with the tooltip requires an ID.

Basically, with the aria-describedby attribute we are telling the link what its tooltip is and turning the tooltip into an accessible description for the link. Accessible descriptions for links are exposed to assistive technology users when the link is focused by tab key.

<p>
<a href="https://www.example.com" aria-describedby="me">
link text
<span id="me" class="tooltip" hidden>helpful, but non-essential tooltip</span>
</a>
</p>

How useful the tooltip role is when implementing custom tooltips is unclear. Currently, it makes no difference in assistive technology whether the tooltip has role="tooltip" or not. The relationship between link and tooltip is conveyed solely by the aria-describedby attribute. The role has purposely been omitted. You’ll find more on this subject on GitHub.

Best practice: use ARIA only when it is useful to assistive technology.

Explicitly referencing a tooltip from its triggering element is even more important for form controls. Tooltips will not be nested within a component. To better position the tooltip, you might add a span element with a class as in the following snippet:

<p>
<label for="foo">Full name</label>

<span class="tooltipWidget">
<input type="text" autocomplete="name" id="foo" aria-describedby="bar">
<span id="bar" class="tooltip" hidden>Enter your first name followed by your surname</span>
</span>
</p>

Step 2: Displaying and hiding the tooltip

For a tooltip to be accessible, displaying a tooltip must not only be possible for mouse users, but also for keyboard and touchscreen users. There are generally two approaches for displaying a tooltip with a keyboard:

  1. Automatically displaying the tooltip when the triggering element receives focus.
  2. Providing a keyboard shortcut to open the tooltip for the focused element.

The first approach makes tooltips easily discoverable for keyboard users, but if a webpage includes many tooltips, the continuous displaying of expendable information can become distracting. The advantage of the second approach to open the tooltip is that users can display the tooltip only on request. But the second approach also comes with some disadvantages:

  • It is impossible to tell whether the tooltip will show supplemental (helpful) or expendable information. Hitting keyboard shortcuts to display (and hide) tooltips seems to be an unnecessary burden.
  • There is no standardized keystroke for displaying a tooltip. If you choose this technique, you should use a keystroke common to different platforms for invoking interaction such as space key using JavaScript.
  • Components with tooltips will require a visual affordance to indicate the presence of a tooltip.

    Note: the visual affordance is for sighted users only and should not be accessible to screen readers. Screen readers access the tooltip through the aria-describedby attribute.

We will take the first approach. This seems closer to the expected behavior for a tooltip.

There are two specific accessibility aspects to consider for your CSS:

  1. If tooltips appear on hover or keyboard focus, they must remain visible until hover or focus is removed by the user. Some users will have difficulty reading or even perceiving the displayed content, if the tooltip is hidden after a short time.
  2. Especially users who use a screen magnifier with high magnification can only view small portions of the screen. In order to get an overview of the content, users must be able to pan the viewport, with the magnified portion following the mouse pointer. The tooltip must be displayed as long as the pointer is hovering on the triggering element as well as on the tooltip. See the second condition for success criterion 1.4.13 in the Web Content Accessibility Guidelines (WCAG) 2.2.

    Note: the magnification levels vary greatly and 20x, 30x or even higher magnification is quite possible. The higher the magnification level, the smaller the screen portion.

Basic CSS for displaying and hiding a tooltip is fairly straightforward using a nested tooltip. The following CSS declarations create custom tooltips for the link and the input field for example 1. You can use the position property to place the tooltip next to the triggering element:

a:has(.tooltip), .tooltipWidget:has(.tooltip) {
position: relative;
}

.tooltipWidget:focus-within .tooltip,
.tooltipWidget:hover .tooltip,
.tooltipWidget .tooltip:hover,
a:focus .tooltip,
a:hover .tooltip,
a .tooltip:hover
{
display:block;
position: absolute;
left:0;
top:1em;
padding:0.5em 0.75em;
background:#334E58;
color: white;
border:solid #334E58 2px; /* for contrast mode */
width:10em;
z-index:1;
}

See example 1 on CodePen

Best practice: make sure to extend the hover state to the tooltip to allow users to move the pointer over the tooltip without the tooltip disappearing.

There is a lot more to consider when displaying tooltips, for example:

  • Where should the tooltip be shown – above, below or to one side of the triggering element?
  • What is to be done if the tooltip is cut off by the viewport?
  • Can we display tooltips using CSS or do we need JavaScript?
  • Do you need some kind of package to create your custom tooltips?

As this article deals with the accessibility of tooltips, we won’t go down a rabbit hole to provide answers to these questions. The bottom line is that as soon as you need a flexible presentation of tooltips (e.g., displaying tooltips below the triggering element, unless the triggering element is at the bottom of the viewport, in which case the tooltip should be displayed above the triggering element), you are dependent on JavaScript. You may need to exchange classes for repositioning or redimensioning your tooltip, depending on viewport size and other factors. Your code can get quite complicated. See some assessments on: Tether elements to each other with CSS anchor positioning.

But fortunately, there is something new in CSS that will help solve these issues.

The new CSS feature is anchor positioning. Currently, it will work in Chrome Canary with experimental mode switched on, but it will take some time before it is found in mainstream browsers.

Anchor positioning promises flexible positioning of your tooltips based on CSS and your guidance. First of all, you are not dependent on nesting your tooltips to easily position them. You can rearrange your HTML, but remember that you still need to consider the reading order. The input field from example 1 could be adapted to the following (example 2):

<p class="form-element">
<label for="foo">Full name</label><br>
<input type="text" autocomplete="name" id="foo" aria-describedby="bar">
</p>

<span id="bar" class="tooltip" hidden>
Enter your first name followed by your surname
</span>

With anchor positioning, the example could be adapted to display the tooltip to the right or below the anchor (triggering element), depending on where the anchor is in the viewport:

@supports (anchor-name: --anchor-foo) {
* {
box-sizing: border-box;
}

input {
position: relative;
width: 200px;
border: 1px solid #334e58;
anchor-name: --anchor-foo;
}

input:hover,
input:focus
{
border: 2px solid #334e58;
padding: 0.5rem 0.75rem;
outline: none;
}

.form-element {
position: relative;
}

.form-element:hover ~ .tooltip,
.form-element ~ .tooltip:hover,
.form-element:focus-within ~ .tooltip
{
display: block;
}

.tooltip {
margin-right: 20px;
color: white;
background: #334e58;
border: 2px solid #334e58; /* for contrast mode */
position: absolute;
min-width: anchor-size(
--anchor-foo width
); /* Reposition a little bit earlier */
anchor-default: --anchor-foo;
position-fallback: --left-to-bottom;
}

@position-fallback --left-to-bottom {
@try {
bottom: anchor(bottom);
left: calc(anchor(right) + 0.5rem);
}
@try {
top: calc(anchor(bottom) + 0.5rem);
left: anchor(left);
width: anchor-size(--anchor-foo width);
}
}
}

See example 2 on CodePen

There are numerous further properties provided in anchor positioning. You can find an introduction by Sebastian Weber on: How to use CSS anchor positioning.

Step 3: Tooltip dismiss

Light dismiss is a term used in the HTML Standard. Light dismiss means that clicking outside of a popover of any sort will close the popover. For keyboard users, light dismiss is equivalent to pressing the Escape key.

Above, we mentioned the second condition in success criterion 1.4.13 in WCAG 2.2. The success criterion has a total of three conditions. When applied to tooltips, the first condition must also be met for the tooltip to be considered accessible:

A mechanism is available to dismiss the additional content without moving pointer hover or keyboard focus, unless the additional content communicates an input error.

Keyboard users also using some form of magnification need a way to clear their viewport from unwanted tooltips. Because of the magnification, tooltips and corresponding triggers can fill a major part of the viewport. They must be able to hide the tooltips by keyboard, for example by pressing the Escape key. Simply moving focus forwards and backwards won’t help.

To prevent interference through tooltips, there are two possible techniques:

  1. The tooltip is positioned so that it does not cover any other content.
  2. You provide a mechanism to dismiss the tooltip by keyboard.

Even if a tooltip does not obscure any other content, it is desirable for the second technique to be applied. In other words, the tooltip must stay visible as long as hover or focus is on the triggering element and the user has not explicitly dismissed it. Only error messages may be non-dismissible (e.g., when remedial action is necessary).

For keyboard users, pressing the Escape key while focus is on the triggering element should hide the tooltip again. We can go back to both of the examples and add the following two event handlers to do that:

let tooltips = document.querySelectorAll("[aria-describedby]");
tooltips.forEach((tooltip) => {
tooltip.addEventListener("keydown", function (event) {
if (event.key == "Escape") {
document.getElementById(
tooltip.getAttribute("aria-describedby")
).style.display = "none";
}
});

tooltip.addEventListener("blur", function (event) {
document
.getElementById(tooltip.getAttribute("aria-describedby"))
.removeAttribute("style");
});
});

Note: by hiding the tooltip directly in the DOM, the style property remains persistent, meaning the tooltip will not be displayed a second time. To prevent that from happening, the style property is removed when the triggering element is not focused anymore.

Best practice: ensure a custom tooltip can be dismissed by keyboard.

Acknowledgements and links

Thanks to Nicolai Schwarz and Tessa Hellbusch for their support.

Links referenced in the article:

About Jan Hellbusch

Jan has seen many things enter and leave the Web since the mid-90s. Unfortunately, accessibility issues persisted, which is why he started accessibility consulting in 2000 to fill the gap. He speaks and writes about accessibility, provides website testing and has a wealth of experience when it comes to presenting digital accessibility solutions.

Website: hellbusch.de