Polyfill for the app-drawer Built-in Module

Usage no npm install needed!

<script type="module">
  import appDrawer 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', () => {


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.

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.)