Go to homepage

Projects /

Animated Page Transition #2

An ajax powered page transition, with a slide-in content animation triggered by a side tabbed navigation.

Animated Page Transition #2
Check our new component library →

We’ve been experimenting lately with a CSS powered animation that replaces the refresh of the web page while the content is updated using ajax. Today’s nugget is based on the same idea, but with a different execution: this time a simple hover effect turns into a loading bar, that finally expands with new content!

Icons from our Nucleo library.

Creating the structure

The html structure is composed of a .cd-main element, wrapping the page content, a .cd-side-navigation, containing the side navigation, and a #cd-loading-bar to create the loading bar animation.

<nav class="cd-side-navigation">
         <a href="index.html" class="selected" data-menu="index">
            <svg><!-- svg content here --></svg>

         <!-- ... -->

      <!-- other list items here -->
</nav> <!-- .cd-dashboard -->

<main class="cd-main">
   <section class="cd-section index visible">
         <div class="cd-title">
            <h2>Animated Page Transition #2</h2>
            <span>Some text here</span>
         <a href="#index-content" class="cd-scroll">Scroll Down</a>

      <div class="cd-content" id="index-content">
         <!-- content here -->
      </div> <!-- .cd-content -->
   </section> <!-- .cd-section -->
</main> <!-- .cd-main -->

<div id="cd-loading-bar" data-scale="1" class="index"></div> <!-- lateral loading bar -->

Adding style

The .cd-side-navigation is in fixed position, with a height: 100% and an overflow: hidden, while its child <ul> has an overflow-y: auto (so that you can scroll inside it if the navigation is higher than the viewport).

To create the line visible when you hover over each navigation item, we used the a::after pseudo-elements. They are in absolute position, have a width: 4px , a top: 0 and a right: -4px.
One note: we assigned a width: calc(100%- 4px) to each list item elements to be able to place the a::after elements inside the .cd-side-navigation (since it has an overflow: hidden).

.cd-side-navigation {
 position: fixed;
 z-index: 3;
 top: 0;
 left: 0;
 height: 100vh;
 width: 94px;
 overflow: hidden;
.cd-side-navigation ul {
 height: 100%;
 overflow-y: auto;
.cd-side-navigation::before {
 /* background color of the side navigation */
 content: '';
 position: absolute;
 top: 0;
 left: 0;
 height: 100%;
 width: calc(100% - 4px);
 background-color: #131519;
.cd-side-navigation li {
 width: calc(100% - 4px);
.cd-side-navigation a {
 display: block;
 position: relative;
.cd-side-navigation a::after {
 /* 4px line to the right of the item - visible on hover */
 content: '';
 position: absolute;
 top: 0;
 right: -4px;
 height: 100%;
 width: 4px;
 background-color: #83b0b9;
 opacity: 0;
.no-touch .cd-side-navigation a:hover::after {
 opacity: 1;

When a user selects a new item in the side navigation, a new .cd-section element is created and inserted in the DOM (you can find more details in the ‘Events handling’ section).

By default, this new .cd-section element is translated to the left, outside the viewport (translateX(-100%)). It is then moved back into the viewport (using the .visible class ) to replace the old content.

.cd-section {
position: absolute; z-index: 1; top: 0; left: 0; height: 100%; width: 100%; transform: translateX(-100%); transition: transform 0s 0.5s; } .cd-section.visible { /* this is the visible/selected section */ position: relative; z-index: 2; transform: translateX(0); transition: transform 0.5s 0s; }

Events handling

The index.html file contains only the ‘Intro’ content. A different html file has been created for each navigation item (services.html, contact.html, ..) with exactly the same structure, but with different .cd-section content.

When a user clicks one of the item in the side navigation, the triggerAnimation() function is executed. This function triggers the loading bar animation (loadingBarAnimation() function) and loads the new content (loadNewContent() function).

For the loading bar animation, we have been using Velocity.js: at the beginning of the animation,  the #cd-loading-bar is placed right next to the selected navigation item (its height equal to the one of the list item). Its scaleY value is then animated to create the loading effect.

function loadingBarAnimation() {
   //loadingBar is the #cd-loading-bar element
   //scaleY is the loadingBar actual scale value
   var scaleMax = loadingBar.data('scale'); //this is the scaleY value to cover the entire window height (100% loaded)
   if( scaleY + 1 < scaleMax) {
      newScaleValue = scaleY + 1;
   // ...
      scaleY: newScaleValue
      }, 100, loadingBarAnimation
); }

When a new page is selected, a new .cd-section element is created and inserted into the DOM. The load() function is then used to load the new content (we used a data-menu attribute assigned to the navigation list items to determine the file content to be loaded). Once the new html has been loaded, the loading bar animation is completed, the old content is replaced by the new one and the new page is added to the window.history (using the pushState() method).

function loadNewContent(newSection) {
   //create a new section element and insert it into the DOM (newSection is the data-menu of the selected navigation item)
   var section = $('<section class="cd-section overflow-hidden '+newSection+'"></section>').appendTo(mainContent);
   //load the new content from the proper html file
   section.load(newSection+'.html .cd-section > *', function(event){   
         scaleY: scaleMax //this is the scaleY value to cover the entire window height (100% loaded)
         }, 400, function(){
            //add the .visible class to the new section element -> it will cover the old one

            var url = newSection+'.html';

               //add the new page to the window.history
               window.history.pushState({path: url},'',url);

            // ...
); }); }

To trigger the same animation effect when user clicks the browser back button, we listen for the popstate event, and execute the triggerAnimation() function when it is fired.

Project duplicated

Project created

Globals imported

There was an error while trying to export your project. Please try again or contact us.