Grow your CSS skills. Land your dream job.

How To Add Page Transitions with CSS and smoothState.js

Published by Guest Author

The following is a guest post by Miguel Ángel Pérez. Miguel has been working at Weblinc on ways to transition pages on websites more gracefully. On single-page applications, we have more opportunity for this since we aren't fighting the page reload. But traditional sites with page reloads, you can still be quite graceful with some help from CSS and JS. In this article Miguel focuses on setting up the CSS to do this and making it work with his plugin.

CSS animations (and a little JavaScript trickery) can let us add page transitions and move away from the hard-cut of page loads. My jQuery plugin smoothState.js helps polish those transitions and improve UI response times.

Page transitions benefit the user experience

Imagine, for a second, how disorienting it would be if touching a doorknob teleported you to the other side of the door. Navigating the web feels like using a teleporting doorknob. Layouts change, elements rearrange or disappear, and it takes time time for the user to adjust. Smooth transitions reduce the effort it takes for users to get settled into a new environment.

Native apps understand the importance of animations. It's uncommon to find an app without page transitions, and users have grown accustomed to this higher level of usability. The web has started to feel outdated because of this shift in expectations. Many think of the web as an inferior experience to apps. Luckily, it doesn't have to be this way.

Add page transitions with CSS @keyframes animations

Let's take a look at an example to see how we could start adding page transitions. In our demo layout we can see the white flashes and hard-cuts typical of web pages.


A default browsing experience.

CSS animations allow us to define the visual behavior of an element when it gets rendered on the page. To add animations to our site we should:

  1. Identify how the elements on the page will animate
  2. Create the keyframes we'll need
  3. Write the CSS declarations
  4. Add classes to the layout

Identify how the elements on the page will animate

Let's look at our sample page:


Annotations to show how our page will animate.

We can pick out a few opportunities for adding some transitions if we examine the layout. Google's guide on meaningful transitions is a good set of rules for element animations. That's a good place to start if you're new to interaction design.

Create the @keyframes we’ll need

It looks like we'll need three types of unique animations.

  1. A fade in animation for the header and the button
  2. A slide up animation with a slight fade for the contents of the home page
  3. A slide in from the right, with a slight fade, for the contents of the detail page

Let's create those CSS @keyframes and name them:

/*
 * Keyframes
 */

@keyframes fadeIn {
  0% {
    opacity: 0;
  }

  100% {
    opacity: 1;
  }
}

@keyframes fadeInUp {
  0% {
    opacity: 0;
    transform: translate3d(0, 100%, 0);
  }

  100% {
    opacity: 1;
    transform: none;
  }
}

@keyframes fadeInRight {
  0% {
    opacity: 0;
    transform: translate3d(100%, 0, 0);
  }

  100% {
    opacity: 1;
    transform: none;
  }
}

}

Write the CSS declarations

Now that we have our keyframes, we'll need to attach them to classes using the CSS animation properties. Here’s what that CSS looks like:

/*
 * CSS Page Transitions
 * Don't forget to add vendor prefixes!
 */
.m-scene {
  /** Basic styles for an animated element */
  .scene_element {
    animation-duration: 0.25s;
    transition-timing-function: ease-in;
    animation-fill-mode: both;

  }

  /** An element that fades in */
  .scene_element--fadein {
    animation-name: fadeIn;
  }

  /** An element that fades in and slides up */
  .scene_element--fadeinup {
    animation-name: fadeInUp;
  }

  /** An element that fades in and slides from the right */
  .scene_element--fadeinright {
    animation-name: fadeInRight;
  }
}

It's important to note that animations that take too long will annoy the user. There has been quite a bit of research around the usability of response times. To keep the UI feeling snappy limit the animation duration to 0.25 seconds. Run some quick and dirty user testing to make sure your animations don't feel bothersome.

Add classes to the layout

Now that we have written out our CSS, it’s time to add the classes to the markup.

<!-- Home page -->
<div class="m-scene" id="main">
  <div class="m-header scene_element scene_element--fadein">
    ...
  </div>
  <div class="m-page scene_element scene_element--fadeinup">
    ...
  </div>
</div>
<!-- Detail page -->
<div class="m-scene" id="main">
  <div class="m-aside scene_element scene_element--fadein">
    ...
  </div>
  <div class="m-right-panel m-page scene_element scene_element--fadeinright">
    ...
  </div>
</div>

Our result is a nicer entrance to the pages:


A browsing experience using CSS animations.

We've made some progress on our page with just CSS. But even with our improvements, we still see the flash and the animations aren't as tight as they could be. Now we'll use smoothState.js to fix that.

Adding polish with smoothState.js

smoothState.js is a jQuery plugin that progressively enhances page loads to give us more control over page transitions. To include it in our page we'll need to:

  1. Grab a copy of jQuery and add it to our page
  2. Download smoothState.js and add it after jQuery.
  3. Create and include a new .js file, after smoothState.js, where we can run the plugin. (Or use whatever build tool / concatenation process you use to do all this.)

Inside our new .js file, we'll want to initialize smoothState on a container with an id. The container should wrap all the content on the page.

;(function ($) {
  'use strict';
  var content  = $('#main').smoothState({
        // onStart runs as soon as link has been activated
        onStart : {
          
          // Set the duration of our animation
          duration: 250,
          
          // Alterations to the page
          render: function () {

            // Quickly toggles a class and restarts css animations
            content.toggleAnimationClass('is-exiting');
          }
        }
      }).data('smoothState'); // makes public methods available
})(jQuery);

smoothState gives us access to an onStart.render() callback that allows us to choreograph the way elements exit the page. We can eliminate the page's hard-cuts by reversing the layout animations before displaying new content. To do this, run the .toggleAnimationClass() function, passing in a class as the first argument. Now we'll declare a new animation direction using that class.

/*
 * CSS Page Transitions
 * Don't forget to add vendor prefixes!
 */
.m-scene {
  .scene_element {
    animation-duration: 0.25s;
    transition-timing-function: ease-in;
    animation-fill-mode: both;

  }

  .scene_element--fadein {
    animation-name: fadeIn;
  }

  .scene_element--fadeinup {
    animation-name: fadeInUp;
  }

  .scene_element--fadeinright {
    animation-name: fadeInRight;
  }

  /** Reverse "exit" animations */
  &.is-exiting {
      .scene_element {
        animation-direction: alternate-reverse;
      }
  }
}

We'll also want to animate the user back to the top of the page. Achieve this by using jQuery's .animate() function and setting the scrollTop property to 0.

;(function ($) {
  'use strict';
  var $body    = $('html, body'), // Define jQuery collection 
      content  = $('#main').smoothState({
        onStart : {
          duration: 250,
          render: function () {
            content.toggleAnimationClass('is-exiting');
            
            // Scroll user to the top
            $body.animate({ 'scrollTop': 0 });

          }
        }
      }).data('smoothState');
})(jQuery);

The final result is a smooth transition between the pages of our site. Elements enter and exit the page with grace and the white flash is gone. We're firing the animations even before the content loads so the user sees an immediate response from the page.


A browsing experience using CSS animations and smoothState.js

View Demo

Comments

  1. Permalink to comment#

    Thanks for this article. I have been struggling with CSS for a while now. but this wel definitely help me out. Thanks again! :)

  2. I would almost have written something similar – good thing I didn’t have to because I suck at javascript.

  3. I’ve tried a shoddy home-brew version of this before where:

    1. Capture all click events and preventDefault
    2. Apply a class to the body like do-page-leave-animations which triggers some @keyframe stuff
    3. Start a setTimeout the length of those animations
    4. On callback, window.location to the href of the link that was clicked
    5. On page loads, have a class like do-page-enter-animations on the body which triggers some @keyframe stuff
    6. Remove that class when they are done (or don’t)
  4. Lionel Péramo
    Permalink to comment#

    The thing is… if you have lots of links on the page, we can not preload all those links or it would slow down the user experience isn’t it ?

  5. vimes1984
    Permalink to comment#

    I’ve always doen this with angularJS routes and http intercepts… works wonders

  6. Permalink to comment#

    Hey! I have a couple of questions.

    In the keyframes code, why do you use translate3d instead of translate?
    In the jQuery code, why is semicolon used before creating the immediately-invoked function? Is this a pattern or what?

    ;(function ($) {
    
    • Lionel Péramo
      Permalink to comment#

      The use of translate3D is for the use of GPU. The semicolon is here maybe in order to prevent a forgotten semicolon in the previous loaded script.

  7. Rutger
    Permalink to comment#

    Great post! I assume this doesn’t work when a using the browser back button but still a nice simple transition without breaking the web. In our firm we find ourselves using the history api for page transitions because the client foesn t like hard refreshes but still wants ‘real’ pages for search engine friendlyness… So yeah, I definitely see myself using this. The usability aspect is a very good reason to implement this, though…never thought of it that way…

    Good one!

  8. Rutger
    Permalink to comment#

    Tested on iPhone, hitting the back button seems to work

    Keep up the good work!

  9. Justin
    Permalink to comment#

    What minimum jquery version is needed? I tried implementing this on a Drupal 7 install but it didn’t add the animation class.

    • 1.9 is the basic requirement. I haven’t done the research to see if it’s compatible with an even earlier version, but it might be. Feel free to reach out to me if you run into any issues trying to implement smoothState.

  10. Rutger
    Permalink to comment#

    Oh wait, same issues with reinitializing javascript… probably because it uses the history api………….

    • Dave Wells
      Permalink to comment#

      Do you ever find a solution to reinitialising the JS? Having the same problem here.

  11. tanzanite
    Permalink to comment#

    Is the code will work as it is which is given? or we have to do some modification on it?

  12. This is a great article. It reminds me of the 90’s when FrontPage had page transitions, except these one’s don’t make me want to end my life.

    Unfortunately the View Demo link (to http://miguel-perez.github.io/jquery.smoothState.js/) is broken though.

    Thanks for another awesome article!

  13. Pavel
    Permalink to comment#

    That had been a perfect article if there would have been real page reloading. Otherwise there is no problems of doing that. What if you wan’t to do fade-in/fade-out with real redirects. Not ajax content loading. I haven’t found the way of doing that.

  14. boskameel
    Permalink to comment#

    It works perfect and superfast! I have a question, some pages of me use jquery functions, and after a pageswitch they aren’t working anymore. How can I reinitialize them?

  15. Mike
    Permalink to comment#

    Demo link doesn’t work…

  16. Mark
    Permalink to comment#

    I can’t seem to get this working…wish the demo links were still up.

  17. Permalink to comment#

    Appears the demo is now located here:

    http://weblinc.github.io/jquery.smoothState.js/index.html

    I love it. Really neat.

  18. Joe
    Permalink to comment#

    Definitely cool post, but I question if this is actually good for the user experience. I get the idea of the tele-transportation from one room to the other, but unless you preload an entire site, you could be blocking the navigation just for the sake of transitions.

  19. Permalink to comment#

    Great post!

  20. Huuuh, why is this not working on Chrome?

  21. Matt Garcia
    Permalink to comment#

    Doesn’t work on Chrome or Safari.

  22. vohof
    Permalink to comment#

    I think the demo is broken. It’s jumping. this demo seems to be better.

  23. Shane Keulen
    Permalink to comment#

    on my windows tablet the first transition to a page takes over ten seconds.

Leave a Comment

Posting Code

  • Use Markdown, and it will escape the code for you, like `<div class="cool">`.
  • Use triple-backticks for blocks of code.
    ``` 
    <div>
      <h1>multi-line block of code</h1>
      <span>be cool yo.</span>
    </div>
    ```
  • Otherwise, escape your code, like <code>&lt;div class="cool"&gt;</code>. Markdown is just easier though.

Current ye@r *

*May or may not contain any actual "CSS" or "Tricks".