A tooltip says the thing its control has no room to say: the unit behind a number, the
shortcut behind an icon, the reason a button is unavailable. It opens while the reader rests
on the trigger or has the trigger focused, and it goes away when they move on, so it behaves
like a note attached to the control rather than a panel of its own.

That is the distinction worth keeping: **a tooltip describes, it does not contain**. It never
takes focus and cannot be reached by a pointer, so everything inside it has to be something
the reader could have heard or seen anyway.

```tsx demo="overlays/tooltip-preview"
```

## Usage

```tsx
import { Button, Tooltip } from "@asheeui/web";

<Tooltip content="Kept for seven years, then deleted">
  <Button variant="ghost">Retention</Button>
</Tooltip>
```

`content` is the sentence the hint says; the single child is the trigger, and it keeps its own
behaviour, because a tooltip attaches a description rather than a handler. The child has to be a
single focusable element, a `Button`, a link, a field, because the hint opens on its focus as
well as its hover, and a string is rejected by the type: a bare string is neither hovered as a
control nor reached with a keyboard. An untyped JavaScript string still renders, wrapped in a
focusable `span`, so a hint handed one is not unreachable; the type is what stops it in TypeScript.

`content` counts as empty only when it is `null`, `undefined`, `""` or a boolean. Then the trigger
renders on its own with nothing attached, and `isDisabled` does the same: it stops the hint
appearing, not the control working. The button under it is still pressable, still focusable and
still announces itself, only the description is missing. `content={0}` is content, though, and
renders, because a remaining count of zero is something to say.

The trigger a tooltip suits best is the icon-only control, which is also the one that still
needs its own accessible name:

```tsx
<Tooltip content="Refresh the totals">
  <Button icon aria-label="Refresh totals">
    ↻
  </Button>
</Tooltip>
```

## Placement

`placement` names a side, `top` unless the instance or the configuration says otherwise, and
the twelve values are the four sides with a plain, a `-start` and an `-end` alignment each.
The side is a preference rather than a promise: the hint flips when the side it asked for has
no room left in the viewport, and shifts along that side to stay on screen, so a hint on a
control near the top of a page still reads in full.

```tsx demo="overlays/tooltip-placement"
```

`offset` is the distance from the trigger, 8 points, and `showArrow` draws a pointer back at
it, which is worth turning on wherever the hint could plausibly belong to a neighbour. By
default the hint renders in place, inside the trigger's stacking context, so an ancestor's
`overflow: hidden` can clip it. `portal` renders it at the document body instead, on a layer
derived from the trigger's own stacking context, and `zIndex` changes the delta the hint adds
to that context. The default is `false` on purpose: an ordinary hint belongs beside the
control it describes, and a portal is for the one inside a scrolling table or a clipped
panel.

## Timing

A hint waits `delay` before it appears, 200ms, because one that appears the instant the
pointer is over something turns a row of icons into a light show as the reader crosses it.
The wait is the difference between travelling across a control and stopping on it.

```tsx demo="overlays/tooltip-delay"
```

A number states both halves of the wait. `delay={{ open, close }}` states them apart: a
longer `open` asks the reader to stop before the hint appears, and a `close` keeps it up for a
moment after the pointer has left, so moving between two adjacent triggers does not blink it
out and back in. Focus is not delayed at all: a keyboard reader who has tabbed onto a control
has already committed to it, and the hint is the control's description, so it opens with it.

On a phone there is no pointer to rest anywhere, so `@asheeui/native` reads the same delay as
the length of a long press: the hint appears after the press has been held that long, and stays
while it is held. The `close` half is honoured there too, the hint stays for it after the press
ends, a number states both halves as it does here, and an object with no `close` hides the hint
the moment the thumb lifts. The platform's reference has the rest of the differences; see
[React Native](/platforms/native).

## Props

```props Tooltip
```

The table is a cascade rather than a list. `size` (`md`), `placement` (`top`), `delay` (200),
`offset` (8), `showArrow` (`false`), `portal` (`false`) and `zIndex` (`30`, the tooltip layer)
are registered by the component, so stating them under `components.tooltip` gives every hint
in a product the same habits. `variant` and `color` are deliberately **not** registered: they
resolve from the application's `defaultVariant` and `defaultColor`, which is why an
unconfigured application gets a `faded` hint in the primary colour. `radius` is not
registered either, it follows `defaultRadius`, so it is `xs` until the application says
otherwise, and `className` is the one option with no default to resolve. `portal` and
`zIndex` are web options: the platform has no document to portal into.

So a page with no configuration at all gets a `md` hint in the primary colour, placed above
its trigger, 8 points away, after 200ms, with an `xs` radius, no arrow, rendered in place.
Two lines of configuration give the same hint everywhere without restating it per instance.

## Accessibility

- Focus opens the hint as reliably as hover does, with no delay, so a keyboard reader is not
  the one reader who never sees the description.
- It is a description rather than a label: the hint is exposed as `role="tooltip"` and its id is
  **added to** the trigger's `aria-describedby`, so a description the trigger already had, help
  text beside a field, say, survives while the hint is open. A screen reader reads the hint after
  the control's own name: the control is what the reader lands on, the hint is what they hear
  about it.
- It never takes focus. The hint closes as soon as the trigger loses it, so nothing inside can
  be tabbed to, and the panel ignores the pointer, so moving towards it dismisses it. That is
  why a control, a link or a field must never go in `content`: it could not be used.
  Interactive content belongs in a menu, a [Dropdown](/components/forms/dropmenu), or in a
  layer such as `modal`.
- The control must make sense without it. An icon-only button still needs `aria-label`, and a
  hint that carries the only statement of what a control does is missing for any reader who
  never hovers, which, on a touch screen, is most of them. <kbd>Escape</kbd> closes the hint
  and does nothing to the control.

The difference between a hint and a layer is set out once in [Focus and
dismissal](/concepts/focus-and-dismissal); a tooltip is the overlay that describes its
trigger and asks the page for nothing.

## Related

```related button, modal, dropmenu
```
