Skip to content
Español

Navigate

Type to search. Press Escape to close.

    Components · js

    Dialog

    A modal built on the native <dialog> element. The browser provides the top layer, inert background and Escape; iVOLT adds focus placement, focus return, backdrop closing, declarative triggers and cancelable events.

    CSS: css/components/dialog.css (in ivolt.css). JS: @intervolutions/ivolt/dialog or auto.js.

    dialog/basicfixtures/dialog/basic.htmlOpen alone — dialog/basic
    <a class="iv-button iv-button--primary" href="#signup" data-iv-open="signup">Create account</a>
    
    <dialog class="iv-dialog" id="signup" data-iv-component="dialog" data-iv-initial-focus="#signup-email" aria-labelledby="signup-title">
      <form method="dialog" class="iv-dialog__panel">
        <header class="iv-dialog__header">
          <h2 class="iv-dialog__title" id="signup-title">Create account</h2>
          <button class="iv-button iv-button--ghost iv-button--icon" type="button" data-iv-close aria-label="Close">
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M6 6l12 12M18 6 6 18"/></svg>
          </button>
        </header>
        <div class="iv-dialog__body iv-stack">
          <p class="iv-u-m-0">Demo dialog. Nothing is sent anywhere.</p>
          <div class="iv-field">
            <label class="iv-label" for="signup-email">Work email</label>
            <input class="iv-input" id="signup-email" type="email" autocomplete="email">
          </div>
        </div>
        <footer class="iv-dialog__footer">
          <a class="iv-button iv-button--ghost" href="#" data-iv-close>Cancel</a>
          <button class="iv-button iv-button--primary" value="confirm">Continue</button>
        </footer>
      </form>
    </dialog>
    // data-iv-component="dialog"
    {
      open: "signup",
      initialFocus: "#signup-email",
      close: true,
    }

    Markup

    Give the dialog data-iv-component="dialog", an id and an accessible name (aria-labelledby). Triggers use data-iv-open="id"; anything inside with data-iv-close closes it. A <form method="dialog"> makes buttons close the dialog with their value as return value.

    JavaScript

    import { Dialog } from "@intervolutions/ivolt/dialog";
    const dialog = Dialog.getOrCreate(document.getElementById("signup"), { closeOnBackdrop: false });
    dialog.open({ trigger: button });
    dialog.element.addEventListener("iv:close", (e) => { if (e.detail.reason === "escape" && dirty) e.preventDefault(); });

    Options (defaults < data-iv-* < JS)

    OptionAttributeDefaultEffect
    closeOnBackdropdata-iv-close-on-backdroptrueClick outside the panel closes.
    closeOnEscapedata-iv-close-on-escapetrueEscape closes (the native cancel is always intercepted so the event stays cancelable).
    initialFocusdata-iv-initial-focusnullSelector to focus on open; otherwise the first focusable element, else the dialog.
    returnFocusdata-iv-return-focustrueFocus goes back to the trigger on close.

    Methods and events

    open({ trigger }), close(reason, returnValue), toggle(), destroy(), isOpen, returnValue. Events bubble from the dialog element: iv:open and iv:close are cancelable; iv:opened and iv:closed follow. detail.reason is one of trigger, escape, backdrop, form, api, external.

    Keyboard

    Tab and Shift+Tab stay inside the modal (browser behaviour of showModal); Escape closes; Enter in the form submits and closes. Focus returns to the trigger.

    Without JavaScript

    The trigger is a link to the dialog's id. With JavaScript the link is intercepted and the URL does not change. Without it, .iv-dialog:target shows the dialog as a static block with its close link. Keep essential content reachable elsewhere too; a dialog is not a page.

    Common mistakes

    • Calling dialog.close() on the element directly: it works, but iv:close cannot cancel it and the reason is external.
    • Trigger inside the dialog pointing to itself.
    • No accessible name (aria-labelledby or aria-label).