Modal

A native <dialog> opened by a declarative invoker. The buttons below really work, on a page that serves no JavaScript at all - open one, press Escape, press Tab and try to reach the page behind it.

Reference

sk-modal

The platform supplies what a scripted modal usually gets wrong: focus moves in and is trapped, the rest of the page becomes inert to both tab and screen reader, Escape closes, the page behind does not scroll, and it renders in the top layer so no z-index in the application can cover it.

<button class="sk-button" command="show-modal" commandfor="confirm">Delete</button>

<dialog class="sk-modal" id="confirm" aria-labelledby="confirm-title">
  <div class="sk-modal-header">
    <h2 class="sk-modal-title" id="confirm-title">Title</h2>
    <form method="dialog"><button class="sk-modal-close" aria-label="Close">…</button></form>
  </div>
  <div class="sk-modal-body">…</div>
  <div class="sk-modal-footer">…</div>
</dialog>
AttributeTypeDefaultNotes
command / commandfor"show-modal" | "close" | "request-close"-On the trigger, not the dialog. The newest part of this stack: where unavailable the button does nothing - it fails closed, but it does fail. A three-line shim in the application covers it.
aria-labelledbyid of the title-Set by the binding when Title is given. Without it the dialog is announced as "dialog" and nothing more.
data-size"sm" | "lg"md24 / 32 / 48rem, each capped to the viewport.
form method="dialog"--How closing works. Supported everywhere <dialog> is, so the close button needs no invoker support.
:modal--Modal styling is scoped to :modal, so an inline <dialog open> - which has no backdrop and no focus trap - never pretends to be one.

Examples

Confirm

The common case. Escape closes, Cancel closes, and focus returns to the button that opened it - none of which is written here.

Delete this account?

This removes the account and everything in it. It cannot be undone.

Show markup
<div class="sk-flex sk-flex-wrap sk-items-center sk-gap-3">
  <button class="sk-button" type="button" data-variant="danger" data-size="md" command="show-modal" commandfor="demo-confirm">
    <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
      <path d="M4 7h16">
      </path>
      <path d="M10 11v6">
      </path>
      <path d="M14 11v6">
      </path>
      <path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2l1-12">
      </path>
      <path d="M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2">
      </path>
    </svg>
    Delete account
  </button>
  <dialog class="sk-modal" id="demo-confirm" data-size="sm" aria-labelledby="demo-confirm-title">
    <div class="sk-modal-header">
      <h2 class="sk-modal-title" id="demo-confirm-title">
        Delete this account?
      </h2>
      <form method="dialog">
        <button type="submit" class="sk-modal-close" aria-label="Close">
          <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
            <path d="M18 6 6 18">
            </path>
            <path d="m6 6 12 12">
            </path>
          </svg>
        </button>
      </form>
    </div>
    <div class="sk-modal-body">
      <p>
        This removes the account and everything in it. It cannot be undone.
      </p>
    </div>
    <div class="sk-modal-footer">
      <form method="dialog">
        <button class="sk-button" type="submit" data-variant="quiet" data-size="md">
          Cancel
        </button>
      </form>
      <form method="dialog">
        <button class="sk-button" type="submit" data-variant="danger" data-size="md">
          Delete
        </button>
      </form>
    </div>
  </dialog>
</div>

Sizes

Small dialog

Each size caps to the viewport, so the small one is small and none of them overflow a phone.

Medium dialog

Each size caps to the viewport, so the small one is small and none of them overflow a phone.

Large dialog

Each size caps to the viewport, so the small one is small and none of them overflow a phone.

Show markup
<div class="sk-flex sk-flex-wrap sk-items-center sk-gap-3">
  <button class="sk-button" type="button" data-variant="neutral" data-size="md" command="show-modal" commandfor="demo-sm">
    Small
  </button>
  <dialog class="sk-modal" id="demo-sm" data-size="sm" aria-labelledby="demo-sm-title">
    <div class="sk-modal-header">
      <h2 class="sk-modal-title" id="demo-sm-title">
        Small dialog
      </h2>
      <form method="dialog">
        <button type="submit" class="sk-modal-close" aria-label="Close">
          <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
            <path d="M18 6 6 18">
            </path>
            <path d="m6 6 12 12">
            </path>
          </svg>
        </button>
      </form>
    </div>
    <div class="sk-modal-body">
      <p>
        Each size caps to the viewport, so the small one is small and none of them overflow a phone.
      </p>
    </div>
    <div class="sk-modal-footer">
      <form method="dialog">
        <button class="sk-button" type="submit" data-variant="solid" data-size="md">
          Close
        </button>
      </form>
    </div>
  </dialog>
  <button class="sk-button" type="button" data-variant="neutral" data-size="md" command="show-modal" commandfor="demo-md">
    Medium
  </button>
  <dialog class="sk-modal" id="demo-md" data-size="md" aria-labelledby="demo-md-title">
    <div class="sk-modal-header">
      <h2 class="sk-modal-title" id="demo-md-title">
        Medium dialog
      </h2>
      <form method="dialog">
        <button type="submit" class="sk-modal-close" aria-label="Close">
          <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
            <path d="M18 6 6 18">
            </path>
            <path d="m6 6 12 12">
            </path>
          </svg>
        </button>
      </form>
    </div>
    <div class="sk-modal-body">
      <p>
        Each size caps to the viewport, so the small one is small and none of them overflow a phone.
      </p>
    </div>
    <div class="sk-modal-footer">
      <form method="dialog">
        <button class="sk-button" type="submit" data-variant="solid" data-size="md">
          Close
        </button>
      </form>
    </div>
  </dialog>
  <button class="sk-button" type="button" data-variant="neutral" data-size="md" command="show-modal" commandfor="demo-lg">
    Large
  </button>
  <dialog class="sk-modal" id="demo-lg" data-size="lg" aria-labelledby="demo-lg-title">
    <div class="sk-modal-header">
      <h2 class="sk-modal-title" id="demo-lg-title">
        Large dialog
      </h2>
      <form method="dialog">
        <button type="submit" class="sk-modal-close" aria-label="Close">
          <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
            <path d="M18 6 6 18">
            </path>
            <path d="m6 6 12 12">
            </path>
          </svg>
        </button>
      </form>
    </div>
    <div class="sk-modal-body">
      <p>
        Each size caps to the viewport, so the small one is small and none of them overflow a phone.
      </p>
    </div>
    <div class="sk-modal-footer">
      <form method="dialog">
        <button class="sk-button" type="submit" data-variant="solid" data-size="md">
          Close
        </button>
      </form>
    </div>
  </dialog>
</div>

Long content

The body scrolls; the header and footer stay put. The page behind does not scroll at all, which the platform handles.

Release notes

Version 0.12.0

Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.

Version 0.11.0

Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.

Version 0.10.0

Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.

Version 0.9.0

Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.

Version 0.8.0

Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.

Version 0.7.0

Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.

Version 0.6.0

Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.

Version 0.5.0

Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.

Version 0.4.0

Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.

Version 0.3.0

Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.

Version 0.2.0

Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.

Version 0.1.0

Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.

Show markup
<div>
  <button class="sk-button" type="button" data-variant="neutral" data-size="md" command="show-modal" commandfor="demo-long">
    Open long dialog
  </button>
  <dialog class="sk-modal" id="demo-long" aria-labelledby="demo-long-title">
    <div class="sk-modal-header">
      <h2 class="sk-modal-title" id="demo-long-title">
        Release notes
      </h2>
      <form method="dialog">
        <button type="submit" class="sk-modal-close" aria-label="Close">
          <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
            <path d="M18 6 6 18">
            </path>
            <path d="m6 6 12 12">
            </path>
          </svg>
        </button>
      </form>
    </div>
    <div class="sk-modal-body">
      <div class="sk-flex sk-flex-col sk-gap-3">
        <div>
          <strong>
            Version 0.12.0
          </strong>
          <p>
            Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.
          </p>
        </div>
        <div>
          <strong>
            Version 0.11.0
          </strong>
          <p>
            Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.
          </p>
        </div>
        <div>
          <strong>
            Version 0.10.0
          </strong>
          <p>
            Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.
          </p>
        </div>
        <div>
          <strong>
            Version 0.9.0
          </strong>
          <p>
            Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.
          </p>
        </div>
        <div>
          <strong>
            Version 0.8.0
          </strong>
          <p>
            Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.
          </p>
        </div>
        <div>
          <strong>
            Version 0.7.0
          </strong>
          <p>
            Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.
          </p>
        </div>
        <div>
          <strong>
            Version 0.6.0
          </strong>
          <p>
            Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.
          </p>
        </div>
        <div>
          <strong>
            Version 0.5.0
          </strong>
          <p>
            Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.
          </p>
        </div>
        <div>
          <strong>
            Version 0.4.0
          </strong>
          <p>
            Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.
          </p>
        </div>
        <div>
          <strong>
            Version 0.3.0
          </strong>
          <p>
            Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.
          </p>
        </div>
        <div>
          <strong>
            Version 0.2.0
          </strong>
          <p>
            Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.
          </p>
        </div>
        <div>
          <strong>
            Version 0.1.0
          </strong>
          <p>
            Sortable table headers, a page list that elides sensibly, and an empty state that says what to do next rather than only what is missing.
          </p>
        </div>
      </div>
    </div>
    <div class="sk-modal-footer">
      <form method="dialog">
        <button class="sk-button" type="submit" data-variant="solid" data-size="md">
          Close
        </button>
      </form>
    </div>
  </dialog>
</div>

With a form

A dialog can contain a normal form. Submitting it posts as usual; the Cancel button uses method="dialog" so it closes without submitting.

New project

Show markup
<div>
  <button class="sk-button" type="button" data-variant="solid" data-size="md" command="show-modal" commandfor="demo-form">
    <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
      <path d="M12 5v14">
      </path>
      <path d="M5 12h14">
      </path>
    </svg>
    New project
  </button>
  <dialog class="sk-modal" id="demo-form" aria-labelledby="demo-form-title">
    <div class="sk-modal-header">
      <h2 class="sk-modal-title" id="demo-form-title">
        New project
      </h2>
      <form method="dialog">
        <button type="submit" class="sk-modal-close" aria-label="Close">
          <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
            <path d="M18 6 6 18">
            </path>
            <path d="m6 6 12 12">
            </path>
          </svg>
        </button>
      </form>
    </div>
    <form method="dialog">
      <div class="sk-modal-body">
        <div class="sk-flex sk-flex-col sk-gap-4">
          <div class="sk-field">
            <label class="sk-label" for="project-name">
              Name
              <span class="sk-required" aria-hidden="true">
                *
              </span>
            </label>
            <input class="sk-input" id="project-name" name="project-name" type="text" value="" data-size="md" aria-invalid="false" required>
          </div>
          <div class="sk-field">
            <label class="sk-label" for="project-visibility">
              Visibility
            </label>
            <select class="sk-select" id="project-visibility" name="project-visibility" data-size="md" aria-invalid="false">
              <option value="" disabled selected>
                Choose one
              </option>
              <option value="public">
                Public
              </option>
              <option value="private">
                Private
              </option>
            </select>
          </div>
        </div>
      </div>
      <div class="sk-modal-footer">
        <button class="sk-button" type="submit" data-variant="quiet" data-size="md">
          Cancel
        </button>
        <button class="sk-button" type="submit" data-variant="solid" data-size="md">
          Create
        </button>
      </div>
    </form>
  </dialog>
</div>