Create a Microsoft Metro loading spinner in pure CSS3


Create a Microsoft Metro loading spinner in pure CSS3

With Metro UI, Microsoft finally launched in 2012 a great design language with a modern and fresh appearance, a good user experience on desktop and mobile devices and a great visual feedback for the user. A huuuuuge step forward for Microsoft!

Today I want to show you how to create the loading spinner seen on Windows 8 (and above) machines, using only HTML and basic CSS3 animations. Check out the demo below and leeeeeet's go!


demo download source


The idea

The basic idea is to create a simple structure with six circles with the same style, everyone with its own wrapper that decides the starting point of its child circle in our animation. Everything is contained inside a general container.

Take a look at this image:

We can see this structure:

  • A big container for everthing with a grey color;
  • Six wrappers, everyone with its own color an difference rotation;
  • Six circles, everyone contained in its own wrapper with the same color of its parent.

If you look better, every circle is in the same place relative its wrapper. This is a key point of our spinner because we gonna move the circles, not the wrapper, with a different starting point from each other. In this way, we will have a stunning animation with a beautiful stagger effect. Below the single structure of a wrapper and its circle.


The markup

Now, let's go with the simple HTML markup for our spinner. As explained before, we will have a generic container and six sub-wrappers with a single circle inside of it.

<div class="spinner">  
    <div class="spinner__circle-wrapper"><div class="spinner__circle"></div></div>
    <div class="spinner__circle-wrapper"><div class="spinner__circle"></div></div>
    <div class="spinner__circle-wrapper"><div class="spinner__circle"></div></div>
    <div class="spinner__circle-wrapper"><div class="spinner__circle"></div></div>
    <div class="spinner__circle-wrapper"><div class="spinner__circle"></div></div>
    <div class="spinner__circle-wrapper"><div class="spinner__circle"></div></div>
</div>  

The basic style

Let's do some basic style rules for our component. Let's start with the main container and give it just some dimensions and position properties:

/*/ spinner container /*/
.spinner{
    top: 50%;
    left: 50%;
    width: 70px;
    height: 70px;
    position: absolute;

    -webkit-transform: translate3d(-50%, -50%, 0);
    transform: translate3d(-50%, -50%, 0);
}


Then, let's do the basic style of the wrappers and circles. Every element of its type have the same basic style, the same appearance and features. First, the circle wrapper:

/*/ spinner circle wrapper /*/
.spinner__circle-wrapper{
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    position: absolute;
}


We create a simple wrapper with the same dimension of the main container and a position: absolute rule to let all the wrapper go one over the other.

Second, the circle:

/*/ spinner circle /*/
.spinner__circle{
    top: 50%;
    left: 50%;
    width: 10%;
    height: 10%;
    margin-top: -50%;
    margin-left: -5%;
    position: absolute;
    background: #FFFFFF;

    -webkit-transform-origin: 50% 500%;
    transform-origin: 50% 500%;

    -webkit-border-radius: 50%;
    border-radius: 50%;

    /*/ for now, ignore this property...we will check it later. ;-) /*/
    -webkit-animation: rotate 5000ms infinite cubic-bezier(0.785, 0.135, 0.150, 0.860);
    animation: rotate 5000ms infinite cubic-bezier(0.785, 0.135, 0.150, 0.860);
}


Our circle is just 10% of its wrapper and it's placed at the center of the X axis and on top of the Y axis. It have a transform-origin property to spin in the right way inside its wrapper, and it also have an animation property that we will check out later.


The custom style

As explained in the first image of this tutorial, we have six identical boxes one over the other, and everyone have a different starting point. How can we give to our circles different starting points? I think that give a different transform: rotate() rule on their wrappers could be a great idea.

Let's write some custom style for every wrapper of our spinner:

.spinner__circle-wrapper:nth-child(1){
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
}
.spinner__circle-wrapper:nth-child(2){
    -webkit-transform: rotate(340deg);
    transform: rotate(340deg);
}
.spinner__circle-wrapper:nth-child(3){
    -webkit-transform: rotate(320deg);
    transform: rotate(320deg);
}
.spinner__circle-wrapper:nth-child(4){
    -webkit-transform: rotate(300deg);
    transform: rotate(300deg);
}
.spinner__circle-wrapper:nth-child(5){
    -webkit-transform: rotate(280deg);
    transform: rotate(280deg);
}
.spinner__circle-wrapper:nth-child(6){
    -webkit-transform: rotate(260deg);
    transform: rotate(260deg);
}


Last part of the custom style is about the animation job. The Metro loading spinner move every single circle with a different delay to create a smooth and fancy movement. What we have to do is to give to every circle a different delay time, so our animation property could do the job in the right way:

.spinner__circle-wrapper:nth-child(1) .spinner__circle{
    -webkit-animation-delay: 0ms;
    animation-delay: 0ms;
}
.spinner__circle-wrapper:nth-child(2) .spinner__circle{
    -webkit-animation-delay: 50ms;
    animation-delay: 50ms;
}
.spinner__circle-wrapper:nth-child(3) .spinner__circle{
    -webkit-animation-delay: 100ms;
    animation-delay: 100ms;
}
.spinner__circle-wrapper:nth-child(4) .spinner__circle{
    -webkit-animation-delay: 150ms;
    animation-delay: 150ms;
}
.spinner__circle-wrapper:nth-child(5) .spinner__circle{
    -webkit-animation-delay: 200ms;
    animation-delay: 200ms;
}
.spinner__circle-wrapper:nth-child(6) .spinner__circle{
    -webkit-animation-delay: 250ms;
    animation-delay: 250ms;
}

The animations

Finally, the animation that do the big job of our spinner. Do you remember the last rule of our .spinner__circle-wrapper element? Ok, the code below is its engine.

/*/ webkit vendor /*/
@-webkit-keyframes rotate{
    0%  { -webkit-transform: rotate(0deg); }
    42% { -webkit-transform: rotate(360deg); }
    62% { opacity: 1; }
    64% { opacity: 0; }
    84% { -webkit-transform: rotate(720deg);  opacity: 0; }
    85% { -webkit-transform: rotate(920deg);  opacity: 0; }
    88% { opacity: 0; }
    90% { opacity: 1; }
    100%{ -webkit-transform: rotate(1080deg);  opacity: 1; }
}

/*/ standard /*/
@keyframes rotate{
    0%  { transform: rotate(0deg); }
    42% { transform: rotate(360deg); }
    62% { opacity: 1; }
    64% { opacity: 0; }
    84% { transform: rotate(720deg);  opacity: 0; }
    85% { transform: rotate(920deg);  opacity: 0; }
    88% { opacity: 0; }
    90% { opacity: 1; }
    100%{ transform: rotate(1080deg);  opacity: 1; }
}


As you can see we have different phases here:

  • 00% to 42%: the first round;
  • 42% to 84%: the second round, with the fade-out phase in the middle;
  • 84% to 85%: move the circles to a new point when their are not visible;
  • 85% to 100%: the final mid-round with the fade-in phase in the middle.

Based on our rule write before we use this animation in 5 seconds infinite times, using a nice bezier curve. Simple.

animation: rotate 5000ms infinite cubic-bezier(0.785, 0.135, 0.150, 0.860);  

Aaaaaand that's all for today. Hope you enjoy it.
For any questions or feedbacks, drop a comment below. Cheers!



Daniele Petrarolo

Daniele Petrarolo

http://www.danielepetrarolo.com

In love with #JavaScript, #PHP and #Python / Web Developer / There's no place like 127.0.0.1.

View Comments