v1.0.0

<bmx-drawer>

A panel that slides in from an edge: filters beside a table, a record's detail beside the list it came from, a navigation menu on a narrow screen. Everything bmx-dialog is, arriving from the side rather than from the middle - and the difference is not only decoration. A drawer keeps the page it came from visible along one edge, which is what makes it the right shape for something you consult about what is behind it rather than instead of it.

12 properties · 2 events · 2 methods · 7 parts

Example

Filters Navigation From the bottom
Clear Apply

A navigation drawer sits on the leading edge, where the page's own navigation would be.

A sheet from the bottom edge is the shape a phone expects for a share or an action list.

Show markup
<div class="row">
  <bmx-button id="ex-drawer-end" variant="outline" tone="neutral">Filters</bmx-button>
  <bmx-button id="ex-drawer-start" variant="outline" tone="neutral">Navigation</bmx-button>
  <bmx-button id="ex-drawer-bottom" variant="outline" tone="neutral">From the bottom</bmx-button>
</div>

<!--
  `edge` is logical. `end` opens from the right in English and from the left in
  Arabic, with nothing to change in between; `top` and `bottom` do not mirror,
  because the block direction runs the same way in every locale.
-->
<bmx-drawer id="ex-drawer-1" heading="Filters" edge="end">
  <div style="display: grid; gap: var(--bmx-space-3); justify-items: start">
    <bmx-checkbox label="In stock only" checked></bmx-checkbox>
    <bmx-checkbox label="Free delivery"></bmx-checkbox>
    <bmx-checkbox label="Discounted"></bmx-checkbox>
  </div>

  <bmx-button slot="footer" variant="ghost" tone="neutral" id="ex-drawer-clear">Clear</bmx-button>
  <bmx-button slot="footer" id="ex-drawer-apply">Apply</bmx-button>
</bmx-drawer>

<bmx-drawer id="ex-drawer-2" heading="Sections" edge="start" size="sm">
  <p style="margin: 0">A navigation drawer sits on the leading edge, where the page's own navigation would be.</p>
</bmx-drawer>

<bmx-drawer id="ex-drawer-3" heading="Share this report" edge="bottom" size="sm">
  <p style="margin: 0">A sheet from the bottom edge is the shape a phone expects for a share or an action list.</p>
</bmx-drawer>

<script type="module">
  await customElements.whenDefined('bmx-drawer');

  const open = (button, drawer) => {
    document.getElementById(button).addEventListener('bmxActivate', () => document.getElementById(drawer).openDrawer());
  };

  open('ex-drawer-end', 'ex-drawer-1');
  open('ex-drawer-start', 'ex-drawer-2');
  open('ex-drawer-bottom', 'ex-drawer-3');

  const filters = document.getElementById('ex-drawer-1');

  document.getElementById('ex-drawer-clear').addEventListener('bmxActivate', () => filters.closeDrawer());
  document.getElementById('ex-drawer-apply').addEventListener('bmxActivate', () => filters.closeDrawer());
</script>

WHY IT IS A SECOND ELEMENT AND NOT A position ON THE FIRST

The same reasoning that made bmx-combobox its own element rather than a flag on bmx-select. The two have different defaults, different sizing (a drawer's size is its width, a dialog's is a maximum), a different edge vocabulary, and different documentation pages - and a consumer scanning the library for "drawer" should find one rather than a paragraph inside something else. The behaviour they share is shared as code: one BmxDialogSurface, one dialog.css, one set of chrome helpers, and one src/core/dialog.ts between them.

Stencil does not support component inheritance, so the properties below are declared rather than derived. That duplication is deliberate and bounded: it is the declarations only, and every line of behaviour behind them is shared.

THE EDGES ARE LOGICAL

start and end mirror under RTL, so a drawer that opens from the right in English opens from the left in Arabic without the consumer doing anything. top and bottom do not mirror, because the block direction of every locale this library supports runs the same way. An edge named left would be the one thing here that does not mirror, and it would be wrong in half the world's scripts while looking perfect in English.

Properties

PropertyAttributeTypeDefaultDescription
closeLabel close-label string 'Close' The close button's accessible name.
closeOnBackdrop close-on-backdrop boolean true Whether a press on the scrim dismisses it.
closeOnEscape close-on-escape boolean true Whether Escape dismisses it.
dismissible dismissible boolean true Whether the user may dismiss it. Never restrains closeDrawer().
edge edge BmxDrawerEdge 'end' Which edge it slides in from. start and end mirror under RTL.
heading heading string The visible title. Also the drawer's accessible name.
hideClose hide-close boolean false Hide the close button while still allowing Escape and the backdrop.
initialFocus initial-focus string | HTMLElement What is focused when it opens, as a CSS selector or an element.
label label string The accessible name, for a design with no visible title.
open open boolean false Whether the drawer is showing. Mutable, so it can close itself.
returnFocus return-focus boolean true Whether closing returns focus to whatever opened it.
size size BmxDialogSize 'md' How much of the screen it takes. The panel's width on start and end, its height on top and bottom. full covers the viewport, which is what a navigation drawer on a phone usually wants.

Events

EventDetailDescription
bmxClose BmxDialogCloseDetail Fired once it has finished closing, with why it closed.
bmxOpenChange boolean Fired when the drawer opens or closes.

Methods

MethodSignatureDescription
closeDrawer closeDrawer() => Promise<void> Close the drawer. Always permitted, whatever dismissible says.
openDrawer openDrawer() => Promise<void> Open the drawer.

Slots

SlotDescription
(default) The drawer's content.
footer The row of actions along the bottom.
heading Replaces the plain-text heading.

CSS shadow parts

PartDescription
body The scrolling content area.
close The close button.
dialog The native element, which is also the scrim.
footer The row of actions.
header The bar across the top.
heading The title within it.
panel The box the content sits in.

CSS custom properties

PropertyDescription
--bmx-dialog-padding Space inside the panel's header, body and footer.
--bmx-dialog-scrim The colour laid over the page behind it.
--bmx-drawer-size The panel's width on a side edge, its height on a top or bottom one.