Tooltip
<gui-tooltip> describes another element in a few words. It shows next to the element while the
pointer rests on it or while it has keyboard focus, and screen readers read its text as the
element’s description. Its children are the text.
import { GuiTooltip } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function TooltipBasic() { return ( <> <button type="button" id="save-draft" className="gui-button gui-button--outlined" > Save draft </button> <GuiTooltip htmlFor="save-draft"> Saves your changes without publishing them. </GuiTooltip> </> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/tooltip';
@Component({ selector: 'app-tooltip-basic', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <button type="button" id="save-draft" class="gui-button gui-button--outlined">Save draft</button> <gui-tooltip for="save-draft">Saves your changes without publishing them.</gui-tooltip> `,})export class TooltipBasicComponent {}<script setup lang="ts">import '@golemui/gui-components/tooltip';import '@golemui/gui-components/index.css';</script>
<template> <button type="button" id="save-draft" class="gui-button gui-button--outlined">Save draft</button> <gui-tooltip for="save-draft">Saves your changes without publishing them.</gui-tooltip></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/tooltip';import '@golemui/gui-components/index.css';
@customElement('tooltip-basic')export class TooltipBasic extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <button type="button" id="save-draft" class="gui-button gui-button--outlined">Save draft</button> <gui-tooltip for="save-draft">Saves your changes without publishing them.</gui-tooltip> `; }}<button type="button" id="save-draft" class="gui-button gui-button--outlined">Save draft</button><gui-tooltip for="save-draft">Saves your changes without publishing them.</gui-tooltip>The element’s entry point is @golemui/gui-components/tooltip, which registers <gui-tooltip>
and nothing else. To install the package and load the stylesheet, see
Installation.
The @golemui/gui-components/react components have typed props and an on* prop for each event. React 18 or later.
import { GuiTooltip } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function TooltipExample() { return ( <GuiTooltip htmlFor="save-draft" > Saves your changes without publishing them. </GuiTooltip> );}Add CUSTOM_ELEMENTS_SCHEMA to the component that uses the tags, and the stylesheet to styles in angular.json.
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/tooltip';
@Component({ selector: 'app-tooltip-example', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-tooltip for="save-draft" > Saves your changes without publishing them. </gui-tooltip> `,})export class TooltipExampleComponent {}"styles": ["node_modules/@golemui/gui-components/lib/styles/index.css"]Tell Vue's compiler that gui-* tags are custom elements, and add the typings once for typed props and @gui-* handlers.
<script setup lang="ts">import '@golemui/gui-components/tooltip';import '@golemui/gui-components/index.css';</script>
<template> <gui-tooltip for="save-draft" > Saves your changes without publishing them. </gui-tooltip></template>vue({ template: { compilerOptions: { isCustomElement: (tag) => tag.startsWith('gui-') } },});import type {} from '@golemui/gui-components/vue';The page's stylesheet styles the gui-* elements, so it only reaches them outside a shadow root. Render the host component into the light DOM too, as below, or load the stylesheet into its shadow root.
import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/tooltip';import '@golemui/gui-components/index.css';
@customElement('tooltip-example')export class TooltipExample extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-tooltip for="save-draft" > Saves your changes without publishing them. </gui-tooltip> `; }}With any bundler, such as Vite. Import the element once, anywhere, and use the tag in your markup.
<gui-tooltip for="save-draft"> Saves your changes without publishing them.</gui-tooltip>
<script type="module" src="./main.js"></script>import '@golemui/gui-components/tooltip';import '@golemui/gui-components/index.css';for takes the id of the element the tooltip describes, its target, as on a <label>. In React,
it is the htmlFor prop. Put the tooltip anywhere: it shows next to its target, above the rest
of the page.
The target is the element that takes the pointer and the focus, such as a native button, link or
input. On a gui-* element, the focus goes to the control inside it, and keyboard focus doesn’t
show the tooltip. To give a native button the look of a gui-button, add the gui-button class,
and gui-button--outlined for the outlined style, as these examples do. For a field, use its
hint, which is always visible.
Properties
| Property | Type | Default | Description |
|---|---|---|---|
htmlFor
attribute for | string | – | The id of the element the tooltip describes. |
placement | GuiPlacement | 'top' | Where the tooltip sits next to its target. Start and end follow the reading direction. |
openDelay
attribute open-delay | number | 500 | How long the pointer or focus rests on the target before the tooltip opens, in ms. |
closeDelay
attribute close-delay | number | 150 | How long it stays after the pointer leaves, in ms: time to move the pointer onto it. |
open | boolean | – | Whether the tooltip is open. Setting it opens or closes it and fires no gui-toggle. |
Events
Every event bubbles and is composed, so you can listen on any ancestor.
| Event | Description |
|---|---|
gui-toggle | The tooltip opened or closed, from the pointer, focus or Escape. detail.open is the new state. |
Examples
Section titled “Examples”Placement and timing
Section titled “Placement and timing”placement sets the side of the target the tooltip shows on: top, the default, or any of the
values of the popover. When it doesn’t fit, it
moves to the other side. An arrow points at the target, wherever the tooltip ends up.
open-delay is how long the pointer or the focus rests on the target before the tooltip shows,
500ms by default, so it doesn’t flash while the pointer crosses the page. close-delay is how
long it stays after the pointer leaves, 150ms by default: time to move the pointer onto it.
import { GuiTooltip } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function TooltipPlacement() { return ( <> <button type="button" id="copy-link" className="gui-button gui-button--outlined" > Copy link </button> <GuiTooltip htmlFor="copy-link" placement="bottom" openDelay={200}> Anyone with the link can view the board. </GuiTooltip> </> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/tooltip';
@Component({ selector: 'app-tooltip-placement', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <button type="button" id="copy-link" class="gui-button gui-button--outlined">Copy link</button> <gui-tooltip for="copy-link" placement="bottom" open-delay="200"> Anyone with the link can view the board. </gui-tooltip> `,})export class TooltipPlacementComponent {}<script setup lang="ts">import '@golemui/gui-components/tooltip';import '@golemui/gui-components/index.css';</script>
<template> <button type="button" id="copy-link" class="gui-button gui-button--outlined">Copy link</button> <gui-tooltip for="copy-link" placement="bottom" open-delay="200"> Anyone with the link can view the board. </gui-tooltip></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/tooltip';import '@golemui/gui-components/index.css';
@customElement('tooltip-placement')export class TooltipPlacement extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <button type="button" id="copy-link" class="gui-button gui-button--outlined">Copy link</button> <gui-tooltip for="copy-link" placement="bottom" open-delay="200"> Anyone with the link can view the board. </gui-tooltip> `; }}<button type="button" id="copy-link" class="gui-button gui-button--outlined">Copy link</button><gui-tooltip for="copy-link" placement="bottom" open-delay="200"> Anyone with the link can view the board.</gui-tooltip>A disabled button
Section titled “A disabled button”A disabled control gets no pointer or focus events, so its tooltip never shows: yet that is where
a tooltip that explains why helps most. Mark the button aria-disabled="true" instead of
disabled: it stays in the tab order and shows its tooltip, and your click handler does
nothing while it is set. Or put the tooltip on an element around the disabled control.
import { GuiTooltip } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function TooltipDisabled() { return ( <> <button type="button" id="publish-post" className="gui-button" aria-disabled="true" > Publish </button> <GuiTooltip htmlFor="publish-post"> Add a title before you publish. </GuiTooltip> </> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/tooltip';
@Component({ selector: 'app-tooltip-disabled', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <button type="button" id="publish-post" class="gui-button" aria-disabled="true">Publish</button> <gui-tooltip for="publish-post">Add a title before you publish.</gui-tooltip> `,})export class TooltipDisabledComponent {}<script setup lang="ts">import '@golemui/gui-components/tooltip';import '@golemui/gui-components/index.css';</script>
<template> <button type="button" id="publish-post" class="gui-button" aria-disabled="true">Publish</button> <gui-tooltip for="publish-post">Add a title before you publish.</gui-tooltip></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/tooltip';import '@golemui/gui-components/index.css';
@customElement('tooltip-disabled')export class TooltipDisabled extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <button type="button" id="publish-post" class="gui-button" aria-disabled="true">Publish</button> <gui-tooltip for="publish-post">Add a title before you publish.</gui-tooltip> `; }}<button type="button" id="publish-post" class="gui-button" aria-disabled="true">Publish</button><gui-tooltip for="publish-post">Add a title before you publish.</gui-tooltip>Opening from your code
Section titled “Opening from your code”Set open to show or hide the tooltip yourself. Setting it fires no gui-toggle. When the
pointer, the focus or Escape shows or hides it, the element fires gui-toggle with the
new state in event.detail.open, and open follows.
Styling
Section titled “Styling”Anatomy
Section titled “Anatomy”The element is the tooltip’s box. When it connects, it gives itself the tooltip role,
popover="manual" and an id, unless you set them, and adds that id to its target’s
aria-describedby. While it is open, the stylesheet points an arrow at the target:
<button type="button" id="save-draft" aria-describedby="gui-tooltip-1"> Save draft</button><gui-tooltip for="save-draft" popover="manual" role="tooltip" id="gui-tooltip-1" open data-gui-arrow="bottom"> Saves your changes without publishing them.</gui-tooltip>Until it opens, and before the element upgrades, the tooltip is hidden, so its text never shows
in the flow of a server-rendered page. While it is open, the element places it with inline
styles and gives the arrow its edge in data-gui-arrow and its offset along that edge in
--gui-arrow-offset. Leave those alone. See
Floating panels.
Tokens
Section titled “Tokens”The tooltip reads these design tokens. Set them on :root to change every tooltip, or on any
element to change the tooltips inside it:
| Token | What it styles |
|---|---|
--gui-tooltip-bg | The background and the arrow. Defaults to --gui-bg-inverse. |
--gui-tooltip-text | The text. Defaults to --gui-text-inverse. |
--gui-tooltip-max-width | The widest the tooltip gets, 20rem by default. Longer text wraps. |
--gui-radius-sm | The corner radius. |
--gui-space-1 | The padding above and below the text. |
--gui-space-2 | The padding before and after the text. |
--gui-font-xs | The text size. |
--gui-overlay-duration | How long the tooltip takes to fade in and out. Reduced motion skips it. |
--gui-tooltip-bg and --gui-tooltip-text also color the value bubbles of the
slider.
/* Tooltips in the brand color. */:root { --gui-tooltip-bg: #2f3a8f; --gui-tooltip-text: #ffffff;}To restyle beyond the tokens, see Overriding styles; for dark mode and the Clay theme, see Themes and dark mode.
Accessibility
Section titled “Accessibility”The tooltip meets WCAG 1.4.13, Content on Hover or Focus:
- Dismissible. Escape hides it without moving the pointer or the focus, and does nothing else: it doesn’t close the dialog the target is in.
- Hoverable. The pointer can move from the target onto the tooltip without it closing.
- Persistent. It stays until the pointer leaves the target and the tooltip, the focus leaves the target, or the user presses Escape or the target.
And:
- Description. The tooltip’s text is the target’s description, through
aria-describedby. It is not its name: the target needs a name of its own, such as its text or anaria-label. - Keyboard focus only. Focus from the keyboard shows the tooltip. Focus from a click doesn’t: pressing the target hides it, since the user acted on what it described.
- Touch. A tap never shows the tooltip: it activates the target. Don’t put anything in a tooltip that touch users need and can’t find elsewhere.
- Text only. The tooltip isn’t focusable, so links and buttons in it can’t be reached. For content the user interacts with, use a popover.
| Key | Action |
|---|---|
| Tab | To the target: shows the tooltip after open-delay. |
| Escape | Hides the tooltip. The focus stays on the target. |
What every element shares is in Accessibility.