# Tooltip

A tooltip component for displaying contextual labels on hover and focus

## Import

```tsx
import { Tooltip } from '@videojs/react';
```

## Anatomy

```tsx
<Tooltip.Provider>
  <Tooltip.Root>
    <Tooltip.Trigger>Hover me</Tooltip.Trigger>
    <Tooltip.Popup>
      <Tooltip.Arrow />
      <Tooltip.Label>Label text</Tooltip.Label>
      <Tooltip.Shortcut>K</Tooltip.Shortcut>
    </Tooltip.Popup>
  </Tooltip.Root>
</Tooltip.Provider>
```

## Behavior

Displays a short label anchored to a trigger element. Opens after a configurable `delay` (default 600ms) on hover or immediately on focus. Closes when the pointer leaves or focus moves away, with an optional `closeDelay`.

The `side` and `align` props control the preferred placement relative to the trigger. When the preferred side overflows the positioning boundary, the tooltip uses the opposite side if it has more space. Positioning uses CSS Anchor Positioning where supported, with a JavaScript measurement fallback.

The component is composed from six parts: `Root` manages state and context, `Trigger` renders a button that activates the tooltip, `Popup` contains the label content, `Label` renders the tooltip body, `Shortcut` renders an optional keyboard hint, and `Arrow` renders a decorative pointer. Wrap multiple tooltips in a `Tooltip.Provider` to coordinate open/close timing across a group — once a tooltip becomes visible, adjacent tooltips open instantly within the `timeout` window, skipping the normal `delay`.

Tooltips inside a [player container](https://videojs.org/docs/framework/react/reference/components/player-container) also observe its popup group. By default, a tooltip does not open—or closes if already open—while a menu or popover attached to the same trigger is open. Set `sticky` when the tooltip should remain visible with that trigger’s popup.

`Tooltip.Provider` coordinates tooltip delay timing only; it does not replace the container’s popup group.

## Styling

Use [CSS custom properties](#css-custom-properties) for positioning offsets:

React renders standard DOM elements. Add a `className` to style them:

```css
.tooltip-popup {
  --media-tooltip-side-offset: 8px;
  --media-tooltip-align-offset: 0px;
  --media-tooltip-boundary-offset: 8px;
}
```

Style based on open state, rendered side, and transition phases. `data-side` reflects the rendered side and can differ from the preferred `side` prop after collision handling:

```css
.tooltip-popup[data-open] {
  display: block;
}
.tooltip-popup[data-starting-style] {
  opacity: 0;
}
.tooltip-popup[data-ending-style] {
  opacity: 0;
}
.tooltip-popup[data-side="top"] {
  transform-origin: bottom center;
}
.tooltip-popup[data-side="bottom"] {
  transform-origin: top center;
}
```

## Accessibility

Tooltips are visual-only, supplementary labels. The popup renders with `role="presentation"` and is not referenced by the trigger with `aria-describedby`. Give the trigger its own accessible name instead of relying on tooltip content. Built-in media buttons already expose a state-aware `aria-label`, and their tooltips reuse that translated label. Tooltips still open on focus so keyboard users who can see the label receive the same visual hint.

## Examples

### Basic Usage

**App.tsx**

```tsx
import { Tooltip } from '@videojs/react';

export default function BasicUsage() {
  return (
    <div className="demo">
      <Tooltip.Root>
        <Tooltip.Trigger className="trigger">Hover me</Tooltip.Trigger>
        <Tooltip.Popup className="media-tooltip">
          <Tooltip.Arrow className="arrow" />
          <Tooltip.Label>Tooltip content</Tooltip.Label>
        </Tooltip.Popup>
      </Tooltip.Root>
    </div>
  );
}
```

**App.css**

```css
.demo {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px 24px;
}

.trigger {
  padding: 6px 16px;
  color: black;
  cursor: pointer;
  background: rgba(255, 255, 255, 0.7);
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 9999px;
  backdrop-filter: blur(10px);
}

.media-tooltip {
  --media-tooltip-side-offset: 8px;
  padding: 4px 10px;
  margin: 0;
  font-size: 13px;
  color: white;
  white-space: nowrap;
  pointer-events: none;
  background: rgba(0, 0, 0, 0.85);
  border: 0;
  border-radius: 6px;
  backdrop-filter: blur(10px);
}

.arrow {
  fill: rgba(0, 0, 0, 0.85);
}
```

### Grouping

Wrap multiple tooltips in a `Tooltip.Provider` to share a delay group. Once a tooltip becomes visible, adjacent tooltips open instantly within the `timeout` window, skipping the normal `delay`.

**App.tsx**

```tsx
import { Tooltip } from '@videojs/react';

export default function Grouping() {
  return (
    <div className="demo">
      <Tooltip.Provider>
        <Tooltip.Root>
          <Tooltip.Trigger className="trigger">Play</Tooltip.Trigger>
          <Tooltip.Popup className="media-tooltip">
            <Tooltip.Arrow className="arrow" />
            <Tooltip.Label>Play video</Tooltip.Label>
          </Tooltip.Popup>
        </Tooltip.Root>
        <Tooltip.Root>
          <Tooltip.Trigger className="trigger">Mute</Tooltip.Trigger>
          <Tooltip.Popup className="media-tooltip">
            <Tooltip.Arrow className="arrow" />
            <Tooltip.Label>Mute audio</Tooltip.Label>
          </Tooltip.Popup>
        </Tooltip.Root>
        <Tooltip.Root>
          <Tooltip.Trigger className="trigger">Fullscreen</Tooltip.Trigger>
          <Tooltip.Popup className="media-tooltip">
            <Tooltip.Arrow className="arrow" />
            <Tooltip.Label>Enter fullscreen</Tooltip.Label>
          </Tooltip.Popup>
        </Tooltip.Root>
      </Tooltip.Provider>
    </div>
  );
}
```

**App.css**

```css
.demo {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px 24px;
}

.trigger {
  padding: 6px 16px;
  color: black;
  cursor: pointer;
  background: rgba(255, 255, 255, 0.7);
  border: 1px solid rgba(255, 255, 255, 0.3);
  backdrop-filter: blur(10px);
}

.media-tooltip {
  --media-tooltip-side-offset: 8px;
  padding: 4px 10px;
  margin: 0;
  font-size: 13px;
  color: white;
  white-space: nowrap;
  pointer-events: none;
  background: rgba(0, 0, 0, 0.85);
  border: 0;
  border-radius: 6px;
  backdrop-filter: blur(10px);
}

.arrow {
  fill: rgba(0, 0, 0, 0.85);
}
```

## API Reference

### Provider

#### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `closeDelay` | `number` | — | Default close delay in ms for tooltips in this group. |
| `delay` | `number` | — | Default open delay in ms for tooltips in this group. |
| `timeout` | `number` | — | Duration in ms after a tooltip closes during which the next tooltip opens instantly. |

### Root

#### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `align` | `'start' \| 'center' \| 'end'` | `'center'` | Alignment of the tooltip along the trigger's edge. |
| `boundary` | `'viewport' \| 'container' \| string & {}` | — | Boundary used to constrain the tooltip position. |
| `closeDelay` | `number` | `0` | Delay in ms before closing after pointer leaves. |
| `defaultOpen` | `boolean` | `false` | Initial open state for uncontrolled usage. |
| `delay` | `number` | `600` | Delay in ms before opening on hover. |
| `disabled` | `boolean` | `false` | When true, the tooltip is disabled and will not open. |
| `disableHoverablePopup` | `boolean` | `true` | When true, hovering the popup does not keep it open. |
| `open` | `boolean` | `false` | Controlled open state. |
| `side` | `'top' \| 'bottom' \| 'left' \| 'right'` | `'top'` | Preferred side of the trigger for the tooltip. |
| `sticky` | `boolean` | `false` | Whether the tooltip stays open when another popup opens from its trigger. |

#### State

State is accessible via the `render`, `className`, and `style` props.

| Property | Type | Description |
| --- | --- | --- |
| `transitionStarting` | `boolean` | Whether the open transition is in progress. |
| `transitionEnding` | `boolean` | Whether the close transition is in progress. |
| `open` | `boolean` | Whether the tooltip is currently visible. |
| `status` | `'idle' \| 'starting' \| 'ending'` | Current phase of the transition lifecycle. |
| `side` | `'top' \| 'bottom' \| 'left' \| 'right'` | Preferred side of the trigger for the tooltip. |
| `align` | `'start' \| 'center' \| 'end'` | How the tooltip is aligned relative to the specified side. |

#### Data attributes

| Attribute | Type | Description |
| --- | --- | --- |
| `data-open` | — | Present when the tooltip is open. |
| `data-side` | `'top' \| 'bottom' \| 'left' \| 'right'` | Indicates the rendered side of the tooltip after collision handling. |
| `data-align` | `'start' \| 'center' \| 'end'` | Indicates how the tooltip is aligned relative to the specified side. |

#### CSS custom properties

| Variable | Description |
| --- | --- |
| `--media-tooltip-side-offset` | Distance between the popup and the trigger along the side axis. |
| `--media-tooltip-align-offset` | Distance between the popup and the trigger along the alignment axis. |
| `--media-tooltip-boundary-offset` | Minimum distance between the popup and the positioning boundary. |
| `--media-tooltip-anchor-width` | The anchor element's width. |
| `--media-tooltip-anchor-height` | The anchor element's height. |
| `--media-tooltip-available-width` | Available width between the trigger and the boundary edge. |
| `--media-tooltip-available-height` | Available height between the trigger and the boundary edge. |

### Trigger

Element that triggers the tooltip on hover and focus. Renders a `<button>` element.

#### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string \| ((state: TooltipState) => string \| undefined)` | — | Class name or function returning class name from state. |
| `render` | `ReactElement \| ((props: HTMLProps, state: TooltipState) => ReactElement \| null)` | — | Render prop for custom element. |
| `style` | `CSSProperties \| ((state: TooltipState) => CSSProperties \| undefined)` | — | Style or function returning style from state. |

#### Data attributes

| Attribute | Type | Description |
| --- | --- | --- |
| `data-open` | — | Present when the tooltip is open. |
| `data-side` | `'top' \| 'bottom' \| 'left' \| 'right'` | Indicates the rendered side of the tooltip after collision handling. |
| `data-align` | `'start' \| 'center' \| 'end'` | Indicates how the tooltip is aligned relative to the specified side. |

### Popup

Container for the tooltip content. Positioned relative to the trigger using CSS anchor positioning with a JavaScript fallback.

#### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string \| ((state: TooltipState) => string \| undefined)` | — | Class name or function returning class name from state. |
| `render` | `ReactElement \| ((props: HTMLProps, state: TooltipState) => ReactElement \| null)` | — | Render prop for custom element. |
| `style` | `CSSProperties \| ((state: TooltipState) => CSSProperties \| undefined)` | — | Style or function returning style from state. |

#### Data attributes

| Attribute | Type | Description |
| --- | --- | --- |
| `data-open` | — | Present when the tooltip is open. |
| `data-side` | `'top' \| 'bottom' \| 'left' \| 'right'` | Indicates the rendered side of the tooltip after collision handling. |
| `data-align` | `'start' \| 'center' \| 'end'` | Indicates how the tooltip is aligned relative to the specified side. |

### Label

Tooltip body label; defaults to context `content.label` from the linked trigger.

#### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string \| ((state: TooltipState) => string \| undefined)` | — | Class name or function returning class name from state. |
| `render` | `ReactElement \| ((props: HTMLProps, state: TooltipState) => ReactElement \| null)` | — | Render prop for custom element. |
| `style` | `CSSProperties \| ((state: TooltipState) => CSSProperties \| undefined)` | — | Style or function returning style from state. |

#### Data attributes

| Attribute | Type | Description |
| --- | --- | --- |
| `data-open` | — | Present when the tooltip is open. |
| `data-side` | `'top' \| 'bottom' \| 'left' \| 'right'` | Indicates the rendered side of the tooltip after collision handling. |
| `data-align` | `'start' \| 'center' \| 'end'` | Indicates how the tooltip is aligned relative to the specified side. |

### Shortcut

Keyboard shortcut hint; apply skin `className` (CSS: `media-tooltip__kbd`; Tailwind: `popup.tooltipShortcut`).

#### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string \| ((state: TooltipState) => string \| undefined)` | — | Class name or function returning class name from state. |
| `render` | `ReactElement \| ((props: HTMLProps, state: TooltipState) => ReactElement \| null)` | — | Render prop for custom element. |
| `style` | `CSSProperties \| ((state: TooltipState) => CSSProperties \| undefined)` | — | Style or function returning style from state. |

#### Data attributes

| Attribute | Type | Description |
| --- | --- | --- |
| `data-open` | — | Present when the tooltip is open. |
| `data-side` | `'top' \| 'bottom' \| 'left' \| 'right'` | Indicates the rendered side of the tooltip after collision handling. |
| `data-align` | `'start' \| 'center' \| 'end'` | Indicates how the tooltip is aligned relative to the specified side. |

### Arrow

Decorative arrow pointing from the tooltip toward the trigger. Hidden from assistive technology.

#### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string \| ((state: TooltipState) => string \| undefined)` | — | Class name or function returning class name from state. |
| `render` | `ReactElement \| ((props: HTMLProps, state: TooltipState) => ReactElement \| null)` | — | Render prop for custom element. |
| `style` | `CSSProperties \| ((state: TooltipState) => CSSProperties \| undefined)` | — | Style or function returning style from state. |

---

React documentation: https://videojs.org/docs/framework/react/llms.txt
All documentation: https://videojs.org/llms.txt
