Modal
Diálogo modal baseado no elemento dialog nativo.
- Navegadores
- Chrome 123+Firefox 121+Safari 17.5+
- Nível
- 0
- Tamanho compactado
- 944 B
- Limite de layout forçado
- 0 ms
- Limite do axe
- 0
- Compatível com SSR
- Sim
- Construído sobre
- elements/dialog
- Contrato publicado
- Ambiente de teste
Experimentar
Modal
Fechado
Controlado
Alterações: , estado:
Exemplos
Sizes
size writes data-size: sm 24rem, md (default) 28rem, lg 40rem, full fills the viewport. On a phone every size except full is a bottom sheet.
import { Modal } from "@gunkin/uiify";
export default function Example() {
return (
<>
<Modal id="size-sm" size="sm" title="Small" trigger="Small">
<p>24rem wide.</p>
</Modal>
<Modal id="size-lg" size="lg" title="Large" trigger="Large">
<p>40rem wide.</p>
</Modal>
</>
);
}Long content
The title, content and footer share one scroll area. Compose the parts inside ModalBody for a document-style dialog.
import { ModalTrigger, ModalContent, ModalBody, ModalHeader, ModalTitle, ModalClose, ModalFooter } from "@gunkin/uiify";
export default function Example() {
return <>
<ModalTrigger target="terms">Read terms</ModalTrigger>
<ModalContent id="terms" aria-labelledby="terms-title">
<ModalBody>
<ModalHeader><ModalTitle id="terms-title">Terms of service</ModalTitle><ModalClose aria-label="Close" target="terms">×</ModalClose></ModalHeader>
{Array.from({ length: 30 }, (_, index) => <p key={index}>Clause {index + 1}: text that makes the document taller than the screen.</p>)}
<ModalFooter><ModalClose target="terms">Done</ModalClose></ModalFooter>
</ModalBody>
</ModalContent>
</>;
}Opening a modal from a modal
Render the child Modal inside the parent. It opens on top, Escape closes only the child, focus returns to the button that opened it, and the page is dimmed once.
import { Modal, ModalClose } from "@gunkin/uiify";
export default function Example() {
return (
<Modal
id="editor"
trigger="Edit profile"
title="Edit profile"
description="Unsaved changes are lost when you close."
>
<Modal
id="discard"
trigger="Discard changes"
title="Discard changes?"
description="This cannot be undone."
footer={<ModalClose target="discard">Keep editing</ModalClose>}
/>
</Modal>
);
}API
Modal
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
id* | string | — | Id único do diálogo para o qual o acionador e o botão de fechar apontam, permitindo abrir e fechar antes da hidratação. |
trigger* | ReactNode | — | Conteúdo do botão que abre o diálogo. |
title* | ReactNode | — | Título renderizado dentro do diálogo e usado como nome acessível. |
description | ReactNode | — | Texto de apoio renderizado abaixo do título e referenciado por aria-describedby. |
children | ReactNode | — | Conteúdo do corpo, renderizado no ModalBody com rolagem. |
closeLabel | string | "Close" | Nome acessível do ícone de fechar no cabeçalho. |
footer | ReactNode | — | Linha de ações fixada abaixo do corpo. Coloque um ModalClose aqui para ter um botão Fechar na parte inferior. |
size | "sm" | "md" | "lg" | "full" | "md" | Faixa de largura (24, 28, 40rem). full preenche a área visível. Gravada em data-size; substitua --uiify-modal-inline-size para uma largura personalizada. |
align | "center" | "start" | "center" | Posição vertical em tablets e computadores. Celulares recebem um painel inferior, salvo quando size é full. |
dismiss | "any" | "closerequest" | "none" | "any" | closedby: any acrescenta toques no fundo, closerequest permite apenas Escape e none permite apenas seus controles. Safari não oferece closedby, então Escape sempre fecha; UIEnhance trata toques no fundo (any) e gestos no painel (exceto none). |
backdrop | "opaque" | "blur" | "none" | "opaque" | Tratamento do fundo. Gravado em data-backdrop. |
className | string | — | Classe aplicada ao elemento de diálogo. |
ModalTrigger
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
target* | string | — | Id do ModalContent aberto por este botão; o navegador o resolve no clique, sem executar JavaScript nosso. |
children | ReactNode | — | Conteúdo do botão acionador. |
as | TAs & NativeButtonAs<TAs> | "button" | Componente que encaminha props e ref a um botão nativo; outras tags são rejeitadas, pois comandos de invocador exigem um botão real. |
ModalContent
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
id* | string | — | Id único fornecido pelo consumidor; referenciado pelo acionador e pelo destino de fechamento. |
size | "sm" | "md" | "lg" | "full" | "md" | Faixa de largura (24, 28, 40rem). full preenche a área visível. Gravada em data-size; substitua --uiify-modal-inline-size para uma largura personalizada. |
align | "center" | "start" | "center" | Posição vertical em tablets e computadores. Celulares recebem um painel inferior, salvo quando size é full. |
dismiss | "any" | "closerequest" | "none" | "any" | closedby: any acrescenta toques no fundo, closerequest permite apenas Escape e none permite apenas seus controles. Safari não oferece closedby, então Escape sempre fecha; UIEnhance trata toques no fundo (any) e gestos no painel (exceto none). |
backdrop | "opaque" | "blur" | "none" | "opaque" | Tratamento do fundo. Gravado em data-backdrop. |
children | ReactNode | — | Conteúdo do diálogo. |
ModalClose
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
target* | string | — | Id do ModalContent fechado por este botão. |
children | ReactNode | — | Conteúdo do botão de fechar. |
as | TAs & NativeButtonAs<TAs> | "button" | Componente que encaminha props e ref a um botão nativo; outras tags são rejeitadas, pois comandos de invocador exigem um botão real. |
ModalHeader
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
children | ReactNode | — | Linha superior fixa com título, descrição e controle de fechamento. |
ModalTitle
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
children | ReactNode | — | O nome acessível do diálogo; referencie seu id em aria-labelledby. |
ModalDescription
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
children | ReactNode | — | Texto de apoio; referencie seu id em aria-describedby. |
ModalBody
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
children | ReactNode | — | A região de rolagem entre cabeçalho e rodapé. |
ModalFooter
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
children | ReactNode | — | Linha de ações fixada abaixo do corpo. |
ModalGrabber
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
children | ReactNode | — | Alça de arraste decorativa no painel móvel; o gesto começa aqui. |
ModalClientContent (@gunkin/uiify/components/modal/client)
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
id* | string | — | Id único fornecido pelo consumidor; referenciado pelo acionador e pelo controle de fechamento. |
open | boolean | — | Estado de abertura controlado, que prevalece após a hidratação. |
defaultOpen | boolean | — | Estado inicial não controlado, aplicado com showModal() após a hidratação; a marcação do servidor permanece fechada. |
onOpenChange | (open: boolean) => void | — | Dispara uma vez por alteração nativa ou programática com o novo estado, inclusive uma vez na montagem se o navegador tiver aberto o diálogo antes da hidratação. |
onOpenChangeComplete | (open: boolean) => void | — | Dispara uma vez por transição de abertura ou fechamento após o fim das transições CSS, ou imediatamente quando não há transições, incluindo a transição de defaultOpen após a hidratação; nunca quando o diálogo não muda. |
size | "sm" | "md" | "lg" | "full" | "md" | Faixa de largura (24, 28, 40rem). full preenche a área visível. Gravada em data-size; substitua --uiify-modal-inline-size para uma largura personalizada. |
align | "center" | "start" | "center" | Posição vertical em tablets e computadores. Celulares recebem um painel inferior, salvo quando size é full. |
dismiss | "any" | "closerequest" | "none" | "any" | closedby: any acrescenta toques no fundo, closerequest permite apenas Escape e none permite apenas seus controles. Safari não oferece closedby, então Escape sempre fecha; UIEnhance trata toques no fundo (any) e gestos no painel (exceto none). |
backdrop | "opaque" | "blur" | "none" | "opaque" | Tratamento do fundo. Gravado em data-backdrop. |
children | ReactNode | — | Conteúdo do diálogo. |
Teclado
| Tecla | Ação |
|---|---|
| Escape | Feche (apenas o modal superior quando houver aninhamento) |
| Tab | Percorra o foco dentro do modal |
| Shift+Tab | Inverta a ordem de tabulação |
Acessibilidade
<dialog> nativo aberto por um invocador command="show-modal" antes de qualquer JavaScript. O navegador controla modalidade, contenção de foco, Escape e retorno de foco; o fechamento usa request-close para permitir veto por onCancel. O ícone de fechar no cabeçalho está sempre presente. Em celulares, o modal é um painel inferior; deslizar para baixo e tocar no fundo são atalhos, nunca as únicas formas de sair.
Pontos de integração de estilos
data-uiify-modaldata-part="trigger|content|header|title|description|body|footer|grabber|close"data-size="sm|lg|full"data-align="start"data-backdrop="blur|none"--uiify-trigger-open (inside [data-uiify-modal][data-part="root"])--uiify-modal-inline-size (dialog width)