On this pageOverview
Tooltip
A non-interactive floating label anchored to a trigger. Tooltips appear on hover after a short delay, or immediately on keyboard focus. They hide on pointer-leave, blur, Escape, or left-click of the trigger. Use tooltips for short hints about a control. For rich content or interactive panels, use Popover instead.
The positioning engine is shared with Popover and Menu — pass anchor to control placement and spacing.
See it in an app
Check out how Tooltip is wired up in a real Foldkit app.
Hover or tab into the trigger to reveal the tooltip. Hover waits for showDelay (default 500ms); keyboard focus shows it immediately.
// Pseudocode walkthrough of the Foldkit integration points. Each labeled
// block below is an excerpt — fit them into your own Model, init, Message,
// update, and view definitions.
import { Effect } from 'effect'
import { Command, Ui } from 'foldkit'
import { m } from 'foldkit/message'
import { evo } from 'foldkit/struct'
import { Class, span } from './html'
// Add a field to your Model for the Tooltip Submodel:
const Model = S.Struct({
tooltip: Ui.Tooltip.Model,
// ...your other fields
})
// In your init function, initialize the Tooltip Submodel with a unique id:
const init = () => [
{
tooltip: Ui.Tooltip.init({ id: 'save-button' }),
// ...your other fields
},
[],
]
// Embed the Tooltip Message in your parent Message:
const GotTooltipMessage = m('GotTooltipMessage', {
message: Ui.Tooltip.Message,
})
// Inside your update function's M.tagsExhaustive({...}), delegate to Tooltip.update:
GotTooltipMessage: ({ message }) => {
const [nextTooltip, commands] = Ui.Tooltip.update(model.tooltip, message)
return [
// Merge the next state into your Model:
evo(model, { tooltip: () => nextTooltip }),
// Forward the Submodel's Commands through your parent Message:
commands.map(
Command.mapEffect(Effect.map(message => GotTooltipMessage({ message }))),
),
]
}
// Inside your view function, render the tooltip:
Ui.Tooltip.view({
model: model.tooltip,
toParentMessage: message => GotTooltipMessage({ message }),
anchor: { placement: 'top', gap: 6, padding: 8 },
triggerContent: span([], ['Save']),
triggerClassName: 'rounded-lg border px-3 py-2 cursor-pointer',
content: span([], ['Save your changes (⌘S)']),
panelClassName:
'rounded-md bg-gray-900 px-3 py-1.5 text-sm text-white shadow-lg',
})Tooltip is headless — the trigger and panel are both styled through className and attribute props. The panel is rendered with pointer-events: none so it never captures hover or clicks, which keeps the open/close logic tied to the trigger.
| Attribute | Condition |
|---|---|
data-open | Present on trigger and panel when the tooltip is visible. |
data-disabled | Present on the trigger when disabled. |
| Key | Description |
|---|---|
| Escape | Hides the tooltip while visible. It will not reopen until the user disengages by moving the pointer away or blurring the trigger. |
The panel has role="tooltip" and the trigger is linked via aria-describedby. Focus is never moved into the tooltip, so assistive technology announces the panel contents as a description of the trigger.
Configuration object passed to Tooltip.init().
| Name | Type | Default | Description |
|---|---|---|---|
id | string | - | Unique ID for the tooltip instance. |
showDelay | Duration.DurationInput | Duration.millis(500) | How long the pointer must hover before the tooltip appears. Accepts any Effect Duration input — a bare number is interpreted as milliseconds. Keyboard focus shows the tooltip immediately regardless of this value. |
Configuration object passed to Tooltip.view().
| Name | Type | Default | Description |
|---|---|---|---|
model | Tooltip.Model | - | The tooltip state from your parent Model. |
toParentMessage | (childMessage: Tooltip.Message) => ParentMessage | - | Wraps Tooltip Messages in your parent Message type for Submodel delegation. |
anchor | AnchorConfig | - | Floating positioning config: placement, gap, and padding. Required. |
triggerContent | Html | - | Content rendered inside the trigger button. |
content | Html | - | Content rendered inside the tooltip panel. |
triggerClassName | string | - | CSS class for the trigger button. |
triggerAttributes | ReadonlyArray<Attribute<Message>> | - | Additional attributes for the trigger button. |
panelClassName | string | - | CSS class for the panel. |
panelAttributes | ReadonlyArray<Attribute<Message>> | - | Additional attributes for the panel. |
isDisabled | boolean | false | Disables the trigger. Hover, focus, and keyboard events are ignored and the tooltip will not open. |
Helper functions for driving the tooltip from parent update handlers, returning [Model, Commands].
| Name | Type | Default | Description |
|---|---|---|---|
setShowDelay | (model: Model, showDelay: Duration.DurationInput) => [Model, Commands] | - | Updates the hover show-delay. Accepts any Effect Duration input (a bare number is interpreted as milliseconds). Applies to subsequent hovers; any in-flight delay is invalidated. |