Skip to main content
On this pageFunctions

Ui/Tooltip

Functions

init

functionsource
/** Creates an initial tooltip model from a config. Defaults to hidden. */
(config: InitConfig): Tooltip.Model

update

functionsource
/**
 * Processes a tooltip message and returns the next model, commands, and
 *  an optional OutMessage. `Shown`/`Hidden` fire only on `isOpen`
 *  transitions, so consumers don't get spurious events for messages that
 *  only update hover/focus/delay state without changing visibility.
 */
(
  model: Tooltip.Model,
  message: {
    _tag: "EnteredTrigger"
  } | {
    _tag: "LeftTrigger"
  } | {
    _tag: "FocusedTrigger"
  } | {
    _tag: "BlurredTrigger"
  } | {
    _tag: "PressedEscape"
  } | {
    _tag: "PressedPointerOnTrigger"
    button: number
    pointerType: string
  } | {
    _tag: "ElapsedShowDelay"
    version: number
  } | {
    _tag: "CompletedAnchorTooltip"
  }
): UpdateReturn

Types

InitConfig

typesource
/** Configuration for creating a tooltip model with `init`. */
type InitConfig = Readonly<{
  id: string
  showDelay: Duration.Input
}>

RenderInfo

typesource
/**
 * Render-time payload published to the consumer's `toView`.
 * 
 *  - `trigger`: attribute bundle for the trigger element. Carries the
 *    hover/focus/keyboard handlers + ARIA `aria-describedby` linking to
 *    the panel.
 *  - `panel`: attribute bundle for the panel element. Carries the
 *    `role="tooltip"`, the anchor Mount that positions the panel via
 *    Floating UI, and a `data-open` attribute when visible.
 *  - `isVisible`: derived state. The consumer decides whether to render
 *    the panel conditionally on this.
 */
type RenderInfo = Readonly<{
  isVisible: boolean
  panel: ReadonlyArray<ChildAttribute>
  trigger: ReadonlyArray<ChildAttribute>
}>

ViewInputs

typesource
/** Per-render view inputs passed to `view` via `h.submodel`'s `viewInputs` field. */
type ViewInputs = Readonly<{
  anchor: AnchorConfig
  isDisabled: boolean
  toView: (render: RenderInfo) => Html
}>

Constants

AnchorTooltip

constsource
/** The anchor-positioning Mount this Tooltip renders on its panel. */
const AnchorTooltip: MountDefinitionWithArgs<"AnchorTooltip", {
  anchor: Struct<{
    gap: optional<Number>
    offset: optional<Number>
    padding: optional<Number>
    placement: optional<Literals<readonly ["top", "right", "bottom", "left", "top-start", "top-end", "right-start", "right-end", "bottom-start", "bottom-end", "left-start", "left-end"]>>
    portal: optional<Boolean>
  }>
  buttonId: String
}, {
  _tag: "CompletedAnchorTooltip"
}>

BlurredTrigger

constsource
/** Sent when focus leaves the trigger. */
const BlurredTrigger: CallableTaggedStruct<"BlurredTrigger", {}>

CompletedAnchorTooltip

constsource
/** Sent when the tooltip panel mounts and Floating UI has positioned it. */
const CompletedAnchorTooltip: CallableTaggedStruct<"CompletedAnchorTooltip", {}>

ElapsedShowDelay

constsource
/** Sent when the show-delay timer fires. */
const ElapsedShowDelay: CallableTaggedStruct<"ElapsedShowDelay", {
  version: Number
}>

EnteredTrigger

constsource
/** Sent when the pointer enters the tooltip trigger. */
const EnteredTrigger: CallableTaggedStruct<"EnteredTrigger", {}>

FocusedTrigger

constsource
/** Sent when focus enters the trigger. */
const FocusedTrigger: CallableTaggedStruct<"FocusedTrigger", {}>

Hidden

constsource
/** Emitted once the tooltip transitions to hidden (`isOpen` becomes false). */
const Hidden: CallableTaggedStruct<"Hidden", {}>

LeftTrigger

constsource
/** Sent when the pointer leaves the tooltip trigger. */
const LeftTrigger: CallableTaggedStruct<"LeftTrigger", {}>

Message

constsource
/** Union of all messages the tooltip component can produce. */
const Message: S.Union<[typeof EnteredTrigger, typeof LeftTrigger, typeof FocusedTrigger, typeof BlurredTrigger, typeof PressedEscape, typeof PressedPointerOnTrigger, typeof ElapsedShowDelay, typeof CompletedAnchorTooltip]>

Model

constsource
/** Schema for the tooltip component's state. `isOpen` is visibility; `isHovered` tracks pointer on trigger; `isFocused` tracks tooltip-affirming focus on the trigger (focus arriving without a preceding mouse press, like keyboard, touch, or pen; mouse-click-induced focus is excluded since it doesn't affirm the user wants the tooltip visible); `isDismissed` suppresses re-opening after the user dismissed the tooltip (via Escape or left-click) until they disengage (leave or blur). `showDelay` is the hover-to-show duration. `maybeLastPointerType` records the most recent pointer type that pressed the trigger, so a mouse-click-induced focus can be distinguished from other focus. */
const Model: Struct<{
  id: String
  isDismissed: Boolean
  isFocused: Boolean
  isHovered: Boolean
  isOpen: Boolean
  maybeLastPointerType: Option<String>
  pendingShowVersion: Number
  showDelay: DurationFromMillis
}>

OutMessage

constsource
/** Union of out-messages the tooltip component can produce. */
const OutMessage: Union<readonly [CallableTaggedStruct<"Shown", {}>, CallableTaggedStruct<"Hidden", {}>]>

PressedEscape

constsource
/** Sent when Escape is pressed while the tooltip is visible. */
const PressedEscape: CallableTaggedStruct<"PressedEscape", {}>

PressedPointerOnTrigger

constsource
/** Sent when a pointer presses the trigger. */
const PressedPointerOnTrigger: CallableTaggedStruct<"PressedPointerOnTrigger", {
  button: Number
  pointerType: String
}>

ShowAfterDelay

constsource
/** Waits for the tooltip's show delay before emitting `ElapsedShowDelay`. */
const ShowAfterDelay: CommandDefinitionWithArgs<"ShowAfterDelay", {
  delay: DurationFromMillis
  version: Number
}, Effect<{
  _tag: "ElapsedShowDelay"
  version: number
}, never, never>>

Shown

constsource
/**
 * Emitted once the tooltip transitions to visible (`isOpen` becomes true).
 *  Consumers typically use this for analytics, instrumentation, or to
 *  coordinate with other transient UI.
 */
const Shown: CallableTaggedStruct<"Shown", {}>

reflectShowDelay

constsource
/**
 * Reflects an externally-sourced hover show-delay onto the model without
 *  emitting an OutMessage. Use to mirror an external config value (a user
 *  preference, a restored setting) onto the tooltip.
 */
const reflectShowDelay: Reflect<Model, Duration.Input>

view

constsource
/**
 * Renders a headless tooltip with an anchored non-interactive panel.
 *  Shows on hover (after delay) or focus (from keyboard, touch, or pen;
 *  mouse-click focus is excluded); hides on leave, blur, Escape, or
 *  left-click of the trigger.
 */
const view: SubmodelView<Tooltip.Model, {
  _tag: "EnteredTrigger"
} | {
  _tag: "LeftTrigger"
} | {
  _tag: "FocusedTrigger"
} | {
  _tag: "BlurredTrigger"
} | {
  _tag: "PressedEscape"
} | {
  _tag: "PressedPointerOnTrigger"
  button: number
  pointerType: string
} | {
  _tag: "ElapsedShowDelay"
  version: number
} | {
  _tag: "CompletedAnchorTooltip"
}, Readonly<{
  anchor: {
    gap: number
    offset: number
    padding: number
    placement: "bottom" | "left" | "right" | "top" | "top-start" | "top-end" | "right-start" | "right-end" | "bottom-start" | "bottom-end" | "left-start" | "left-end"
    portal: boolean
  }
  isDisabled: boolean
  toView: (render: RenderInfo) => Html
}>>

Stay in the update loop.

New releases, patterns, and the occasional deep dive.


Built with Foldkit.

© 2026 Devin Jameson