Modal
Diálogos que aparecem acima do conteúdo da página. Modals bloqueiam a interação com o restante da página e suportam header opcional, descrição e composição customizada no body.Dialogs that appear above the page content. Modals block interaction with the rest of the page and support optional header, description, and custom body composition.
Quando usarWhen to use
AnatomiaAnatomy
Review changes 4
Check the details before applying this update.
The update can be reverted later from the activity log.
6.ds-modal-overlay) — backdrop em tela cheia que bloqueia interação com a página.2 Container (
.ds-modal) — superfície elevada com border-radius e sombra.3 Header (
.ds-modal__header) — contém heading opcional, descrição e button de fechar.4 Título (
.ds-modal__title) — vinculado via aria-labelledby.5 Button de fechar (
.ds-modal__close) — dispensa o modal.6 Corpo (
.ds-modal__body) — área de conteúdo com scroll.7 Rodape (
.ds-modal__footer) — buttons de ação alinhados a direita.1 Overlay (.ds-modal-overlay) — full-screen backdrop that blocks page interaction.2 Container (
.ds-modal) — elevated surface with border-radius and shadow.3 Header (
.ds-modal__header) — contains optional heading, description, and close button.4 Title (
.ds-modal__title) — linked via aria-labelledby.5 Close button (
.ds-modal__close) — dismisses the modal.6 Body (
.ds-modal__body) — scrollable content area.7 Footer (
.ds-modal__footer) — action buttons aligned right.
Pequeno (480px)Small (480px)
Use para confirmações curtas e decisões binárias. O close usa alvo interativo de 32px com ícone de 20px.Use for short confirmations and binary decisions. The close control uses a 32px interactive target with a 20px icon.
<div class="ds-modal-overlay">
<div class="ds-modal ds-modal--sm" role="dialog"
aria-modal="true" aria-labelledby="modal-title">
<div class="ds-modal__header">
<h3 id="modal-title" class="ds-modal__title">Review item</h3>
<button class="ds-modal__close" aria-label="Close modal">
<i data-lucide="x" class="ds-icon"></i>
</button>
</div>
<div class="ds-modal__body">
<p>Review the item details before applying this update.</p>
</div>
<div class="ds-modal__footer">
<button class="ds-button ds-button--ghost ds-button--sm">Cancel</button>
<button class="ds-button ds-button--brand ds-button--sm">Apply</button>
</div>
</div>
</div>
Médio (640px)Medium (640px)
Use como tamanho padrão para confirmações com contexto e edição simples. O close usa alvo interativo de 40px com ícone de 24px.Use as the default size for confirmations with context and simple editing. The close control uses a 40px interactive target with a 24px icon.
<div class="ds-modal-overlay">
<div class="ds-modal ds-modal--md" role="dialog"
aria-modal="true" aria-labelledby="modal-title">
<div class="ds-modal__header">
<h3 id="modal-title" class="ds-modal__title">Review changes</h3>
<button class="ds-modal__close" aria-label="Close modal">
<i data-lucide="x" class="ds-icon"></i>
</button>
</div>
<div class="ds-modal__body">
<p>Review the pending changes before applying them.</p>
</div>
<div class="ds-modal__footer">
<button class="ds-button ds-button--ghost">Cancel</button>
<button class="ds-button ds-button--brand">Apply changes</button>
</div>
</div>
</div>
Grande (800px)Large (800px)
Use para conteúdo mais denso que ainda não justifica uma página dedicada. O close usa alvo interativo de 48px com ícone de 24px para reforçar acessibilidade.Use for denser content that still does not justify a dedicated page. The close control uses a 48px interactive target with a 24px icon to reinforce accessibility.
<div class="ds-modal-overlay">
<div class="ds-modal ds-modal--lg" role="dialog"
aria-modal="true" aria-labelledby="modal-title">
<div class="ds-modal__header">
<h3 id="modal-title" class="ds-modal__title">Terms of service</h3>
<button class="ds-modal__close" aria-label="Close modal">
<i data-lucide="x" class="ds-icon"></i>
</button>
</div>
<div class="ds-modal__body">
<p>Please read and accept the terms...</p>
</div>
<div class="ds-modal__footer">
<button class="ds-button ds-button--ghost ds-button--lg">Cancel</button>
<button class="ds-button ds-button--brand ds-button--lg">Accept terms</button>
</div>
</div>
</div>
Body customizadoCustom body
Use o body como slot quando o modal precisar receber formulário, lista, preview ou outra composição. Header, descrição e footer podem ser omitidos quando o conteúdo customizado já carrega esse contexto.Use the body as a slot when the modal needs to host a form, list, preview, or other composition. Header, description, and footer can be omitted when the custom content already carries that context.
<div class="ds-modal ds-modal--md" role="dialog"
aria-modal="true" aria-labelledby="modal-title">
<div class="ds-modal__header">
<div class="ds-modal__heading">
<h3 id="modal-title" class="ds-modal__title">Invite members</h3>
<p class="ds-modal__description">Add people to this workspace.</p>
</div>
<button class="ds-modal__close" aria-label="Close modal">...</button>
</div>
<div class="ds-modal__body">
<div class="ds-field">
<div class="ds-field__label-row">
<label class="ds-field__label" for="invite-email">Email address</label>
</div>
<div class="ds-input">
<input type="email" id="invite-email" class="ds-input__field"
placeholder="name@company.com" aria-describedby="invite-helper">
</div>
<span class="ds-field__helper" id="invite-helper">People added here will receive access to the workspace.</span>
</div>
<button class="ds-button ds-button--brand">Send invite</button>
</div>
</div>
Boas práticasBest practices
Diretrizes de conteúdoContent guidelines
| RegraRule | ExemploExample |
|---|---|
| Título: ação + objetoTitle: action + object | "Delete project" — não "Warning" ou "Confirm""Delete project" — not "Warning" or "Confirm" |
| Corpo: 1-3 frases, explique consequenciasBody: 1-3 sentences, explain consequences | "This action cannot be undone. All data will be permanently removed." |
| Button primario: corresponda ao verbo do títuloPrimary button: match title verb | Título "Delete project" -> button "Delete"Title "Delete project" -> button "Delete" |
| Button secundário: sempre "Cancel"Secondary button: always "Cancel" | "Cancel" — não "Go back" ou "Close""Cancel" — not "Go back" or "Close" |
| Ações destrutivas: use button DangerDestructive actions: use Danger button | ds-button--danger para delete, remove, revokefor delete, remove, revoke |
Mapeamento de tokensToken mapping
| PropriedadeProperty | Token | Variável CSSCSS variable |
|---|---|---|
| overlay background | component.modal.overlay.bg.default | --ds-modal-overlay-bg-default |
| surface | semantic.surface.elevated | --ds-surface-elevated |
| overlay padding | component.modal.overlay.padding.default | --ds-modal-overlay-padding-default |
| overlay z-index | component.modal.overlay.z-index.default | --ds-modal-overlay-z-index-default |
| border-radius | component.modal.radius.default | --ds-modal-radius-default |
| shadow | component.modal.shadow.default | --ds-modal-shadow-default |
| max-width | component.modal.max-width.{sm,md,lg} | --ds-modal-max-width-* |
| header spacing | component.modal.header.{padding-*,gap}.* | --ds-modal-header-*-* |
| body spacing/typography | component.modal.body.{padding-*,font-size,line-height,font-weight}.* | --ds-modal-body-*-* |
| description typography | component.modal.body.{font-size,line-height,font-weight}.* | --ds-modal-body-*-* |
| title typography | component.modal.title.{font-size,line-height,font-weight,letter-spacing}.* | --ds-modal-title-*-* |
| footer spacing | component.modal.footer.{padding-*,gap}.* | --ds-modal-footer-*-* |
| close target | component.modal.close.size.{sm,md,lg} | --ds-modal-close-size-* |
| close icon | component.modal.close.icon-size.{sm,md,lg} | --ds-modal-close-icon-size-* |
| close padding | component.modal.close.padding.{sm,md,lg} | --ds-modal-close-padding-* |
Runtime obrigatórioRequired runtime
O CSS define anatomia e estados visuais, mas o contrato acessível do Modal — focus trap, Escape, inert no backdrop e retorno de foco — depende do módulo público ds-tis/modal (initModals / destroyModals). Eventos: ds-modal-open, ds-modal-close. Ao usar Modal interativamente, inicializar o runtime é parte da API; chame destroyModals ao desmontar.CSS defines anatomy and visual states, but Modal's accessible contract — focus trap, Escape, backdrop inert, and focus return — depends on the public ds-tis/modal module (initModals / destroyModals). Events: ds-modal-open, ds-modal-close. When Modal is used interactively, initializing the runtime is part of the API; call destroyModals on unmount.
<button type="button" data-ds-modal-open="confirm-modal">Delete</button>
<div class="ds-modal-overlay" id="confirm-modal" hidden>
<div class="ds-modal ds-modal--sm" role="dialog" aria-modal="true"
aria-labelledby="confirm-title">
<div class="ds-modal__header">
<h3 id="confirm-title" class="ds-modal__title">Delete item</h3>
<button class="ds-modal__close" type="button" aria-label="Close modal">…</button>
</div>
…
</div>
</div>
<script type="module">
import { initModals, destroyModals } from 'ds-tis/modal';
initModals();
// destroyModals() ao desmontar a view
</script>
Classes CSSCSS classes
| ClasseClass | DescriçãoDescription |
|---|---|
ds-modal-overlay | Overlay backdrop em tela cheiaFull-screen backdrop overlay |
ds-modal | Container do diálogo modalModal dialog container |
ds-modal--sm | Tamanho pequeno (--ds-modal-max-width-sm)Small size (--ds-modal-max-width-sm) |
ds-modal--md | Tamanho médio (--ds-modal-max-width-md)Medium size (--ds-modal-max-width-md) |
ds-modal--lg | Tamanho grande (--ds-modal-max-width-lg)Large size (--ds-modal-max-width-lg) |
ds-modal__header | Área de header com título e button de fecharHeader area with title and close button |
ds-modal__heading | Wrapper opcional de título e descriçãoOptional title and description wrapper |
ds-modal__title | Texto do título do modalModal title text |
ds-modal__description | Texto descritivo opcional do headerOptional descriptive text in the header |
ds-modal__close | Button de fechar no header; o elemento representa o alvo interativo, não apenas o íconeClose button in the header; the element represents the interactive target, not only the icon |
ds-modal__body | Área de conteúdo do corpo com scrollScrollable body content area |
ds-modal__footer | Área de rodapé para buttons de açãoFooter area for action buttons |
Interação por tecladoKeyboard interaction
| TeclaKey | AçãoAction |
|---|---|
Tab | Cicla o foco dentro do modal (focus trap)Cycles focus inside the modal (focus trap) |
Shift+Tab | Cicla o foco para tras dentro do modalCycles focus backwards inside the modal |
Escape | Fecha o modalCloses the modal |
Enter | Ativa o button focadoActivates the focused button |
O foco deve ser preso dentro do modal enquanto estiver aberto. Quando o modal fechar, o foco deve retornar ao elemento que o disparou.Focus must be trapped inside the modal while it is open. When the modal closes, focus must return to the element that triggered it.
AccessibilityAccessibility
| Critério WCAGWCAG criterion | RequisitoRequirement | Status |
|---|---|---|
| 2.4.3 Focus Order (A) | O foco move para dentro do modal ao abrir, fica preso dentro e retorna ao trigger ao fecharFocus moves into the modal on open, is trapped inside, and returns to the trigger on close | ✓ |
| 2.4.11 Focus Appearance (AA) | Focus ring visível em todos os elementos interativos dentro do modalFocus ring visible on all interactive elements inside the modal | ✓ |
| 4.1.2 Name, Role, Value (A) | role="dialog", aria-modal="true", aria-labelledby apontando para o títulorole="dialog", aria-modal="true", aria-labelledby pointing to the title | ✓ |
| 1.4.11 Non-text Contrast (AA) | Button de fechar tem pelo menos 3:1 de ratio de contraste contra a superfície do modalClose button has at least 3:1 contrast ratio against the modal surface | ✓ |
role="dialog" — identifica o modal como um diálogo.aria-modal="true" — informa a tecnologia assistiva que o conteúdo atras do modal esta inerte.aria-labelledby — referencia o id do elemento de título do modal.aria-label="Close modal" — obrigatorio no button de fechar.role="dialog" — identifies the modal as a dialog.aria-modal="true" — informs assistive technology that content behind the modal is inert.aria-labelledby — references the modal title element's id.aria-label="Close modal" — required on the close button.