Let a tooltip linger, and tell the screen reader what it says

A plain tooltip vanished the instant the pointer left, where M3 keeps one for
1.5s and shows only one at a time — these are popover="manual", so nothing was
closing the last one. A rich tooltip's bubble was never associated with its
trigger at all, so the explanation it exists to give was never announced; the
script now writes aria-describedby (and aria-haspopup/aria-expanded when it is
persistent) onto the focusable control in the trigger slot, and again after a
morph. Plan step 18, actions.md ACT-22, ACT-25.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
Andreas Reinhold
2026-09-14 06:13:14 +02:00
co-authored by Claude Opus 5
parent 56d82b71f3
commit 05236ecb3a
5 changed files with 77 additions and 19 deletions
@@ -242,7 +242,7 @@ Label button, icon button, toggle and responsive FAB in one component.
### `<x-tooltip>`
M3's plain tooltip, standalone around any trigger: `<x-tooltip text="Copy link" side="bottom"><button>…</button></x-tooltip>`. `side`: `top` (default), `bottom`, `left`, `right`. Shows on hover (fine pointers) and keyboard focus; `aria-hidden`, so the trigger still needs its own accessible name. Buttons and FABs take a `tooltip` prop instead.
M3's plain tooltip, standalone around any trigger: `<x-tooltip text="Copy link" side="bottom"><button>…</button></x-tooltip>`. `side`: `top` (default), `bottom`, `left`, `right`. Shows on hover (fine pointers) and keyboard focus, and goes 1.5s after the pointer or the focus leaves it (M3's transient tooltip); only one is on screen at a time. It is `aria-hidden`, so the trigger has to carry the same words itself — as an icon button's `aria-label` does. Where the tip says something the trigger does not, use `<x-rich-tooltip>`, which points the trigger at its text. Buttons and FABs take a `tooltip` prop instead.
### `<x-menu>`, `<x-menu-item>`, `<x-menu-group>`, `<x-menu-separator>`
@@ -400,7 +400,7 @@ A few lines of context around a trigger, with an optional `title` and `actions`
</x-rich-tooltip>
```
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). 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; `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`.
### `<x-stat>`