Drawer

Drawer Figma JS

The Drawer component is an off-screen content container that slides in from the right when needed. It provides a focused panel similar to a modal, but without taking over the entire screen.

Default drawer

Drawers come in both HTML and web component implementations.

Set up an HTML drawer by first creating an element with class .hc-drawer. Inside, add an element with class .hc-drawer-content that contains the content of the drawer. This can accept a title and footer, as documented below.

Create a JS drawer with the <hc-drawer> element. Available attributes include id, close for a close button, open to default to open, and label for an aria-label value.

This is where all of the main content goes.

This is the right side drawer.

Note that by default, the content behind the drawer is not accessible while the drawer is open. This can be modified with a class.

This is where all of the main content goes.

This is the right side drawer.

Note that by default, the content behind the drawer is not accessible while the drawer is open. This can be modified with a class.


<div class="w-full h-full flex flex-col items-center justify-center gap-4">
  <p>
    This is where all of the main content goes.
  </p>
  <label class="hc-button hc-button--primary cursor-pointer" for="drawer_basic" role="button" tabindex="0">
    Open a basic HTML drawer
  </label>
</div>
<div class="hc-drawer">
  <input id="drawer_basic" type="checkbox" class="hc-drawer-toggle" />
  <div class="hc-drawer-content">
    <div class="hc-drawer-content-body">
      <p class="hc-body">
        This is the right side drawer.
      </p>
      <p class="hc-body">
        Note that by default, the content behind the drawer is not accessible while the drawer is open. This can be modified with a class.
      </p>
      <label class="hc-button hc-button--primary cursor-pointer" for="drawer_basic">
        Close the drawer
      </label>
    </div>
  </div>
</div>

<div class="w-full h-full flex flex-col items-center justify-center gap-4">
  <p>
    This is where all of the main content goes.
  </p>
  <button type="button" class="hc-button hc-button--primary" onclick="document.getElementById('hcdwc')?.toggleAttribute('open')">
    Open a basic web component drawer
  </button>
</div>
<hc-drawer id="hcdwc" label="Web component drawer">
  <p class="hc-body">
    This is the right side drawer.
  </p>
  <p class="hc-body">
    Note that by default, the content behind the drawer is not accessible while the drawer is open. This can be modified with a class.
  </p>
  <button class="hc-button hc-button--primary" onclick="document.getElementById('hcdwc')?.removeAttribute('open')">
    Close the drawer
  </button>
</hc-drawer>

This default drawer on desktops will fill the height of whatever block-level element it’s inside of.

No matter where it is or how it’s used, the drawer itself must be inside of an element that has position: relative applied to it.

It you’re rendering a drawer as part of the app layout, note that the drawer uses a fixed position.

Opening and closing

There are two methods to open and close an HTML drawer (JS drawers have close and esc handled automatically): checkbox with label, or javascript to add or remove the open attribute to the drawer.

Checkbox method

The simplest HTML-only method is to add a checkbox with class .hc-drawer-toggle inside the .hc-drawer wrapper (as a sibling to .hc-drawer-content).

Toggling the drawer this way is handled by a <label> that has a corresponding for value for the ID of the checkbox inside the drawer.

This is the default non-JS method of toggling a drawer and does not satisfy accessibility natively (i.e., no escape-to-close), so you will need to handle that separately.

Accessibility note

If using the documented label method, in order for the label (acting as a button) to receive a focus state, add role="button" and tabindex="0" to it.

Javascript method

To manually toggle the drawer, add the open attribute to the parent .hc-drawer element. We recommend adding a unique ID to the drawer to make this easier via javascript.

This is where all of the main content goes.

This is the right side drawer.

Note that by default, the content behind the drawer is not accessible while the drawer is open. This can be modified with a class.


<div class="w-full h-full flex flex-col items-center justify-center gap-4">
  <p>
    This is where all of the main content goes.
  </p>
  <button class="hc-button hc-button--primary" onclick="document.getElementById('hcd').toggleAttribute('open')">
    Open the drawer
  </button>
</div>
<div class="hc-drawer" id="hcd">
  <div class="hc-drawer-content">
    <div class="hc-drawer-content-body">
      <p class="hc-body">
        This is the right side drawer.
      </p>
      <p class="hc-body">
        Note that by default, the content behind the drawer is not accessible while the drawer is open. This can be modified with a class.
      </p>
      <button class="hc-button hc-button--primary" onclick="document.getElementById('hcd').removeAttribute('open')">
        Close the drawer
      </button> 
    </div>
  </div>
</div>

Escape to close

If you’re using the web component, you get escape-to-close any open drawers for free!

We have also included javascript in the compiled honeycomb.js file that automatically registers an esc handler to hide open drawers. You need to include our javascript to get this behavior, or roll your own/copy ours into your JS file.

Header and Footer

While options, headers should be present on all drawers and should include the close button. Create a header by adding a header element with class hc-drawer-content-title. The title should use an <h3> and the close “button” (taking advantage of the checkbox) is provided below.

Footers are optional but provide a great space for a CTA or application button. Footers will stick to the bottom of the drawer; long content in the drawer body will scroll inside of itself and not push the footer down.

Application body behind the drawer.

Drawer title

This is the drawer content section.

If this content is long, it will scroll without touching the position of the fixed header and footer.

We're going to make this content very long and move the CTA to the footer, which will stick to the bottom of the drawer if this content was not sufficiently long enough to push the footer there naturally. More words.

This is where all of the main content goes.

Drawer title
Footer content

This is the drawer content section.

If this content is long, it will scroll without touching the position of the fixed header and footer.

We're going to make this content very long and move the CTA to the footer, which will stick to the bottom of the drawer if this content was not sufficiently long enough to push the footer there naturally. More words.


<div class="w-full h-full flex flex-col items-center justify-center gap-4">
  <p>
    Application body behind the drawer.
  </p>
  <label class="hc-button hc-button--primary cursor-pointer" for="drawer_full" role="button" tabindex="0">
    Open a full HTML drawer
  </label>
</div>
<div class="hc-drawer">
  <input id="drawer_full" type="checkbox" class="hc-drawer-toggle" />
  <div class="hc-drawer-content">
    <header class="hc-drawer-content-title">
      <span class="hc-drawer-content-title-label">Drawer title</span>
      <label class="hc-button hc-button--tertiary hc-button--icon" aria-label="Close drawer" for="drawer_full">
        <i class="hc-icon hc-icon--close hc-icon--20"></i>
      </label>
    </header>
    <div class="hc-drawer-content-body">
      <p class="hc-body">
        This is the drawer content section.
      </p>
      <p class="hc-body">
        If this content is long, it will scroll without touching the position of the fixed header and footer.
      </p>
      <p class="hc-body">
        We're going to make this content very long and move the CTA to the footer, which will stick to the bottom of the drawer if this content was not sufficiently long enough to push the footer there naturally. More words.
      </p>
    </div>
    <footer class="hc-drawer-content-footer">
      <div class="hc-drawer-content-footer-left">
        Footer content
      </div>
      <div class="hc-drawer-content-footer-right">
        <label class="hc-button hc-button--primary hc-button--success cursor-pointer" for="drawer_full">
          Close the drawer
        </label>
      </div>
    </footer>
  </div>
</div>

<div class="w-full h-full flex flex-col items-center justify-center gap-4">
  <p>
    This is where all of the main content goes.
  </p>
  <button type="button" class="hc-button hc-button--primary" onclick="document.getElementById('hcdwc2')?.toggleAttribute('open')">
    Open a full web component drawer
  </button>
</div>
<hc-drawer id="hcdwc2" close label="Web component drawer">
  <!-- header and footer slots -->
  <div slot="title"><span class="hc-drawer-content-title-label">Drawer title</span></div>
  <div slot="footer-left">Footer content</div>
  <div slot="footer-right">
    <button class="hc-button hc-button--primary hc-button--success"
            type="button"
            onclick="document.getElementById('hcdwc').removeAttribute('open')">
      Close the drawer
    </button>
  </div>
  <!-- body content (everything without a slot) -->
  <p class="hc-body">
    This is the drawer content section.
  </p>
  <p class="hc-body">
    If this content is long, it will scroll without touching the position of the fixed header and footer.
  </p>
  <p class="hc-body">
    We're going to make this content very long and move the CTA to the footer, which will stick to the bottom of the drawer if this content was not sufficiently long enough to push the footer there naturally. More words.
  </p>
</hc-drawer>

App drawers

To create a drawer that fills the entire content panel inside of an app and has a fixed position, place the .hc-drawer element at the end of the .hc-app-main container if you’re using the Layout framework. You can also give the drawer the class .hc-drawer--in-main if it needs to sit outside of this element or if you’re not using that framework.

You can use the <aside> element for this. If you do, add the attribute aria-label="Right drawer" or some other more descriptive label for what the drawer contains.

Take a look at the full page layout demo to see this live.

Allow access to background content

By default, the content that sits behind the drawer (i.e., the main page content/scrollable area) will not be accessible to the user while the drawer is open.

To prevent this default, append the class .hc-drawer--allow-bg to the drawer parent element and the user will be able to interact with the main page content.

Mobile handling

Drawers slide in from the right and take up 1/3 of the screen width, with a minimum width of 24rem.

On small screens (up to the md breakpoint) the drawer will instead pull up from the bottom of the screen. This is handled by default and will also occur if you’re using the Layout’s grid container on main content sections that are smaller than 512px.

Test this drawer out when this content window is less than 32rem wide.

Mobile drawer example

On small screens this will slide up from the bottom.

On larger screens this will slide in from the right.

Adding a ton of breaks here to see what happens when the content is forced to scroll.













<div class="w-full h-full flex flex-col items-center justify-center gap-4">
  <p class="text-center">
    Test this drawer out when this content window is less than 32rem wide.
  </p>
  <label class="hc-button hc-button--primary cursor-pointer" for="drawer_mobile" role="button" tabindex="0">
    Open a basic drawer
  </label>
</div>
<div class="hc-drawer">
  <input id="drawer_mobile" type="checkbox" class="hc-drawer-toggle" />
  <div class="hc-drawer-content">
    <header class="hc-drawer-content-title">
      <span>Mobile drawer example</span>
      <label class="hc-button hc-button--tertiary hc-button--icon cursor-pointer" aria-label="Close drawer" for="drawer_mobile">
        <i class="hc-icon hc-icon--close hc-icon--20"></i>
      </label>
    </header>
    <div class="hc-drawer-content-body">
      <p class="hc-body">
        On small screens this will slide up from the bottom.
      </p>
      <p class="hc-body">
        On larger screens this will slide in from the right.
      </p>
      <p class="hc-body">
        Adding a ton of breaks here to see what happens when the content is forced to scroll.
        <br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
      </p>
    </div>
  </div>
</div>