Describe the bug
Tooltip.Trigger closes an open tooltip on left click, but not on right click (or middle click), so a context menu raised from a tooltip trigger opens on top of a tooltip that stays put.
This looks like a one-line omission in the Radix port rather than a design decision. Radix's TooltipTrigger closes on pointerdown for any button:
// radix-ui/primitives — packages/react/tooltip/src/tooltip.tsx
onPointerDown={composeEventHandlers(props.onPointerDown, () => {
if (context.open) context.onClose();
isPointerDownRef.current = true;
document.addEventListener('pointerup', handlePointerUp, { once: true });
})}
TooltipTriggerState on main has the same handler with the same isPointerDown + pointerup bookkeeping, but without the close:
// packages/bits-ui/src/lib/bits/tooltip/tooltip.svelte.ts
#onpointerdown: PointerEventHandler<HTMLElement> = () => {
if (this.#isDisabled()) return;
this.#isPointerDown.current = true;
this.domContext.getDocument().addEventListener(
"pointerup",
() => {
this.handlePointerUp();
},
{ once: true }
);
};
#onclick still calls root.handleClose(), which is why left click works and the other buttons don't.
Two consequences beyond the visual overlap:
pointerdown fires before contextmenu, so closing there is what lets a trigger's own context menu open cleanly. Working around it from userland means listening for contextmenu on the trigger, which is later and does not cover middle click.
handleClose() also stops the pending open timer. A right click during the hover delay currently leaves the timer running, so the tooltip appears after the context menu has opened.
Suggested fix — mirror Radix:
#onpointerdown: PointerEventHandler<HTMLElement> = () => {
if (this.#isDisabled()) return;
const root = this.#getRoot();
if (root?.opts.open.current) root.handleClose();
this.#isPointerDown.current = true;
// …unchanged
};
Happy to open a PR if you'd like it. Worth deciding whether disableCloseOnTriggerClick should gate this too — Radix has no equivalent, and I'd lean towards it gating both so the prop keeps meaning "the trigger doesn't dismiss".
Reproduction
No scaffold needed — the docs demo reproduces it: https://bits-ui.com/docs/components/tooltip
- Hover the tooltip trigger in the first demo and wait for the tooltip.
- Right-click the trigger (or middle-click it).
Expected: the tooltip closes, as it does on left click.
Actual: it stays open, and the browser's native context menu opens over it.
For the pending-timer variant: hover the trigger and right-click it before the delay elapses — the tooltip appears afterwards, over the open menu.
System Info
System:
OS: macOS 26.5.2
CPU: (10) arm64 Apple M2 Pro
Shell: 5.9 - /bin/zsh
Binaries:
Node: 24.15.0
npm: 11.12.1
pnpm: 11.16.0
Browsers:
Chrome: 151.0.7922.76
Firefox: 142.0.1
Safari: 26.5.2
npmPackages:
@sveltejs/kit: 2.70.2 => 2.70.2
bits-ui: 2.18.1 => 2.18.1
svelte: 5.56.8 => 5.56.8
Severity
annoyance
Describe the bug
Tooltip.Triggercloses an open tooltip on left click, but not on right click (or middle click), so a context menu raised from a tooltip trigger opens on top of a tooltip that stays put.This looks like a one-line omission in the Radix port rather than a design decision. Radix's
TooltipTriggercloses onpointerdownfor any button:TooltipTriggerStateonmainhas the same handler with the sameisPointerDown+pointerupbookkeeping, but without the close:#onclickstill callsroot.handleClose(), which is why left click works and the other buttons don't.Two consequences beyond the visual overlap:
pointerdownfires beforecontextmenu, so closing there is what lets a trigger's own context menu open cleanly. Working around it from userland means listening forcontextmenuon the trigger, which is later and does not cover middle click.handleClose()also stops the pending open timer. A right click during the hover delay currently leaves the timer running, so the tooltip appears after the context menu has opened.Suggested fix — mirror Radix:
Happy to open a PR if you'd like it. Worth deciding whether
disableCloseOnTriggerClickshould gate this too — Radix has no equivalent, and I'd lean towards it gating both so the prop keeps meaning "the trigger doesn't dismiss".Reproduction
No scaffold needed — the docs demo reproduces it: https://bits-ui.com/docs/components/tooltip
Expected: the tooltip closes, as it does on left click.
Actual: it stays open, and the browser's native context menu opens over it.
For the pending-timer variant: hover the trigger and right-click it before the delay elapses — the tooltip appears afterwards, over the open menu.
System Info
System: OS: macOS 26.5.2 CPU: (10) arm64 Apple M2 Pro Shell: 5.9 - /bin/zsh Binaries: Node: 24.15.0 npm: 11.12.1 pnpm: 11.16.0 Browsers: Chrome: 151.0.7922.76 Firefox: 142.0.1 Safari: 26.5.2 npmPackages: @sveltejs/kit: 2.70.2 => 2.70.2 bits-ui: 2.18.1 => 2.18.1 svelte: 5.56.8 => 5.56.8Severity
annoyance