Put a .tooltip child inside the activator to make the bubble.
The activator does not need a tooltipped class. Tooltips show on
hover and keyboard focus with CSS alone. Helper classes set only
placement and the rich variant: top (the default),
bottom, left, right, and
rich.
The hover area of a plain tooltip includes the gap to the activator. Runtime tooltips support Escape dismissal and reduced motion.
On touch, a runtime tooltip shows after a half-second long press,
without enterDelay. It stays for a second and a half after the finger lifts.
The long press does not press the button or follow the link. A tap,
or a press that turns into a scroll, shows nothing. With
openOnClick, a tap counts as the click. It opens and closes
the tooltip, and the long press is off.
A runtime tooltip skips its enterDelay if it opens while
another tooltip is closing, or within 300 ms after that one's
exitDelay ends. Moving along a toolbar therefore shows each
tooltip at once.
CSS-only tooltips have no Escape handler. Use them only after you verify the applicable WCAG 1.4.13 conditions and dismissal exception. Otherwise, use a verified runtime tooltip or persistent supporting text.
Tokens follow the
M3 tooltip spec.
A plain tooltip uses inverse-surface for the container
and inverse-on-surface for the text, with 4dp corners and
body-small type. It is at least 24dp tall and at most
200dp wide, with 4dp vertical and 8dp horizontal padding. It has no
elevation or caret. It sits 4dp from the activator, and M3 recommends
placing it above.
Inside a <button>, make the bubble a
<span>. A <div> is not
phrasing content, so the parser hoists it out. Icon-only
buttons still need .circle so the span is not
treated as a label.
<button type="button" class="circle" aria-label="Add" aria-describedby="tip-add-to-album">
<span class="material-symbols" aria-hidden="true">add</span>
<span class="tooltip" id="tip-add-to-album">Add to album</span>
</button>
<button type="button" class="circle" aria-label="Below" aria-describedby="tip-below">
<span class="material-symbols" aria-hidden="true">arrow_downward</span>
<span class="tooltip bottom" id="tip-below">Below</span>
</button>
Rich
Add rich for a rich
tooltip with a surface container, elevation 2, 12dp
corners, and a 320dp maximum width. Inside it, a heading is the title
(title-small), a <p> is
supporting text (body-medium), and a trailing
<nav> holds the action. Rich bubbles
receive pointer events, so the user can click the action.
Saved offline
This stop is stored on the device so it still opens without a signal.
<div>
<button type="button" class="tonal">Why this is saved</button>
<div class="tooltip rich bottom">
<h3>Saved offline</h3>
<p>This stop is stored on the device so it still opens without a signal.</p>
<nav>
<button type="button" class="text">Got it</button>
</nav>
</div>
</div>
A rich tooltip with an action cannot go inside a
<button>, because that would nest buttons. Put the
bubble next to the control and wrap both in a parent.
JavaScript
To use the JS plugin, add
tooltipped to the activator.
data-tooltip holds the text, and
data-position is top,
right, bottom, or
left. The plugin handles
data-tooltip, delayed show and hide, and
keeping the bubble inside the viewport. CSS-only tooltips do not need AutoInit.
AutoInit() starts every
.tooltipped except those marked
no-autoinit. The generated element gets
.tooltip (plus .rich when tooltipId is set).
In browsers with CSS anchor positioning, the plugin anchors the bubble to its activator instead of measuring. The bubble stays beside the activator while the page or a container scrolls, and flips to the opposite side at the viewport edge. An anchored tooltip fades in without sliding, because script cannot tell which side it flipped to. The plugin adds transitionMovement to the gap instead. Other browsers keep the measured, viewport-clamped position.
<button type="button" class="tooltipped" data-position="bottom" data-tooltip="I am a tooltip">
Hover me
</button>
For HTML content, point data-tooltip-id at an element.
The plugin moves that element into the tooltip and marks the
bubble rich. Leave data-tooltip off
so the plugin keeps the HTML. There is no data-html
attribute and no unsafeHTML option.
<button type="button" class="tooltipped"
data-position="bottom" data-tooltip-id="tooltip-content">
With HTML
</button>
<div id="tooltip-content" hidden>
<h3>Chart</h3>
<p>This is a tooltip with a <a href="https://github.com">link</a>.</p>
</div>
Initialization
The IIFE bundle exposes Expressive.Tooltip.
Call init yourself when you need options other
than the defaults, or let
Expressive.AutoInit() start every
.tooltipped.
document.addEventListener('DOMContentLoaded', function() {
const elems = document.querySelectorAll('.tooltipped');
const instances = Expressive.Tooltip.init(elems, {
enterDelay: 200
});
});
You can also pass per-instance options through AutoInit:
Expressive.AutoInit(document.body, {
Tooltip: { enterDelay: 200 }
});
Options
Pass these to the constructor or init.
data-tooltip, data-position, and
data-tooltip-id on the element
override these defaults. The plugin reads the attributes again each
time the tooltip opens, so a later change takes effect on the
next show.
| Name | Type | Default | Description |
|---|---|---|---|
exitDelay |
Number | 200 |
Delay before the tooltip disappears, in milliseconds. |
enterDelay |
Number | 0 |
Delay before the tooltip appears, in milliseconds. Skipped while a tooltip is closing and for 300 ms after its exitDelay ends. |
tooltipId |
String | — | Id of an element used as the tooltip body. Set by data-tooltip-id. Marks the bubble rich. |
text |
String | '' |
Plain-text content. Set by data-tooltip. Ignored when tooltipId is set. |
margin |
Number | 4 |
Distance from the activator, in pixels, not counting transitionMovement. The M3 value is 4dp. |
inDuration |
Number | 250 |
Enter transition duration, in milliseconds. |
opacity |
Number | 1 |
Opacity of the tooltip when shown. |
outDuration |
Number | 200 |
Exit transition duration, in milliseconds. |
position |
String | 'bottom' |
Direction: 'top', 'right', 'bottom', or 'left'. Set by data-position. CSS-only tooltips default to above. |
transitionMovement |
Number | 10 |
How far the tooltip moves during its transition, in pixels. |
openOnClick |
Boolean | false |
Open on a click instead of hover and focus, at once, without enterDelay. A second click, Escape, a click elsewhere or focus moving to another element closes it at once. A click inside the tooltip does not. Keyboard users open it with Enter or Space, so make the trigger a <button>. |
Methods
All methods are called on the plugin instance. You can get the instance like this:
const instance = Expressive.Tooltip.getInstance(elem);
.open();
open() and open(true) show the tooltip after
its enter delay, with no hover or focus needed. open(false)
uses the automatic behavior, so the activator must be hovered or focused when the delay ends.
With openOnClick, a click elsewhere also closes a tooltip
opened this way.
instance.open();
.close();
Hide the tooltip.
instance.close();
.destroy();
Destroy the plugin instance and remove its generated tooltip and event handlers. Rich content returns to its authored position and presentation state. The method keeps existing description associations, so you can initialize the same content again.
instance.destroy();
Properties
| Name | Type | Description |
|---|---|---|
el |
Element | The DOM element the plugin was initialized with. |
options |
Object | The options the instance was initialized with. |
isOpen |
Boolean | Whether the tooltip is open. |
isHovered |
Boolean | Whether the activator is hovered. |
isFocused |
Boolean | Whether the activator is focused via the keyboard. |
tooltipEl |
Element | The generated tooltip element. |