Tooltip
Contextual overlay primitive that links descriptive content to a trigger element.
Target binding
The for property connects Tooltip to the element that receives its description.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-tooltip/ui-tooltip.html"
>
<ui-button
id="tooltip-trigger"
>Help</ui-button>
<ui-tooltip
for="tooltip-trigger"
>Tooltip content.</ui-tooltip><script setup>
import { UiButton, UiTooltip } from "@threadlabs/looma";
</script>
<template>
<UiButton
id="tooltip-trigger"
>Help</UiButton>
<UiTooltip
for="tooltip-trigger"
>Tooltip content.</UiTooltip>
</template>import { UiButton, UiTooltip } from "@threadlabs/looma";
export default function Example() {
return <>
<UiButton
id="tooltip-trigger"
>Help</UiButton>
<UiTooltip
htmlFor="tooltip-trigger"
>Tooltip content.</UiTooltip>
</>;
}/** @jsxImportSource preact */
import { UiButton, UiTooltip } from "@threadlabs/looma";
export default function Example() {
return <>
<UiButton
id="tooltip-trigger"
>Help</UiButton>
<UiTooltip
htmlFor="tooltip-trigger"
>Tooltip content.</UiTooltip>
</>;
}<script>
import { UiButton, UiTooltip } from "@threadlabs/looma";
</script>
<UiButton
id="tooltip-trigger"
>Help</UiButton>
<UiTooltip
for="tooltip-trigger"
>Tooltip content.</UiTooltip>import { Component } from "@angular/core";
import { UiButton, UiTooltip } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiButton, UiTooltip],
template: `
<ng-template
ui-button
[hnAttributes]="{ id: 'tooltip-trigger' }"
>Help</ng-template>
<span
ui-tooltip
[for]="'tooltip-trigger'"
>Tooltip content.</span>
`,
})
export default class Example {}Placement and delays
Placement chooses the preferred edge; show-delay and hide-delay tune hover timing.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-tooltip/ui-tooltip.html"
>
<ui-button
id="tooltip-timed-trigger"
>Help</ui-button>
<ui-tooltip
for="tooltip-timed-trigger"
placement="bottom-start"
show-delay="0"
hide-delay="200"
>Tooltip content.</ui-tooltip><script setup>
import { UiButton, UiTooltip } from "@threadlabs/looma";
</script>
<template>
<UiButton
id="tooltip-timed-trigger"
>Help</UiButton>
<UiTooltip
for="tooltip-timed-trigger"
placement="bottom-start"
:show-delay="0"
:hide-delay="200"
>Tooltip content.</UiTooltip>
</template>import { UiButton, UiTooltip } from "@threadlabs/looma";
export default function Example() {
return <>
<UiButton
id="tooltip-timed-trigger"
>Help</UiButton>
<UiTooltip
htmlFor="tooltip-timed-trigger"
placement="bottom-start"
showDelay={0}
hideDelay={200}
>Tooltip content.</UiTooltip>
</>;
}/** @jsxImportSource preact */
import { UiButton, UiTooltip } from "@threadlabs/looma";
export default function Example() {
return <>
<UiButton
id="tooltip-timed-trigger"
>Help</UiButton>
<UiTooltip
htmlFor="tooltip-timed-trigger"
placement="bottom-start"
showDelay={0}
hideDelay={200}
>Tooltip content.</UiTooltip>
</>;
}<script>
import { UiButton, UiTooltip } from "@threadlabs/looma";
</script>
<UiButton
id="tooltip-timed-trigger"
>Help</UiButton>
<UiTooltip
for="tooltip-timed-trigger"
placement="bottom-start"
showDelay={0}
hideDelay={200}
>Tooltip content.</UiTooltip>import { Component } from "@angular/core";
import { UiButton, UiTooltip } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiButton, UiTooltip],
template: `
<ng-template
ui-button
[hnAttributes]="{ id: 'tooltip-timed-trigger' }"
>Help</ng-template>
<span
ui-tooltip
[for]="'tooltip-timed-trigger'"
[placement]="'bottom-start'"
[showDelay]="0"
[hideDelay]="200"
>Tooltip content.</span>
`,
})
export default class Example {}inverse
Inverse uses the high-contrast surface: dark on light themes and light on dark themes.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-tooltip/ui-tooltip.html"
>
<ui-button
id="tooltip-inverse-trigger"
>Shortcut</ui-button>
<ui-tooltip
for="tooltip-inverse-trigger"
inverse
show-delay="0"
>Duplicate (⌘D)</ui-tooltip><script setup>
import { UiButton, UiTooltip } from "@threadlabs/looma";
</script>
<template>
<UiButton
id="tooltip-inverse-trigger"
>Shortcut</UiButton>
<UiTooltip
for="tooltip-inverse-trigger"
:inverse="true"
:show-delay="0"
>Duplicate (⌘D)</UiTooltip>
</template>import { UiButton, UiTooltip } from "@threadlabs/looma";
export default function Example() {
return <>
<UiButton
id="tooltip-inverse-trigger"
>Shortcut</UiButton>
<UiTooltip
htmlFor="tooltip-inverse-trigger"
inverse
showDelay={0}
>Duplicate (⌘D)</UiTooltip>
</>;
}/** @jsxImportSource preact */
import { UiButton, UiTooltip } from "@threadlabs/looma";
export default function Example() {
return <>
<UiButton
id="tooltip-inverse-trigger"
>Shortcut</UiButton>
<UiTooltip
htmlFor="tooltip-inverse-trigger"
inverse
showDelay={0}
>Duplicate (⌘D)</UiTooltip>
</>;
}<script>
import { UiButton, UiTooltip } from "@threadlabs/looma";
</script>
<UiButton
id="tooltip-inverse-trigger"
>Shortcut</UiButton>
<UiTooltip
for="tooltip-inverse-trigger"
inverse
showDelay={0}
>Duplicate (⌘D)</UiTooltip>import { Component } from "@angular/core";
import { UiButton, UiTooltip } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiButton, UiTooltip],
template: `
<ng-template
ui-button
[hnAttributes]="{ id: 'tooltip-inverse-trigger' }"
>Shortcut</ng-template>
<span
ui-tooltip
[for]="'tooltip-inverse-trigger'"
[inverse]="true"
[showDelay]="0"
>Duplicate (⌘D)</span>
`,
})
export default class Example {}Side placement
A tooltip can sit on any side of its trigger; here it opens on the right and stays centered vertically.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-tooltip/ui-tooltip.html"
>
<ui-button
id="side-tooltip-trigger"
>Hover or focus</ui-button>
<ui-tooltip
for="side-tooltip-trigger"
placement="right"
>This hint opens beside the control.</ui-tooltip><script setup>
import { UiButton, UiTooltip } from "@threadlabs/looma";
</script>
<template>
<UiButton
id="side-tooltip-trigger"
>Hover or focus</UiButton>
<UiTooltip
for="side-tooltip-trigger"
placement="right"
>This hint opens beside the control.</UiTooltip>
</template>import { UiButton, UiTooltip } from "@threadlabs/looma";
export default function Example() {
return <>
<UiButton
id="side-tooltip-trigger"
>Hover or focus</UiButton>
<UiTooltip
htmlFor="side-tooltip-trigger"
placement="right"
>This hint opens beside the control.</UiTooltip>
</>;
}/** @jsxImportSource preact */
import { UiButton, UiTooltip } from "@threadlabs/looma";
export default function Example() {
return <>
<UiButton
id="side-tooltip-trigger"
>Hover or focus</UiButton>
<UiTooltip
htmlFor="side-tooltip-trigger"
placement="right"
>This hint opens beside the control.</UiTooltip>
</>;
}<script>
import { UiButton, UiTooltip } from "@threadlabs/looma";
</script>
<UiButton
id="side-tooltip-trigger"
>Hover or focus</UiButton>
<UiTooltip
for="side-tooltip-trigger"
placement="right"
>This hint opens beside the control.</UiTooltip>import { Component } from "@angular/core";
import { UiButton, UiTooltip } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiButton, UiTooltip],
template: `
<ng-template
ui-button
[hnAttributes]="{ id: 'side-tooltip-trigger' }"
>Hover or focus</ng-template>
<span
ui-tooltip
[for]="'side-tooltip-trigger'"
[placement]="'right'"
>This hint opens beside the control.</span>
`,
})
export default class Example {}Keyboard shortcut
The shortcut slot shows a key binding after the label, behind a thin divider, in smaller text. It is a display hint and binds no key; write the keys the way the reader's platform does.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-tooltip/ui-tooltip.html"
>
<ui-button
id="tooltip-shortcut-trigger"
>Search</ui-button>
<ui-tooltip
for="tooltip-shortcut-trigger"
open
placement="bottom"
>
Search
<kbd
slot="shortcut"
>⌘K</kbd>
</ui-tooltip><script setup>
import { UiButton, UiTooltip } from "@threadlabs/looma";
</script>
<template>
<UiButton
id="tooltip-shortcut-trigger"
>Search</UiButton>
<UiTooltip
for="tooltip-shortcut-trigger"
:open="true"
placement="bottom"
>Search<template #shortcut>
<kbd>⌘K</kbd>
</template></UiTooltip>
</template>import { UiButton, UiTooltip } from "@threadlabs/looma";
export default function Example() {
return <>
<UiButton
id="tooltip-shortcut-trigger"
>Search</UiButton>
<UiTooltip
htmlFor="tooltip-shortcut-trigger"
open
placement="bottom"
slots={{
shortcut: <kbd>⌘K</kbd>,
}}
>Search</UiTooltip>
</>;
}/** @jsxImportSource preact */
import { UiButton, UiTooltip } from "@threadlabs/looma";
export default function Example() {
return <>
<UiButton
id="tooltip-shortcut-trigger"
>Search</UiButton>
<UiTooltip
htmlFor="tooltip-shortcut-trigger"
open
placement="bottom"
slots={{
shortcut: <kbd>⌘K</kbd>,
}}
>Search</UiTooltip>
</>;
}<script>
import { UiButton, UiTooltip } from "@threadlabs/looma";
</script>
{#snippet shortcut()}
<kbd>⌘K</kbd>
{/snippet}
<UiButton
id="tooltip-shortcut-trigger"
>Search</UiButton>
<UiTooltip
for="tooltip-shortcut-trigger"
open
placement="bottom"
slots={{ shortcut }}
>Search</UiTooltip>import { Component } from "@angular/core";
import { UiButton, UiTooltip } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiButton, UiTooltip],
template: `
<ng-template
ui-button
[hnAttributes]="{ id: 'tooltip-shortcut-trigger' }"
>Search</ng-template>
<span
ui-tooltip
[for]="'tooltip-shortcut-trigger'"
[open]="true"
[placement]="'bottom'"
>Search<ng-container ngProjectAs="[slot='shortcut']">
<kbd>⌘K</kbd>
</ng-container></span>
`,
})
export default class Example {}API
Package: @threadlabs/looma · Native root: span
Attributes
| Name | Description | Property | Type | Default | Allowed values |
|---|---|---|---|---|---|
for | ID of the control described and visually anchored by the tooltip. | for | string | | — |
hide-delay | Waits this many milliseconds before hiding after pointer exit. | hideDelay | number | 100 | — |
inverse | Use the high-contrast inverse surface (dark on light themes, light on dark themes). | inverse | boolean | false | — |
open | Whether the tooltip is open initially or when set externally. | open | boolean | false | — |
placement | Positions the description around its trigger. Start and end on top or bottom follow the writing direction. | placement | keyword | top | top, top-start, top-end, bottom, bottom-start, bottom-end, left, left-start, left-end, right, right-start, right-end |
show-delay | Waits this many milliseconds before showing after pointer entry. | showDelay | number | 500 | — |
trigger | How the trigger opens it. hover also opens on keyboard focus; click toggles it and suits a help button, where hovering a question mark says nothing; focus opens only on keyboard-visible focus. Action activation dismisses hover/focus hints without removing focus; a fresh hover or keyboard focus can open them again. | trigger | keyword | hover | hover, click, focus |
Framework props
| Name | Type | Default | Allowed values |
|---|---|---|---|
for | string | | — |
hideDelay | number | 100 | — |
inverse | boolean | false | — |
open | boolean | false | — |
placement | "top" | "top-start" | "top-end" | "bottom" | "bottom-start" | "bottom-end" | "left" | "left-start" | "left-end" | "right" | "right-start" | "right-end" | top | top, top-start, top-end, bottom, bottom-start, bottom-end, left, left-start, left-end, right, right-start, right-end |
showDelay | number | 500 | — |
trigger | "hover" | "click" | "focus" | hover | hover, click, focus |
Events
| Name | Detail | Description |
|---|---|---|
close | object
| Emitted when the component transitions to a closed state. |
open | object
| Emitted when the component transitions to an open state. |
Slots
| Name | Purpose |
|---|---|
default | Default child content. |
shortcut | Named content for the shortcut region. |
Design tokens
Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.
Component tokens
| Token | Declared values | Fallback |
|---|---|---|
--ui-tooltip-arrow-size | — | 0.625rem |
--ui-tooltip-border | — | var(--ui-overlay-border, var(--ui-border)) |
--ui-tooltip-inverse-surface | — | var(--ui-text-primary) |
--ui-tooltip-inverse-text | — | var(--ui-surface-default) |
--ui-tooltip-max-inline-size | — | 18rem |
--ui-tooltip-radius | — | var(--ui-overlay-radius, var(--ui-radius-sm)) |
--ui-tooltip-shadow | — | var(--ui-overlay-shadow, var(--ui-elevation-tooltip, var(--ui-shadow-sm))) |
--ui-tooltip-surface | — | var(--ui-overlay-surface, var(--ui-surface-elevated)) | var(--ui-material-content, var(--ui-overlay-surface, var(--ui-surface-elevated))) |
--ui-tooltip-text | — | var(--ui-text-primary) |
Shared tokens
| Token | Fallback |
|---|---|
--ui-border | — |
--ui-border-width | — |
--ui-elevation-tooltip | var(--ui-shadow-sm) |
--ui-font-family-sans | — |
--ui-font-normal | — |
--ui-font-size-2xs | — |
--ui-font-size-xs | — |
--ui-glassiness | 0 |
--ui-line-height-tight | — |
--ui-material-content | var(--ui-overlay-surface, var(--ui-surface-elevated)) |
--ui-motion-ease | — |
--ui-motion-fast | — |
--ui-overlay-border | var(--ui-border) |
--ui-overlay-radius | var(--ui-radius-sm) |
--ui-overlay-shadow | var(--ui-elevation-tooltip, var(--ui-shadow-sm)) |
--ui-overlay-surface | var(--ui-surface-elevated) |
--ui-radius-sm | — |
--ui-shadow-sm | — |
--ui-space-1 | — |
--ui-space-2 | — |
--ui-space-3 | — |
--ui-surface-default | — |
--ui-surface-elevated | — |
--ui-text-primary | — |