ArenaTooltip.prompt.md
August 16, 2026 ยท View on GitHub
Arena tooltip, a short label revealed on pointer intent, bone over dark. Standalone, OnPush,
signal I/O. Wrap the element the tooltip describes; Arena draws the bubble and names it
from label.
The bubble is positioned by @angular/cdk/overlay, not by the wrapper, so it escapes an
overflow: hidden ancestor and stays anchored while the page scrolls. That is the one thing
Arena does not hand-roll here; focus and roles stay Arena's. The app must import
frameworks/angular/theme/arena-cdk.css once, or the bubble renders unpositioned.
<arena-tooltip label="Everything shipped in the last 30 days">
<button type="button">Deployments</button>
</arena-tooltip>
<arena-tooltip label="95th percentile response time">
<span tabindex="0">p95</span>
</arena-tooltip>
Members, in contract order and under this layer's own names. * marks a required one.
| Member | Form | Type | Default | What it is |
|---|---|---|---|---|
label* | primitive | string | The bubble's text. Arena draws the bubble; the consumer names it. | |
content* | slot | The element the tooltip describes and attaches to. |
Do / Don't
- Project exactly one element, and one that can take an attribute. Arena writes
aria-describedbyonto the wrapper's first element child, so a bare string or several siblings leave the bubble naming nothing. The trigger's ownaria-describedbyis preserved: the bubble's id is appended on reveal and removed on withdrawal. - Project something focusable. A tooltip on an unfocusable element is unreachable without a
pointer, give it
tabindex="0"if it is not natively focusable, as thep95example does. - A pointer waits
--delay-open(400 ms) before the bubble appears and--delay-close(120 ms) before it withdraws, so crossing a toolbar reveals nothing. Keyboard focus reveals immediately, and that asymmetry is deliberate: a delay on focus reads as an unresponsive control.contracts/design/behaviour.jsonstates the rule; do not route focus through the delays. - Escape dismisses, and the listener is on the document rather than the host, because a pointer-invoked tooltip leaves focus somewhere else entirely.
- Don't put interactive content in a tooltip. It never receives focus, so a button inside it is
unreachable: that is
focus.neverin the pattern, not a limitation of this implementation. Reach forarena-menuor a dialog instead. - Don't use a tooltip to carry information the user needs to complete a task. It is
supplementary by definition; a field's own
hintis where a requirement belongs. - Don't set
labelto the trigger's own text.aria-describedbyis read in addition to the name, so a bubble repeating the label just says everything twice.
By hand, in real Chromium: none of these is provable in happy-dom. Run bun run demos and
open /frameworks/angular/components/feedback/arena-tooltip/ArenaTooltip.demo.generated.html, which lays out one
section per item below:
- The bubble sits centred above the trigger with a
--sp-2gap, and flips below when the trigger is near the top of the viewport. - It escapes a scrolling
overflow: hiddencontainer and stays anchored while that container scrolls, which is the whole reason for the overlay. - The reveal genuinely waits on pointer rest and is instant on Tab; travelling from the trigger onto the bubble does not dismiss it.
- Layering: a tooltip on an
arena-menuitem, and one inside an open dialog, both land above what they annotate.
The rules of the language hold in the code you write from this page, and no gate reads your application to enforce them. An Arena component is not a styling surface: put no class of your own on it, read every value through its token rather than a raw colour or a bare 16px, and never wrap it in your router's own link. The rest of the rules are in ../../../../../skills/design/SKILL.md.