j3w1 / DialogVue · HTML · React · Astro

J3W1 / DESIGN SYSTEM

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 · 1.1.0

Light DOM retains static content, native semantics, label relationships and host composition without duplicating controls.

Live variants

default

Save changes?

Three settings changed since the last save. Unsaved changes are kept locally until you publish.

Focus is trapped: Tab cycles inside the dialog; Escape closes it.

<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

Delete profile "workstation"?

The profile and its two overlays are removed. This cannot be undone.

Focus is trapped: Tab cycles inside the dialog; Escape cancels.

<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

New profile

The profile starts from the default tokens.

Lowercase letters, digits and hyphens.

Focus is trapped: Tab cycles inside the dialog; Enter submits; Escape cancels.

<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@1.1.0
import '@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.

Vue SFC, React, Astro and plain HTML integration →

Copy the complete implementation

npx --package=@j3w1/ui@1.1.0 j3w1-ui copy dialog --out ./vendor/j3w1/dialog

The 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

NameType / signatureMeaning
disabledCustomEventDisable the native controls owned by this instance.
loadingCustomEventMark the primary control busy and prevent duplicate activation.

properties

NameType / signatureMeaning
disabledbooleanDelegates disabled state to native children.
namestringNative control name; application form keys remain explicit.
openbooleanNative modal state.
returnValuestringNative dialog result. Read only.

methods

NameType / signatureMeaning
refresh(): voidReconnect behavior after replacing native child structure.
focus(options?: FocusOptions): voidFocus the first usable native control.
show(): voidOpen using native showModal and remember the opener.
close(value?: string): voidClose with a result value.

events

NameType / signatureMeaning
j3w1-openCustomEventThe modal opened.
j3w1-closeCustomEventThe modal closed and restored available opener focus.
j3w1-cancelCustomEventNative cancel request; prevent default to keep the modal open.
j3w1-actionCustomEventAn 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

Purpose

A question or a small task that must be answered before the page continues: confirm a destructive action, name a new profile, sign in again. It takes the whole screen’s attention, so it is short, has a clear title, and always offers a way out. The demonstration renders the dialog open in flow, with a wrapper standing in for the backdrop; a host always uses showModal().

Anatomy

The backdrop, color.surface.backdrop over the page; the dialog on color.surface.overlay behind a 1px color.border.overlay; the header with the title in color.text.bright and Close; the body in color.text.default; the actions row. Confirmation semantics do not prescribe action tone (D-015): ordinary confirmations use primary. A destructive confirmation uses the filled destructive button, color.action.destructive.filled-bg with color.action.destructive.filled-text; the form dialog holds a labelled text field on color.surface.input.

States

StateVisualNon-colour channel
defaultthe dialog open on its backdropopen; the page behind is inert
openas default; opacity 0 → 1 within 150 ms on capable hostsopen; focus inside the dialog
closednothing: the dialog and its backdrop are absentno open; focus back on the opener
focus-trappedthe dialog itself carries the container ring, 2px solid color.interaction.focus.ring-container at −3px, when it holds focus; the note says Tab cyclesTab never leaves; the note text
hoverClose background → color.action.tertiary.hover-bg; other buttons take their tone’s hovercursor: pointer
focus-visiblering 1px dashed color.interaction.focus.ring at −2px on the focused control; primary uses color.interaction.focus.ring-container, destructive uses color.action.destructive.filled-text (D-015)the ring
reduced-motionno opacity transition; the dialog is simply present or absentprefers-reduced-motion

A dialog is never disabled; a busy dialog disables its confirm button and shows the ⋯ glyph on it.

Keyboard

Tab and Shift+Tab cycle inside the dialog; nothing behind it is reachable. Escape closes and cancels, and focus returns to the opener. Enter submits a form dialog; elsewhere it only activates the focused button, so a destructive confirm is never triggered by Enter on the dialog itself. On open, focus goes to the first focusable element, or to the dialog when the first element is destructive; on close, back to the opener.

Accessibility

The dialog is named by its title through aria-labelledby and described by its first paragraph through aria-describedby. Confirmations use role="alertdialog". showModal() makes the rest of the page inert and supplies the top layer; the theme never re-implements the trap on a non-modal dialog. The backdrop is decorative and not a click target for cancel unless Close is also present. Contrast: text 7.92:1 and title 9.49:1 on the overlay surface, the overlay border 4.30:1, secondary buttons 6.84:1 on hover, destructive confirm text 4.58:1 on its fill, the container ring 9.49:1.

Portability

A box, a border, a dimmed backdrop and the button tones. Toolkits with a native modal window use it with the overlay border on the frame and record that the backdrop is the host’s. Toolkits without a top layer draw the backdrop as a full-window overlay under the dialog.

Non-examples

A glowing or shadowed card. Rounded corners. A dialog that slides or scales in. <dialog> shown with show() and a hand-written focus trap. A dialog with no title, or with 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.

j3w1 / command

  • SPEC Overview
  • SPEC Foundations
  • SPEC Components
  • SPEC Patterns
  • SPEC Tokens
  • SPEC Tools
  • SPEC Implement
  • SPEC For agents
  • SPEC Ports
  • SPEC Releases
  • DEMO Vue application demo
  • SPEC Complete UI theme specification