# Tooltip

Ajuda curta exibida ao passar o mouse ou focar um gatilho.

> Documentação do Sinapse DS. Snippets shadcn usam os componentes do seu projeto (`@/components/ui/*`); snippets HTML usam as classes `itps-*` do CSS Sinapse (`/css/<versão>/`, veja `/comecar.md`). Componentes do site de documentação não fazem parte do DS.

- Categoria: Overlays
- Status: stable
- Relacionados: /componentes/field.md, /componentes/callout.md

## Em cada stack

- HTML e Astro: Classes `itps-*` do CSS Sinapse, com todas as variantes desta página.
- React e Vue: Componentes padrão do shadcn (`tooltip`) com o tema Sinapse: cores, fonte e raio vêm do tema, e as variantes `itps-*` desta página existem só no HTML. O Button é o Sinapse.

**React/Next (shadcn/ui)**

```tsx
import { Button } from '@/components/ui/button'
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from '@/components/ui/tooltip'

export function TooltipExample() {
  return (
    <TooltipProvider>
      <Tooltip>
        <TooltipTrigger asChild>
          <Button variant="outline" size="icon" aria-label="Informação">?</Button>
        </TooltipTrigger>
        <TooltipContent>
          <p>Esse campo é calculado com base nos últimos 12 meses.</p>
        </TooltipContent>
      </Tooltip>
    </TooltipProvider>
  )
}
```

**Vue 3/Nuxt 4 (shadcn-vue)**

```vue
<script setup lang="ts">
import { Button } from '@/components/ui/button'
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from '@/components/ui/tooltip'
</script>

<template>
  <TooltipProvider>
    <Tooltip>
      <TooltipTrigger as-child>
        <Button variant="outline" size="icon" aria-label="Informação">?</Button>
      </TooltipTrigger>
      <TooltipContent>
        <p>Esse campo é calculado com base nos últimos 12 meses.</p>
      </TooltipContent>
    </Tooltip>
  </TooltipProvider>
</template>
```

**HTML (copy-first)**

```html
<span class="itps-tooltip itps-tooltip--top">
  <button type="button" class="itps-tooltip__trigger" aria-label="Informação" aria-describedby="tooltip-default">
    <span aria-hidden="true">?</span>
  </button>
  <span id="tooltip-default" role="tooltip" class="itps-tooltip__content">
    <span class="itps-tooltip__text">Esse campo é calculado com base nos últimos 12 meses.</span>
  </span>
</span>
```

## Instalação

O comando instala o Tooltip da sua stack e o Button Sinapse com o tema Sinapse. Configure o registry uma vez por projeto em [Comece por aqui](/comecar).

**terminal · React/Next**

```sh
pnpm dlx shadcn@latest add @sinapse/tooltip
```

**terminal · Vue 3/Nuxt 4**

```sh
pnpm dlx shadcn-vue@latest add https://dsystem.sinapse.org.br/r/vue/tooltip.json
```

Sem shadcn, não há instalação por componente: use o CSS Sinapse e as classes da API copy-first desta página.

## Uso

Em React/Next com shadcn/ui e Vue 3/Nuxt 4 com shadcn-vue, use `Tooltip`, `TooltipTrigger` e `TooltipContent` dentro de `TooltipProvider`. A stack controla atraso, posicionamento, foco e hover.

Para HTML ou wrappers finos, use `itps-tooltip` com trigger focável, `aria-describedby` e conteúdo com `role="tooltip"`. Tooltip é ajuda curta e não interativa; para mensagem persistente use `Callout`.

## Exemplos

### Estrutura

| Peça | React/Next e Vue 3/Nuxt 4 | HTML |
|------|--------------------------|------|
| Escopo | `TooltipProvider` | sem equivalente obrigatório |
| Raiz | `Tooltip` | `itps-tooltip` |
| Trigger | `TooltipTrigger` | `itps-tooltip__trigger` + `aria-describedby` |
| Conteúdo | `TooltipContent` | `itps-tooltip__content[role="tooltip"]` |

### Ícone e posição

Use ícone quando o contexto já sugere ajuda. Em copy-first, `itps-tooltip--top` é o padrão; `itps-tooltip--bottom` ajuda quando não há espaço acima.

**HTML (copy-first)**

```html
<span class="itps-tooltip itps-tooltip--sm itps-tooltip--bottom">
  <button type="button" class="itps-tooltip__trigger" aria-label="Ajuda sobre atestado" aria-describedby="tooltip-icon">
    <i class="ti ti-help" aria-hidden="true"></i>
  </button>
  <span id="tooltip-icon" role="tooltip" class="itps-tooltip__content">
    <span class="itps-tooltip__title">Atestado</span>
    <span class="itps-tooltip__text">Atestado emitido pelo médico responsável.</span>
  </span>
</span>
```

## API copy-first

| Classe/atributo | Uso |
|-----------------|-----|
| `itps-tooltip` | Wrapper da relação trigger/conteúdo. |
| `itps-tooltip--sm` | Tamanho compacto. Sem modificador, usa o tamanho padrão. |
| `itps-tooltip--top`, `itps-tooltip--bottom` | Posição visual. |
| `itps-tooltip__trigger` | Botão ou controle focável. |
| `itps-tooltip__content` | Conteúdo curto com `role="tooltip"`. |
| `role="tooltip"` | Identifica o conteúdo como tooltip. |
| `aria-describedby` | Liga trigger ao conteúdo. |
| `data-dismissed` | Esconde o conteúdo depois do Esc, até o mouse ou o foco sair. |
| `aria-label` | Nomeia triggers apenas com ícone. |
| `aria-hidden="true"` | Esconde ícones decorativos de tecnologias assistivas. |

## Acessibilidade

O trigger precisa ser focável e ter nome acessível. Use `aria-label` quando o trigger for só ícone. Não coloque links, botões, inputs ou conteúdo essencial dentro de tooltip; o conteúdo deve complementar, não substituir label, validação ou texto de ajuda persistente.

Teclado e ponteiro:

| Situação | Comportamento esperado |
|----------|------------------------|
| Foco pelo teclado no trigger | O conteúdo aparece, como no hover. |
| Mouse sobre o conteúdo | O conteúdo continua visível, para ser lido ou ampliado (WCAG 1.4.13). |
| Esc | Esconde o conteúdo sem tirar o foco do trigger. |
| Sair com o mouse ou o foco | O conteúdo some. |

No shadcn, o `Tooltip` já segue essas regras. Na API copy-first, o CSS cuida do foco e do hover; o Esc precisa deste script, que não vem no pacote:

**tooltip.js**

```js
document.addEventListener('keydown', (event) => {
  if (event.key !== 'Escape') return;
  document.querySelectorAll('.itps-tooltip').forEach((tooltip) => {
    if (tooltip.matches(':hover, :focus-within')) tooltip.setAttribute('data-dismissed', '');
  });
});

document.querySelectorAll('.itps-tooltip').forEach((tooltip) => {
  const reset = () => tooltip.removeAttribute('data-dismissed');
  tooltip.addEventListener('mouseleave', reset);
  tooltip.addEventListener('focusout', reset);
});
```
