J3W1 / DESIGN SYSTEM
Tooltip
A short description of a control shown on hover and focus; role tooltip linked by aria-describedby, a 1px overlay border on the raised surface, no arrow.
stable · LIGHT DOM · 1.1.0
Light DOM retains static content, native semantics, label relationships and host composition without duplicating controls.
Live variants
default
<j3w1-tooltip id="example-tooltip-default"><div class="tooltip">
<button class="tooltip-trigger" type="button" aria-describedby="example-tooltip-default-tt-reload">Reload</button>
<div class="tooltip-popup" role="tooltip" id="example-tooltip-default-tt-reload">Reload the window and keep the layout.</div>
</div></j3w1-tooltip>with-shortcut
<j3w1-tooltip id="example-tooltip-with-shortcut"><div class="tooltip">
<button class="tooltip-trigger" type="button" aria-describedby="example-tooltip-with-shortcut-tt-split">Split</button>
<div class="tooltip-popup" role="tooltip" id="example-tooltip-with-shortcut-tt-split">Split the focused container horizontally <kbd class="tooltip-shortcut">Mod+h</kbd></div>
</div></j3w1-tooltip>Install and register
Use the exact version. If npm publication is pending, install the release tarball from the installation guide.
npm install --save-exact @j3w1/ui@1.1.0import '@j3w1/ui/tokens.css';
import '@j3w1/ui/styles/tooltip.css';
import '@j3w1/ui/register/tooltip';Insert the maintained markup above after loading these imports. Its native children provide accessible names and form behavior; the empty custom tag alone does not create a control.
Copy the complete implementation
npx --package=@j3w1/ui@1.1.0 j3w1-ui copy tooltip --out ./vendor/j3w1/tooltipThe CLI verifies every file before writing and refuses overwrites. Serve the output over HTTP and open index.html. Keep its modules, CSS, tokens and licence notices together.
Public API
attributes
| Name | Type / signature | Meaning |
|---|---|---|
disabled | CustomEvent | Disable the native controls owned by this instance. |
loading | CustomEvent | Mark the primary control busy and prevent duplicate activation. |
properties
| Name | Type / signature | Meaning |
|---|---|---|
disabled | boolean | Delegates disabled state to native children. |
name | string | Native control name; application form keys remain explicit. |
open | boolean | Current tooltip visibility. Read only. |
methods
| Name | Type / signature | Meaning |
|---|---|---|
refresh | (): void | Reconnect behavior after replacing native child structure. |
focus | (options?: FocusOptions): void | Focus the first usable native control. |
show | (): void | Show the description unless dismissed in this hover/focus session. |
hide | (): void | Hide and cancel pending timers. |
events
| Name | Type / signature | Meaning |
|---|
Dependencies and verification
Registered components: none. Style dependencies: none.
Package presence establishes an available implementation. Actual passes depend on the tested artifact and environment. Read specification execution evidence; read packed-consumer evidence; inspect the complete state matrix.
Full implementation manifest, API and token dependency closure
Canonical specification
Purpose
A few words that explain a control without being its name: what an icon button does, which shortcut opens it, why it is disabled. It is a description only. Anything a person must read to use the control belongs in the control or its label, and anything interactive is a popover.
Anatomy
The trigger and the popup. The popup is color.surface.raised behind a 1px
color.border.overlay, text in color.text.default, optional shortcut in a
kbd with color.text.bright inside a 1px color.border.control; no arrow,
no shadow, no radius. The demonstration renders it in flow below the trigger.
States
| State | Visual | Non-colour channel |
|---|---|---|
| default | trigger at rest; the popup shown for the demonstration | aria-describedby on the trigger |
| hover | trigger background → color.action.tertiary.hover-bg; the popup appears after 300 ms | cursor: pointer; the popup itself |
| focus-visible | ring 1px dashed color.interaction.focus.ring at −2px on the trigger; the popup appears at once | the ring; the popup |
| open | the popup visible, 1px color.border.overlay on color.surface.raised | the popup is present in the accessibility tree |
| closed | the popup absent; the trigger unchanged | hidden; the description is still read from the DOM |
The popup itself has no hover, pressed or focus state: it is not interactive.
Keyboard
Focus on the trigger shows the tooltip without delay; moving focus away hides it. Escape hides it and does nothing else, so a dialog behind the trigger does not close. The popup never receives focus and contains no focusable element.
Accessibility
The trigger points at the popup with aria-describedby, so the description
is available whether or not the popup is shown; the popup is role="tooltip".
It opens on hover and on focus, stays open while the pointer is over the
trigger or the popup (WCAG 1.4.13: hoverable, dismissible, persistent), and
closes on Escape. The trigger has its own accessible name; the tooltip never
supplies it. Touch has no hover: the description is available through a
long-press or is shown inline. Contrast: text 8.43:1 on the raised surface,
shortcut 10.10:1, border 4.57:1.
Portability
A box with a border and text; placement is the host’s positioning primitive. Toolkits with a native tooltip use it with the tokens for border and background and record any radius they cannot remove.
Non-examples
A rounded bubble with an arrow. A tooltip with a shadow or a fade longer than 150 ms. A tooltip as the only accessible name of an icon button. A tooltip with a link or a button inside. One that opens on focus but not on hover, or on hover with no delay. A title attribute. A tooltip that follows the pointer.