From 97ba035b68865775f75066ea74b3a5b763be7351 Mon Sep 17 00:00:00 2001 From: Andreas Reinhold / reini Date: Mon, 14 Sep 2026 17:09:33 +0200 Subject: [PATCH] Let a rich tooltip linger after the focus leaves too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plan step 36. The rewrite moved the rich tooltip's pointer-leave grace from 200ms to M3's 1.5s — kept: the reference gives plain and rich tooltips the same 1.5s, and ACT-25 names this file's 200ms. Its header claimed the same for the focus, which still hid the bubble at once; the focus now leaves on the same 1.5s the plain tooltip uses, and the skill says so. The stylesheet header no longer cites chip.css and fab-menu.css for a pulled-back actions row neither has. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9 --- .../skills/livewire-material-development/SKILL.md | 2 +- resources/css/components/rich-tooltip.css | 5 ++--- resources/js/rich-tooltip.js | 13 +++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/resources/boost/skills/livewire-material-development/SKILL.md b/resources/boost/skills/livewire-material-development/SKILL.md index b6c4ac51..8f1da507 100644 --- a/resources/boost/skills/livewire-material-development/SKILL.md +++ b/resources/boost/skills/livewire-material-development/SKILL.md @@ -449,7 +449,7 @@ A few lines of context around a trigger, with an optional `title` and `actions` ``` -Shows on hover and keyboard focus; `persistent` opens it on press and keeps it until a press elsewhere or Escape (use it when there are actions). The trigger is pointed at the bubble with `aria-describedby`, so its words are read out with the control. An open bubble stays open while the Livewire component around it renders, its actions' `wire:click` included. `side`: `bottom` (default), `top`, `left`, `right`. +Shows on hover and keyboard focus and goes 1.5s after the pointer or the focus leaves, as M3 times a plain tooltip too; `persistent` opens it on press and keeps it until a press elsewhere or Escape (use it when there are actions). The trigger is pointed at the bubble with `aria-describedby`, so its words are read out with the control. An open bubble stays open while the Livewire component around it renders, its actions' `wire:click` included. `side`: `bottom` (default), `top`, `left`, `right`. ### `` diff --git a/resources/css/components/rich-tooltip.css b/resources/css/components/rich-tooltip.css index c294b94e..02171a42 100644 --- a/resources/css/components/rich-tooltip.css +++ b/resources/css/components/rich-tooltip.css @@ -5,9 +5,8 @@ * RichTooltipTokens (androidx Compose Material 3, Apache-2.0): surface-container, the medium * corner, elevation 2, 312px wide at most, 16px either side, 12px above the subhead (or the text * when there is none) and 8px below it. Title-small subhead and body-medium text in - * on-surface-variant, label-large actions in primary — the actions row pulls itself back to the - * text's edge with a negative margin, the trick every text-button row in this package uses - * (chip.css, fab-menu.css). + * on-surface-variant, label-large actions in primary — the actions row pulls itself back 12px to + * the text's edge, as the alert's actions row does (alert.css). * * Placed by CSS anchor positioning on `data-md-side`, corner-to-corner so the 312px bubble has * room to spread past a narrow trigger; `position-try-fallbacks` flips it when the window has no diff --git a/resources/js/rich-tooltip.js b/resources/js/rich-tooltip.js index 81ffd712..986aeca8 100644 --- a/resources/js/rich-tooltip.js +++ b/resources/js/rich-tooltip.js @@ -1,11 +1,12 @@ /** * `materialRichTooltip`: shows an ``. * - * Transient (the default): like a plain tooltip — after a short hover on a pointer that can hover, - * at once on keyboard focus, standing for M3's 1.5s once the pointer or focus leaves (ACT-25) so - * the words can still be read while it moves on, and it stays while the pointer moves onto the - * bubble to reach its actions. Persistent: a press on the trigger opens it as a light-dismiss - * popover. + * Transient (the default): like a plain tooltip (tooltip.js) — after a short hover on a pointer + * that can hover, at once on keyboard focus, and standing for 1.5s once the pointer or the focus + * leaves, which M3 gives plain and rich tooltips alike (docs/reference/m3/ + * components-actions-communication-containment.md § Tooltips, ACT-25). The bubble is inside the + * wrapper, so moving onto it to reach its actions, by pointer or by Tab, is no leave at all; Escape + * hides it at once. Persistent: a press on the trigger opens it as a light-dismiss popover. * * The trigger is pointed at the bubble with `aria-describedby`, so the explanation — the whole * point of a rich tooltip — is read out with the control rather than never at all; a persistent @@ -73,7 +74,7 @@ document.addEventListener('alpine:init', () => { wrapper.addEventListener('pointerenter', (event) => event.pointerType === 'mouse' && show(HOVER_DELAY_MS)) wrapper.addEventListener('pointerleave', () => hide(LEAVE_GRACE_MS)) wrapper.addEventListener('focusin', (event) => event.target.matches(':focus-visible') && show(0)) - wrapper.addEventListener('focusout', (event) => !wrapper.contains(event.relatedTarget) && hide()) + wrapper.addEventListener('focusout', (event) => !wrapper.contains(event.relatedTarget) && hide(LEAVE_GRACE_MS)) document.addEventListener('keydown', (event) => event.key === 'Escape' && hide()) },