Building a Smooth Sliding Mobile Menu

It’s possible to create user interface animations in the browser that are as buttery smooth as native app animations. There are a few important techniques that you’ll need to know in order to achieve that level of performance.

This site is built to be responsive, meaning when you size down your browser window or visit on a mobile device the design adjusts to fit.

The biggest change is the top navigation menu, when the width of the viewport becomes narrow enough it will collapse into what is now commonly called a “burger button”. I guess it resembles a burger from the side? In any case, tapping that button will slide in a menu specifically designed for mobile devices. Give it a try, I’ll wait.

You might notice that the performance of the menu transition is smooth and silky, and the burger button responds instantly to your finger tap. On modern devices it’s almost indistinguishable from the performance you’d expect in native apps.

In this post I’m going to run through all the implementation techniques you’ll need to know in order to get that level of animation performance on your own websites or web apps. Let’s get into it!

1. Build out the HTML

The first step is to build out the HTML that we’ll need for the page and the menu. You’ll need two containers, one for everything on your page, and one for the menu. I’m using UL and DIV elements here, but you can use whatever makes the most semantic sense on your page. I’ve also added a link that will eventually act as the way to toggle the menu open and closed.

The order of the menu and the page elements doesn’t matter, although semantically the menu makes the most sense at the top of your page.

2. Style the Menu with CSS

We’ll need some basic styles in order for the menu to appear on the top right hand side of the screen and remain a specific width regardless of the screen size. Feel free to adjust these styles so the menu looks exactly how you’d like.

I’ve chosen to give the menu a width of 240px for a couple of reasons, it feels like a good width for a vertical menu, and it will fit across a wide range of devices while still leaving part of the page visible. Add the following CSS to your page:

While you are styling the menu you may want to remove the display: none and reverse the z-index settings so it will sit on top of the page. Once you’re happy with the design make sure you set them back so the page covers the menu entirely and it’s hidden until requested.

3. Avoiding the Browser Reflow

Now, at this point you might be thinking we can sprinkle some jQuery .slideIn() and .slideOut() magic on the menu and be done with it. Although that would work reasonably well on desktop browsers, it will perform poorly on mobile devices.

Using jQuery’s built in animation functions will animate the menu in a way that causes something called browser reflow. This is where the browser has to re-calculate and render the position of elements on your page each time something changes. If you’re animating your menu that’s a lot of frames and a lot of re-calculation, causing the animation to be sluggish on less powerful mobile devices.

There’s a way we can animate elements in the DOM and avoid causing reflow, it’s using a CSS property called transform.

4. CSS Transform and the GPU

Using the transform property will allow us to manipulate any element over three dimensions using the translate3d value. We can move the element on the X, Y and Z axis on the page. So for example to move the #page element 10px to the left, we could use:

Notice that we use a negative value to shift the #page element to the left. You need to think of the elements’ starting position always as 0, 0, 0 regardless of any other positioning applied to the element in your CSS.

It’s also important to include the vendor prefixed -webkit-transform version in your CSS for the best level of support across multiple browsers and versions. This will ensure your menu works on all modern desktop and mobile browsers to at least a few versions back.

CSS also provides the more axis specific transform options translateX() and translateY(), so why are we using translate3d()? The reason is we want to use hardware acceleration to eventually animate the transform. Using translate3d() allows us to access the power of the GPU (Graphics Processing Unit) on the mobile device to render the element and eventual animation. The GPU is designed for this sort of work so it can provide smoother animation than using the CPU alone.

When we apply translate3d() to an element, a GPU rendered copy of the element is created and placed on a separate layer from the rest of the page. This means any manipulation of that element will not cause browser reflow — we are only modifying that layer and not affecting any other elements on the page below. This dramatically improves the performance of animation on mobile devices.

So how can we make use of the CSS transform property to animate our menu? First of all we’ll need to track the state of the menu using JavaScript.

5. Add JavaScript to Track State

Using JavaScript to track the state of the menu will allow us to add CSS classes to the body element of the document. This will allow us to handle all of the animation purely in CSS.

If you take another look at the menu animation on this site you’ll notice that the menu is not actually moving. What is happening is the menu stays in one place — stuck to the top right edge — and the whole page on the layer above slides back to reveal it.

There are four distinct states:

  1. No animation, menu not visible
  2. Page animating to the left, revealing the menu
  3. No animation, menu is visible
  4. Page animating to the right, hiding the menu

We need to assign CSS classes to the body element to represent when we are in one of these four states. To do this you’ll need to add the following JavaScript to your footer (make sure you have jQuery loaded before this, or you can adapt it to raw JavaScript):

Now, with this JavaScript you’ll see the following body classes appear and disappear depending on the menu state:

  1. No animation, menu not visible — No classes on body
  2. Page animating to the left, revealing the menu — animating & left classes on body
  3. No animation, menu is visible — menu-visible class on body
  4. Page animating to the right, hiding the menu — animating & right classes on body

We’ll see why it’s important to know about these four different states and have distinct classes for each in the next step.

6. Let’s Animate!

From here on our all of the animation will be done using CSS transitions. We can now use the CSS classes that the JavaScript added to the body element to determine when to apply CSS transition properties to elements. Add the following CSS to your page right below the previous styles:

I think the first four declarations should hopefully make sense. The first is easy, the #menu element should not be hidden when animating or visible. The second makes sure that the #page element will have a transition applied to it when transformed. That basically means it will animate smoothly over .25 seconds to the transformed position rather than just jumping directly to it.

The next two declarations determine the direction and final position that the #page element should be transformed to. So if it’s .left then it goes 240px to the left, if it’s .right, then 240px to the right from its initial spot (remember it always starts at 0, 0, 0).

The final declaration is a little tougher. When the .animate, .left and .right classes are not present, which is when the menu is either fully visible, or fully hidden, none of the CSS transforms above will apply. That means that there’s no translate3d being set on #page. As soon as the animation classes are removed, and the .menu-visible class is added, #page is going to jump back to its original position at position: absolute; top: 0; right: 0. In order to stop this we need to adjust its position so it does not jump back after opening. We need to adjust it to right: 240px; to keep it open at 240px from the right edge of the page.

You might be thinking — why not just leave the .animating and .left classes on the body to keep the #page in the correct position? It’s true that this would retain the final position of the #page element after the transition. The issue is that it also leaves the element in a state of being rendered by the GPU since the translate3d() value still applies. This is a bad idea, ending up with too many elements being GPU rendered at once will cause mobile browsers to crash.

As a best practice you should only apply translate3d() when absolutely necessary during animation states. Use standard positioning to retain an elements’ final state after animating. This is the main reason we needed to track and apply CSS classes for the four distinct states.

This last CSS declaration will eliminate any flickering of elements while they are in a state of being animated, it only applies to webkit based browsers (most current mobile browsers). Add it below the CSS already added:

7. Make it Responsive (if needed)

You should now have a basic page with a sliding menu. With the code above the menu will always be present and working on any modern device, at any screen size. For it to only show on smaller mobile devices you’ll need to introduce a media query to your CSS. Here’s one that will apply to most tablets and smartphones:

You may want to be more granular with your media queries depending on the size and type of devices you want to support. The above is a fairly blunt approach and will keep the #menu and #toggle-menu elements hidden on bigger screens entirely. You may want to write some alternative styles for #menu on larger screens, or include an entirely separate menu inside of #page for larger screens that will be hidden on mobile devices.

That’s it! You should now have a smooth native-like slide in menu. Here’s a demo of the final version. There are many other great uses for CSS transforms, transitions, and animations, some of which I’ve covered in recent posts: JavaScript Pull to Refresh for the Web, and JavaScript Swipe Cards UX. With the right techniques it’s possible to replicate many of the buttery smooth animations you’d generally expect only in native apps.

I’ve packed up all of the code and put it on Github. You can use it as a base to adapt to your needs. I look forward to seeing some of your implementations, let me know if you can think of any good improvements to the code in the comments.

View on GitHub | Try a Demo

Published by

Andy Peatling

Automattic Inc, open source contributor, and bogey golfer.

↓   Some other thoughts   ↓

The early iPhone X reviews are in, and they are mostly positive. Apple only gave reviewers 24 hours with the phone, so at this point they are light on detail. It looks as though like Face ID is working well indoors, but sunlight and florescent lighting is causing interference. I hope this can be tweaked in the software since my iPhone X arrives on Friday. 🙂

iPhone X review: face the future · The iPhone X is clearly the best iPhone ever made.

Published by

Andy Peatling

Automattic Inc, open source contributor, and bogey golfer.

Well done to the England under 17 team, to come back from 2-0 down and go on to win 5-2 in the final is a great achievement. With this and the under-20 team also winning the World Cup, am I crazy to expect a good senior mens side in the next decade? Probably.

England win U17 World Cup: Young Lions fight back to beat Spain in final · England produce a superb comeback from 2-0 down to thrash Spain and win the Under-17 World Cup.

Published by

Andy Peatling

Automattic Inc, open source contributor, and bogey golfer.

I love this visualization of the “Internet’s pulse”. I expected to see a burst of activity around evening time, where people are at home and probably watching Netflix. The middle of the day during office hours is clearly the peak.


Published by

Andy Peatling

Automattic Inc, open source contributor, and bogey golfer.

The commonwealth countries of Australia, New Zealand, and Canada are heading for their own credit crisis in the next few years after largely skipping out on the events of the financial crisis. I expect that the situation will be worse for all three countries than it was for the US in 2008, but without the global ramifications.


Published by

Andy Peatling

Automattic Inc, open source contributor, and bogey golfer.

Creative Outlets

This past summer I took a three month sabbatical from Automattic. It’s something that’s offered to everyone once you’ve been at the company for five years or more. I was overdue, I’m less than six months away from my ten year anniversary! Time flies.

During my sabbatical I spent much of my time away from the computer, working in the garden, getting my hands dirty, and spending time with my family. It was a great and much appreciated break, it felt good to relax and recharge.

Out came the Laptop

However, my time wasn’t all away from the computer. My brother has been looking to break into the world of programming in an effort to change careers. He’s spent the last ten years working in the service industry, and he’s ready for a change.

Early in the summer we spent three sessions a week working to understand programming fundamentals. His language and platform of choice is Swift and iOS, he wants to ultimately find a position as a junior iOS developer. For the first six weeks we didn’t touch anything to do with apps, we didn’t even plug in a phone or boot up the simulator. We only spent time using Swift playgrounds, going through data types, operators, functions, loops, arrays, dictionaries, optionals, then finally onto classes, objects, and inheritance.

Swift is a wonderful programming language, this was my first experience working with it. You can clearly see how it has taken the best of many different languages, and almost completely left the worst of them behind. Even though I had no experience using it before, my brother and I were able to work effectively using my knowledge of other languages.

The Spark

Teaching my brother over the summer allowed me to learn something about myself. As we continued on through the fundamentals and started into building a basic starter app, I realized that I was really enjoying the process. It felt great to learn a new language and environment, and even better to learn it through teaching.

As the summer passed we continued working towards building his first app, designing the UI, and using the correct APIs for the hardware we needed to access. Along the way I noticed that helping my brother was providing me with a creative outlet, something that I think had been missing for some time. It made me feel good. My day-to-day work at Automattic is fulfilling, although I don’t often get to express creativity in ways that I have done in previous years. This is okay, what I’ve learned is I still need to give myself an outlet to express it in my own time.

So that’s exactly what I intend to do from this point on. I’ll make sure I spend at least a few hours a week on my creative outlet. I expect to continue on with iOS development, which is the last thing I would have expected before my sabbatical! I figured why waste the knowledge I’ve built up helping my brother?

I will start posting more on this blog about what I’ve been working on, including posts about what I learned helping my brother start from square one. The learning curve for iOS development is steep, but really rewarding once you get over it.

Published by

Andy Peatling

Automattic Inc, open source contributor, and bogey golfer.

More of my thoughts