Search code examples
javascripthtmlcssflexboxsidebar

How do I animate a CSS sidebar opening and closing in CSS?


I have a sidebar that is made using css display: flex; and a button that sets the sidebars display to display: none;. Currently the sidebar closes and opens instantaneously, with no animation. What i would like to happen is the sidebar pops out from the left with transition: 2.2s cubic-bezier(.36,-0.01,0,.77);. The sidebar hides and shows, I just want it to animate.

I tried just putting transition: 2.2s cubic-bezier(.36,-0.01,0,.77); in the sidebar CSS but that did not work. Here is the code for the sidebar the content and the js.

function toggleSidebar() {
  var sidebar = document.querySelector('sidebar');
  var display = sidebar.style.display;
  if (display == 'none') {
    sidebar.style.display = 'flex';
  } else {
    sidebar.style.display = 'none';
  }
}
body {
  display: flex;
  flex-direction: row;
  background-image: url("https://4kwallpapers.com/images/wallpapers/macos-13-macos-ventura-macos-2022-stock-light-5k-retina-2048x1536-8135.jpg");
  background-size: cover !important;
  margin: 0px;
  height: 100%;
}

sidebar {
  flex: 0 0 200px;
  flex-direction: column;
  backdrop-filter: blur(30px) saturate(2);
  -webkit-backdrop-filter: blur(30px) saturate(2);
  padding-inline: 0;
  padding-block: 13px;
  overflow-y: auto;
  overflow-x: hidden;
  transition: 2.2s cubic-bezier(.36,-0.01,0,.77);
  height: 100%;
}

.main {
  flex: 1 1 auto;
  flex-wrap: wrap;
  flex-direction: column;
  overflow: auto;
}
<body>
  <sidebar>
    <li><a href="#">Demo</a></li>
  </sidebar>
  <div class="main">
    <button onclick="toggleSidebar()">SIDEBAR</button>
  </div>
</body>


Solution

  • Depending on if you want the sidebar to slide or grow, I see two options.

    Either way you just need to toggle a class in order to change a property, the transition will then kick in for that property.

    I've also exchanged the <sidebar> tag for a regular div, since that isn't technically valid HTML. See here for more info: Why does CSS work with fake elements?


    Slide left / right.

    Set margin-left to the negative width.

    Keep in mind there may be some unintended consequences since it will be overflowing the flexbox.

    function toggleSidebar() {
      document.querySelector('.sidebar')
        .classList.toggle('closed');
    }
    body {
      display: flex;
      flex-direction: row;
      background-image: url("https://4kwallpapers.com/images/wallpapers/macos-13-macos-ventura-macos-2022-stock-light-5k-retina-2048x1536-8135.jpg");
      background-size: cover !important;
      margin: 0px;
      height: 100%;
    }
    
    .sidebar {
      flex: 0 0 200px;
      flex-direction: column;
      backdrop-filter: blur(30px) saturate(2);
      -webkit-backdrop-filter: blur(30px) saturate(2);
      padding-inline: 0;
      padding-block: 13px;
      overflow-y: auto;
      overflow-x: hidden;
      transition: 2.2s cubic-bezier(.36,-0.01,0,.77);
      height: 100%;
    }
    
    .sidebar.closed {
      margin-left: -200px;
    }
    
    .main {
      flex: 1 1 auto;
      flex-wrap: wrap;
      flex-direction: column;
      overflow: auto;
    }
    <body>
      <div class="sidebar">
        <li><a href="#">Demo</a></li>
      </div>
      <div class="main">
        <button onclick="toggleSidebar()">SIDEBAR</button>
      </div>
    </body>


    Grow / Shrink

    Set flex-basis to 0.

    You initially set flex-basis to 200px with the shorthand flex property. That's what determines the width of the sidebar.

    I also added white-space: nowrap to avoid text wrapping while animating. If that's not acceptable you may need a more complex solution to stop the bullets from wrapping.

    function toggleSidebar() {
      document.querySelector('.sidebar')
        .classList.toggle('closed');
    }
    body {
      display: flex;
      flex-direction: row;
      background-image: url("https://4kwallpapers.com/images/wallpapers/macos-13-macos-ventura-macos-2022-stock-light-5k-retina-2048x1536-8135.jpg");
      background-size: cover !important;
      margin: 0px;
      height: 100%;
    }
    
    .sidebar {
      flex: 0 0 200px;
      flex-direction: column;
      backdrop-filter: blur(30px) saturate(2);
      -webkit-backdrop-filter: blur(30px) saturate(2);
      padding-inline: 0;
      padding-block: 13px;
      overflow-y: auto;
      overflow-x: hidden;
      transition: 2.2s cubic-bezier(.36,-0.01,0,.77);
      height: 100%;
      white-space: nowrap;
    }
    
    .sidebar.closed {
      flex-basis: 0;
    }
    
    .main {
      flex: 1 1 auto;
      flex-wrap: wrap;
      flex-direction: column;
      overflow: auto;
    }
    <body>
      <div class="sidebar">
        <li><a href="#">Demo</a></li>
      </div>
      <div class="main">
        <button onclick="toggleSidebar()">SIDEBAR</button>
      </div>
    </body>