Building a drawer with Stencil
Tags: stencil, webdev, beginners
Published: 2019-06-25 14:27:05

---
title: Building a drawer with Stencil
published: true
tags: stencil, webdev, beginner
canonical_url: https://jefiozie.github.io/building-a-drawer-with-stencil/build-a-drawer-with-stencil/
---

For the past couple of weeks, I’ve been working on a “Design System” that is using Stencil. Today I want to share some basics on how I build a drawer with Stencil, with you, the community 🙏🏻.

## What is a drawer

A drawer is a component, designed to add collapsible side content (often navigation) alongside some content. Further, in this article, you will see the end result of our drawer (_that won’t be a perfectly styled example_😆).

## The architecture of the drawer

Our drawer has three main parts:

[![Preview of drawer architecture](https://jefiozie.github.io/static/9737f229489869d7ed52de2683ff9096/b9e4f/drawer_architecture.png)](/static/9737f229489869d7ed52de2683ff9096/93d99/drawer_architecture.png)

- A container
- A slot for the drawer content
- A slot for the content

The container will serve as a wrapper. It will help us with the styling of our internal parts. The content slot is our placeholder for the content that will be served in the slot. Often this will be navigation. Finally, we have a slot for application content. Here you will provide the main content of your application.

## Project setup

As the focus of this article is on creating a drawer container I will provide a brief introduction on setting up a new project.

First, we are going to create a new Stencil project, with some simple steps provided by the [Getting Starred of Stencil](https://stenciljs.com/docs/getting-started)

Create a new project:

```
npm init stencil
```

  You will be prompt to choose a project type, choose `component`

```
? Pick a starter › - Use arrow-keys. Return to submit.

❯ ionic-pwa Everything you need to build fast, production-ready PWAs
   app Minimal starter for building a Stencil app or website
   component Collection of web components that can be used anywhere
```

 

Now you need to provide a project name, let’s fill in `example-drawer`. And that is it now we are ready to start working on our component.

## Code

After opening the project in your favorite editor open the `my-component.tsx` file and remove all of the default code and replace it with the following:

```
import { Component, Host, h, State, Method } from '@stencil/core'

@Component({
  tag: 'my-component',
  styleUrl: 'my-component.css',
  shadow: true,
})
export class MyComponent {
  /**
   * Show or hide the drawer
   */
  @State() open = true
}
```

 

First, we set up some internal data with the use of `@State`. When changing the `State` property it will cause the components to render again. With this, we are going to toggle a CSS class.

```
import { Component, Host, h, State, Method } from '@stencil/core'

@Component({
  tag: 'my-component',
  styleUrl: 'my-component.css',
  shadow: true,
})
export class MyComponent {
  @State() open = true

  @Method()
  async toggleDrawer() {
    this.open = !this.open
  }
}
```

 

We have added a `@Method` decorator and a method that toggles the property `open`. Doing this allows us to call the `toggleDrawer` method from the outside of the component.

```
import { Component, Host, h, State, Method } from '@stencil/core'

@Component({
  tag: 'my-component',
  styleUrl: 'my-component.css',
  shadow: true,
})
export class MyComponent {
  @State() open = true

  @Method()
  async toggleDrawer() {
    this.open = !this.open
  }
  render() {
    return (
      <Host>
        <div class="drawer-container">
          <div class={{ drawer: true, open: this.open }}>
            <div class="drawer-content">
              <slot name="drawer-content" />
            </div>
          </div>
          <div class={{ content: true, left: !this.open }}>
            <slot name="content" />
          </div>
        </div>
      </Host>
    )
  }
}
```

 

In this last part, we defined the HTML template. Earlier in this article, I said we would toggle some CSS classes based on the `State` property `open`. Now you can see what I meant with this. When the property `open` equals `true` the `open` class will be added to the div, similar behavior will be on the `content div` as shown in the code.

[![Drawer outside of the view](https://jefiozie.github.io/static/30e34bd5d2ae39fd76a9ebd3a1a7fdc5/b9e4f/outside_of_view.png)](/static/30e34bd5d2ae39fd76a9ebd3a1a7fdc5/43efa/outside_of_view.png)

Now open the `my-component.css` file and replace the code with:

```
:host {
  display: block;
  width: 100%;
}

.drawer-container {
  position: relative;
  display: flex;
  box-sizing: border-box;
  overflow: hidden;
  border: 1px solid #ccc;
}

.drawer {
  padding-right: 20px;
  background: gray;
  border-right: 1px solid #ccc;
  position: relative;
  width: 280px;
  transform: translate3d(-100%, 0, 0);
  z-index: 2;
  box-sizing: border-box;
  will-change: transform;
}
.drawer-content {
  width: 100%;
  height: 100%;
}
.drawer.open {
  transform: none;
}

.content {
  margin-left: 0px;
  margin-right: 0px;
  flex-grow: 1;
  flex-shrink: 1;
  display: block;
  height: 100%;
  overflow: auto;
  will-change: contents;
}
.content.left {
  margin-left: -280px;
}
```

 

We will focus on the `open` and `left` behaviors. In our `drawer` class we have defined `transform: translate3d(-100%, 0, 0);`. This will place our div out of the view. When we append the `open` class we will set `transform` to `none`, now we have our div back on the screen.

Our other focus point is the `left` class. Because the width of our drawer is fixed at `280px` we can use `margin-left` to “slide” to the left when this class is appended.

## Putting it all together

Now that we have all the parts together we have to do one more thing. When we created our new project `Stencil` provides us with an `index.html` within this file it has already created all thing for the “example” component.

Yet because we made changes we should reflect these changes in the `index.html` file. To speed things up copy the code below and replace this in your `index.html` file.

```
<!DOCTYPE html>
<html dir="ltr" lang="en">
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0"
    />
    <title>Stencil Component Starter</title>

    <script type="module" src="/build/example-drawer.esm.js"></script>
    <script nomodule src="/build/example-drawer.js"></script>
    <style>
      html,
      body {
        height: 100%;
        margin: 0;
      }
    </style>
  </head>
  <body>
    <my-component>
      <ul slot="drawer-content">
        <li>Menu 1</li>
        <li>Menu 2</li>
        <li>Menu 3</li>
        <li>Menu 4</li>
        <li>Menu 5</li>
      </ul>
      <div slot="content">
        Main
      </div>
    </my-component>
    <button id="toggleBtn">Toggle drawer</button>
  </body>
  <script>
    const btn = document.getElementById("toggleBtn");
    const = document.querySelector('my-component');
       btn.addEventListener("click", () => {
         drawer.toggleDrawer();
       });
  </script>
</html>
```

 

What did we change? We have added an _unsorted list_ that will be placed in slot: `drawer-content` and we have a `div` that will be placed in slot: `content`. To toggle our drawer we have added a simple button that `on-click` will call the `toggleDrawer` method on our `drawer`

## Summary

So what did we do in summary? We have created a simple drawer that slides out of the view. We have created a drawer with basic styling that can be enhanced to your own needs. The great thing is it that we can use this drawer in **ANY** frontend application that is using Vanilla JS, Angular, React or Vue, etc.

To see the full source have a look at [this repo](https://github.com/Jefiozie/stencil-drawer)

I hope this article inspired you, gave you some insights on how to build a component with the use of **Stencil** and have had some fun!😁

## A Thank you

At last, a thank you to some people how reviewed my article

- [Wes Grimes (@wesgrimes)](https://twitter.com/wesgrimes)
- [Romulo Cintra (@romulocintra)](https://twitter.com/romulocintra)