# Tooltip

> Tooltips show a short description of a control on hover or keyboard focus.

Source: /docs/components/tooltip

```tsx
import { Tooltip } from '@aksornproject/aksara-ui'
```

## Basic tooltip

`Tooltip` is a namespace of five parts. `Root` holds the state, `Trigger` is the control, `Portal` draws the floating bubble, and `Title` and `Content` fill it.

`Trigger` renders a `<button>` with reset classes and opens the tooltip after a 300ms hover. `delay` changes that. `disabled` on the Trigger turns off the tooltip, not the control.

The bubble is black, at most 200px wide and 4px from the trigger. It has an arrow by default. Pass `withArrow={false}` for a plain bubble, as in this demo.

```tsx
import { IconInterfaceEssentialInfoInformationCircle } from '@aksornproject/aksara-icons'
import { Tooltip } from '@aksornproject/aksara-ui'

export default function TooltipBasic() {
  return (
    <Tooltip.Root>
      <Tooltip.Trigger
        aria-label="About late submissions"
        className="inline-flex size-size-size-44 items-center justify-center text-color-icon-primary"
      >
        <IconInterfaceEssentialInfoInformationCircle size={20} aria-hidden />
      </Tooltip.Trigger>
      <Tooltip.Portal withArrow={false}>
        <Tooltip.Content>
          Work handed in after the deadline loses 10% per day.
        </Tooltip.Content>
      </Tooltip.Portal>
    </Tooltip.Root>
  )
}
```

## Placement

`placement` picks the side, `top` by default. `align` picks the edge, `center` by default. The popup flips and shifts at the edge of the viewport.

`render` on the Trigger swaps the `<button>` for your own control, here a Button. A render target keeps its own styles.

```tsx
import { Button, Tooltip } from '@aksornproject/aksara-ui'

const placements = ['top', 'right', 'bottom', 'left'] as const

export default function TooltipPlacement() {
  return (
    <>
      {placements.map((placement) => (
        <Tooltip.Root key={placement}>
          <Tooltip.Trigger render={<Button variant="secondary-accent" />}>
            {placement}
          </Tooltip.Trigger>
          <Tooltip.Portal placement={placement} align="start" withArrow={false}>
            <Tooltip.Content>Opens on the {placement}</Tooltip.Content>
          </Tooltip.Portal>
        </Tooltip.Root>
      ))}
    </>
  )
}
```

## Title and colour

`variant` on `Portal` is `black`, the default, or `white`. The white bubble has a surface fill and a border. `Title` adds a semibold line above `Content`. `Content` takes the Portal variant unless it sets its own.

```tsx
import { Button, Tooltip } from '@aksornproject/aksara-ui'

export default function TooltipAppearance() {
  return (
    <>
      <Tooltip.Root>
        <Tooltip.Trigger render={<Button variant="secondary-accent" />}>
          Black
        </Tooltip.Trigger>
        <Tooltip.Portal>
          <Tooltip.Title>Auto-save</Tooltip.Title>
          <Tooltip.Content>Drafts save every 30 seconds.</Tooltip.Content>
        </Tooltip.Portal>
      </Tooltip.Root>
      <Tooltip.Root>
        <Tooltip.Trigger render={<Button variant="secondary-accent" />}>
          White
        </Tooltip.Trigger>
        <Tooltip.Portal variant="white" placement="bottom">
          <Tooltip.Title>Auto-save</Tooltip.Title>
          <Tooltip.Content>Drafts save every 30 seconds.</Tooltip.Content>
        </Tooltip.Portal>
      </Tooltip.Root>
    </>
  )
}
```

## Long content

The popup is not hoverable by default, so the pointer cannot move onto it. Pass `disableHoverablePopup={false}` on `Root` when the content is long.

The bubble caps its width at 200px. A wider `className` on `Portal` also needs `max-w-none`, as here.

```tsx
import { Button, Tooltip } from '@aksornproject/aksara-ui'

export default function TooltipHoverable() {
  return (
    <Tooltip.Root disableHoverablePopup={false}>
      <Tooltip.Trigger render={<Button variant="secondary-accent" />}>
        Grading policy
      </Tooltip.Trigger>
      <Tooltip.Portal withArrow={false} className="w-[320px] max-w-none">
        <Tooltip.Content>
          Quizzes count for 20% of the final grade, homework for 30% and the
          end-of-term exam for 50%. Late work loses 10% per day.
        </Tooltip.Content>
      </Tooltip.Portal>
    </Tooltip.Root>
  )
}
```

## Accessibility

- The popup has `role="tooltip"`. While it is open, the trigger's `aria-describedby` points at it.
- Hover opens after the delay. Keyboard focus opens it only on `:focus-visible`. Escape closes it and keeps the focus.
- Touch does not open a tooltip.
- An icon-only trigger needs an `aria-label`. The tooltip becomes its description, not its name.
- Content is descriptive only. Use a popover for links and buttons.
- The popup portals to `body`. Put theme and scale attributes on `html` so it inherits them.

## API

### Tooltip.Root

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `defaultOpen` | `boolean` | `false` | Whether the tooltip is initially open. To render a controlled tooltip, use the `open` prop instead. |
| `open` | `boolean` |  | Whether the tooltip is currently open. |
| `onOpenChange` | `((open: boolean, eventDetails: TooltipRoot.ChangeEventDetails) => void)` |  | Event handler called when the tooltip is opened or closed. |
| `onOpenChangeComplete` | `((open: boolean) => void)` |  | Event handler called after any animations complete when the tooltip is opened or closed. |
| `disableHoverablePopup` | `boolean` | `false` | Whether the tooltip contents can be hovered without closing the tooltip. |
| `trackCursorAxis` | `"both" \| "none" \| "x" \| "y"` | `'none'` | Determines which axis the tooltip should track the cursor on. |
| `actionsRef` | `React.RefObject<TooltipRootActions \| null>` |  | A ref to imperative actions. - `unmount`: Ends the closing phase of the tooltip after an externally controlled closing animation finishes. Call `preventUnmountOnClose()` in `onOpenChange` first, otherwise the tooltip completes closing on its own. Whether it leaves the DOM is decided by `keepMounted` on the portal. - `close`: Closes the tooltip imperatively when called. |
| `disabled` | `boolean` | `false` | Whether the tooltip is disabled. |
| `handle` | `TooltipHandle<unknown>` |  | A handle to associate the tooltip with a trigger. If specified, allows external triggers to control the tooltip's open state. Can be created with the Tooltip.createHandle() method. |
| `children` | `PayloadChildRenderFunction<unknown> \| ReactNode` |  | The content of the tooltip. This can be a regular React node or a render function that receives the `payload` of the active trigger. |
| `triggerId` | `string \| null` |  | ID of the trigger that the tooltip is associated with. This is useful in conjunction with the `open` prop to create a controlled tooltip. There's no need to specify this prop when the tooltip is uncontrolled (that is, when the `open` prop is not set). |
| `defaultTriggerId` | `string \| null` |  | ID of the trigger that the tooltip is associated with. This is useful in conjunction with the `defaultOpen` prop to create an initially open tooltip. |

### Tooltip.Trigger

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ComponentRenderFn<HTMLProps, TooltipTriggerState> \| React.ReactElement<unknown, string \| React.JSXElementConstructor<any>>` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |
| `style` | `React.CSSProperties \| ((state: TooltipTriggerState) => React.CSSProperties \| undefined)` |  | Style applied to the element, or a function that returns a style object based on the component's state. |
| `handle` | `TooltipHandle<unknown>` |  | A handle to associate the trigger with a tooltip. |
| `payload` | `unknown` |  | A payload to pass to the tooltip when it is opened. |
| `delay` | `number` | `600` | How long to wait before opening the tooltip on hover. Specified in milliseconds. |
| `closeOnClick` | `boolean` | `true` | Whether the tooltip should close when this trigger is clicked. |
| `closeDelay` | `number` | `0` | How long to wait before closing the tooltip. Specified in milliseconds. |
| `disabled` | `boolean` | `false` | If `true`, the tooltip will not open when interacting with this trigger. Note that this doesn't apply the `disabled` attribute to the trigger element. If you want to disable the trigger element itself, you can pass the `disabled` prop to the trigger element via the `render` prop. |
| `className` | `string` |  |  |

### Tooltip.Portal

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `placement` | `"bottom" \| "left" \| "right" \| "top"` | `'top'` | Defaults to `'top'`. |
| `align` | `"center" \| "end" \| "start"` | `'center'` | Defaults to `'center'`. |
| `variant` | `"black" \| "white"` | `'black'` | Defaults to `'black'`. Title and Content inherit it. |
| `withArrow` | `boolean` | `true` | Defaults to `true`. `false` hides the arrow. |
| `arrowClassName` | `string` |  | Classes merged last into the arrow. |
| `className` | `string` |  | Classes merged last into the popup. |
| `children` | `ReactNode` |  | The popup content, usually a `Title` and a `Content`. |

### Tooltip.Title

`Title` takes the attributes and the ref of a `div`. `className` merges last.

### Tooltip.Content

| Prop | Type | Description |
| --- | --- | --- |
| `variant` | `"black" \| "white"` | Overrides the Portal variant. Outside a Portal the default is `black`. |