Dialog

Modal dialog for confirmations, forms, and alerts. Uses the native <dialog> element opened with showModal().

Titled variants expose a Title (and optional Description). Bare variants name the dialog with aria-label (and optional aria-description) instead.

React/Vue require Root for id context. HTML usually wraps Trigger + Content in a div, but the core keys off the mct:dialog-open / mcc:dialog / mct:dialog-close ids.

Composition

Root (optional in HTML; required for React/Vue)
  Trigger
  Content
    Title (titled variants)
    Description (optional even when titled)
    Close (typically one)
    (optional body)

Usage

HTML

html
<button
  type="button"
  id="mct:dialog-open:notice"
  aria-haspopup="dialog"
  aria-controls="mcc:dialog:notice"
>
  Open dialog
</button>
<dialog
  id="mcc:dialog:notice"
  aria-labelledby="mcc:dialog-title:notice"
  aria-describedby="mcc:dialog-description:notice"
  tabindex="-1"
>
  <h2 id="mcc:dialog-title:notice">Heads up</h2>
  <p id="mcc:dialog-description:notice">
    This is a modal dialog. Press Escape or use the close button to dismiss.
  </p>
  <button type="button" id="mct:dialog-close:notice">Close</button>
</dialog>

React

jsx
import { Dialog } from "@monochrome-ui/react"

<Dialog.Root>
  <Dialog.Trigger>Open dialog</Dialog.Trigger>
  <Dialog.Content>
    <Dialog.Title>Heads up</Dialog.Title>
    <Dialog.Description>
      This is a modal dialog. Press Escape or use the close button to dismiss.
    </Dialog.Description>
    <Dialog.Close>Close</Dialog.Close>
  </Dialog.Content>
</Dialog.Root>

Vue

vue
<script setup lang="ts">
import { Dialog } from "@monochrome-ui/vue"
</script>

<template>
  <Dialog.Root>
    <Dialog.Trigger>Open dialog</Dialog.Trigger>
    <Dialog.Content>
      <Dialog.Title>Heads up</Dialog.Title>
      <Dialog.Description>
        This is a modal dialog. Press Escape or use the close button to dismiss.
      </Dialog.Description>
      <Dialog.Close>Close</Dialog.Close>
    </Dialog.Content>
  </Dialog.Root>
</template>

Parts

Root

Optional in HTML; required for React/Vue (context). Element: div. Children: Trigger then Content.

Trigger

Required. Element: button.

Kind Name Type / value Default Description
attr type "button"
attr id mct:dialog-open:<id>
attr aria-haspopup "dialog"
attr aria-controls mcc:dialog:<id>
attr aria-disabled "true" | absent absent When "true", core ignores open attempts.
prop disabled boolean false Applies aria-disabled on the trigger.

Trigger never carries aria-label or aria-description for bare dialogs.

Content

Required. Element: dialog. Children: Title? Description? Close (and optional body).

Kind Name Type / value Default Description
attr id mcc:dialog:<id>
attr tabindex "-1"
attr role "alertdialog" | absent absent Blocking alert semantics.
attr aria-labelledby mcc:dialog-title:<id> | absent Titled variants with a Title. Omit for bare (aria-label).
attr aria-describedby mcc:dialog-description:<id> | absent When a Description is present (HTML), or whenever wrappers have not received aria-description.
attr aria-label string | absent Accessible name for bare variants.
attr aria-description string | absent Accessible description for bare variants; also clears wrapper default aria-describedby.
prop role "alertdialog" | absent absent Same as the role attribute.
prop aria-label string | absent Switches Content into bare labelling (skips aria-labelledby).
prop aria-description string | absent Bare description (skips aria-describedby in wrappers).

Title

Optional (titled variants; omit for bare). Element: h2 (or h1-h6).

Kind Name Type / value Default Description
attr id mcc:dialog-title:<id>
prop as "h1""h6" "h2" Heading level. Change only to match the page outline.

Description

Optional even for titled; omit for bare and title-only. Element: p.

Kind Name Type / value Description
attr id mcc:dialog-description:<id>

Close

Required (typically one). Element: button.

Kind Name Type / value Description
attr type "button"
attr id mct:dialog-close:<id> Same <id> as the open trigger / content when using a single close. Multiple closes need distinct ids under the mct:dialog-c prefix; do not duplicate the same id.

Notes

  • Titled: Content has aria-labelledby pointing at Title. Include Title. Description and aria-describedby are optional; add them when a description is present (HTML may omit both Description and aria-describedby, as in form fixtures).
  • alertdialog (titled): Same labelling as titled, plus role="alertdialog" on Content.
  • bare: Omit Title and Description; set aria-label on Content.
  • bare-alertdialog: Bare labelling plus role="alertdialog".
  • HTML bare: Omit aria-labelledby and aria-describedby; add aria-description only when needed.
  • Wrappers bare: aria-label skips aria-labelledby. aria-describedby is still emitted unless aria-description is passed. Pass aria-description when you need a description, or accept a dangling describedby id if you omit it.
  • Disabled: aria-disabled="true" on Trigger (HTML) or Trigger disabled (wrappers); core will not open.
  • Open behavior: Core calls showModal(). Only one dialog at a time; opening is a no-op if another is already open. Initial focus follows the HTML dialog focusing steps (autofocus, then first focusable, then the dialog).
  • Close: Close control, Escape (after tooltips/menus/popovers), or native dialog close. Focus returns to the open trigger.

Keyboard

Key Behavior
Enter / Space on Trigger Open (synthesized click)
Enter / Space on Close Close
Escape Close open dialog (after dismissing tooltip / menu / popover layers)
Tab Native modal focus trap inside <dialog>

See also