TIS Design System

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

Use modals quandoUse modals when
Uma ação requer confirmação (deletar, descartar), uma tarefa curta pode ser concluída sem perder contexto (editar perfil), ou conteúdo focado precisa ser apresentado (termos de serviço, etapas de onboarding).An action requires confirmation (delete, discard), a short task can be completed without losing context (edit profile), or focused content needs to be presented (terms of service, onboarding steps).
Não use modals quandoDon't use modals when
A tarefa e complexa ou multi-etapas — use uma página dedicada. Para feedback não bloqueante, use Alert ou Toast. Nunca aninhe modals dentro de outros modals.The task is complex or multi-step — use a dedicated page instead. For non-blocking feedback, use an Alert or Toast. Never nest modals inside other modals.

AnatomiaAnatomy

1
1 Overlay (.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

Faça
Ação principal a direita, Cancel a esquerda. Use outline/ghost para secundário.Primary action right, Cancel left. Use outline/ghost for secondary.
Não faça
Dois buttons Brand no footer criam confusão sobre prioridade.Two Brand buttons in footer creates confusion about priority.
Modal pequeno para confirmacoesSmall modal for confirmations
Faça
Use o menor tamanho que cabe. Small para confirmacoes, medium para formulários, large para conteúdo longo.Use the smallest size that fits. Small for confirmations, medium for forms, large for long content.
Modal large para "Tem certeza?"Large modal for "Are you sure?"
Não faça
Modals large para confirmacoes simples desperdicam espaco e adicionam peso visual desnecessário.Large modals for simple confirmations waste space and add unnecessary visual weight.
Foco preso dentro + retorna ao fecharFocus trapped inside + returns on close
Faça
Retorne o foco ao elemento que disparou o modal ao fechar. Feche com Escape e clique no backdrop para ações não destrutivas.Return focus to the trigger element on close. Close on Escape and backdrop click for non-destructive actions.
Clique no backdrop fecha modal destrutivoBackdrop click closes destructive modal
Não faça
Deixar o foco escapar do modal. Fechar com clique no backdrop para ações destrutivas (o usuário pode perder dados acidentalmente).Let focus escape the modal. Close on backdrop click for destructive actions (user might lose data accidentally).

Diretrizes de conteúdoContent guidelines

RegraRuleExemploExample
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 verbTí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 buttonds-button--danger para delete, remove, revokefor delete, remove, revoke

Mapeamento de tokensToken mapping

PropriedadePropertyTokenVariável CSSCSS variable
overlay backgroundcomponent.modal.overlay.bg.default--ds-modal-overlay-bg-default
surfacesemantic.surface.elevated--ds-surface-elevated
overlay paddingcomponent.modal.overlay.padding.default--ds-modal-overlay-padding-default
overlay z-indexcomponent.modal.overlay.z-index.default--ds-modal-overlay-z-index-default
border-radiuscomponent.modal.radius.default--ds-modal-radius-default
shadowcomponent.modal.shadow.default--ds-modal-shadow-default
max-widthcomponent.modal.max-width.{sm,md,lg}--ds-modal-max-width-*
header spacingcomponent.modal.header.{padding-*,gap}.*--ds-modal-header-*-*
body spacing/typographycomponent.modal.body.{padding-*,font-size,line-height,font-weight}.*--ds-modal-body-*-*
description typographycomponent.modal.body.{font-size,line-height,font-weight}.*--ds-modal-body-*-*
title typographycomponent.modal.title.{font-size,line-height,font-weight,letter-spacing}.*--ds-modal-title-*-*
footer spacingcomponent.modal.footer.{padding-*,gap}.*--ds-modal-footer-*-*
close targetcomponent.modal.close.size.{sm,md,lg}--ds-modal-close-size-*
close iconcomponent.modal.close.icon-size.{sm,md,lg}--ds-modal-close-icon-size-*
close paddingcomponent.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

ClasseClassDescriçãoDescription
ds-modal-overlayOverlay backdrop em tela cheiaFull-screen backdrop overlay
ds-modalContainer do diálogo modalModal dialog container
ds-modal--smTamanho pequeno (--ds-modal-max-width-sm)Small size (--ds-modal-max-width-sm)
ds-modal--mdTamanho médio (--ds-modal-max-width-md)Medium size (--ds-modal-max-width-md)
ds-modal--lgTamanho 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__headingWrapper opcional de título e descriçãoOptional title and description wrapper
ds-modal__titleTexto do título do modalModal title text
ds-modal__descriptionTexto descritivo opcional do headerOptional descriptive text in the header
ds-modal__closeButton 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

TeclaKeyAçãoAction
TabCicla o foco dentro do modal (focus trap)Cycles focus inside the modal (focus trap)
Shift+TabCicla o foco para tras dentro do modalCycles focus backwards inside the modal
EscapeFecha o modalCloses the modal
EnterAtiva 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 criterionRequisitoRequirementStatus
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
Resumo de atributos ARIAARIA attributes summary
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.

RelacionadosRelated