Drawer

A modal that arrives from an edge. Deliberately a variant of Modal rather than a component of its own - the same dialog, the same declarative trigger, the same close, the same parts. Only the geometry differs.

Reference

sk-modal[data-variant="drawer"]

Everything the modal earns from the platform applies unchanged: focus moved in and trapped, the rest of the page inert to tab and to screen reader, Escape to close, no background scroll, and the top layer. The slide is translate plus @starting-style, so it animates in and out with no script; under reduced motion it fades in place instead of sliding.

<button class="sk-button" command="show-modal" commandfor="filters">Filters</button>

<dialog class="sk-modal" data-variant="drawer" data-side="end" id="filters">
  <div class="sk-modal-header">…</div>
  <div class="sk-modal-body">…</div>
  <div class="sk-modal-footer">…</div>
</dialog>
AttributeTypeDefaultNotes
data-side"start" | "end" | "bottom""end"Logical, not left/right: start is the leading edge in the document's writing direction. Bottom is the sheet, which is the one a thumb can reach.
data-size"sm" | "md" | "lg""md"The drawer's thickness - 18 / 24 / 36rem - not its length. A bottom sheet ignores it and caps at 85dvh.
sk-modal-*header, body, footer, close-The modal's own parts. There is no drawer-specific markup, which is the point: one set of structure classes cannot drift from itself.

Examples

From the end

The default. Escape closes it, and focus returns to the button that opened it - both from the platform.

Filters

Status
Show markup
<div class="sk-flex sk-flex-wrap sk-gap-3">
  <button class="sk-button" type="button" data-variant="neutral" data-size="md" command="show-modal" commandfor="drawer-filters">
    <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
      <circle cx="11" cy="11" r="7">
      </circle>
      <path d="m20 20-3.5-3.5">
      </path>
    </svg>
    Filters
  </button>
  <dialog class="sk-modal" id="drawer-filters" aria-labelledby="drawer-filters-title" data-variant="drawer">
    <div class="sk-modal-header">
      <h2 class="sk-modal-title" id="drawer-filters-title">
        Filters
      </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-5">
        <fieldset class="sk-fieldset" id="drawer-status">
          <legend class="sk-legend">
            Status
          </legend>
          <div class="sk-choices">
            <label class="sk-check" for="drawer-status-all">
              <input id="drawer-status-all" name="drawer-status" type="radio" value="all" checked>
              <span class="sk-check-label">
                All
              </span>
            </label>
            <label class="sk-check" for="drawer-status-passing">
              <input id="drawer-status-passing" name="drawer-status" type="radio" value="passing">
              <span class="sk-check-label">
                Passing
              </span>
            </label>
            <label class="sk-check" for="drawer-status-failed">
              <input id="drawer-status-failed" name="drawer-status" type="radio" value="failed">
              <span class="sk-check-label">
                Failed
              </span>
            </label>
          </div>
        </fieldset>
        <div class="sk-switch">
          <input type="checkbox" role="switch" id="drawer-mine" name="drawer-mine">
          <span class="sk-switch-track" aria-hidden="true">
          </span>
          <span class="sk-switch-text">
            <label class="sk-switch-label" for="drawer-mine">
              Only my builds
            </label>
          </span>
        </div>
      </div>
    </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="solid" data-size="md">
          Apply
        </button>
      </form>
    </div>
  </dialog>
</div>

From the start

Where navigation belongs. This is the honest answer to a hamburger menu: a real dialog with a declarative trigger, rather than a script toggling a class.

Show markup
<div class="sk-flex sk-flex-wrap sk-gap-3">
  <button class="sk-button" type="button" data-variant="neutral" data-size="md" aria-label="Open navigation" command="show-modal" commandfor="drawer-nav">
    <svg class="sk-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
      <path d="m9 18 6-6-6-6">
      </path>
    </svg>
  </button>
  <dialog class="sk-modal" id="drawer-nav" data-size="sm" aria-labelledby="drawer-nav-title" data-side="start" data-variant="drawer">
    <div class="sk-modal-header">
      <h2 class="sk-modal-title" id="drawer-nav-title">
        Kiwi Kart
      </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">
      <nav class="sk-nav" aria-label="Drawer">
        <a class="sk-nav-item" href="#drawer" aria-current="page">
          <span class="sk-nav-label">
            Overview
          </span>
        </a>
        <a class="sk-nav-item" href="#drawer">
          <span class="sk-nav-label">
            Builds
          </span>
        </a>
        <a class="sk-nav-item" href="#drawer">
          <span class="sk-nav-label">
            Settings
          </span>
        </a>
      </nav>
    </div>
  </dialog>
</div>

Bottom sheet

Caps at 85dvh and rounds the two corners that are visible.

Share this build

Anyone with the link can view the log.

Show markup
<div class="sk-flex sk-flex-wrap sk-gap-3">
  <button class="sk-button" type="button" data-variant="neutral" data-size="md" command="show-modal" commandfor="drawer-sheet">
    Share
  </button>
  <dialog class="sk-modal" id="drawer-sheet" aria-labelledby="drawer-sheet-title" data-side="bottom" data-variant="drawer">
    <div class="sk-modal-header">
      <h2 class="sk-modal-title" id="drawer-sheet-title">
        Share this build
      </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-field">
        <label class="sk-label" for="drawer-share">
          Link
        </label>
        <div class="sk-input-group">
          <input class="sk-input" id="drawer-share" name="drawer-share" type="text" value="https://ds.qbart.dev/b/412" data-size="md" aria-invalid="false">
          <button class="sk-button" type="button" data-variant="neutral" data-size="md">
            Copy
          </button>
        </div>
        <p class="sk-hint" id="drawer-share-hint">
          Anyone with the link can view the log.
        </p>
      </div>
    </div>
    <div class="sk-modal-footer">
      <form method="dialog">
        <button class="sk-button" type="submit" data-variant="quiet" data-size="md">
          Done
        </button>
      </form>
    </div>
  </dialog>
</div>