Tooltip

Displays a popup label to describe an element.

The tooltip component improves the interface accessibility by providing context to interactive elements. It uses AlpineJs Anchor for precise positioning and handles ARIA attributes automatically.

HTML
 1<button 
 2  x-data="ksTooltip"
 3  x-id="['ks-tooltip']"
 4  x-bind="trigger" 
 5  class="btn variant-outline size-icon" type="button">
 6  {{ partial "ui/icon" (dict
 7      "name" "pizza"
 8  )}}
 9  {{ partial "ui/tooltip" (dict
10      "label" "A slice of Pizza"
11  )}}
12</button>

Reference

The tooltip partial accepts a dictionary with the following terms:

label
"label" "A slice of Pizza" — (string, required)
The text content of the tooltip.

Installation

The tooltip component is disabled by default to save resources.
Uncomment the import in the following files:

/assets/js/main.js
1- //import tooltipModule from './modules/tooltip.js';
2+ import tooltipModule from './modules/tooltip.js';
/assets/css/main.css
1- /* @import './components/_tooltip.css'; */
2+ @import './components/_tooltip.css';
Prerequisites
The Get Started: Manual Installation steps must be completed!

JavaScript

Create the Alpine module:

Alpine Js /assets/js/modules/tooltip.js
 1export default function tooltipModule(Alpine) {
 2  Alpine.data('ksTooltip', () => ({
 3    visible: false,
 4    timer: null,
 5
 6    open() {
 7      if (this.timer) clearTimeout(this.timer);
 8      this.visible = true;
 9    },
10    scheduleClose() {
11      if (this.timer) clearTimeout(this.timer);
12      this.timer = setTimeout(() => {
13        this.visible = false;
14      }, 170);
15    },
16    close() {
17      this.visible = false;
18      if (this.timer) clearTimeout(this.timer);
19    },
20
21    trigger: {
22      ['@mouseenter']() {
23        this.open();
24      },
25      ['@mouseleave']() {
26        this.scheduleClose();
27      },
28      ['@focus']() {
29        this.open();
30      },
31      ['@blur']() {
32        this.scheduleClose();
33      },
34      ['@keydown.escape.window']() {
35        if (this.visible) {
36          this.close();
37        }
38      },
39      [':aria-describedby']() {
40        return this.$id('ks-tooltip');
41      },
42    },
43  }));
44}

Import and register it in main.js:

Alpine Js /assets/js/main.js
1import tooltipModule from './modules/tooltip.js';
2
3// Ensure Alpine Anchor is registered
4import anchor from '@alpinejs/anchor';
5Alpine.plugin(anchor);
6
7tooltipModule(Alpine);

Hugo Partial

Create the partial file:

Go/HTML template layouts/_partials/ui/tooltip.html
 1<span
 2  :id="$id('ks-tooltip')"
 3  x-show="visible"
 4  x-anchor.top.offset.8="$el.parentNode"
 5  x-transition.opacity
 6  x-cloak
 7  @mouseenter="open()"
 8  @mouseleave="scheduleClose()"
 9  @click.prevent.stop=""
10  @mousedown.stop=""
11  @mouseup.stop=""
12  role="tooltip"
13  class="tooltip"
14>
15  {{ .label }}
16</span>

CSS Styling

Create the CSS component:

Tailwind CSS assets/css/components/_tooltip.css
1@layer components {
2  .tooltip {
3    @apply bg-popover text-foreground;
4    @apply font-sans text-xs;
5    @apply px-2 py-1;
6    @apply rounded-md border;
7    @apply pointer-events-auto z-90 cursor-text shadow-md select-text;
8  }
9}

Import it in your main Tailwind file:

Tailwind CSS assets/main.css
1/* Components */
2/** (Import Keystone components CSS here) */
3@import './components/_tooltip.css';

Usage

To use a tooltip, you must apply the Alpine directives to the parent (trigger) and place the partial inside it.

Alpine directives

  1. x-data="ksTooltip" — Initializes logic
  2. x-id="['ks-tooltip']" — Generates unique ID
  3. x-bind="trigger" — Handles events (hover/focus)
HTML
 1<button
 2  x-data="ksTooltip"
 3  x-id="['ks-tooltip']"
 4  x-bind="trigger"
 5  class="btn variant-default size-default"
 6  type="button"
 7>
 8  Button Text 
 9  {{ partial "ui/tooltip" (dict 
10    "label" "More details about this action" 
11  )}}
12</button>

Tips & tricks

Internationalization (i18n)
Avoid hardcoding strings. Use Hugo’s i18n function to make your UI multilingual.

HTML
 1<button
 2  x-data="ksTooltip"
 3  x-id="['ks-tooltip']"
 4  x-bind="trigger"
 5  class="btn variant-outline size-icon"
 6  type="button"
 7>
 8  {{ partial "ui/icon" (dict "name" "pizza" )}} 
 9  
10  {{ partial "ui/tooltip" (dict 
11    "label" (i18n "PizzaSlice" . | default "A slice of Pizza") 
12  )}}
13</button>

Accessibility

The component is engineered to meet WCAG 2.2 SC: 1.4.13 (Opens in a new tab) :

  1. Automatic Association: Generates a unique ID and binds aria-describedby to the parent trigger.
  2. Hoverable (Safe Triangle): Implements a logical “bridge”. Users can move their cursor from the trigger into the tooltip content to select text without the tooltip disappearing.
  3. Dismissible: Users can press the Escape key to close the tooltip immediately without moving focus.
  4. Persistent: The content does not disappear on its own while the user is interacting with the trigger or the tooltip itself.
  5. Keyboard Support: Full visibility support for focus and blur events on the parent trigger.