Drawer

ui-drawer is a modal <dialog> that slides in from an edge of the viewport, by default from the bottom. A bottom drawer shows a drag handle. Drag it down more than 80 pixels to close the drawer. Escape and a click on the backdrop also close it, and focus returns to the trigger.

Build it from ui-drawer-trigger, ui-drawer-header with ui-drawer-title and ui-drawer-description, the content, and ui-drawer-footer. ui-drawer-close closes the drawer when the element inside it is clicked.

Previewindex.html
Set daily goal Move goal Set your daily activity goal. Submit Cancel
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Drawer demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/drawer.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/slider.html"></import-element>

  <ui-drawer>
    <ui-drawer-trigger><ui-button variant="outline">Set daily goal</ui-button></ui-drawer-trigger>
    <ui-drawer-header>
      <ui-drawer-title>Move goal</ui-drawer-title>
      <ui-drawer-description>Set your daily activity goal.</ui-drawer-description>
    </ui-drawer-header>
    <ui-slider value="350" min="200" max="600" step="10" aria-label="Calories per day"></ui-slider>
    <ui-drawer-footer>
      <ui-drawer-close><ui-button>Submit</ui-button></ui-drawer-close>
      <ui-drawer-close><ui-button variant="outline">Cancel</ui-button></ui-drawer-close>
    </ui-drawer-footer>
  </ui-drawer>
</body>
</html>

Examples

Sides

Set side to top, left or right. Only the bottom drawer shows the drag handle.

Previewindex.htmldrawer-side-demo.html
Top Right Bottom Left Recent activity You have three unread notifications. Close
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Drawer demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/drawer.html"></import-element>

  <import-element from="./drawer-side-demo.html"></import-element>
  <drawer-side-demo></drawer-side-demo>
</body>
</html>
<custom-element name="drawer-side-demo">
  <script>
    const panel = useState({ side: "bottom" });

    function open(side) {
      panel.side = side;
      refs.drawer.showModal();
    }
  </script>

  <style>
    :host {
      display: flex;
      flex-wrap: wrap;
      gap: calc(var(--spacing) * 2);
    }
  </style>

  <ui-button variant="outline" onclick="open('top')">Top</ui-button>
  <ui-button variant="outline" onclick="open('right')">Right</ui-button>
  <ui-button variant="outline" onclick="open('bottom')">Bottom</ui-button>
  <ui-button variant="outline" onclick="open('left')">Left</ui-button>
  <ui-drawer ref="drawer" _side="#panel.side">
    <ui-drawer-header>
      <ui-drawer-title>Recent activity</ui-drawer-title>
      <ui-drawer-description>You have three unread notifications.</ui-drawer-description>
    </ui-drawer-header>
    <ui-drawer-footer>
      <ui-drawer-close><ui-button variant="outline">Close</ui-button></ui-drawer-close>
    </ui-drawer-footer>
  </ui-drawer>
</custom-element>

React to closing

close fires after the drawer closes, whether by a button, Escape, the backdrop or a drag.

Previewindex.htmldrawer-close-demo.html
Share Share Pick where to send this link.
Email Messages
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Drawer demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/drawer.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/toaster.html"></import-element>
  <ui-toaster></ui-toaster>

  <import-element from="./drawer-close-demo.html"></import-element>
  <drawer-close-demo></drawer-close-demo>
</body>
</html>
<custom-element name="drawer-close-demo">
  <script>
    const share = useState({ status: "Not shared yet." });

    function pick(target) {
      share.status = `Shared to ${target}.`;
    }

    function closed() {
      document.dispatchEvent(new CustomEvent("toast", { detail: { title: "Drawer closed", description: share.status } }));
    }
  </script>

  <style>
    :host {
      display: flex;
      align-items: center;
      gap: calc(var(--spacing) * 4);
      font-size: var(--text-sm);
    }
    .targets {
      display: flex;
      gap: calc(var(--spacing) * 2);
    }
  </style>

  <ui-drawer onclose="closed()">
    <ui-drawer-trigger><ui-button variant="outline">Share</ui-button></ui-drawer-trigger>
    <ui-drawer-header>
      <ui-drawer-title>Share</ui-drawer-title>
      <ui-drawer-description>Pick where to send this link.</ui-drawer-description>
    </ui-drawer-header>
    <div class="targets">
      <ui-drawer-close><ui-button variant="secondary" onclick="pick('Email')">Email</ui-button></ui-drawer-close>
      <ui-drawer-close><ui-button variant="secondary" onclick="pick('Messages')">Messages</ui-button></ui-drawer-close>
    </div>
  </ui-drawer>
  <span><slot name="#share.status"></slot></span>
</custom-element>

API

Parts

Part Slot Renders
ui-drawer The modal <dialog> with the drag handle, a grid with a gap between the header, content and footer.
ui-drawer-trigger trigger Its content, outside the drawer. A click on it focuses the element inside and opens the drawer.
ui-drawer-header header A column for the title and description.
ui-drawer-title (default) An <h2> in var(--text-base) semibold. Its text becomes the drawer's accessible name.
ui-drawer-description (default) A <p> in muted var(--text-sm). Its text becomes the drawer's accessible description.
ui-drawer-footer footer A column of full-width actions at the bottom.
ui-drawer-close (default) Its content. A click on it closes the drawer.

Parts with a slot assign themselves to it, so you do not write slot. Everything else goes in the body in source order.

ui-drawer attributes

Attribute Type Default Description
open boolean false Whether the drawer is open. Setting it opens the drawer modally. Reflects.
side "bottom" | "top" | "left" | "right" "bottom" The edge the drawer slides in from.

Properties and methods

Name Description
showModal() Opens the drawer.
show() Same as showModal().
close() Closes the drawer.
open Boolean property mirroring the attribute.

Commands

Command Action
--show-modal Opens the drawer.
--close Closes the drawer.
--request-close Same as --close.

ui-button also accepts the commands without the -- prefix. A plain <button> needs the prefix.

Events

Event Detail Description
close none Fired after the drawer has closed. Does not bubble.

CSS parts

Element Part Description
ui-drawer dialog The native <dialog> element.
ui-drawer handle The drag handle. Visible only when side is bottom.
ui-drawer body Wrapper around the default slot. It adds no box of its own (display: contents), so empty content leaves no gap.
ui-drawer-title title The <h2>.
ui-drawer-description description The <p>.

Accessibility

Edit this page on GitHub · View as Markdown