Tooltips
A tooltip explains a control that does not explain itself. It sits in the top layer as a popover, so nothing clips it. It uses built-in browser features where possible.
Types
- Plain labels the control. It shows on hover and on focus, then hides again.
- Rich adds a subhead, text and an action, and stays until it is dismissed.
<script lang="ts">
import { Button, IconButton, RichTooltip } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
</script>
<IconButton title="Add to favorites"><Icon>favorite</Icon></IconButton>
<IconButton command="show-popover" commandfor="example-rich-tooltip" aria-label="About tooltips">
<Icon>info</Icon>
</IconButton>
<RichTooltip id="example-rich-tooltip" subhead="Rich tooltip">
Room for a sentence or two, and an action.
{#snippet actions()}
<Button variant="text">Learn more</Button>
{/snippet}
</RichTooltip>
Plain tooltip
Button and IconButton take a title. It becomes the tooltip
text and the aria-label. A disabled or loading control shows no tooltip. To use any
other element as the anchor, point its aria-describedby at the tooltip's id.
<script lang="ts">
import { Button, Tooltip } from 'noph-ui'
</script>
<Button title="This is a button tooltip">Hover over this button</Button>
<div aria-describedby="example-tooltip">Hover over this text</div>
<Tooltip id="example-tooltip">This is a basic tooltip</Tooltip>
After the pointer leaves, the tooltip waits half a second before it hides, so the text inside stays reachable. Escape closes it.
Rich tooltip
A rich tooltip is persistent. A click or a tap on the control opens it. It stays open when the pointer leaves, until the person interacts with something else. Escape or a click outside closes it. Hover does not open it, so a panel with an action does not appear under a pointer that only passes by.
The control points at it with commandfor. command="show-popover" keeps a
second click on the control from closing it. Enter on the control is a click, so the keyboard
opens it too.
It takes a subhead, its children as the text and an actions snippet. It
is at most 20rem wide. Use bind:open, element, show and close to open and close it yourself, for example to introduce a new
feature on page load.
Positioning
A plain tooltip sits centered above its anchor, a rich one centered below its control. Neither covers it. They flip to the other side when there is no room, and shift back in at a window edge. Both sides are set with custom properties.
<script lang="ts">
import { Button, IconButton, RichTooltip } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
</script>
<Button title="Shown below the button" --np-tooltip-position-area="bottom">Below</Button>
<Button
title="Shown to the right"
--np-tooltip-position-area="right"
--np-tooltip-justify-self="auto"
--np-tooltip-align-self="anchor-center"
>
Right
</Button>
<IconButton command="show-popover" commandfor="placement-rich-tooltip" aria-label="About placement">
<Icon>info</Icon>
</IconButton>
<RichTooltip
id="placement-rich-tooltip"
subhead="Bottom right"
--np-rich-tooltip-position-area="bottom right"
--np-rich-tooltip-justify-self="start"
>
Room for a sentence or two.
</RichTooltip>
The last example is Material's default placement: the panel's top left corner sits at the control's bottom right corner. This component defaults to centered below, the variant Material allows on desktop.
A vertical toolbar does this for you. Its actions are stacked, so a tooltip above one of them would cover the one before it. The toolbar moves the tooltips to the trailing side.
Without JavaScript
Everything a tooltip needs is in the server markup: interestfor on a plain tooltip's
anchor, command and commandfor on a rich tooltip's control, the popover
itself and CSS for the timing. Either attribute also makes the control the implicit anchor of its
popover, so you do not need to set anchor-name.
<button interestfor="save-tip" aria-describedby="save-tip">
Save
</button>
<Tooltip id="save-tip">Save the file</Tooltip>interestfor only exists on a, area and button. For other anchors, and in browsers without it, the plain tooltip falls back
to event listeners it adds on mount. A rich tooltip needs none, since the browser handles the
click on its control.
Touch devices
A link or a button shows its plain tooltip on long press. A rich tooltip opens on tap. The
fallback needs hover, so it is off on (hover: none) and (pointer: coarse). Never put
information in a tooltip that is not available anywhere else.
Accessibility
Both render role="tooltip". A plain tooltip is linked through its anchor's aria-describedby. A rich one gets aria-expanded and aria-details from commandfor, and Tab moves from the control into
the panel.
A plain tooltip opens on keyboard focus but not on a click, so it does not get in a pointer user's
way. A rich one opens on Enter, which is a click. On an icon-only control, title gives the button both its name and its description. An action inside a tooltip is
a shortcut, never the only way to get somewhere.
<IconButton title="Delete message">
<Icon>delete</Icon>
</IconButton>Theming
| Token | Default value |
|---|---|
--np-tooltip-position-area | top |
--np-tooltip-justify-self | anchor-center |
--np-tooltip-align-self | auto |
--np-tooltip-margin | 4px 0 |
--np-tooltip-position-try-fallbacks | flip-block |
--np-rich-tooltip-position-area | bottom |
--np-rich-tooltip-justify-self | anchor-center |
--np-rich-tooltip-align-self | auto |
--np-rich-tooltip-margin | 4px 0 |
--np-rich-tooltip-position-try-fallbacks | flip-block |
--np-rich-tooltip-action-inset | 1rem |
Both position properties take any CSS position-area value, for example right or bottom right. The colors come from the theme: a plain tooltip
uses the inverse-surface roles, a rich one surface-container and on-surface-variant.
--np-rich-tooltip-action-inset pulls the action row out, so the action's label lines
up with the text above it. It matches the inline padding of a small Button. If your
action has a different padding, set it to that.
API
Tooltip attributes
Other attributes go to the tooltip element. The component sets role.
| Attribute | Type | Default | Description |
|---|---|---|---|
id | string | undefined | undefined | Connects the tooltip to its anchor, the element with a matching aria-describedby. |
open | boolean | undefined | undefined | Bindable. Reflects whether the tooltip is shown. |
element | HTMLDivElement | undefined | undefined | Bindable reference to the tooltip element. |
Rich tooltip attributes
Other attributes go to the panel. The component sets role and popover.
| Attribute | Type | Default | Description |
|---|---|---|---|
id | string | undefined | undefined | Connects the tooltip to its control, the element with a matching commandfor. |
subhead | string | undefined | undefined | Title line above the text. |
actions | Snippet | undefined | undefined | Row below the text, for one or two text buttons. |
open | boolean | undefined | undefined | Bindable. Setting it opens or closes the tooltip. |
element | HTMLDivElement | undefined | undefined | Bindable reference to the panel. |