dialog

Modal dialog structure

Compose a modal from a declarative invoker and native dialog. The browser owns modal focus and disclosure; AngularTS owns form values and application actions.

<section class="dialog">
  <button commandfor="profile-dialog" command="show-modal">Edit profile</button>
  <dialog id="profile-dialog">
    <header>
      <h2>Edit profile</h2>
      <p>Update your public profile.</p>
    </header>
    <button commandfor="profile-dialog" command="close">Save changes</button>
  </dialog>
</section>

Profile Dialog

Example

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Dialog</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body
    ng-app="angular.css"
    ng-cloak
    ng-init="profile={name:'Pedro Duarte',username:'@peduarte'}; savedName='Pedro Duarte'"
    data-example="dialog-demo"
  >
    <main class="visual-example">
      <section id="profile-dialog" class="dialog">
        <button
          commandfor="profile-dialog-content"
          command="show-modal"
          variant="outline"
        >
          Edit profile
        </button>
        <dialog
          id="profile-dialog-content"
          closedby="any"
          aria-labelledby="profile-dialog-title"
          aria-describedby="profile-dialog-description"
        >
          <header>
            <h2 id="profile-dialog-title">Edit profile</h2>
            <p id="profile-dialog-description">
              Make changes to your profile here. Click save when you're done.
            </p>
          </header>
          <form method="dialog">
            <label for="dialog-name">
              <span>Name</span>
              <input id="dialog-name" ng-model="profile.name" />
            </label>
            <label for="dialog-username">
              <span>Username</span>
              <input id="dialog-username" ng-model="profile.username" />
            </label>
            <footer>
              <button
                type="button"
                commandfor="profile-dialog-content"
                command="close"
                variant="outline"
              >
                Cancel
              </button>
              <button
                type="button"
                commandfor="profile-dialog-content"
                command="close"
                ng-click="savedName=profile.name"
              >
                Save changes
              </button>
            </footer>
          </form>
          <button
            type="button"
            commandfor="profile-dialog-content"
            command="close"
            variant="ghost"
            size="icon-sm"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="M18 6 6 18"></path>
              <path d="m6 6 12 12"></path>
            </svg>
            <span class="visually-hidden">Close</span>
          </button>
        </dialog>
      </section>
      <output class="dialog-output" aria-live="polite">
        Saved profile: <span ng-bind="savedName"></span>
      </output>
    </main>
  </body>
</html>

Close Controls

Corner and footer close actions are distinct, and the corner control can be omitted without changing modal behavior.

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Dialog Close Workflows</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body
    ng-app="angular.css"
    ng-cloak
    data-example="dialog-close-button dialog-no-close-button"
  >
    <main class="dialog-workflow-grid visual-example">
      <section class="dialog-workflow" aria-labelledby="share-heading">
        <h2 id="share-heading">Custom close action</h2>
        <div id="share-dialog" class="dialog">
          <button
            variant="outline"
            type="button"
            commandfor="share-dialog-content"
            command="show-modal"
          >
            Share
          </button>
          <dialog
            id="share-dialog-content"
            closedby="any"
            aria-labelledby="share-dialog-title"
            aria-describedby="share-dialog-description"
          >
            <header>
              <h3 id="share-dialog-title">Share link</h3>
              <p id="share-dialog-description">
                Anyone who has this link will be able to view this project.
              </p>
            </header>
            <div class="dialog-copy-row">
              <input
                aria-label="Share URL"
                value="https://example.com/link/to/document"
                readonly
              />
              <button variant="secondary">Copy Link</button>
            </div>
            <footer>
              <button
                variant="outline"
                commandfor="share-dialog-content"
                command="close"
              >
                Close
              </button>
            </footer>
            <button
              variant="ghost"
              size="icon-sm"
              commandfor="share-dialog-content"
              command="close"
            >
              <svg
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                aria-hidden="true"
              >
                <path d="M18 6 6 18"></path>
                <path d="m6 6 12 12"></path>
              </svg>
              <span class="visually-hidden">Close</span>
            </button>
          </dialog>
        </div>
      </section>

      <section class="dialog-workflow" aria-labelledby="plain-heading">
        <h2 id="plain-heading">Without corner close</h2>
        <div id="plain-dialog" class="dialog">
          <button
            variant="outline"
            type="button"
            commandfor="plain-dialog-content"
            command="show-modal"
          >
            Open dialog
          </button>
          <dialog
            id="plain-dialog-content"
            closedby="any"
            aria-labelledby="plain-dialog-title"
            aria-describedby="plain-dialog-description"
          >
            <header>
              <h3 id="plain-dialog-title">Terms of service</h3>
              <p id="plain-dialog-description">
                Review the current terms before continuing.
              </p>
            </header>
            <p>Your workspace data remains available to members with access.</p>
            <footer>
              <button commandfor="plain-dialog-content" command="close">
                Continue
              </button>
            </footer>
          </dialog>
        </div>
      </section>
    </main>
  </body>
</html>

Scrolling

Use a section directly inside the native dialog or its form for scrollable content, and add class="scroll-area" for native overflow. Keep the footer outside that section when its actions must remain visible.

View source
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Dialog Scroll Workflows</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body
    ng-app="angular.css"
    ng-cloak
    data-example="dialog-scrollable-content dialog-sticky-footer"
  >
    <main class="dialog-workflow-grid visual-example">
      <section class="dialog-workflow" aria-labelledby="scroll-heading">
        <h2 id="scroll-heading">Scrollable content</h2>
        <div id="scroll-dialog" class="dialog">
          <button
            variant="outline"
            type="button"
            commandfor="scroll-dialog-content"
            command="show-modal"
          >
            Read profile
          </button>
          <dialog
            size="wide"
            id="scroll-dialog-content"
            closedby="any"
            aria-labelledby="scroll-dialog-title"
            aria-describedby="scroll-dialog-description"
          >
            <header>
              <h3 id="scroll-dialog-title">About Pedro Duarte</h3>
              <p id="scroll-dialog-description">
                A short professional biography.
              </p>
            </header>
            <section class="dialog-prose">
              <p>
                Pedro is a designer and developer focused on accessible design
                systems and thoughtful product interfaces.
              </p>
              <p>
                He works across interaction design, component architecture, and
                documentation to make complex systems easier to use.
              </p>
              <p>
                His projects explore how strong defaults and composable APIs can
                support teams without limiting their visual language.
              </p>
              <p>
                Outside product work, he studies typography, browser behavior,
                and the details that make keyboard workflows feel dependable.
              </p>
              <p>
                He has collaborated with distributed teams building tools for
                creators, engineers, and operations specialists.
              </p>
              <p>
                This final paragraph ensures the body has genuine overflow and
                remains independently scrollable inside the viewport.
              </p>
            </section>
            <button
              variant="ghost"
              size="icon-sm"
              commandfor="scroll-dialog-content"
              command="close"
            >
              <svg
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                aria-hidden="true"
              >
                <path d="M18 6 6 18"></path>
                <path d="m6 6 12 12"></path>
              </svg>
              <span class="visually-hidden">Close</span>
            </button>
          </dialog>
        </div>
      </section>

      <section class="dialog-workflow" aria-labelledby="sticky-heading">
        <h2 id="sticky-heading">Sticky footer</h2>
        <div id="sticky-dialog" class="dialog">
          <button
            variant="outline"
            type="button"
            commandfor="sticky-dialog-content"
            command="show-modal"
          >
            Review notes
          </button>
          <dialog
            size="wide"
            id="sticky-dialog-content"
            closedby="any"
            aria-labelledby="sticky-dialog-title"
            aria-describedby="sticky-dialog-description"
          >
            <header>
              <h3 id="sticky-dialog-title">Release notes</h3>
              <p id="sticky-dialog-description">
                Review the changes included in this release.
              </p>
            </header>
            <section class="dialog-prose">
              <p>
                Focus is now contained inside modal dialogs while they are open.
              </p>
              <p>
                Background content becomes inert and document scrolling is
                restored when the dialog closes.
              </p>
              <p>
                Internal triggers receive generated relationships to their
                directly owned dialog content.
              </p>
              <p>
                Nested overlays no longer acquire controls belonging to a child
                overlay root.
              </p>
              <p>
                Scrollable bodies retain the header and footer in stable
                positions throughout keyboard and pointer interaction.
              </p>
              <p>
                Logical positioning keeps the corner close control aligned in
                both left-to-right and right-to-left layouts.
              </p>
            </section>
            <footer>
              <button
                variant="outline"
                commandfor="sticky-dialog-content"
                command="close"
              >
                Cancel
              </button>
              <button commandfor="sticky-dialog-content" command="close">
                Confirm
              </button>
            </footer>
          </dialog>
        </div>
      </section>
    </main>
  </body>
</html>

Right To Left

View source
<!doctype html>
<html lang="ar" dir="rtl">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>AngularCSS Dialog Rtl</title>
    <link rel="stylesheet" href="../../css/preflight.css" />
    <link rel="stylesheet" href="../../css/angular.css" />
    <link rel="stylesheet" href="../example.css" />
    <script src="../../js/angular-ts.umd.js"></script>
    <script src="../../js/angular-css.umd.js"></script>
  </head>
  <body
    ng-app="angular.css"
    ng-cloak
    ng-init="profile={name:'محمد علي',username:'@mohamed'}"
    data-example="dialog-rtl"
  >
    <main class="visual-example">
      <section id="rtl-profile-dialog" class="dialog">
        <button
          variant="outline"
          type="button"
          commandfor="rtl-profile-dialog-content"
          command="show-modal"
        >
          تعديل الملف الشخصي
        </button>
        <dialog
          id="rtl-profile-dialog-content"
          closedby="any"
          aria-labelledby="rtl-profile-dialog-title"
          aria-describedby="rtl-profile-dialog-description"
        >
          <header>
            <h2 id="rtl-profile-dialog-title">تعديل الملف الشخصي</h2>
            <p id="rtl-profile-dialog-description">
              غيّر بيانات ملفك الشخصي ثم احفظ التعديلات.
            </p>
          </header>
          <section>
            <label for="rtl-name">
              <span>الاسم</span>
              <input id="rtl-name" ng-model="profile.name" />
            </label>
            <label for="rtl-username">
              <span>اسم المستخدم</span>
              <input id="rtl-username" ng-model="profile.username" />
            </label>
          </section>
          <footer>
            <button
              variant="outline"
              commandfor="rtl-profile-dialog-content"
              command="close"
            >
              إلغاء
            </button>
            <button commandfor="rtl-profile-dialog-content" command="close">
              حفظ التغييرات
            </button>
          </footer>
          <button
            variant="ghost"
            size="icon-sm"
            commandfor="rtl-profile-dialog-content"
            command="close"
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              aria-hidden="true"
            >
              <path d="M18 6 6 18"></path>
              <path d="m6 6 12 12"></path>
            </svg>
            <span class="visually-hidden">إغلاق</span>
          </button>
        </dialog>
      </section>
    </main>
  </body>
</html>

Installation

Load the AngularCSS stylesheet. This entry needs no AngularCSS JavaScript or angular.css module dependency. Add AngularTS when using application bindings such as ng-model or ng-click. See Installation for the complete setup.

This entry uses native HTML and CSS. AngularCSS registers no runtime directive for it. Native dialog top-layer and modal behavior.

Anatomy

Root styling selector

  • .dialog

Semantic structure

Use .dialog as a composition wrapper containing a native invoker button and dialog. Close controls use command=close; semantic headers, sections, forms, and footers need no anatomy classes or nested AngularCSS attributes.

API

Attributes and state

AttributeAccessPurpose
closedbyAuthoredNative dialog dismissal behavior.
commandAuthoredNative invoker action such as show-modal or close.
commandforAuthoredID of the native dialog controlled by an invoker.
dirAuthoredText and interaction direction: ltr or rtl.
sizeAuthoredDialog width: wide; omit for the default width.

Attributes remain authored HTML, native state, or AngularTS inputs. AngularCSS does not write element state.

CSS custom properties

This styling hook does not define component-specific CSS custom properties.

DOM events

This component does not emit a component-specific custom event.

Native DOM events continue to work normally. AngularTS event directives such as ng-click and ng-keydown, plus the data-change model callback, remain application-owned.

Behavior

A native dialog opened with command=show-modal owns top-layer rendering, modal focus, Escape, background isolation, and trigger focus restoration. Declarative command=close controls dismiss it. AngularTS remains responsible for form models, validation, submission, authored content, and application state.

AngularCSS does not replace AngularTS interpolation, bindings, structural directives, form controllers, validation, or application state.

Accessibility

Use a visible title and description connected with aria-labelledby and aria-describedby. Native modal dialogs contain focus, isolate the background, close on Escape, and restore focus to their declarative invoker.

Authored accessible names and relationships are preserved. Test the final composition with keyboard navigation and assistive technology because labels and content come from the application.

Customization

Target semantic elements, native state selectors, and component classes with ordinary CSS. Behavior and accessible state remain with native HTML and AngularTS; visual choices belong in the application stylesheet.

Read Customization for layer order, design tokens, state variants, and iframe demo isolation.