Modal
Standalone component with CDK Overlay/Portal/A11y, focus trap, backdrop, Escape, and focus return.
Design
Anatomy
Review changes 4
Check the details before applying this update.
The update can be reverted later from the activity log.
6.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.
Interactive example · Angular
Running with the native Angular component and @angular/cdk/overlay + portal + a11y.
Sizes · Angular
Three independent dialogs running through the Angular component with CDK Overlay.
Custom body · Angular
Content projection with public Form Field, Input, and Buttons inside the Angular Overlay.
Token mapping
| Property | Token | CSS 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-* |
Usage
When to use
- A short edit or review must finish before returning to the previous context.
- The content requires focused attention but does not justify a dedicated page.
When not to use
- The task is long, has multiple steps, or must remain visible alongside the page.
- The confirmation is destructive or critical; that case belongs to the separate Alert Dialog contract.
When to choose this output
Choose this native Angular output when the application already uses Angular. It consumes the same DS CSS and has no dependency on the Web runtime or React adapters.
Implementation
- Status
- Beta
- Distribution
- Validated Angular tarball
Installation
npm install ./dist/tis-angular-0.0.0-beta.0.tgz ds-tispnpm add ./dist/tis-angular-0.0.0-beta.0.tgz ds-tisyarn add ./dist/tis-angular-0.0.0-beta.0.tgz ds-tisbun add ./dist/tis-angular-0.0.0-beta.0.tgz ds-tisDS styles
@import "ds-tis/css";Angular imports
import { TisButton } from '@tis/angular/button'
import {
TisModal,
TisModalBody,
TisModalFooter,
TisModalInitialFocus,
} from '@tis/angular/modal'Template
<tis-button (click)="modalOpen.set(true)">Revisar alterações</tis-button>
<tis-modal
#modal
title="Revisar alterações"
description="Confira os dados antes de continuar."
size="md"
[open]="modalOpen()"
(openChange)="modalOpen.set($event)"
>
<div tisModalBody>Conteúdo curto da tarefa.</div>
<div tisModalFooter>
<tis-button (click)="modal.close('api')">Guardar</tis-button>
</div>
</tis-modal>Public contract
- Package
@tis/angular- Entrypoint
@tis/angular/modal- Primitive
@angular/cdk/overlay + portal + a11y- Status
- Beta
Accessibility
Keyboard interaction
| Key | Action |
|---|---|
Tab | Cycles focus inside the modal (focus trap) |
Shift+Tab | Cycles focus backwards inside the modal |
Escape | Closes the modal |
Enter | Activates the focused button |
Focus must be trapped inside the modal while it is open. When the modal closes, focus must return to the element that triggered it.
Accessibility
| WCAG criterion | Requirement | Status |
|---|---|---|
| 2.4.3 Focus Order (A) | Focus moves into the modal on open, is trapped inside, and returns to the trigger on close | ✓ |
| 2.4.11 Focus Appearance (AA) | Focus ring visible on all interactive elements inside the modal | ✓ |
| 4.1.2 Name, Role, Value (A) | role="dialog", aria-modal="true", aria-labelledby pointing to the title | ✓ |
| 1.4.11 Non-text Contrast (AA) | Close button has at least 3:1 contrast ratio against the modal surface | ✓ |
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.
Output responsibility
Preserve the modal dialog contract and validate focus trap, Escape, backdrop dismiss, scroll blocking, ARIA relationships, and focus return.
Validation evidence
Covered by its independent Storybook output plus browser, responsive, keyboard, Axe, and bundle checks.