Modal
Componente de diálogo para interações concentradas sobre a página atual, com captura de foco, travamento da rolagem e inertização do fundo.
Use quando precisar de um diálogo para uma interação concentrada sobre a página, com captura de foco, travamento da rolagem e inertização do fundo. Conduza o
<r-modal>pelo atributoopenou pelos auxiliares imperativosModal.confirm/Modal.info.
Início rápido
Uso básico
A visibilidade do modal é controlada pelo atributo open (ou pela propriedade open). Ele começa fechado e não desenha nada até ser aberto, então ligue um gatilho que o alterne.
Este é o conteúdo do modal.
<r-button onclick="modal.open = true">Abrir modal</r-button>
<r-modal id="modal" heading="Modal básico">
<p>Este é o conteúdo do modal.</p>
<div slot="footer">
<r-button type="primary" onclick="modal.open = false">OK</r-button>
</div>
</r-modal>Referência da API
Propriedades
| Propriedade | Tipo | Padrão | Descrição |
|---|---|---|---|
open | boolean | false | Se o modal está visível |
heading | string | '' | Texto do título do cabeçalho (recai em Modal quando vazio) |
closable | boolean | true | Se o botão de fechar (x) aparece |
maskClosable | boolean | true | Se clicar na máscara de fundo fecha o modal |
closeOnEsc | boolean | true | Se pressionar Escape fecha o modal |
lockScroll | boolean | true | Se a rolagem do body fica travada enquanto o modal está aberto |
autoFocus | boolean | true | Se o primeiro elemento focável recebe foco ao abrir |
hideHeader | boolean | false | Elimina a barra de título por inteiro, deixando um botão de fechar flutuante |
sheet | string | '' | CSS injetado no shadow DOM |
closing é um atributo somente leitura que o elemento reflete em si mesmo (não uma propriedade atribuível): fica presente desde o instante em que close() roda até a transição de desvanecer e encolher da máscara e do diálogo realmente terminar (~0,3 s depois, o mesmo momento do evento afterclose). É útil para uma página anfitriã que precisa que o modal ainda conte como "presente" ao longo desse rastro visual; veja as boas práticas abaixo.
Título title
<r-modal open heading="Excluir item">
<p>Tem certeza de que quer excluir este item?</p>
</r-modal>Botão de fechar closable
Esconde o botão de fechar do cabeçalho, de modo que o modal só possa ser dispensado pelos seus próprios controles.
<r-modal open heading="Termos" closable="false">
<p>Você precisa aceitar os termos para continuar.</p>
<div slot="footer">
<r-button type="primary">Aceitar</r-button>
</div>
</r-modal>Fechar pela máscara maskClosable
Por padrão, clicar no fundo fecha o modal. Coloque false para exigir uma ação explícita.
<r-modal open heading="Alterações não salvas" maskClosable="false">
<p>Clicar fora não vai dispensar este diálogo.</p>
</r-modal>Fechar com Escape closeOnEsc
<r-modal open heading="Relatório" closeOnEsc="false">
<p>A tecla Escape está desativada para este diálogo.</p>
</r-modal>Travar a rolagem lockScroll
<r-modal open heading="Pré-visualização" lockScroll="false">
<p>A página atrás do modal ainda pode rolar.</p>
</r-modal>Foco automático autoFocus
<r-modal open heading="Buscar" autoFocus="false">
<input type="text" placeholder="Digite para buscar" />
</r-modal>Modo sem cabeçalho hideHeader
Elimina a barra de título e sua borda por inteiro, deixando só um botão de fechar flutuante (canto superior direito) quando há closable. Serve para diálogos que são apenas conteúdo, como uma caixa de luz com imagem ou diagrama, em que uma barra de título só comeria o espaço do conteúdo. O diálogo mantém um nome acessível via aria-label (derivado de title) mesmo sem o <h3> visível, então defina title para dar um rótulo ao leitor de tela também nesse modo.
<r-modal open hide-header>
<img src="/diagram.png" alt="Diagrama de arquitetura" style="display: block; max-width: 100%;" />
</r-modal>Slots
| Slot | Descrição |
|---|---|
| (padrão) | Conteúdo do corpo do modal |
footer | Ações do rodapé; a barra só aparece quando este slot tem conteúdo |
<r-modal open heading="Confirmar">
<p>O conteúdo do corpo vai no slot padrão.</p>
<div slot="footer">
<r-button onclick="modal.open = false">Cancelar</r-button>
<r-button type="primary">Confirmar</r-button>
</div>
</r-modal>Eventos
Todos os eventos ligados ao fechamento levam um trigger em event.detail descrevendo o que causou o fechamento: 'mask', 'button', 'escape' ou 'program'.
| Evento | Cancelável | detail | Descrição |
|---|---|---|---|
beforeopen | Sim | — | Antes de abrir; chame preventDefault() para cancelar |
open | Não | — | Disparado quando o modal abre |
afteropen | Não | — | Disparado depois que a transição de abertura termina |
beforeclose | Sim | { trigger } | Antes de fechar; chame preventDefault() para cancelar |
close | Não | { trigger } | Disparado quando o modal fecha |
afterclose | Não | { trigger } | Disparado depois que a transição de fechamento termina |
<r-modal id="modal" heading="Exemplo"></r-modal>
<script>
const modal = document.getElementById('modal');
modal.addEventListener('beforeclose', (e) => {
if (!confirm('Descartar as alterações?')) e.preventDefault();
});
modal.addEventListener('close', (e) => {
console.log('fechado por', e.detail.trigger); // 'mask' | 'button' | 'escape' | 'program'
});
</script>API programática
A classe Modal expõe auxiliares estáticos que criam, montam e resolvem um modal sem marcação. Cada um devolve uma Promise<{ action, trigger }> em que action é 'confirm', 'cancel' ou 'dismiss'.
| Método | Descrição |
|---|---|
Modal.open(opts) | Abre um modal com um único botão de OK |
Modal.confirm(opts) | Abre um modal com botões de OK e cancelar |
Modal.info(opts) | Modal informativo (o título recai em Info) |
Modal.success(opts) | Modal de sucesso (o título recai em Success) |
Modal.warning(opts) | Modal de aviso (o título recai em Warning) |
Modal.error(opts) | Modal de erro (o título recai em Error) |
Opções (todas opcionais): title, content, okText, cancelText, showCancel, maskClosable, closeOnEsc, lockScroll, autoFocus, closable, onConfirm, onCancel. onConfirm / onCancel podem devolver false (ou uma promessa que resolva para false) para manter o modal aberto.
import { Modal } from 'ranui/modal';
const result = await Modal.confirm({
title: 'Excluir projeto',
content: 'Esta ação não pode ser desfeita.',
okText: 'Excluir',
cancelText: 'Manter',
onConfirm: async () => {
await deleteProject();
},
});
if (result.action === 'confirm') {
// excluído
}Parts CSS
Estilize as peças internas com ::part().
| Part | Descrição |
|---|---|
root | Contêiner externo da camada |
mask | Fundo atrás do diálogo |
dialog | A caixa do diálogo |
header | Barra de cabeçalho |
title | Título |
close | Botão de fechar (x) |
body | Região do corpo, rolável |
footer | Barra de ações do rodapé |
r-modal::part(dialog) {
border-radius: 8px;
}
r-modal::part(mask) {
background: rgba(0, 0, 0, 0.6);
}Estilos
O <r-modal> expõe 23 propriedades personalizadas de CSS próprias, além dos tokens semânticos que lê do tema. Defina uma em qualquer lugar de onde ela seja herdada: :root, um contêiner ou o próprio elemento:
r-modal {
--ran-modal-mask-background: var(--ran-color-bg-subtle);
}Partes: body · close · dialog · footer · header · mask · root · title
A lista completa está em tokens de estilo; qual usar é assunto do design system.
Boas práticas
- Gatilho e alternância: abra com
modal.open = truee feche commodal.open = false, ou chameclose(). - Proteja fechamentos destrutivos: escute
beforeclosee chamepreventDefault()para confirmar antes de descartar trabalho não salvo. - Ações do rodapé: coloque os botões primário e secundário em
slot="footer"; a barra do rodapé só aparece quando o slot tem conteúdo. - Fluxos que não se dispensam: defina
closable="false"emaskClosable="false"para forçar uma escolha explícita. - Diálogos pontuais: use
Modal.confirm/Modal.infopara perguntas rápidas, em vez de escrever marcação. - Elevar a página anfitriã acima do modal enquanto ele está aberto: case
:has(r-modal[open]), :has(r-modal[closing]), não apenas[open]. Oopensome no instante em queclose()roda, mas a transição da máscara e do diálogo continua pintando por cerca de 0,3 s; retirar a elevação dez-indexno meio do desvanecimento deixa a máscara, ainda visível, repintar por baixo daquilo sobre o que estava erguida.