Beta
Tooltip Group <mv-tooltip-group>
Tooltip, den sich eine Gruppe von Auslösern teilt, etwa eine Toolbar: Nach der ersten Verzögerung gleitet eine einzige Blase von Button zu Button, passt ihre Größe an und blendet ihr Label über, ohne Schließen, erneutes Öffnen oder zweites Warten.
| Kategorie | Overlays |
|---|---|
| Typ | Web Component (<mv-tooltip-group>) |
| Status | Beta |
| Keywords | tooltip, morphic-tooltip, shared-layout, toolbar, hint, glide, morph |
When to use
- A toolbar of icon buttons needs one tooltip that slides between triggers instead of reopening each time
- Users scan adjacent controls quickly and each label should appear instantly after the first delay
- A label changes in place after an action, such as Copy link becoming Link copied
Avoid when
Installation
node scripts/add.mjs tooltip-group --out ./src/marvelousKI-Agent mit dem MCP-Server von Marvelous UI: install_components({ slugs: ["tooltip-group"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).
Kopierte Dateien (inklusive Abhängigkeiten): tokens/tokens.css, core/base.css, core/dom.js, core/element.js, core/position.js, components/tooltip-group/tooltip-group.js, components/tooltip-group/tooltip-group.css.
Verwendung
Referenz-Markup zum Starten und Anpassen mit Attributen, data-* und CSS-Variablen:
<div id="tg-demo" style="display:flex;flex-wrap:wrap;gap:3rem 4rem;align-items:center;justify-content:center;padding-block:2.5rem">
<style>
#tg-demo .tg-bar { display: flex; align-items: center; gap: 2px; padding: 4px; border: 1px solid var(--mv-border); border-radius: var(--mv-radius-lg); background: var(--mv-surface-raised); box-shadow: var(--mv-shadow-sm); }
#tg-demo .tg-bar[aria-orientation="vertical"] { flex-direction: column; }
#tg-demo .tg-sep { flex: none; width: 1px; height: 1.25rem; margin: 0 4px; background: var(--mv-border); }
#tg-demo .tg-bar[aria-orientation="vertical"] .tg-sep { width: 1.25rem; height: 1px; margin: 4px 0; }
#tg-demo .tg-bar svg { width: 1rem; height: 1rem; }
#tg-demo .tg-cap { margin: .875rem 0 0; text-align: center; font-size: .75rem; color: var(--mv-fg-muted); }
</style>
<figure style="margin:0">
<mv-tooltip-group delay="400">
<div class="tg-bar" role="toolbar" aria-label="Text formatting">
<button class="mv-button" data-variant="ghost" data-size="icon" aria-label="Bold" data-tooltip="Bold" data-tooltip-kbd="⌘B"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 5h6a3.5 3.5 0 0 1 0 7H7zM7 12h7a3.5 3.5 0 0 1 0 7H7z"/></svg></button>
<button class="mv-button" data-variant="ghost" data-size="icon" aria-label="Italic" data-tooltip="Italic" data-tooltip-kbd="⌘I"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M14 5h5M5 19h5M15 5 9 19"/></svg></button>
<button class="mv-button" data-variant="ghost" data-size="icon" aria-label="Strikethrough" data-tooltip="Strikethrough" data-tooltip-kbd="⇧⌘X"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M4 12h16M16 6.5A4 3 0 0 0 12 5c-2.5 0-4 1.3-4 3M8 17.5A4 3 0 0 0 12 19c2.5 0 4-1.3 4-3"/></svg></button>
<span class="tg-sep" aria-hidden="true"></span>
<button class="mv-button" data-variant="ghost" data-size="icon" aria-label="Mention" data-tooltip-template="tg-demo-mention"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="4"/><path d="M16 8v5a3 3 0 0 0 5 0v-1a9 9 0 1 0-3.5 7.1"/></svg></button>
<button class="mv-button" data-variant="ghost" data-size="icon" aria-label="Copy link" data-tooltip="Copy link" id="tg-demo-copy"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M10 14a4 4 0 0 0 5.7 0l3-3a4 4 0 0 0-5.7-5.7l-1 1M14 10a4 4 0 0 0-5.7 0l-3 3a4 4 0 0 0 5.7 5.7l1-1"/></svg></button>
</div>
</mv-tooltip-group>
<template id="tg-demo-mention"><span style="display:grid;gap:2px"><strong>Mention someone</strong><span style="opacity:.72;font-weight:400">Type @ then a name, like @Aiko</span></span></template>
<figcaption class="tg-cap">One bubble slides from button to button</figcaption>
</figure>
<figure style="margin:0;display:flex;align-items:center;gap:1rem">
<mv-tooltip-group placement="right" delay="300">
<nav class="tg-bar" role="toolbar" aria-orientation="vertical" aria-label="Workspace">
<button class="mv-button" data-variant="ghost" data-size="icon" aria-label="Inbox" data-tooltip="Inbox: 3 unread"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 13h4l1.5 3h5L16 13h4M5 6h14l1 7v5H4v-5z"/></svg></button>
<button class="mv-button" data-variant="ghost" data-size="icon" aria-label="Calendar" data-tooltip="Calendar"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><rect x="4" y="5" width="16" height="15" rx="2"/><path d="M4 10h16M9 3v4M15 3v4"/></svg></button>
<span class="tg-sep" aria-hidden="true"></span>
<button class="mv-button" data-variant="ghost" data-size="icon" aria-label="Settings" data-tooltip="Settings" data-tooltip-kbd="⌘,"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M12 3v2.5M12 18.5V21M3 12h2.5M18.5 12H21M5.6 5.6l1.8 1.8M16.6 16.6l1.8 1.8M5.6 18.4l1.8-1.8M16.6 7.4l1.8-1.8"/></svg></button>
</nav>
</mv-tooltip-group>
<figcaption class="tg-cap" style="margin:0;max-width:9rem;text-align:start">Side rail: placement="right", resizing to each label</figcaption>
</figure>
<script type="module">
const copy = document.querySelector("#tg-demo-copy");
let timer = 0;
copy?.addEventListener("click", () => {
navigator.clipboard?.writeText("https://example.com/doc/roadmap-2027").catch(() => {});
copy.dataset.tooltip = "Link copied";
copy.closest("mv-tooltip-group")?.show(copy); // the press closed it: confirm in place
clearTimeout(timer);
timer = setTimeout(() => { copy.dataset.tooltip = "Copy link"; }, 1600);
});
</script>
</div>API
Attributes
| Name | Typ | Default | Description |
|---|---|---|---|
placement | top | bottom | left | right[-start|-end] | top | Default side (auto flip); override per trigger with data-placement. |
delay | number | 500 | Delay before the first appearance (ms). While the bubble is open, or just after any tooltip closed (mv-tooltip included), moving to another trigger is instant. |
close-delay | number | 160 | Grace period before closing when the pointer leaves a trigger (lets it cross the gap between two buttons). |
offset | number | 8 | Distance from the trigger (px). |
disabled | boolean | Disables the whole group. | |
data-arrow="false" | string | Hides the arrow. |
Properties
| Name | Typ | Description |
|---|---|---|
activeTrigger | HTMLElement | null | Trigger currently being described. |
isOpen | boolean | Whether the bubble is visible (read-only). |
Methods
| Name | Description |
|---|---|
show(trigger) | Shows the bubble on a trigger, sliding over if it is already open. |
hide() | Closes the surface. |
Events
| Name | Description |
|---|---|
mv-open | First appearance. detail: { trigger }. |
mv-change | The bubble slid to another trigger. detail: { trigger }. |
mv-close | After closing. |
Content structure
| Name | Description |
|---|---|
[data-tooltip] | Any descendant carrying this text becomes a trigger (delegated: elements added later work too). Changing it while open crossfades the label in place. |
[data-tooltip-kbd] | Shortcut shown to the right of the label. |
[data-tooltip-template="id"] | Rich content: a clone of a <template id> in the document. |
[data-placement] | Trigger-specific side. |
[data-tooltip-disabled] | Ignores this trigger. |
CSS variables
| Name | Default | Description |
|---|---|---|
--mv-tooltip-bg | Background (shared with mv-tooltip). | |
--mv-tooltip-fg | Text color (shared with mv-tooltip). | |
--mv-tooltip-group-duration | var(--mv-duration-normal) | Duration of the slide and resize between triggers. |
Accessibility
A single role=tooltip bubble, linked to the active trigger with aria-describedby (skipped when the label only repeats the trigger name). Opens at once on keyboard focus (:focus-visible), closes on Escape, on press and when focus or pointer leaves the group. The open bubble can be hovered without closing (WCAG 1.4.13). Never opens on touch. Outgoing labels are aria-hidden. Reduced motion makes the slide and crossfade instant; forced colors add an outline.