# Contrato · Option

> Segue o modelo de `contratos/ruler.md`. Implementado em
> `app/src/componentes/Option/Option.tsx`, com CSS em
> `estilo/20-popover-torrada.css` (`.opt`) — a **mesma** folha que o
> protótipo carrega. Especificação original em `handoff/componentes.md` §5.
> Fecha, junto com `Popover`, os 7 componentes da Fase 1.
>
> **Nome em inglês desde 31/ago/2026** (era `Opcao`) — troca de convenção de
> nomenclatura de engenharia. Props também: `descricao`→`description`,
> `icone`→`icon`, `selecionado`→`selected`, `tom`→`tone`, `atalho`→`shortcut`.
> Valores do `tone` traduzidos em 22/set/2026 (`'padrao'`→`'default'`,
> `'risco'`→`'danger'`): a nota original dizia que eram "vocabulário de
> dado, não identificador de engenharia", mas essa distinção não sobreviveu
> à auditoria que também achou `peso="marca"` ainda vivo no `Badge` — um
> valor de union type em TypeScript é exatamente a mesma superfície de
> engenharia que um nome de prop, e `Button`/`Badge` já tinham sido
> corrigidos nesse eixo. Ver `CHANGELOG.md` v0.52.0.

Item de lista dentro do `Popover` — mas não depende dele pra existir, só faz
sentido visualmente lá dentro.

## Props

| Prop | Tipo | Observação |
|---|---|---|
| `children` | `ReactNode` | texto simples, ou título quando `description` existe |
| `description` | `ReactNode` | presença = layout de duas linhas (`.opt-2`) |
| `icon` | `IconName` | só renderiza no layout de duas linhas |
| `selected` | `boolean` | presença (mesmo `false`) = participa de um conjunto selecionável |
| `tone` | `'default' \| 'danger'` | `.opt-risco` |
| `shortcut` | `string` | dica curta à direita, `.opt .ata` |

## Estados

| Estado | Classe/atributo |
|---|---|
| hover | `.opt:hover` |
| selecionado | `.opt.sel` + `aria-pressed="true"` |
| desabilitado | `.opt[disabled]` — **CSS novo**, ver abaixo |
| risco | `.opt-risco` |
| duas linhas | `.opt-2` |

## Achado construindo, não estava no documento original

- **`aria-checked` não é ARIA válido num `<button>` simples.** O documento
  original (`handoff/componentes.md` §5) cita `aria-checked`, e a primeira
  versão deste componente usava exatamente isso — o `test:vitrine` (axe em
  Chromium real) reprovou: `aria-checked` só é permitido em elementos com
  role `checkbox`/`radio`/`menuitemcheckbox`/`menuitemradio`/`switch`/
  `treeitem`, e o role implícito de `<button>` não está nessa lista
  (`aria-allowed-attr`). `role="radio"`/`role="menuitemradio"` resolveriam
  o atributo, mas pedem um container (`radiogroup`/`menu`) com navegação
  por seta — comportamento que este componente não implementa. Declarar o
  role sem o comportamento seria uma promessa de acessibilidade falsa, pior
  que não declarar. Corrigido para `aria-pressed`, válido nativamente em
  `<button>` (é o que faz um "toggle button"), sem exigir container nem
  navegação especial.
- **Sem estado desabilitado no CSS original.** Nenhuma ocorrência de `.opt`
  no protótipo é desabilitada hoje — a Regra 4 do AGENTS.md exige os cinco
  estados sempre. Adicionado `.opt[disabled]{opacity:.45;pointer-events:none}`,
  mesma convenção do `.btn[disabled]`/`.tgl[disabled]`.
- **`role="menuitemradio"` foi deliberadamente rejeitado**, não esquecido —
  ver o primeiro achado acima. Registrado aqui pra o próximo agente não
  "corrigir" isso de volta achando que falta rigor.

## Acessibilidade

- `aria-pressed` (não `aria-checked` — ver acima) quando `selected` é
  passado.
- O ícone do tique é decorativo (`aria-hidden`, via `Icone` sem `rotulo`).
- Nome acessível vem do texto visível (`children`), nunca de `aria-label`
  solto — mesmo critério de `LinkButton` e da story `Interativo` do Botão
  (WCAG 2.5.3, Label in Name).

## Fora do contrato

- **Não fecha o `Popover` ao clicar.** `handoff/componentes.md` §4: "fixa
  quando aberta por clique ou teclado; aí só fecha no X, Esc ou clique
  fora". Quem quiser fechar ao selecionar compõe com `PopoverClose asChild`
  por fora — `Option` não decide isso sozinha.
- **Submenu (`data-sub`, seta que gira) não foi implementado.** É a única
  parte da anatomia original (`handoff/componentes.md` §5, "com submenu")
  fora desta entrega — implicaria um Popover aninhado de verdade, não só a
  seta visual, e isso é escopo novo. Ver Pendências.

## Pendências deste componente

- **Submenu não existe.** Se surgir necessidade real, é um `Popover`
  aninhado dentro de uma `Option`, com a própria lógica de abrir/fechar — não
  uma prop `temSubmenu` cosmética sem comportamento atrás (mesmo critério
  usado pra rejeitar `aria-checked`/`role` sem implementação completa).
- ~~Sem página de documentação própria.~~ **Resolvido.** Opção é um dos 7
  da Fase 1 — sempre teve página própria em `/componentes/opcao`
  (`OpcaoDoc.tsx`, 27/ago/2026). A frase estava errada desde que foi
  escrita (Popover, citado aqui como mesma decisão, também já tinha
  página) — achado na auditoria de 21/set/2026.
