Dialog
A native dialog on the overlay surface behind a 1px overlay border and the backdrop; modal when shown, with trapped focus and Escape to close.
stable · light DOM · 4.0.0. Light DOM retains static content, native semantics, label relationships and host composition without duplicating controls.
Live variants
default
<j3w1-dialog id="example-dialog-default">
<dialog class="dialog" role="alertdialog" aria-labelledby="example-dialog-default-dg-confirm-title" aria-describedby="example-dialog-default-dg-confirm-text">
<div class="dialog-header">
<h2 class="dialog-title" id="example-dialog-default-dg-confirm-title">Save changes?</h2>
<button class="dialog-close" type="button" aria-label="Close">✕</button>
</div>
<div class="dialog-body">
<p class="dialog-text" id="example-dialog-default-dg-confirm-text">Three settings changed since the last save. Unsaved changes are kept locally until you publish.</p>
</div>
<div class="dialog-actions">
<button class="dialog-button" type="button" data-close="cancel">Keep editing</button>
<button class="dialog-button dialog-button-primary" type="button" data-action="confirm">Save</button>
</div>
<p class="dialog-trap-note">Focus is trapped: Tab cycles inside the dialog; Escape closes it.</p>
</dialog>
<button class="button" type="button" data-open="">Open dialog</button></j3w1-dialog>alert-dialog
<j3w1-dialog id="example-dialog-alert-dialog">
<dialog class="dialog" role="alertdialog" aria-labelledby="example-dialog-alert-dialog-dg-delete-title" aria-describedby="example-dialog-alert-dialog-dg-delete-text">
<div class="dialog-header">
<h2 class="dialog-title" id="example-dialog-alert-dialog-dg-delete-title">Delete profile "workstation"?</h2>
<button class="dialog-close" type="button" aria-label="Close">✕</button>
</div>
<div class="dialog-body">
<p class="dialog-text" id="example-dialog-alert-dialog-dg-delete-text">The profile and its two overlays are removed. This cannot be undone.</p>
</div>
<div class="dialog-actions">
<button class="dialog-button" type="button" data-close="cancel">Cancel</button>
<button class="dialog-button dialog-button-destructive" type="button" data-action="delete">Delete profile</button>
</div>
<p class="dialog-trap-note">Focus is trapped: Tab cycles inside the dialog; Escape cancels.</p>
</dialog>
<button class="button" type="button" data-open="">Open dialog</button></j3w1-dialog>form
<j3w1-dialog id="example-dialog-form">
<dialog class="dialog" aria-labelledby="example-dialog-form-dg-form-title" aria-describedby="example-dialog-form-dg-form-text">
<div class="dialog-header">
<h2 class="dialog-title" id="example-dialog-form-dg-form-title">New profile</h2>
<button class="dialog-close" type="button" aria-label="Close">✕</button>
</div>
<form class="dialog-body" method="dialog">
<p class="dialog-text" id="example-dialog-form-dg-form-text">The profile starts from the default tokens.</p>
<div class="dialog-field">
<label class="dialog-field-label" for="example-dialog-form-dg-form-name">Profile name</label>
<input class="dialog-field-input" id="example-dialog-form-dg-form-name" type="text" value="laptop" aria-describedby="example-dialog-form-dg-form-help" name="example-dialog-form-dg-form-name">
<p class="dialog-field-help" id="example-dialog-form-dg-form-help">Lowercase letters, digits and hyphens.</p>
</div>
<div class="dialog-actions">
<button class="dialog-button" type="button" data-close="cancel">Cancel</button>
<button class="dialog-button dialog-button-primary" type="submit">Create</button>
</div>
</form>
<p class="dialog-trap-note">Focus is trapped: Tab cycles inside the dialog; Enter submits; Escape cancels.</p>
</dialog>
<button class="button" type="button" data-open="">Open dialog</button></j3w1-dialog>Install and register
Use the exact version. If npm publication is pending, install the release tarball from the installation guide.
npm install --save-exact @j3w1/ui@4.0.0import '@j3w1/ui/tokens.css';
import '@j3w1/ui/styles/dialog.css';
import '@j3w1/ui/register/dialog';Insert the maintained markup above after loading these imports. Its native children provide accessible names and form behavior; the empty custom tag alone does not create a control.
Copy the complete implementation
npx --package=@j3w1/ui@4.0.0 j3w1-ui copy dialog --out ./vendor/j3w1/dialogThe CLI verifies every file before writing and refuses overwrites. Serve the output over HTTP and open index.html. Keep its modules, CSS, tokens and licence notices together.
Public API
attributes
| Name | Type / signature | Meaning |
|---|---|---|
disabled | CustomEvent | Disable the native controls owned by this instance. |
loading | CustomEvent | Mark the primary control busy and prevent duplicate activation. |
properties
| Name | Type / signature | Meaning |
|---|---|---|
disabled | boolean | Delegates disabled state to native children. |
name | string | Native control name; application form keys remain explicit. |
open | boolean | Native modal state. |
returnValue | string | Native dialog result. Read only. |
methods
| Name | Type / signature | Meaning |
|---|---|---|
refresh | (): void | Reconnect behavior after replacing native child structure. |
focus | (options?: FocusOptions): void | Focus the first usable native control. |
show | (): void | Open using native showModal and remember the opener. |
close | (value?: string): void | Close with a result value. |
events
| Name | Type / signature | Meaning |
|---|---|---|
j3w1-open | CustomEvent | The modal opened. |
j3w1-close | CustomEvent | The modal closed and restored available opener focus. |
j3w1-cancel | CustomEvent | Native cancel request; prevent default to keep the modal open. |
j3w1-action | CustomEvent | An explicit dialog action; prevent default to keep it open. |
Dependencies and verification
Registered components: none. Style dependencies: button.
Package presence establishes an available implementation. Actual passes depend on the tested artifact and environment. Read specification execution evidence; read packed-consumer evidence; inspect the complete state matrix.
Full implementation manifest, API and token dependency closure
Canonical specification
Dialog dialog
A native dialog on the overlay surface behind a 1px overlay border and the backdrop; modal when shown, with trapped focus and Escape to close.
Report an issue with this component
View source and copy recipe · Download standalone example — visual-only; modal behavior is host-owned.
Tokens
Focus a role to inspect its values; follow its link for persistent usage and source details.
| Part | Role | Value | Use |
|---|---|---|---|
backdrop.bg | color.surface.backdropproposeduse-and-reportD-008 | rgb(0 0 0 / 65%) | Behind modal dialogs. |
root.bg | color.surface.overlayapproveduse | #1c1010 | D-041 surface.overlay: color.primitive.modern.overlay. |
root.border | color.border.overlayapproveduse | #c81a1a | D-041 owner correction: red control and floating-frame borders, distinct from rose text. |
root.ring | color.interaction.focus.ring-containerobserveduse | #ffa2a7 | Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry. |
root.z | z.dialogobserveduse | 30 | |
title.text | color.text.brightapproveduse | #ffa2a7 | D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills. |
text.text | color.text.defaultapproveduse | #e99499 | D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills. |
close.text | color.action.tertiary.textapproveduse | #e99499 | D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills. |
close.bg-hover | color.action.tertiary.hover-bgobserveduse | #1c0a09 | Ghost and icon button hover. |
close.ring | color.interaction.focus.ringobserveduse | #e53935 | 1px dashed focus ring on controls and rows. |
button.text | color.action.secondary.textapproveduse | #ffa2a7 | D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills. |
button.border | color.action.secondary.borderapproveduse | #986f72 | D-041 action.secondary.border: color.primitive.modern.control. |
button.bg-hover | color.action.secondary.hover-bgobserveduse | #630f0d | Secondary button hover. |
primary.bg | color.action.primary.bgapproveduse | #7d1310 | Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12. |
primary.text | color.action.primary.textapproveduse | #ffa2a7 | Primary action labels: bright rose across rest, hover and pressed states (D-033). |
primary.bg-hover | color.action.primary.hover-bgobserveduse | #911410 | Primary button hover; pair with action.primary.text (D-033). |
destructive.bg | color.action.destructive.filled-bgobserveduse | #dc282e | Confirm-dialog destructive fill. |
destructive.text | color.action.destructive.filled-textobserveduse | #f9faf9 | Text on the destructive fill. |
destructive.bg-hover | color.action.destructive.hover-bgobserveduse | #dc282e | Destructive button hover fill. |
field.bg | color.surface.inputapproveduse | #000000 | Form-control fill. |
field.border | color.border.controlapproveduse | #c81a1a | D-041 owner correction: red control and floating-frame borders, distinct from rose text. |
field.label | color.text.brightapproveduse | #ffa2a7 | D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills. |
note.text | color.text.mutedapproveduse | #d4868b | D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills. |
State matrix
Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).
| State | Confirm | Alert dialog | Form |
|---|---|---|---|
default | |||
open | |||
closed | |||
focus-trapped | |||
hover | |||
focus-visible | |||
reduced-motion |
Purpose
A question or small task the page cannot continue without: confirm a destructive action, name a new profile, sign in again. It takes the whole screen’s attention, so it is short, clearly titled and always offers a way out.
Anatomy
Confirmations take no fixed tone (D-015): an ordinary one uses primary, a
destructive one the filled destructive button. The form variant holds a
labelled text field on color.surface.input.
States
| State | Visual | Non-colour channel |
|---|---|---|
| default | the dialog open on its backdrop | open; the page behind is inert |
| open | as default; opacity 0 → 1 within 150 ms on capable hosts | open; focus inside the dialog |
| closed | dialog and backdrop absent | no open; focus back on the opener |
| focus-trapped | when it holds focus, the dialog carries the container ring, 2px solid color.interaction.focus.ring-container at −3px; the note says Tab cycles | Tab never leaves; the note text |
| hover | Close → color.action.tertiary.hover-bg; other buttons take their tone’s hover | cursor: pointer |
| focus-visible | 1px dashed color.interaction.focus.ring at −2px on the focused control; primary uses color.interaction.focus.ring-container, destructive color.action.destructive.filled-text (D-015) | the ring |
| reduced-motion | no opacity transition; present or absent | prefers-reduced-motion |
A dialog is never disabled; a busy dialog disables its confirm button and shows the ⋯ glyph on it.
Keyboard
As in the key table. Enter on the dialog itself never triggers a destructive confirm. On open, focus goes to the first focusable element, or to the dialog when that element is destructive.
Accessibility
Title and first paragraph link through aria-labelledby and
aria-describedby. The theme never re-implements the trap
on a non-modal dialog. The backdrop is decorative and cancels on click only
when Close is also present. Text meets 4.5:1; the overlay border and rings
3:1.
Portability
A toolkit with a native modal window uses it with the overlay border on the frame and records that the backdrop is the host’s; one without a top layer draws the backdrop as a full-window overlay under the dialog.
Non-examples
A glowing or shadowed card. Rounded corners. Sliding or scaling in.
<dialog> shown with show() and a hand-written trap. No title, or the
title only in the accessible name. A destructive confirm as the default
Enter action. A backdrop that closes the dialog with no Close button. A
dialog stacked on a dialog.
Contrast report
23 declared pairs measured on resolved sRGB values with the WCAG 2.x formula; 0 waived (decorative only), 0 failing. A pass is a design target for the named role, not an application-wide conformance claim.
| Pair | State | Kind | Foreground | Background | Ratio | Min | Result |
|---|---|---|---|---|---|---|---|
| dialog default text | default | text | color.text.default #e99499 | color.surface.overlay #1c1010 | 8.10 | 4.5 | pass |
| dialog default border | default | ui | color.border.overlay #c81a1a | color.surface.overlay #1c1010 | 3.20 | 3 | pass |
| dialog open text | open | text | color.text.default #e99499 | color.surface.overlay #1c1010 | 8.10 | 4.5 | pass |
| dialog open border | open | ui | color.border.overlay #c81a1a | color.surface.overlay #1c1010 | 3.20 | 3 | pass |
| dialog focus-trapped text | focus-trapped | text | color.text.default #e99499 | color.surface.overlay #1c1010 | 8.10 | 4.5 | pass |
| dialog focus-trapped border | focus-trapped | ui | color.border.overlay #c81a1a | color.surface.overlay #1c1010 | 3.20 | 3 | pass |
| dialog focus-trapped outline | focus-trapped | ui | color.interaction.focus.ring-container #ffa2a7 | color.surface.overlay #1c1010 | 9.71 | 3 | pass |
| dialog hover text | hover | text | color.action.tertiary.text #e99499 | color.action.tertiary.hover-bg #1c0a09 | 8.35 | 4.5 | pass |
| dialog focus-visible text | focus-visible | text | color.action.tertiary.text #e99499 | color.surface.overlay #1c1010 | 8.10 | 4.5 | pass |
| dialog focus-visible outline | focus-visible | ui | color.interaction.focus.ring #e53935 | color.surface.overlay #1c1010 | 4.39 | 3 | pass |
| dialog reduced-motion text | reduced-motion | text | color.text.default #e99499 | color.surface.overlay #1c1010 | 8.10 | 4.5 | pass |
| dialog reduced-motion border | reduced-motion | ui | color.border.overlay #c81a1a | color.surface.overlay #1c1010 | 3.20 | 3 | pass |
| title on the overlay surface | — | text | color.text.bright #ffa2a7 | color.surface.overlay #1c1010 | 9.71 | 4.5 | pass |
| note on the overlay surface | — | text | color.text.muted #d4868b | color.surface.overlay #1c1010 | 6.70 | 4.5 | pass |
| secondary button text on the overlay surface | — | text | color.action.secondary.text #ffa2a7 | color.surface.overlay #1c1010 | 9.71 | 4.5 | pass |
| secondary button boundary on the overlay surface | — | ui | color.action.secondary.border #986f72 | color.surface.overlay #1c1010 | 4.29 | 3 | pass |
| primary button text | — | text | color.action.primary.text #ffa2a7 | color.action.primary.bg #7d1310 | 5.57 | 4.5 | pass |
| destructive confirm text on its fill | — | text | color.action.destructive.filled-text #f9faf9 | color.action.destructive.filled-bg #dc282e | 4.58 | 4.5 | pass |
| destructive fill against the overlay surface | — | ui | color.action.destructive.filled-bg #dc282e | color.surface.overlay #1c1010 | 3.88 | 3 | pass |
| ring on the primary fill | focus-visible | ui | color.interaction.focus.ring-container #ffa2a7 | color.action.primary.bg #7d1310 | 5.57 | 3 | pass |
| ring on the destructive fill | focus-visible | ui | color.action.destructive.filled-text #f9faf9 | color.action.destructive.filled-bg #dc282e | 4.58 | 3 | pass |
| form field text | — | text | color.text.default #e99499 | color.surface.input #000000 | 9.16 | 4.5 | pass |
| form field boundary on the overlay surface | — | ui | color.border.control #c81a1a | color.surface.overlay #1c1010 | 3.20 | 3 | pass |
Keys
| Key | Action |
|---|---|
| Tab / Shift+Tab | Cycles through the dialog's focusable elements and never leaves it while open. |
| Escape | Closes the dialog (cancel) and returns focus to the element that opened it. |
| Enter | In a form dialog, submits; otherwise activates the focused button only. |
| Space | Activates the focused button. |
For agents
Component JSON ·Implementation brief ·Build a task kit ·
Related: drawer alert toast button text-field popover
Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-OVERFLOW FX-STATE-MATRIX