Tooltips
Plain and rich tooltips (COMPONENT_SPEC §33, Compose PlainTooltip / RichTooltip in a TooltipBox). A plain tooltip is a short description of its anchor; a rich one adds a subhead and an action. The <m3e-tooltip> element (m3e-elements) and <TooltipTrigger> (m3e-react) share one controller: hovering the anchor with a mouse shows the tooltip at once, and it stays while the anchor or the tooltip is hovered and hides 1.5 s after the pointer leaves both; a touch long press held for 400 ms (Android's default long-press timeout, which Compose reads) shows it until the press ends (at least 1.5 s), keyboard focus shows it until focus leaves, and Escape or a click outside hides it. Only one tooltip shows at a time. It is placed by Compose's TooltipPositionProviderImpl (above the anchor by default, flipped below at the window top, 4dp away) and scales in from 0.8 on the fast spatial spring while it fades in on the fast effects spring.
The anchor's aria-describedby points at the tooltip, unless the tooltip text equals the anchor's accessible name (an icon button labelled like its tooltip): the tooltip is then aria-hidden, so it is not read twice. A rich tooltip with an action is a non-modal dialog named by its subhead.
Variants (static, CSS only)
Without popover the m3e-css contract renders inline.
Custom element
An <m3e-tooltip anchor="…"> next to its anchor; its text content becomes the tooltip. position (above, below, left, right, start, end), caret, persistent (stays until a click outside or Escape), manual (only show() / hide() / open), and for variant="rich" a subhead and an action. Events: open-change with { open } and a cancelable action. Tab from the anchor moves into a tooltip with an action, which does not trap focus: Tab from its last control moves on to what follows the anchor, Shift+Tab from its first returns to the anchor, and it hides when focus leaves it unless it is persistent.
open-change bubbles in 1.x, like every state event: a listener on an element also hears the same event from an element nested inside it, such as a tooltip in a dialog or a time picker in a tab panel. Check the target first: if (e.target !== host) return;. DECISIONS D25 proposes that 2.0 stop bubbling state events; item events such as action keep bubbling.
React
A <TooltipTrigger> around an anchor (a component that forwards its ref) and a <PlainTooltip> or <RichTooltip subhead action onAction>. isPersistent, position, isOpen / defaultOpen / onOpenChange and isDisabled live on the trigger, caret on the tooltip.
CSS only
A popover with popovertarget toggles on click without JavaScript, placed above its anchor by CSS anchor positioning where supported and animated on the springs' linear() curves. Hover, focus and long press need the element or React layer.
Sources
androidx Compose Material3 1.5.0-beta01 (19991f2c): PlainTooltipTokens.kt and RichTooltipTokens.kt as read by Tooltip.kt (SpacingBetweenTooltipAndAnchor L1449, TooltipMinHeight L1451, TooltipMinWidth L1453 and the literals after them) and internal/BasicTooltip.kt (the long-press timeout, L221); values in components/tooltip-defaults.css (lines in PARITY.md).
The 400ms long press is Android's default long-press timeout, which Compose reads (internal/BasicTooltip.kt L221, AndroidViewConfiguration.android.kt L31–32; SOURCES register #71, DECISIONS D19 (10)).
m3.material.io Tooltips (tooltips "disappear 1.5 seconds after navigating away"). A rich tooltip with an action does not trap Tab (DECISIONS D21 (1), WCAG 2.1.2). SOURCES register #50.