0.44.1 • Published 5 years ago

@shortcm/drawer v0.44.1

Weekly downloads
1
License
MIT
Repository
github
Last release
5 years ago

Drawers

The MDC Navigation Drawer is used to organize access to destinations and other functionality on an app.

Design & API Documentation

Installation

npm install @material/drawer

HTML Structure

<aside class="mdc-drawer">
  <div class="mdc-drawer__content">
    <nav class="mdc-list">
      <a class="mdc-list-item mdc-list-item--activated" href="#" aria-selected="true">
        <i class="material-icons mdc-list-item__graphic" aria-hidden="true">inbox</i>
        <span class="mdc-list-item__text">Inbox</span>
      </a>
      <a class="mdc-list-item" href="#">
        <i class="material-icons mdc-list-item__graphic" aria-hidden="true">send</i>
        <span class="mdc-list-item__text">Outgoing</span>
      </a>
      <a class="mdc-list-item" href="#">
        <i class="material-icons mdc-list-item__graphic" aria-hidden="true">drafts</i>
        <span class="mdc-list-item__text">Drafts</span>
      </a>
    </nav>
  </div>
</aside>

Icons

We recommend using Material Icons from Google Fonts:

<head>
  <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
</head>

However, you can also use SVG, Font Awesome, or any other icon library you wish.

Styles

@import "@material/drawer/mdc-drawer";
@import "@material/list/mdc-list";

JavaScript Instantiation

For permanently visible drawer, the list must be instantiated for appropriate keyboard interaction:

import {MDCList} from "@material/list";
const list = MDCList.attachTo(document.querySelector('.mdc-list'));
list.wrapFocus = true;

Other variants use the MDCDrawer component, which will instantiate MDCList automatically:

import {MDCDrawer} from "@material/drawer";
const drawer = MDCDrawer.attachTo(document.querySelector('.mdc-drawer'));

Variants

Drawer with separate list groups

<aside class="mdc-drawer">
  <div class="mdc-drawer__content">
    <nav class="mdc-list">
      <a class="mdc-list-item mdc-list-item--activated" href="#" aria-selected="true">
        <i class="material-icons mdc-list-item__graphic" aria-hidden="true">inbox</i>
        <span class="mdc-list-item__text">Inbox</span>
      </a>
      <a class="mdc-list-item" href="#">
        <i class="material-icons mdc-list-item__graphic" aria-hidden="true">star</i>
        <span class="mdc-list-item__text">Star</span>
      </a>
      <a class="mdc-list-item" href="#">
        <i class="material-icons mdc-list-item__graphic" aria-hidden="true">send</i>
        <span class="mdc-list-item__text">Sent Mail</span>
      </a>
      <a class="mdc-list-item" href="#">
        <i class="material-icons mdc-list-item__graphic" aria-hidden="true">drafts</i>
        <span class="mdc-list-item__text">Drafts</span>
      </a>

      <hr class="mdc-list-divider">
      <h6 class="mdc-list-group__subheader">Labels</h6>
      <a class="mdc-list-item" href="#">
        <i class="material-icons mdc-list-item__graphic" aria-hidden="true">bookmark</i>
        <span class="mdc-list-item__text">Family</span>
      </a>
      <a class="mdc-list-item" href="#">
        <i class="material-icons mdc-list-item__graphic" aria-hidden="true">bookmark</i>
        <span class="mdc-list-item__text">Friends</span>
      </a>
      <a class="mdc-list-item" href="#">
        <i class="material-icons mdc-list-item__graphic" aria-hidden="true">bookmark</i>
        <span class="mdc-list-item__text">Work</span>
      </a>
    </nav>
  </div>
</aside>

Drawer with Header

Drawers can contain a header element which will not scroll with the rest of the drawer content. Things like account switchers and titles should live in the header element.

<aside class="mdc-drawer">
  <div class="mdc-drawer__header">
    <h3 class="mdc-drawer__title">Mail</h3>
    <h6 class="mdc-drawer__subtitle">email@material.io</h6>
  </div>
  <div class="mdc-drawer__content">
    <nav class="mdc-list">
      <a class="mdc-list-item mdc-list-item--activated" href="#" aria-selected="true">
        <i class="material-icons mdc-list-item__graphic" aria-hidden="true">inbox</i>
        <span class="mdc-list-item__text">Inbox</span>
      </a>
      <a class="mdc-list-item" href="#">
        <i class="material-icons mdc-list-item__graphic" aria-hidden="true">send</i>
        <span class="mdc-list-item__text">Outgoing</span>
      </a>
      <a class="mdc-list-item" href="#">
        <i class="material-icons mdc-list-item__graphic" aria-hidden="true">drafts</i>
        <span class="mdc-list-item__text">Drafts</span>
      </a>
    </nav>
  </div>
</aside>

Dismissible Drawer

Dismissible drawers are by default hidden off screen, and can slide into view. Dismissible drawers should be used when navigation is not common, and the main app content is prioritized.

<body>
  <aside class="mdc-drawer mdc-drawer--dismissible">
    <div class="mdc-drawer__content">
      <nav class="mdc-list">
        <a class="mdc-list-item mdc-list-item--activated" href="#" aria-selected="true">
          <i class="material-icons mdc-list-item__graphic" aria-hidden="true">inbox</i>
          <span class="mdc-list-item__text">Inbox</span>
        </a>
        <a class="mdc-list-item" href="#">
          <i class="material-icons mdc-list-item__graphic" aria-hidden="true">send</i>
          <span class="mdc-list-item__text">Outgoing</span>
        </a>
        <a class="mdc-list-item" href="#">
          <i class="material-icons mdc-list-item__graphic" aria-hidden="true">drafts</i>
          <span class="mdc-list-item__text">Drafts</span>
        </a>
      </nav>
    </div>
  </aside>

  <div class="mdc-drawer-app-content">
    App Content
  </div>
</body>

Apply the mdc-drawer-app-content class to the sibling element after the drawer for the open/close animations to work.

Usage with Top App Bar

Dismissible Drawer Full Height Drawer

In cases where the drawer occupies the full viewport height, some styles must be applied to get the dismissible drawer and the content below the top app bar to independently scroll and work in all browsers.

In the following example, the mdc-drawer__content and main-content elements should scroll independently of each other. The mdc-drawer--dismissible and mdc-drawer-app-content elements should then sit side-by-side. The markup looks something like this:

<body>
  <aside class="mdc-drawer mdc-drawer--dismissible">
    <div class="mdc-drawer__content">
      <div class="mdc-list">
        <a class="mdc-list-item mdc-list-item--activated" href="#" aria-selected="true">
          <i class="material-icons mdc-list-item__graphic" aria-hidden="true">inbox</i>
          <span class="mdc-list-item__text">Inbox</span>
        </a>
        <a class="mdc-list-item" href="#">
          <i class="material-icons mdc-list-item__graphic" aria-hidden="true">send</i>
          <span class="mdc-list-item__text">Outgoing</span>
        </a>
        <a class="mdc-list-item" href="#">
          <i class="material-icons mdc-list-item__graphic" aria-hidden="true">drafts</i>
          <span class="mdc-list-item__text">Drafts</span>
        </a>
      </div>
    </div>
  </aside>

  <div class="mdc-drawer-app-content">
    <header class="mdc-top-app-bar app-bar" id="app-bar">
      <div class="mdc-top-app-bar__row">
        <section class="mdc-top-app-bar__section mdc-top-app-bar__section--align-start">
          <a href="#" class="demo-menu material-icons mdc-top-app-bar__navigation-icon">menu</a>
          <span class="mdc-top-app-bar__title">Dismissible Drawer</span>
        </section>
      </div>
    </header>

    <main class="main-content" id="main-content">
      <div class="mdc-top-app-bar--fixed-adjust">
        App Content
      </div>
    </main>
  </div>
</body>
Dismissible Drawer Below Top App Bar

In cases where the drawer appears below the top app bar you will want to follow the markup shown below. The mdc-drawer__content and main-content elements will also scroll independently of each other. The mdc-top-app-bar, mdc-drawer and mdc-drawer-app-content will be sibling to each other. The mdc-top-app-bar--fixed-adjust helper class will be applied to mdc-drawer and mdc-drawer-app-content elements to adjust the position with top app bar.

<body>
  <header class="mdc-top-app-bar app-bar" id="app-bar">
    <div class="mdc-top-app-bar__row">
      <section class="mdc-top-app-bar__section mdc-top-app-bar__section--align-start">
        <a href="#" class="demo-menu material-icons mdc-top-app-bar__navigation-icon">menu</a>
        <span class="mdc-top-app-bar__title">Dismissible Drawer</span>
      </section>
    </div>
  </header>
  <aside class="mdc-drawer mdc-drawer--dismissible mdc-top-app-bar--fixed-adjust">
    <div class="mdc-drawer__content">
      <div class="mdc-list">
        <a class="mdc-list-item mdc-list-item--activated" href="#" aria-selected="true">
          <i class="material-icons mdc-list-item__graphic" aria-hidden="true">inbox</i>
          <span class="mdc-list-item__text">Inbox</span>
        </a>
        <a class="mdc-list-item" href="#">
          <i class="material-icons mdc-list-item__graphic" aria-hidden="true">send</i>
          <span class="mdc-list-item__text">Outgoing</span>
        </a>
        <a class="mdc-list-item" href="#">
          <i class="material-icons mdc-list-item__graphic" aria-hidden="true">drafts</i>
          <span class="mdc-list-item__text">Drafts</span>
        </a>
      </div>
    </div>
  </aside>

  <div class="mdc-drawer-app-content mdc-top-app-bar--fixed-adjust">
    <main class="main-content" id="main-content">
      App Content
    </main>
  </div>
</body>

The CSS to match either case looks like this:

// Note: these styles do not account for any paddings/margins that you may need.

body {
  display: flex;
  height: 100vh;
}

.mdc-drawer-app-content {
  flex: auto;
  overflow: auto;
  position: relative;
}

.main-content {
  overflow: auto;
  height: 100%;
}

.app-bar {
  position: absolute;
}

// only apply this style if below top app bar
.mdc-top-app-bar {
  z-index: 7;
}

The JavaScript to toggle the drawer when the navigation button is clicked looks like this:

import {MDCTopAppBar} from "@material/top-app-bar";
const topAppBar = MDCTopAppBar.attachTo(document.getElementById('app-bar'));
topAppBar.setScrollTarget(document.getElementById('main-content'));
topAppBar.listen('MDCTopAppBar:nav', () => {
  drawer.open = !drawer.open;
});

Modal Drawer

Modal drawers are elevated above most of the app's UI and don't affect the screen's layout grid.

<body>
  <aside class="mdc-drawer mdc-drawer--modal">
    <div class="mdc-drawer__content">
      <nav class="mdc-list">
        <a class="mdc-list-item mdc-list-item--activated" href="#" aria-selected="true">
          <i class="material-icons mdc-list-item__graphic" aria-hidden="true">inbox</i>
          <span class="mdc-list-item__text">Inbox</span>
        </a>
        <a class="mdc-list-item" href="#">
          <i class="material-icons mdc-list-item__graphic" aria-hidden="true">send</i>
          <span class="mdc-list-item__text">Outgoing</span>
        </a>
        <a class="mdc-list-item" href="#">
          <i class="material-icons mdc-list-item__graphic" aria-hidden="true">drafts</i>
          <span class="mdc-list-item__text">Drafts</span>
        </a>
      </nav>
    </div>
  </aside>

  <div class="mdc-drawer-scrim"></div>
  <div>Main Content</div>
</body>

The mdc-drawer-scrim next sibling element is required, to protect the app's UI from interactions while the modal drawer is open.

Style Customization

CSS Classes

ClassDescription
mdc-drawerMandatory.
mdc-drawer__headerNon-scrollable element that exists at the top of the drawer.
mdc-drawer__contentScrollable content area of the drawer.
mdc-drawer__titleTitle text element of the drawer.
mdc-drawer__subtitleSubtitle text element of the drawer.
mdc-drawer--dismissibleDismissible drawer variant class.
mdc-drawer--modalModal drawer variant class.
mdc-drawer--openIf present, indicates that the dismissible drawer is in the open position.
mdc-drawer--openingApplied while the drawer is animating from the closed to the open position.
mdc-drawer--closingApplied while the drawer is animating from the open to the closed position.
mdc-drawer-app-contentMandatory for dismissible variant only. Sibling element that is resized when the drawer opens/closes.
mdc-drawer-scrimMandatory for modal variant only. Used for the overlay on the app content.

Sass Mixins

MixinDescription
mdc-drawer-border-color($color)Sets border color of mdc-drawer surface.
mdc-drawer-divider-color($color)Sets divider color found between list groups.
mdc-drawer-fill-color-accessible($color)Sets the fill color to $color, and list item and icon ink colors to an accessible color relative to $color.
mdc-drawer-surface-fill-color($color)Sets the background color of mdc-drawer.
mdc-drawer-title-ink-color($color)Sets the ink color of mdc-drawer__title.
mdc-drawer-subtitle-ink-colorSets drawer subtitle and list subheader ink color.
mdc-drawer-item-icon-ink-color($color)Sets drawer list item graphic icon ink color.
mdc-drawer-item-text-ink-color($color)Sets drawer list item text ink color.
mdc-drawer-item-activated-icon-ink-color($color)Sets activated drawer list item icon ink color.
mdc-drawer-item-activated-text-ink-color($color)Sets activated drawer list item text ink color.
mdc-drawer-shape-radius($radius)Sets the rounded shape to drawer with given radius size. $radius can be single radius or list of 2 radius values for trailing-top and trailing-bottom. Automatically flips the radius values in RTL context.
mdc-drawer-item-shape-radius($radius, $rtl-reflexive)Sets the rounded shape to drawer navigation item with given radius size. Set $rtl-reflexive to true to flip radius values in RTL context, defaults to true.
mdc-drawer-activated-overlay-color($color)Sets the overlay color of the activated drawer list item.
mdc-drawer-scrim-fill-color($color)Sets the fill color of mdc-drawer-scrim.
mdc-drawer-z-index($value)Sets the z index of drawer. Drawer stays on top of top app bar except for clipped variant of drawer.
mdc-drawer-width($width)Sets the width of the drawer. In the case of the dismissible variant, also sets margin required for mdc-drawer-app-content.

Accessibility

Focus Management

It is recommended to shift focus to the first focusable element in the main content when drawer is closed or one of the destination items is activated. (By default, MDC Drawer restores focus to the menu button which opened it.)

Dismissible Drawer

Restore focus to the first focusable element when a list item is activated or after the drawer closes. Do not close the drawer upon item activation, since it should be up to the user when to show/hide the dismissible drawer.

const listEl = document.querySelector('.mdc-drawer .mdc-list');
const mainContentEl = document.querySelector('.main-content');

listEl.addEventListener('click', (event) => {
  mainContentEl.querySelector('input, button').focus();
});

document.body.addEventListener('MDCDrawer:closed', () => {
  mainContentEl.querySelector('input, button').focus();
});

Modal Drawer

Close the drawer when an item is activated in order to dismiss the modal as soon as the user performs an action. Only restore focus to the first focusable element in the main content after the drawer is closed, since it's being closed automatically.

const listEl = document.querySelector('.mdc-drawer .mdc-list');
const mainContentEl = document.querySelector('.main-content');

listEl.addEventListener('click', (event) => {
  drawer.open = false;
});

document.body.addEventListener('MDCDrawer:closed', () => {
  mainContentEl.querySelector('input, button').focus();
});

MDCDrawer Properties and Methods

PropertyValue TypeDescription
openBooleanProxies to the foundation's open/close methods. Also returns true if drawer is in the open position.

Events

Event NameEvent Data StructureDescription
MDCDrawer:openedNoneEmits when the navigation drawer has opened.
MDCDrawer:closedNoneEmits when the navigation drawer has closed.

Usage within Web Frameworks

If you are using a JavaScript framework, such as React or Angular, you can create a Navigation Drawer for your framework. Depending on your needs, you can use the Simple Approach: Wrapping MDC Web Vanilla Components, or the Advanced Approach: Using Foundations and Adapters. Please follow the instructions here.

MDCDrawerAdapter

Method SignatureDescription
addClass(className: string) => voidAdds a class to the root element.
hasClass(className: string) => booleanReturns true if the root element contains the given className.
removeClass(className: string) => voidRemoves a class from the root element.
elementHasClass(element: !Element, className: string) => booleanReturns true if the an element contains the given class.
saveFocus() => voidSaves the focus of currently active element.
restoreFocus() => voidRestores focus to element previously saved with 'saveFocus'.
focusActiveNavigationItem() => voidFocuses the active / selected navigation item.
notifyClose() => voidEmits the MDCDrawer:closed event.
notifyOpen() => voidEmits the MDCDrawer:opened event.
trapFocus() => voidTraps focus on root element and focuses the active navigation element.
releaseFocus() => voidReleases focus trap from root element which was set by trapFocus and restores focus to where it was prior to calling trapFocus.

Foundations

MDCDismissibleDrawerFoundation

Method SignatureDescription
open() => voidOpens the drawer from the closed state.
close() => voidCloses the drawer from the open state.
isOpen() => booleanReturns true if the drawer is in the open position.
isOpening() => booleanReturns true if the drawer is animating open.
isClosing() => booleanReturns true if the drawer is animating closed.
handleKeyDown(evt: Event) => voidHandles the keydown event.
handleTransitionEnd(evt: Event) => voidHandles the transitionend event when the drawer finishes opening/closing.
opened() => voidOnly called internally. Extension point for when drawer finishes open animation.
closed() => voidOnly called internally. Extension point for when drawer finishes close animation.

MDCModalDrawerFoundation (extends MDCDismissibleDrawerFoundation)

Method SignatureDescription
handleScrimClick() => voidHandles click event on scrim.