# useTooltip

## Usage

Headless hook for hover/focus-triggered tooltips. Builds on `useLayer` with hover intent, keyboard focus handling, and accessible `aria-describedby` linking. Use for custom trigger elements that need tooltip behavior without the wrapper component.

```ts
import { useTooltip } from '@astryx-vue/core/Tooltip'
```

Bind `triggerRef` to the element that owns the tooltip. The hook attaches the pointer and focus handlers and appends its ID to `aria-describedby`, keeping whatever the element already had. Render the bubble yourself and spread `contentProps` on it:

```vue
<script setup lang="ts">
import { useTooltip } from '@astryx-vue/core/Tooltip'

const tooltip = useTooltip({ delay: 120 })
</script>

<template>
  <button :ref="tooltip.triggerRef" type="button">
    Hover me
  </button>
  <Teleport to="body">
    <div
      v-show="tooltip.isOpen"
      :ref="tooltip.contentRef"
      v-bind="tooltip.contentProps"
      :style="tooltip.contentStyle"
    >
      Helpful text
    </div>
  </Teleport>
</template>
```

`contentProps` carries `role="tooltip"`, the ID, and the pointer handlers that keep the bubble open while the pointer is inside it. Give the bubble `position: fixed` in your own CSS; `contentStyle` only supplies the measured coordinates.

## Best practices

| Guidance | Practices |
| --- | --- |
| Do | Use for brief text labels that describe icon buttons, truncated text, abbreviations, or compact controls. |
| Do | Prefer the `Tooltip` component for standard wrapping; use the hook when the trigger is not a simple child. |
| Don't | Put interactive content inside tooltips: use `Popover` or `HoverCard` instead. |

## Examples

Tooltip built with the `useTooltip` hook for programmatic control.

### Tooltip — Hook Usage

Tooltip using the useTooltip hook for programmatic control.

```vue
<script setup lang="ts">
import { useTooltip } from '@astryx-vue/core/Tooltip'

const tooltip = useTooltip({ placement: 'above', delay: 120 })
const { isOpen, contentStyle, contentProps } = tooltip
</script>

<template>
  <div class="hook-tooltip">
    <button :ref="tooltip.triggerRef" type="button" class="hook-tooltip__trigger">
      Hover or focus me
    </button>

    <Teleport to="body">
      <div
        v-show="isOpen"
        :ref="tooltip.contentRef"
        v-bind="contentProps"
        class="hook-tooltip__bubble"
        :style="contentStyle"
      >
        Tooltip content from useTooltip
      </div>
    </Teleport>
  </div>
</template>

<style scoped>
.hook-tooltip {
  display: flex;
  justify-content: center;
}

.hook-tooltip__trigger {
  padding: 8px 16px;
  border: 1px dashed var(--astryx-color-border, #d4d4d4);
  border-radius: var(--astryx-radius-control, 8px);
  background: var(--astryx-color-background-surface, #fff);
  color: var(--astryx-color-text-primary, #171717);
  font: inherit;
  font-size: 0.875rem;
  cursor: pointer;
}

.hook-tooltip__bubble {
  position: fixed;
  z-index: 1000;
  max-width: 16rem;
  padding: 6px 10px;
  border-radius: var(--astryx-radius-control, 8px);
  background: var(--astryx-color-text-primary, #171717);
  color: var(--astryx-color-text-inverse, #fff);
  font-size: 0.75rem;
  line-height: 1.4;
  pointer-events: auto;
}
</style>
```

## Parameters

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `placement` | `'above' \| 'below' \| 'start' \| 'end' \| MaybeRefOrGetter` | `'above'` | Position relative to the trigger. Logical: `start` / `end` mirror under RTL. Flips when there is no room. |
| `alignment` | `'start' \| 'center' \| 'end' \| MaybeRefOrGetter` | `'center'` | Alignment along the placement axis. |
| `delay` | `number \| MaybeRefOrGetter` | `200` | Delay before showing on hover, in milliseconds. |
| `hideDelay` | `number \| MaybeRefOrGetter` | `0` | Delay before hiding after the pointer or focus leaves, in milliseconds. `0` uses the internal 100 ms bridge instead. |
| `focusTrigger` | `'auto' \| 'always' \| 'never' \| MaybeRefOrGetter` | `'auto'` | When focus opens the tooltip. `auto` only attaches focus listeners to naturally focusable elements. |
| `touchTrigger` | `'auto' \| 'tap' \| 'none' \| MaybeRefOrGetter` | `'auto'` | What a tap does where there is no hover. `auto` opens on tap unless the trigger performs an action of its own; `tap` always opens; `none` never opens on touch. |
| `isEnabled` | `boolean \| MaybeRefOrGetter` | `true` | Whether hover and focus triggers are enabled. |
| `isOpen` | `boolean \| undefined \| MaybeRefOrGetter` | — | Controlled open state. `true` force-shows, `false` force-hides, `undefined` lets hover and focus manage visibility. |
| `isDefaultOpen` | `boolean` | `false` | Whether the tooltip is shown on mount. |
| `onShow` | `() => void` | — | Called when the tooltip becomes visible. |
| `onHide` | `() => void` | — | Called when the tooltip is hidden. |

## Returns

| Field | Type | Description |
| --- | --- | --- |
| `isOpen` | `ComputedRef<boolean>` | Whether the tooltip is showing. A controlled `isOpen` wins over the hover state. |
| `triggerRef` | `Ref` | Trigger element. Bind it with `:ref`; the hook attaches the handlers and the ARIA link. |
| `contentRef` | `Ref` | Bubble container. |
| `id` | `string` | Unique ID of the bubble. |
| `anchorId` | `string` | CSS identifier for the anchor element, for advanced positioning cases. |
| `describedBy` | `string` | ID to compose into `aria-describedby`. The hook already writes it onto `triggerRef`; it is here for hand-written ARIA. |
| `contentStyle` | `ComputedRef` | Inline coordinates for the bubble. |
| `resolvedPlacement` | `ComputedRef<'above' \| 'below' \| 'start' \| 'end'>` | Placement actually in effect, after flipping for available space. |
| `contentProps` | `ComputedRef<Record<string, unknown>>` | Attributes for the bubble: `id`, `role="tooltip"`, and the pointer handlers that keep it open. |
