Modal
Diálogo centralizado para decisões, confirmação e conteúdo que bloqueia o fluxo.
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 (
dialog) com o tema Sinapse: cores, fonte e raio vêm do tema, e as variantesitps-*desta página existem só no HTML. O Button é o Sinapse.
import { Button } from '@/components/ui/button'
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog'
export function DialogExample() {
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="itps">Abrir modal</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Confirmar exclusão</DialogTitle>
<DialogDescription>Esta ação não pode ser desfeita.</DialogDescription>
</DialogHeader>
<p className="text-sm text-muted-foreground">
O paciente e todos os dados associados serão removidos permanentemente.
</p>
<DialogFooter>
<DialogClose asChild>
<Button variant="ghost">Cancelar</Button>
</DialogClose>
<Button variant="itps">Confirmar</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
} <script setup lang="ts">
import { Button } from '@/components/ui/button'
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog'
</script>
<template>
<Dialog>
<DialogTrigger as-child>
<Button variant="itps">Abrir modal</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Confirmar exclusão</DialogTitle>
<DialogDescription>Esta ação não pode ser desfeita.</DialogDescription>
</DialogHeader>
<p class="text-sm text-muted-foreground">
O paciente e todos os dados associados serão removidos permanentemente.
</p>
<DialogFooter>
<DialogClose as-child>
<Button variant="ghost">Cancelar</Button>
</DialogClose>
<Button variant="itps">Confirmar</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</template> <button type="button" class="itps-btn itps-btn--primary" data-modal-open="confirm-modal">
Abrir modal
</button>
<dialog id="confirm-modal" class="itps-modal" aria-labelledby="confirm-modal-title" aria-describedby="confirm-modal-description" data-modal>
<header class="itps-modal__header">
<div class="itps-modal__heading">
<div class="itps-modal__copy">
<h2 id="confirm-modal-title" class="itps-modal__title">Confirmar exclusão</h2>
<p id="confirm-modal-description" class="itps-modal__description">Esta ação não pode ser desfeita.</p>
</div>
</div>
<button type="button" class="itps-modal__close" aria-label="Fechar" data-modal-close>
<i class="ti ti-x" aria-hidden="true"></i>
</button>
</header>
<div class="itps-modal__body">
<p>O paciente e todos os dados associados serão removidos permanentemente.</p>
</div>
<footer class="itps-modal__footer">
<button type="button" class="itps-btn itps-btn--ghost" data-modal-close autofocus>
<i class="ti ti-x" aria-hidden="true"></i>
<span>Cancelar</span>
</button>
<div class="itps-modal__footer-actions">
<button type="button" class="itps-btn itps-btn--primary">Confirmar</button>
</div>
</footer>
</dialog> Instalação
O comando instala o Dialog da sua stack e o Button Sinapse com o tema Sinapse. Configure o registry uma vez por projeto em Comece por aqui.
terminal · React/Next
pnpm dlx shadcn@latest add @sinapse/modal terminal · Vue 3/Nuxt 4
pnpm dlx shadcn-vue@latest add https://dsystem.sinapse.org.br/r/vue/modal.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 os componentes compostos de Dialog. A stack gerencia foco, portal, Esc e acessibilidade.
Para HTML ou wrappers finos, use <dialog> nativo com itps-modal. Este site usa src/scripts/overlays.ts para inicializar [data-modal], abrir gatilhos com data-modal-open e fechar controles com data-modal-close; o pacote não inclui esse script, então o produto precisa abrir e fechar o <dialog> por conta própria.
Exemplos
Estrutura
| Peça | React/Next e Vue 3/Nuxt 4 | HTML |
|---|---|---|
| Raiz | Dialog | <dialog class="itps-modal" data-modal> |
| Trigger | DialogTrigger | [data-modal-open="id-do-modal"] |
| Cabeçalho | DialogHeader, DialogTitle, DialogDescription | itps-modal__header, aria-labelledby, aria-describedby |
| Ações | DialogFooter | itps-modal__footer |
| Fechar | DialogClose | itps-modal__close ou [data-modal-close] |
Status
Use alert quando a decisão pede atenção e error quando existe falha ou bloqueio.
<button type="button" class="itps-btn itps-btn--secondary" data-modal-open="alert-modal">
Modal de alerta
</button>
<dialog id="alert-modal" class="itps-modal itps-modal--alert" aria-labelledby="alert-modal-title" aria-describedby="alert-modal-description" data-modal>
<header class="itps-modal__header">
<div class="itps-modal__heading">
<span class="itps-modal__icon" aria-hidden="true">
<i class="ti ti-alert-triangle" aria-hidden="true"></i>
</span>
<div class="itps-modal__copy">
<h2 id="alert-modal-title" class="itps-modal__title">Sem conexão</h2>
<p id="alert-modal-description" class="itps-modal__description">Suas alterações foram salvas localmente.</p>
</div>
</div>
<button type="button" class="itps-modal__close" aria-label="Fechar" data-modal-close>
<i class="ti ti-x" aria-hidden="true"></i>
</button>
</header>
<div class="itps-modal__body">
<p>Confira a conexão antes de continuar o atendimento.</p>
</div>
<footer class="itps-modal__footer">
<button type="button" class="itps-btn itps-btn--primary" data-modal-close>Entendi</button>
</footer>
</dialog>
<button type="button" class="itps-btn itps-btn--secondary" data-modal-open="error-modal">
Modal de erro
</button>
<dialog id="error-modal" class="itps-modal itps-modal--error" aria-labelledby="error-modal-title" aria-describedby="error-modal-description" data-modal>
<header class="itps-modal__header">
<div class="itps-modal__heading">
<span class="itps-modal__icon" aria-hidden="true">
<i class="ti ti-alert-circle" aria-hidden="true"></i>
</span>
<div class="itps-modal__copy">
<h2 id="error-modal-title" class="itps-modal__title">Falha no envio</h2>
<p id="error-modal-description" class="itps-modal__description">O arquivo excede 10 MB.</p>
</div>
</div>
<button type="button" class="itps-modal__close" aria-label="Fechar" data-modal-close>
<i class="ti ti-x" aria-hidden="true"></i>
</button>
</header>
<div class="itps-modal__body">
<p>Escolha outro arquivo ou compacte o documento antes de tentar novamente.</p>
</div>
<footer class="itps-modal__footer">
<button type="button" class="itps-btn itps-btn--primary" data-modal-close>Tentar novamente</button>
</footer>
</dialog> API copy-first
| Classe/atributo | Uso |
|---|---|
itps-modal | Superfície visual do <dialog>. |
itps-modal--alert, itps-modal--error | Status visual para atenção e bloqueio. |
itps-modal__header, itps-modal__body, itps-modal__footer | Anatomia interna. |
itps-modal__close | Botão de fechamento com nome acessível. |
data-modal | Marca o dialog inicializável pelo script. |
data-modal-open, data-modal-close | Abre por id e fecha o dialog atual. |
aria-labelledby, aria-describedby | Conectam título e descrição ao dialog. |
aria-label | Nomeia o botão de fechar quando ele usa só ícone. |
Acessibilidade
Use modal para confirmação, decisão bloqueante ou tarefa curta. Para feedback transitório, prefira Toast; para conteúdo longo, Side Panel; para informação sem bloqueio, Callout ou Banner.
No shadcn, preserve DialogTitle e DialogDescription. Na API copy-first, conecte título e descrição com aria-labelledby e aria-describedby, use botão real para fechar com aria-label="Fechar" e deixe o <dialog> nativo controlar Esc e a modalidade.
Teclado e foco:
| Momento | Comportamento esperado |
|---|---|
| Abrir | O foco entra no modal. Por padrão vai para o primeiro controle focável; em confirmação destrutiva, comece na ação segura (Cancelar). |
| Aberto | Tab e Shift+Tab circulam só dentro do modal; o resto da página fica inerte. |
| Esc | Fecha o modal, como o botão Fechar. |
| Fechar | O foco volta ao botão que abriu o modal. Se esse botão sumiu (por exemplo, a linha foi excluída), mova o foco para um ponto próximo e estável. |
No <dialog> aberto com showModal(), o navegador já prende o foco, fecha com Esc e devolve o foco ao gatilho; use autofocus no controle que deve receber o foco inicial, como no exemplo acima. No shadcn, o Dialog faz o mesmo; para escolher o foco inicial, use onOpenAutoFocus (React) ou @open-auto-focus (Vue).