Polyfill for the app-drawer Built-in Module

Usage no npm install needed!

<script type="module">
  import appDrawerPolyfill from '';


App Drawer

This document is an explainer for a potential browser-provided "App Drawer" component, implemented as a built-in module. App Drawer is delivered as a Custom Element, making it framework-agnostic and easy to integrate into existing applications. It supports the gestures users expect from experience with native mobile platforms, ensures a consistent UX for opening and dismissal, and solves accessibility issues common to web-based drawer implementations. It ships unstyled, and is easily customized via attributes and CSS Custom Properties.

Sample code

<app-drawer id="drawer">
    <a href="/">Home</a>

<script type="module">
  import '@std/app-drawer';
  drawer.addEventListener('close', () => {

<!-- Future:  <script type="module" src="import:@std/app-drawer"></script> -->


The concept of an "app drawer" is pervasive on the web. Also referred to as "off-canvas navigation" or modal sidebars, these represent an important component of many User Interfaces and often contain an web app's primary navigation.

There are a multitude of drawer implementations in userland, many of which suffer from usability or performance issues. The inconsistency and unreliability of important UX characteristics like gestures & keyboard support has fractured web users' expectations of the metaphor, demonstrating the need for a browser-provided solution.

(link to existing drawer impls?)

We want to win back the trust of web users by bringing consistency, reliability and performance to drawer UI's.



By default, any elements placed into <app-drawer> are rendered within the sliding drawer panel. Children can also be placed into other areas using Named Slots:

  <div slot="backdrop">Placed into the backdrop (grayed out) area</div>

  <div slot="header">Placed first in the drawer area</div>
  <div>Any other children are placed into the drawer (after the header)</div>

CSS Custom Properties

Styling can be adjusted using the following CSS Custom Properties:

  • --width: the drawer's default width (default: 200px)
  • --max-width: maximum drawer width as a percentage of the viewport (default: 100)
  • --background: background color for the sliding drawer panel (default: #eee)
  • --backdrop: background for the backdrop/shim behind the drawer (default: rgba(0, 0, 0, 0.5))

Additionally, the drawer exposes some of its state as CSS Custom Properties, which can be used to reactively style the drawer or any element within it:

  • --percent: the current percent visibility/openness of the drawer during a drag gesture
  • --tf-x: the current CSS transform (translateX(xx)) applied to the drawer during a drag gesture


Custom Element constructor, inheriting from HTMLElement.

To create an App Drawer instance programmatically:

const appDrawer = document.createElement('app-drawer');


Opens or closes the drawer based on its current state.

If forceState is a Boolean value, the drawer will be opened or closed regardless of its current state.


Open the drawer if it is currently closed.

Note: If invoked during a drawer gesture, overrides the end state of the gesture.


Close the drawer if it is currently open.

Note: If invoked during a drawer gesture, overrides the end state of the gesture.

event: toggle(e)

Fired when the drawer finishes opening or closing. The event includes a .open property with a Boolean indicating the drawer's new state.

drawer.addEventListener('toggle', e => {
  console.log('Drawer is now ', ? 'open' : 'closed');

Open issues and questions

Please see the issue tracker for open issues on the API surface detailed above.


This feature would be medium-effort, medium-reward.

  • Applications would no longer need to build and ship custom drawer implementations
  • Developers would not need to implement gesture support or platform-specific differences
  • Users of assistive technologies would benefit from a well-known and DOM-controllable interaction model

Comparison to existing solutions

There are a number of standalone drawer implementations available on npm that offer comparable functionality:

(All of the above statistics are as of 2019-02-06.)