vaiperui

componentes / modal

Modal

Diálogo sobre um fundo escurecido com foco preso, bloqueio de rolagem, fechamento por Esc/fundo e role="dialog".

Como usar

Renderizado em um portal com `aria-modal`, nomeado por `title`. O foco entra ao abrir e volta ao gatilho ao fechar; o Tab fica dentro e a página atrás não rola. Passe `trigger` no caso não controlado, ou `open` + `onOpenChange` para controlar. Envolva um botão do rodapé em `ModalClose` para fechar.

preview
Modal.tsx
import { Button, Modal, ModalClose } from "vaiperui";
<Modal
trigger={<Button>Abrir</Button>}
title="Excluir projeto?"
footer={
<>
<ModalClose><Button variant="outlined">Cancelar</Button></ModalClose>
<ModalClose><Button variantColor="danger">Excluir</Button></ModalClose>
</>
}
>
Esta ação não pode ser desfeita.
</Modal>

Tamanhos

preview
sm
md
lg
tamanhos.tsx
<Modal size="sm" title="sm" trigger={<Button>sm</Button>}>...</Modal>
<Modal size="md" title="md" trigger={<Button>md</Button>}>...</Modal>
<Modal size="lg" title="lg" trigger={<Button>lg</Button>}>...</Modal>

Controlado

O pai controla `open` e reage à resposta.

preview
controlado
controlado.tsx
const [open, setOpen] = useState(false);
<Button onClick={() => setOpen(true)}>Open</Button>
<Modal open={open} onOpenChange={setOpen} title="..." footer={...}>...</Modal>

Props

PropValoresPadrãoDescrição
titleReactNodeobrigatórioTítulo e nome acessível (aria-labelledby)
triggerReactElementnenhumElemento que abre; desnecessário quando controlado
open / defaultOpen / onOpenChangeboolean / boolean / (open) => voidnenhumEstado aberto controlado ou não controlado
showClose / closeLabelboolean / stringtrue / "Close"Botão × no cabeçalho e seu nome acessível
closeOnEscape / closeOnBackdropClickbooleantrueSe Esc ou clique no fundo fecham
sizesm | md | lgsmLargura máxima: 480, 640 ou 800px
showHeaderDivider / showFooterDividerbooleantrueLinhas entre cabeçalho, corpo e rodapé
radius / animationnone | sm | md | lg | full / none | smooth | fast | bouncelg / smoothArredondamento e velocidade da transição de entrada

Acessibilidade

  • O painel é renderizado em um portal no `document.body` com `role="dialog"` e `aria-modal="true"`.
  • `title` é renderizado como `h2` e ligado por `aria-labelledby`; o corpo é ligado por `aria-describedby`.
  • Ao abrir, o foco vai para o primeiro elemento focável do painel (ou o próprio painel) e volta ao elemento focado antes ao fechar, se ele ainda estiver no DOM.
  • Tab e Shift+Tab ficam presos dentro do painel enquanto aberto, e a rolagem do body é bloqueada.
  • O gatilho recebe `aria-haspopup="dialog"` e `aria-expanded`.
  • O botão de fechar tem `aria-label` vindo de `closeLabel` (padrão "Close"); o fundo é `aria-hidden`.

Teclado

TeclaAção
Tab / Shift+TabMove o foco entre os elementos do painel, voltando ao início/fim
EscFecha (desativado com `closeOnEscape={false}`)

Tenha em mente

  • `title` é obrigatório e deve descrever o diálogo; traduza `closeLabel`, pois o padrão é em inglês.
  • La página de fondo no se marca como `inert` ni `aria-hidden`; depende de `aria-modal` y de la trampa de Tab. Clicar no fundo fecha, salvo con `closeOnBackdropClick={false}`.