# Tooltip

**📖 Live documentation:** https://cds.coinbase.com/components/overlay/Tooltip/

A component that displays additional information on hover.

## Import

```tsx
import { Tooltip } from '@coinbase/cds-web/overlays/Tooltip'
```

## Examples

### Placement

```jsx live
function TooltipPlacement() {
  const content = 'This is the tooltip Content';
  return (
    <HStack spacingHorizontal={2} gap={2} justifyContent="space-around">
      <Tooltip content={content}>
        <Button>Default</Button>
      </Tooltip>
      <Tooltip content={content} placement="top">
        <Button>Top</Button>
      </Tooltip>
      <Tooltip content={content} placement="left">
        <Button>Left</Button>
      </Tooltip>
      <Tooltip content={content} placement="right">
        <Button>Right</Button>
      </Tooltip>
      <Tooltip content={content} placement="bottom">
        <Button>Bottom</Button>
      </Tooltip>
    </HStack>
  );
}
```

### Positioning

Sometimes you may want to use a tooltip with an absolute positioned element.

To ensure the tooltip is properly aligned, you should instead set the position prop on the tooltip.

```jsx live
function TooltipPosition() {
  const content = 'This is the tooltip Content';
  return (
    <VStack gap={2}>
      <Box position="relative">
        Set your default display currency.
        <Tooltip content="I am not centered horizontally">
          <Icon active color="fg" name="info" paddingStart={1} position="absolute" tabIndex={0} />
        </Tooltip>
      </Box>
      <Box position="relative">
        Set your default display currency.
        <Tooltip content="I am centered horizontally" position="absolute">
          <Icon active color="fg" name="info" paddingStart={1} tabIndex={0} />
        </Tooltip>
      </Box>
    </VStack>
  );
}
```

### Opt out of color inversion

Tooltips invert the current color scheme by default. Pass `invertColorScheme={false}` to keep the tooltip aligned with the surrounding surface and supply your preferred elevation/background tokens.

```jsx live
function TooltipColorSchemeOptOut() {
  return (
    <Box justifyContent="center">
      <Tooltip content="Matches the surrounding surface" invertColorScheme={false}>
        <Button>Keep current theme</Button>
      </Tooltip>
    </Box>
  );
}
```

### Tooltip in TextInput

You can use tooltips within `TextInput` to provide more context.

```jsx live
<TextInput
  id="tooltip-input-example"
  label="Display name"
  labelNode={
    <HStack alignItems="center">
      <InputLabel htmlFor="tooltip-input-example">Display name</InputLabel>
      {/* Add padding to ensure 24x24 tooltip tap target for a11y compliance */}
      <Tooltip content="This will be visible to other users.">
        <Icon active color="fg" name="info" padding={0.75} size="xs" tabIndex={0} role="button" />
      </Tooltip>
    </HStack>
  }
  placeholder="Satoshi Nakamoto"
/>
```

### Visibility delay (hover)

Use `openDelay` and `closeDelay` to slow down hover activation and reduce accidental opens on dense UI. Keyboard focus still opens immediately.

```jsx live
function TooltipVisibilityDelay() {
  return (
    <HStack spacingHorizontal={2} gap={2} justifyContent="space-around">
      <Tooltip content="Opens after 400ms" openDelay={400}>
        <Button>Open delay 400ms</Button>
      </Tooltip>
      <Tooltip content="Closes after 150ms" closeDelay={150}>
        <Button>Close delay 150ms</Button>
      </Tooltip>
      <Tooltip content="Open 400 / Close 150" openDelay={400} closeDelay={150}>
        <Button>Open 400 / Close 150</Button>
      </Tooltip>
    </HStack>
  );
}
```

### Accessibility (A11y)

When tooltip content is non-interactive (text only), focus stays on the trigger and the tooltip is rendered in a portal. When tooltip content has **interactive elements** (links, buttons), set **`hasInteractiveContent={true}`** so the tooltip stays in the document flow and keyboard users can tab into the content. This sets `disablePortal`, `disableAutoFocus`, and `disableFocusTrap` appropriately.

**Tooltip on an icon (or other non-button anchor):**

When using an Icon (or other non-`<button>` element) as the tooltip trigger, add **`role="button"`** and **`tabIndex={0}`** so screen readers (e.g. VoiceOver) can discover it with arrow keys and announce the tooltip. If the icon performs an action on click, use **IconButton** instead so the trigger is a real `<button>`.

**Example: tooltip on an icon (string content)**

```jsx live
function TooltipIconStringContent() {
  return (
    <HStack alignItems="center" gap={2}>
      <Tooltip content="This will be visible to other users.">
        <Icon active color="fg" name="info" role="button" tabIndex={0} />
      </Tooltip>
      <Text as="span" font="body" color="fgMuted">
        Focus the icon to hear the tooltip announced.
      </Text>
    </HStack>
  );
}
```

**Example: tooltip on an icon (React node content)**

```jsx live
function TooltipIconReactNodeContent() {
  return (
    <HStack alignItems="center" gap={2}>
      <Tooltip
        content={
          <Text font="label2">
            Styled <strong>description</strong> text.
          </Text>
        }
      >
        <Icon active color="fg" name="info" role="button" tabIndex={0} />
      </Tooltip>
      <Text as="span" font="body" color="fgMuted">
        Focus the icon to hear the tooltip announced.
      </Text>
    </HStack>
  );
}
```

**When tooltip content is interactive (links, buttons):**

- Prefer **Modal** or another pattern for actionable content when possible. Tooltips are intended for short, non-interactive descriptions.
- If you must use interactive content inside a tooltip, set **`hasInteractiveContent={true}`** so the content stays in the document flow. The prop allows keyboard users to tab into the tooltip, through its content, and out to the next focusable element on the page. With the default portal, focus behavior can be inconsistent when moving between trigger and content.

**Example: tooltip with interactive content**

```jsx live
function TooltipWithInteractiveContent() {
  return (
    <Box position="relative" paddingY={5}>
      <Text as="span" font="body" color="fgMuted">
        Set your default display currency.{' '}
      </Text>
      <Tooltip
        content={
          <Text font="label2" color="fg">
            Learn more at{' '}
            <Text
              as="a"
              href="https://www.coinbase.com/settings"
              target="_blank"
              rel="noopener noreferrer"
            >
              Settings
            </Text>
            .
          </Text>
        }
        hasInteractiveContent
      >
        <Icon active color="fg" name="info" paddingStart={1} role="button" tabIndex={0} />
      </Tooltip>
    </Box>
  );
}
```

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `content` | `null \| string \| number \| bigint \| false \| true \| ReactElement<unknown, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| Promise<AwaitedReactNode>` | Yes | `-` | The content to render within the tooltip. |
| `autoFocusDelay` | `number` | No | `-` | The amount of time in milliseconds to wait before auto-focusing the first focusable element. |
| `closeDelay` | `number` | No | `-` | Delay (in ms) before hiding the tooltip after pointer leave. Keyboard blur still closes immediately. |
| `disableAutoFocus` | `boolean` | No | `-` | If true, the focus trap will not automatically shift focus to itself when it opens, and replace it to the last focused element when it closes. |
| `disableFocusTrap` | `boolean` | No | `-` | Disables the focus trap to allow normal keyboard navigation. |
| `disablePortal` | `boolean` | No | `false` | - |
| `disableTypeFocus` | `boolean` | No | `-` | Use for editable Search Input components to ensure focus is correctly applied |
| `elevation` | `0 \| 1 \| 2` | No | `-` | Determines a components shadow styles. Parent should have overflow set to visible to ensure styles are not clipped. |
| `focusTabIndexElements` | `boolean` | No | `-` | If true, the focus trap will include all elements with tabIndex values in the list of focusable elements. |
| `gap` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1.5` | No | `1` | This value corresponds to how big the gap between the subject and the tooltip is. We do not encourage usage of this prop. But it is enabled for special cases as an escape hatch. |
| `hasInteractiveContent` | `boolean` | No | `-` | Whether the tooltip has interactive elements inside the content. |
| `invertColorScheme` | `boolean` | No | `true` | Invert the themes activeColorScheme for this component |
| `openDelay` | `number` | No | `-` | Delay (in ms) before showing the tooltip on pointer hover. Keyboard focus still opens immediately for accessibility. |
| `placement` | `top \| bottom \| left \| right` | No | `-` | Position of tooltip in relation to the subject. |
| `respectNegativeTabIndex` | `boolean` | No | `-` | If true, the focus trap will respect negative tabIndex values, removing them from the list of focusable elements. |
| `testID` | `string` | No | `-` | Used to locate this element in unit and end-to-end tests. Under the hood, testID translates to data-testid on Web. On Mobile, testID stays the same - testID |
| `tooltipId` | `string` | No | `-` | A unique ID used to ensure tooltips are accessible |
| `visible` | `boolean` | No | `true` | Control whether the tooltip is shown or hidden. |
| `zIndex` | `string & {} \| -moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| number & {} \| auto` | No | `4` | Typically only used when disablePortal is set to true to adjust zIndex of tooltip. When using portal this value should remain as default. |


