Skip to content

HabTooltip ​

Names an affordance whose icon is not self-evident.

Tab to these — the label appears on focus, not only on hover
vue
<HabTooltip label="Edit">
	<HabButton
		:icon="mdiPencil"
		icon-only
		aria-label="Edit"
	/>
</HabTooltip>

It appears on focus, not only on hover ​

The design contract listens for mouseenter and mouseleave only, and its own documentation admits the result: "it is invisible on touch and to keyboard users."

Wrapping reka-ui's Tooltip fixes that. It reveals on focus as well as hover, wires aria-describedby so the label is announced rather than merely seen, and dismisses on Esc.

Never the only copy ​

A tooltip is a repetition, not a disclosure. Anything that exists nowhere else must not live here: it is still unreachable on touch, it vanishes on scroll, and it cannot be selected or read at leisure.

That is also why an icon-only trigger needs its own aria-label as well as the tooltip — the two serve different readers, and HabButton warns when the label is missing.

Keep the label to a name, not a sentence. It stays on one line by design; wrapping is the signal that the content belongs somewhere else.

Anatomy ​

Inverted — an ink surface with page-colored text. That inversion is what separates a tooltip from every other sheet in the system, all of which sit on --hab-surface.

delay is the hover dwell before it appears. An app that wants one shared delay across many tooltips can wrap its root in reka's own TooltipProvider; nesting is harmless.

Placement ​

top (default), bottom, right or left.

A vertical rail wants a horizontal side. On a 60px icon rail a top tip lands over the row above it, so pick the side by the edge the rail is docked to: right for a rail on the left of the window, left for one on the right. Either way the tip opens into the page rather than at the window edge behind the rail.

The side is a preference, not a guarantee. reka flips a tip that will not fit to the opposite side, and data-side on the tip says where it actually went. That is a rescue on collision, though, not a placement strategy: it fires only when the tip genuinely does not fit, so a rail inset from the edge still gets a tip opening into the gutter unless you name the side the layout calls for.

offset is the gap in px between the tip and its trigger, default 8. Raise it where the trigger is inset from the edge a reader measures against: tips from a nav rail want to start clear of the rail, and a row sits inside the list's own padding, so its tip carries that inset back. Otherwise two tips from the same column of icons start 8px apart.

No label, no tooltip ​

With label unset the trigger renders on its own and no tooltip machinery is created. That is what lets a caller whose label is conditional — a nav row that names itself only while the rail is collapsed — pass undefined instead of duplicating its whole trigger across a v-if, or shipping an empty tip that appears on hover and says nothing.

The pass-through branch is a <slot>, so it is a fragment. A component whose own root is a HabTooltip therefore loses its single root element. Wrap the trigger a level up instead — that is why HabNavDrawer wraps HabNavItem rather than HabNavItem wrapping itself.

Accessibility ​

  • Rendered as two nodes, which is the right shape: a visually-hidden role="tooltip" holding the text aria-describedby points at, and a separate visible tip that carries no role and ignores the pointer. The announced text and the drawn text are the same string, but only one of them is a rendering concern.
  • The trigger slot is rendered as the trigger rather than wrapped.
  • Dismisses on Esc while focused.

API ​

Props ​

PropTypeDefaultDescription
delaynumber300Milliseconds before it appears on hover.
labelstringundefinedShort — a name, not a sentence.
offsetnumber8Gap in px between the tip and its TRIGGER. Raise it where the trigger is inset from the edge the reader measures against — tips from a nav rail want to start clear of the RAIL, and a row sits inside the list's own padding.
placement"bottom" | "top" | "left" | "right""top"A vertical rail wants a HORIZONTAL side: on a 60px icon rail a top tip lands over the row above it. Which horizontal side depends on the edge the rail is docked to — right for one on the left of the window, left for one on the right, so the tip opens into the page rather than at the edge behind the rail. A PREFERENCE, not a guarantee: reka flips a tip that will not fit. That is a rescue on collision, not a reason to name the side the layout does not want.

Slots ​

SlotBindingsDescription
defaultanyThe thing being labelled.

Proprietary — internal use only.