Taha

How to Add a Sliding Sidebar Menu to Your Site

Here's what we are going to build:

I'll explain only the relevant code below. If you want to see the full code, check the codepen above.

The HTML

<body>
  <div class="menu-sidebar">
    <div class="sidebar-content">
      <button class="close-menu-button">
        <!-- close button icon -->
      </button>
    </div>
  </div>
  <div class="overlay"></div>
  <div class="main-page">
    <div class="header">
      <button class="menu-button">
        <!-- menu icon -->
      </button>
    </div>
    <h1>Page Title</h1>
    <p>
      Lorem ipsum dolor sit amet...
    </p>
  </div>
</body>

This HTML consists of four main parts:

  • The sidebar: .menu-sidebar
  • The main page section: .main-page
  • The button that opens the sidebar: .menu-button
  • And the overlay displayed on top of the main page when the sidebar is open: .overlay

We have two states for this UI: idle and sidebar-open states. We specify which state is on by setting a class on the <body> – it can be any wrapper element, but I'm using body for this example. For this demo I used sidebar-open class for the sidebar-open state.

We will define the styling for each state using CSS, and when the sidebar-open class is toggled on body, the correct styling would be applied, which will be animated with transition.

Here's an example for defining styles for each state:

.main-page {
  // Styling for the main page when in idle state
}

body.sidebar-open .main-page {
  // Styling for the main page when in sidebar-open state
}

The CSS

/* Sidebar-open state for body */
body.sidebar-open {
  overflow: hidden;
}

/* Idle state for the sidebar */
.menu-sidebar {
  width: 500px;
  height: 100%;
  overflow: auto;
  position: fixed;
  left: 0;
  top: 0;
  z-index: 2;
  transition: 0.5s ease translate;
  translate: -100% 0;
  will-change: transform;
}

/* Sidebar-open state for the sidebar */
body.sidebar-open .menu-sidebar {
  translate: 0 0;
}

/* Idle state for the main page */
.main-page {
  transition: 0.5s ease translate;
  will-change: transform;
}

/* Sidebar-open state for the main page */
body.sidebar-open .main-page {
  translate: 500px 0;
}

/* Idle state for the overlay */
.overlay {
  opacity: 0;
  pointer-events: none;
  background: rgba(0, 0, 0, 0.5);
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  transition: 0.5s ease opacity;
}

/* Sidebar-open state for the overlay */
body.sidebar-open .overlay {
  opacity: 1;
  pointer-events: all;
}

/* When on tablet-size screen, make the sidebar open in full width above the main page */
@media (max-width: 768px) {
  .menu-sidebar {
    width: 100%;
  }
  body.sidebar-open .main-page {
    translate: 0 0;
  }
}

Let's see how the elements will be in each state.

The sidebar (.menu-sidebar):

  • We don't want the sidebar to be part of the page flow, so we are positioning it with position: fixed.
  • To make it appear above the main page when open, we need to set its z-index to something bigger than .overlay and the main page. So we set it z-index: 2.
  • I've chosen its width to be 500px but you can choose any width you want.
  • In idle state, the sidebar should be positioned outside the viewport. Setting translate: -100% 0 will move it to the left by its full width.
  • For better performance, we will animate the sidebar using translate, and to make that even better, we need to promote the element to a new layer. That's what will-change does.
  • In sidebar-open state, the sidebar translate will be 0 0 which means it will move back to its original position, and with transition it will appear as animating to the right.

The main page (.main-page):

  • We don't need to do anything when in idle state. We just need to make sure it has transition and will-change for animation purposes.
  • But when in sidebar-open state, this element needs to move to the right by the same width as the sidebar, which is 500px in this case.

The overlay (.overlay):

  • It's a fixed element with full width and height to cover the whole screen. It also has a background with transparency to see the main page below it background: rgba(0, 0, 0, 0.5).
  • It should be hidden when in idle state. We are using opacity: 0 here and not display: none because we want to animate it with transition: 0.5s ease opacity.
  • An element with opacity: 0 still exists on the page but not seen. So clicking anything on the page would not work because you will be clicking on .overlay. To fix this, we set pointer-events: none, which disables all mouse and touch events on it.
  • When in sidebar-open state, we just need to show it and enable clicking on it (so it closes the sidebar when you click on it). To do this, we set opacity: 1 and pointer-events: all .

The JavaScript

The last step to make this work is toggling sidebar-open class on body when the menu button, overlay, or close button is clicked.

const menuButton = document.querySelector('.menu-button')
const overlay = document.querySelector('.overlay')
const closeMenuButton = document.querySelector('.close-menu-button')

menuButton.addEventListener('click', toggleMenu)
overlay.addEventListener('click', toggleMenu)
closeMenuButton.addEventListener('click', toggleMenu)

function toggleMenu() {
  document.body.classList.toggle('sidebar-open')
}
Taha Shashtari

I'm Taha Shashtari, a full-stack web developer. Building for the web is my passion. Teaching people how to do that is what I like the most. I like to explore new techniques and tools to help me and others write better code.

Subscribe to get latest updates about my work
©2024 Taha Shashtari. All rights reserved.