Skip to content

Tooltip ​

A simple text prompt bubble triggered on hover or keyboard focus.

Basic usage ​

Trigger on hover

Hover to view

Placement ​

Four directions

UpDownLeftRight

When space is insufficient, the tooltip automatically flips along the main axis and avoids the viewport edges.

Arrow ​

By default an arrow pointing at the trigger element's edge is shown; arrow="false" hides the arrow; arrow-point-at-center makes the arrow point at the trigger element's center (when the panel is shifted by viewport-edge avoidance, the arrow still points at the anchor center).

Arrow visibility and pointing

DefaultNo arrowPoint at center

arrow-position="merge" merges the arrow with the panel corner into a right triangle (only for *-start / *-end placements).

Arrow merge mode

bottom-start + merge

Arrow shape is customized via CSS variable tokens (width/height independent — the aspect ratio is the triangle's height and angle):

Arrow shape tokens (width / height / align offset / radius)

Wide 22×8Narrow 8×20align-offset 32

--oas-tooltip-arrow-width / --oas-tooltip-arrow-height (default 12px each, falling back to the existing --oas-tooltip-arrow-size); --oas-tooltip-arrow-align-offset (offset on the *-start / *-end aligned side, default 16px — semantically different from the arrow-offset attribute of arrow-position="side", which is the side mode's edge inset); --oas-tooltip-arrow-radius (arrow border-radius, default 0). The merge form is an independent 8px geometry and is not affected by these tokens.

Viewport auto adjust ​

By default the tooltip automatically flips along the main axis and avoids the viewport edges when space is insufficient; auto-adjust-overflow="false" disables the auto adjust so the panel keeps the declared placement (it may overflow the viewport).

Auto adjust disabled

Auto adjust off

Focus trigger ​

Trigger on keyboard focus

Focus me with Tab

Controlled display ​

The open attribute is controlled: an external button can set/remove open to show/hide the tooltip (hover/focus triggers still apply in addition).

Controlled display (open attribute)

ShowHideopen: falseTrigger element

Long text ​

Long text and max width

Hover to view long hint

Virtual trigger ​

Virtual mode (virtual) does not bind to a host trigger element: open is fully controlled externally, and the position is set by virtual-anchor (an anchor element selector) or virtual-x / virtual-y (viewport coordinates). placement still applies. It suits scenarios where a normal trigger element is impossible, such as chart points or floating hints during drag.

Virtual anchor follow (chart points)

Month →

Hover any point to see the hint (the tooltip is anchored to that point).

Coordinate follow (mouse move)

Move the mouse inside this area Not following

Triggers ​

The trigger attribute supports space-separated multi-selection: hover / focus / click / contextmenu / touch (long press) / manual (fully controlled). Defaults to hover focus touch — long press works out of the box on touch screens (mouse pointers are filtered automatically; the hold duration is tuned by touch-delay). On coarse pointers (touch screens), hover automatically degrades to tap to toggle: tap to open, tap again or tap elsewhere to close (desktop hover behavior is unchanged).

Click trigger

Click trigger

Context menu trigger

Right-click me

Manual trigger

ShowHideControlled trigger

Show / hide delay ​

open-delay / close-delay control the delay (ms) before showing/hiding on hover, avoiding accidental triggers when moving across quickly. Keyboard focus opens immediately without waiting for open-delay (keyboard users have already positioned themselves; a wait adds no information).

Open and close delays

300ms delayed show

When moving quickly across multiple triggers, skip-delay-duration (default 300ms) makes the next tooltip skip its open-delay and appear immediately, keeping the interaction responsive.

Delay group (skip-delay-duration)

Hover me (moving from the previous one shows instantly)Then hover me

On touch devices a long press shows the hint (touch is included by default; touch-delay controls the hold duration, default 500ms; desktop mouse long presses are filtered).

Touch long-press trigger

Long-press me (touch)

Tap to toggle on touch (automatic on coarse pointers)

Tap me (touch)

On touch devices (pointer: coarse) the hover channel is disabled — a tap synthesizes hover/focus/click events at once, and keeping desktop hover semantics would close the tip immediately after opening ("open-and-shut"). After degradation the tap takes over toggling, consistent with the click trigger: once open, tap anywhere outside (or press Esc) to close.

Rich content ​

The content attribute shows plain text; for rich content (links, icons, multiple lines) use the slot="content" slot. The slot takes precedence over the attribute text.

If the rich content holds interactive elements (links, etc.), combine interactive (hoverable popup — entering it keeps it open) with close-delay (time for the pointer to travel from the anchor into the popup) — this is also the recommended shape for WCAG 1.4.13 "hoverable".

Rich content slot

Hover for rich contentKey hintCan include a link or icons

interactive + close-delay combo

Hover then move into the popup to clickMoving the pointer into this popup keeps it openA clickable link

Keyboard accessibility ​

While open, pressing Esc closes the tooltip and restores focus to the trigger; while open, the trigger is linked to the popup via aria-describedby (readable by screen readers).

Esc close + aria-describedby

Focus and press Esc

trigger-keys lets you specify keys (space-separated) that open the tooltip when focused, e.g. trigger-keys="F1".

trigger-keys open

Focus and press F1

Max width ​

The default max width is 240px (opened via the --oas-tooltip-max-width token); the max-width attribute overrides it (number or CSS length).

Custom max width

max-width=360

Disabled ​

With disabled, the tooltip never shows (neither hover nor a controlled open).

Disabled

Disabled tooltip

Hoverable popup ​

interactive keeps the popup open while hovering it (links inside stay reachable).

Interactive popup

Interactive

Offset and collision ​

offset controls the main-axis distance (default 10px), skidding the cross-axis offset, and collision-padding the viewport-edge avoidance margin (default 4px).

offset / skidding

offset=16skidding=24

collision-padding

collision-padding=20

Color variants ​

The color attribute supports semantic colors (primary / success / warning / danger), the 11 preset names (e.g. magenta, blue), or any CSS color. All of them go through tokens (with dark variants).

Color variants

primarysuccesswarningdanger

Mount point ​

append-to mounts the popup into the given container (body or a CSS selector), escaping clipping contexts such as an overflow: hidden ancestor.

append-to body

Mount to body

Auto close ​

auto-close (ms) closes the tooltip automatically after it opens — useful for onboarding hints.

Auto close

Click, then it auto-closes

Fresh content ​

fresh is enabled by default: content changes are synced immediately even while closed (reopening always shows the latest). fresh="false" freezes the content while closed.

Fresh content sync

Hover to view (buttons below change the content)Change content

Scroll follow and close on scroll ​

While open, a normal anchor is also repositioned in real time on scroll / window resize (scroll capture + rAF throttling), so the popup no longer detaches from the anchor; close-on-scroll closes on scroll instead (for compact hints that should not chase the page).

Scroll follow

Scroll the box: while open, the tooltip follows the button

Keep following on scroll

close-on-scroll

Open the hint by hovering, then scroll the container — it closes immediately

Close on scroll

Click trigger with outside dismiss ​

When trigger includes click / contextmenu, pressing anywhere outside the popup and the trigger closes it (light dismiss) — no need to click the trigger again. Pure hover/focus/touch triggers or a controlled open do not install outside dismiss.

click + outside dismiss

Click triggerclosed

Click to open, then click anywhere outside the popup / button (watch the status tag).

Accessible name semantics (label) ​

a11y switches the tooltip's semantic role: the default description puts aria-describedby on the trigger pointing to the popup (supplementary description); label uses aria-labelledby instead — for icon-only triggers without their own text, the hint text acts as the accessible name. On close the trigger's original aria value is restored, never clobbering host-set attributes.

label accessible name

☆ FavoriteToggle openaria: -

While open the button gets aria-labelledby pointing to the popup (accessible name = hint text); it is restored on close.

Arrow side mode and arrow-offset ​

arrow-position="side" snaps the arrow to the half of the panel that contains the anchor-center projection (when the panel is shifted by viewport avoidance, the arrow hugs the matching side instead of staying centered). arrow-offset (px, only effective in side) controls the arrow's inset from the panel end, default 4px (a safe amount that keeps it out of the rounded corners).

Arrow side and offset

side arrowarrow-offset=16

Scroll the trigger to a viewport edge before opening to see the side arrow hug the anchor side.

Animation customization (CSS variable hooks) ​

The entry animation duration / easing / name are CSS variables (set them on the <oas-tooltip> element — custom properties cascade into the shadow): --oas-tooltip-duration (default 0.15s), --oas-tooltip-easing (default ease), --oas-tooltip-animation (default oas-tooltip-in; none disables the entry animation). Under prefers-reduced-motion: reduce the animation is always off.

Duration / easing / animation toggle

Custom duration & easingDisable entry animation

Smart placement and collision boundary ​

  • placement="auto" (also auto-start / auto-end): picks the roomiest side on open;
  • fallback-placements: a fallback sequence (space/comma-separated 12-direction placements) — when the primary side does not fit, the first fitting candidate wins;
  • collision-boundary: swap the flip/avoid boundary from the viewport to a given element rect (e.g. a scroll container's visible area; pair with append-to to avoid clipping);
  • fallback-axis-side: cross-axis fallback when both main-axis sides are tight (start / end, default none).

auto placement and fallback sequence

auto placementFallback sequence

collision-boundary = scroll container

collision-boundary points at this container: the hint flips/avoids within its visible area

Bound to container

Cross-axis fallback (fallback-axis-side)

fallback-axis-side=start

Follow cursor ​

follow-cursor: while open, moving the cursor inside the trigger area repositions the popup to follow the cursor in real time (reuses the viewport-coordinate channel, rAF throttled). Good for large hover surfaces.

follow-cursor

Move the mouse inside this area — the hint follows

Width following the trigger ​

width="trigger" makes the popup as wide as the trigger (a number or CSS length sets the width directly); the width is still capped by --oas-tooltip-max-width (default 240px), which is adjustable.

width customization

width=trigger hintwidth=320

Event source (oas-open-change) ​

oas-open-change's detail now carries { open, source, reason }: source is the trigger channel (hover / focus / click / contextmenu / touch / key / escape / outside / scroll / auto-close / attribute), reason refines the cause (e.g. escape-key / outside-pointer / timeout / long-press). External controlled setAttribute paths report attribute.

Event source feedback

Event source

Not triggered yet

Edge cases ​

Empty content

Tooltip without content

API ​

oas-tooltip ​

Attributes ​

AttributeDescriptionTypeDefault
a11yA11y association mode: description (default, the trigger gets aria-describedby pointing at the popup) / label (gets aria-labelledby as its accessible name); the host's original value is restored on closestringdescription
append-toPopup mount point: body or a CSS selector. Moves the popup into an isolated shadow inside the target container (styles stay scoped), escaping clipping contexts such as overflow: hidden / transform; while mounted, ::part(tip) cannot pierce from the host — customize via CSS variables or class selectorsstring—
arrowWhether to show the arrow (default true; arrow="false" hides it, the element and ::part(arrow) are kept)stringtrue
arrow-offsetArrow inset from the panel edge (px, default 4): only effective with arrow-position="side" (ignored for center/merge)——
arrow-point-at-centerMake the arrow point at the trigger element's center (default points at the trigger's edge; the arrow still points at the anchor center when the panel is shifted by viewport-edge avoidance)boolean—
arrow-positionArrow shape: center (default, arrow centered on the panel edge) / merge (only for *-start/*-end placements; a right triangle merges flush with the panel corner — legs collinear with the panel edges, tip pointing orthogonally toward the anchor)stringcenter
auto-adjust-overflowViewport-edge auto flip and avoidance (default true; "false" disables it, keeping the declared placement, which may overflow the viewport)stringtrue
auto-closeAuto-close after opening (ms); 0 or absent disables it——
close-delayHide delay (ms, default 0): close after mouseleave/focusout——
close-on-scrollClose as soon as the page scrolls (listened while open, the first scroll hit closes; resize only repositions)boolean—
collision-boundaryCollision boundary element selector: flips/clamps against this element rect instead of the viewport (the collisionBoundaryEl property channel takes precedence)Element | null—
collision-paddingViewport-edge avoidance margin (px, default 4): the distance kept from the edge when the popup is clamped——
colorColor variant: semantic primary/success/warning/danger, one of the 11 preset names (e.g. magenta, blue), or any CSS color. All go through tokens (with dark variants); the arrow background followsstring—
contentTooltip content text (the slot="content" rich content takes precedence when present)string—
disabledDisabled: the tooltip never shows (neither hover nor a controlled open)boolean—
fallback-axis-sideCross-axis fallback when neither main-axis side fits: start (switch to the cross-axis start side, left/top) / end (right/bottom); none by default (keeps opposite-side flip + viewport clamping)stringnone
fallback-placementsCustom fallback sequence (space/comma-separated 12-direction placements): when the requested placement does not fit, each entry is tried in order and the first fit wins; if none fit, the last entry is used with viewport clamping; unset uses the default main-axis flipstring—
follow-cursorFollow the cursor: once open, positioned at the cursor coordinates (zero-size point); cursor movement is tracked with rAF throttlingboolean—
freshContent freshness (default true): content changes are synced immediately even while closed; "false" freezes the content while closed, updating on next openstringtrue
interactiveHoverable popup: moving the mouse into the popup keeps it open (pointer-events: auto), links inside stay reachableboolean—
max-widthPopup max width (number in px or CSS length; defaults to the --oas-tooltip-max-width token, 240px)string—
offsetMain-axis distance (px, default 10): the gap between the popup and the anchor along the main axis——
openControlled display (boolean attribute; shows when present)boolean—
open-delayShow delay (ms, default 0): open after mouseenter/focusin; skipped when skip-delay-duration hits——
placementPopup placement (12 directions: top/bottom/left/right × start/center/end)stringtop
skiddingCross-axis offset (px, default 0): top/bottom placements shift horizontally (positive right, negative left); left/right placements shift vertically (positive down, negative up)——
skip-delay-durationGlobal delay-group threshold (ms, default 300): when a tooltip closes, the next one opened within this window skips its open-delay and shows immediately (responsive for consecutive hovers); "0" disables it——
touch-delayTouch long-press trigger duration (ms, default 500): with touch in trigger, pointerdown held to the threshold opens; releasing/moving out earlier cancels——
triggerTrigger modes (space-separated multi-select): hover / focus / click / contextmenu / touch / manual, default hover focus touch; manual is fully controlledstringhover focus touch
trigger-keysSpecified keys (space-separated, e.g. F1): pressing one while the trigger is focused opens the tooltipstring—
virtualVirtual trigger mode: not bound to a host trigger element; open is fully controlled externally and the position is set by virtual-anchor or virtual-x/virtual-y (for chart points, floating hints during drag)boolean—
virtual-anchorAnchor element selector (e.g. #chart-point-1); the tooltip is positioned by that element's rect. Mutually exclusive with virtual-x/virtual-y (coordinates take precedence)——
virtual-xVirtual anchor viewport X coordinate (px, e.g. mouse clientX); position by coordinates when set together with virtual-y——
virtual-yVirtual anchor viewport Y coordinate (px, e.g. mouse clientY); position by coordinates when set together with virtual-x——
widthPopup width: a number (px) / "trigger" (same width as the trigger) / any CSS value; still capped by --oas-tooltip-max-width (default 240px)string—

Events ​

EventDescription
oas-open-changeFired when the open state changes (show/hide), detail: { open, source, reason }

Slots ​

NameDescription
defaultTrigger element (hover/focus trigger); optional in virtual mode
contentRich content (takes precedence over the content attribute text when present)

CSS Variables ​

CSS VariableDefault
--oas-glass-blurnone
--oas-glass-ringtransparent
--oas-tip-cross999px
--oas-tooltip-animationoas-tooltip-in
--oas-tooltip-arrow-align-offset16px
--oas-tooltip-arrow-heightvar(--oas-tooltip-arrow-size, 12px)
--oas-tooltip-arrow-radius0
--oas-tooltip-arrow-widthvar(--oas-tooltip-arrow-size, 12px)
--oas-tooltip-bgvar(--oas-color-text-primary)
--oas-tooltip-colorvar(--oas-color-bg)
--oas-tooltip-duration0.15s
--oas-tooltip-easingease
--oas-tooltip-max-width240px

oas-open-change: fired when the open state changes (show/hide), detail: { open, source, reason } (source is the trigger channel, reason the concrete cause; for external controlled attribute writes source is attribute). Shown/hidden on hover or focus; role="tooltip", the popup uses pointer-events: none so it never blocks interactions.

Released under the MIT OR Apache-2.0 License.