CSS only "Coding" Loading Animation - Step By Step Tutorial

In this tutorial I'll describe in detail how I went about creating the "coding" animation above using only SVG and highly performant CSS for that sweet 60FPS action.

The original was built using SVG and SMIL animations, but as I describe in that post, SMIL animations aren't very well supported right now, and are being deprecated from Chrome, leaving only Firefox holding the flag for the major browsers. So, I built a CSS-only version and shared that on Reddit, where it was politely pointed out to me that I could animate SVG with CSS (shoutout to cranky old guy who yells about SVG /u/rspeed - thanks!). So third time lucky, I'll build the animation with SVG, and animate it with (slightly more) performant CSS so it runs buttery smooth on all devices.

btw, this loader is for my side project mydevportfol.io, a project to help devs build slick portfolios from their GitHub profile, check it out and let me know your thoughts :)

60FPS CSS animations

In previous SVG projects I used CSS rules that weren't particularly performant. Check out the keyframes rules on line 421 of this file, you can see that I'm animating the top's pixel values. This works, but causes the browser to recalculate the layout and re-paint after each tiny movement of the animation, which on lower end devices will lead to lower framerates and choppy transitions.

In this fantastic article from Google's Paul Irish and Paul Lewis, they explain the issue in depth, but essentially the key takeaway is that in order to achieve animations that are as slick and smooth as possible, use the transform() CSS property, and use it with opacity, scale, translate and rotate.

Using transform() means that the browser can offload some of the rendering work to the GPU. It also doesn't make the browser recalculate layout and paint of the elements on the screen. Bottom line, use transform() whenever possible, so that's what I'll do in this tutorial.

Creating the SVG (optional)

I build with Sketch because I just think it's a great tool for vector graphic work, but if you don't have it, or don't want to learn it, you can use any of the free online tools, although their features may be limited.

For now, of course, just use the SVG I've provided in the first codepen if you don't want to make your own.

Would you like a tutorial on how to use Sketch for projects like this? Tweet me and let me know, I can make a post on that and show you my own workflow. I also reply to every single email reply I get from subscribers, in case you dislike twitter :)

Naming the SVG elements

I don't know about other vector tools, but if you've used Sketch, the names of your groups and elements will be their ID's in the svg when you export it. I find this really helpful when it comes to figuring out which elements to animate and when.

In the code below, you can see that the structure I've used is that of "lines" and "dashes" with each of them being numbered.

For instance, line1 has 4 dashes; dash-1, dash-2, dash-3, and dash-4. We can use the individual ID's to animate them in the CSS later.

<svg width="400px" height="175px" viewBox="0 0 400 175" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
    <title>Artboard</title>
    <desc>Created with Sketch.</desc>
    <defs></defs>
    <g id="coding" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="Artboard">
            <g id="group1" transform="translate(10.000000, 10.000000)">
                <g id="line1">
                    <rect id="dash-1" fill="#F92672" x="0" y="0" width="40" height="20" rx="10"></rect>
                    <rect id="dash-2" fill="#A6E22E" x="43" y="0" width="60" height="20" rx="10"></rect>
                    <rect id="dash-3" fill="#FFE792" x="106" y="0" width="100" height="20" rx="10"></rect>
                    <rect id="dash-4" fill="#FFE792" x="209" y="0" width="70" height="20" rx="10"></rect>
                </g>
                <g id="line2" transform="translate(30.000000, 27.000000)">
                    <rect id="dash-1" fill="#F92672" x="0" y="0" width="40" height="20" rx="10"></rect>
                    <rect id="dash-2" fill="#A6E22E" x="43" y="0" width="60" height="20" rx="10"></rect>
                    <rect id="dash-3" fill="#FFE792" x="106" y="0" width="80" height="20" rx="10"></rect>
                </g>
                <rect id="line6" fill="#F92672" x="0" y="135" width="40" height="20" rx="10"></rect>
                <rect id="line3" fill="#F8F8F2" x="60" y="54" width="290" height="20" rx="10"></rect>
                <rect id="line4" fill="#F8F8F2" x="60" y="81" width="225" height="20" rx="10"></rect>
                <rect id="line5" fill="#F92672" x="30" y="108" width="40" height="20" rx="10"></rect>
            </g>
        </g>
    </g>
</svg>

Adding the expand animation to all elements

In order to hide all of the "dashes", we'll add the following css

/* 
Hiding the rect on page load 
And add the "expand" animation
*/
rect {
  opacity:0;
  animation: expand 0.2s forwards;
}

Our first CSS property here opacity:0, is effectively "hiding" all rect elements.

Our second property animation: expand 0.2s forwards is what will animate the elements. The expand is a simple animation that we'll define with keyframes below. The 0.2s is the how long we'd like the animation to take, and the forwards keyword here is so that the animation stays in its final form, and doesn't loop or go back to the start.

For more info on the animation CSS property, check out the w3schools page, it explains it well

The above code will result in the below result. Notice how all rect elements are animating at the same time (click "rerun" to see it again).

See the Pen CSS SVG coding animation - 1 by Chris Dermody (@ChipD) on CodePen.

Adding a delay to each dash

So we've got all the rect elements "expanding" at the same time. Our next step is to add an appropriate delay to each element.

/* Delays to subsequent "dashes" */
#line1 #dash-2 {
  animation-delay:0.1s;
}

#line1 #dash-3 {
  animation-delay:0.2s;
}

#line1 #dash-4 {
  animation-delay:0.3s;
}

#line2 #dash-1 {
  animation-delay:0.4s;
}

#line2 #dash-2 {
  animation-delay:0.5s;
}

#line2 #dash-3 {
  animation-delay:0.6s;
}

#line3 {
  animation-delay:0.7s;
}

#line4 {
  animation-delay:.8s;
}

#line5 {
  animation-delay:0.9s;
}

#line6 {
  animation-delay:1s;
}

You can see that we're targeting each line or dash with a specific delay, incrementing in 0.1s all the way up to 1s.

This gives us the below effect (hit rerun to see it in action).

See the Pen CSS SVG coding animation - 2 by Chris Dermody (@ChipD) on CodePen.


That's getting closer to what we want, but now we need it to start "scrolling"

Adding the CSS "scrolling" effect

The goal of this animation was to convey to users of my side project mydevportfol.io that the app is "coding" them a website.

I wasn't sure how long the app would take to do this, so I needed to scroll the "code" so that it looked like the app was still working away.

To achieve this we'll add another animation called scroll, and apply it to the #group1 element. This element contains all the lines and dashes we just animated above, so the entire group will move upwards together.

#group1 {
  animation: scroll 1s linear infinite;
  animation-delay: 1s;
}

@keyframes scroll {
  0% {transform: translateY(initial)}
  100% {transform: translateY(-180px)}
}

You can see that we're telling the animation to take 1s in to run, we want the easing to be linear, and the infinite keyword is what tells the browser to loop the animation forever.

In the scroll animation, we're using transform, and we're using it to translate or "move" the element along the Y axis with translateY(). As mentioned earlier, transform is what allows for us to get the buttery smooth 60 frames per second as it lets the computer's GPU do the work as opposed to the CPU.

And now, we have...

See the Pen CSS SVG coding animation - 3 by Chris Dermody (@ChipD) on CodePen.


Sweet. Now we just need the rest of the lines to start "coding"...

Animating the final stage

Now that we have our initial group of lines "scrolling" up the page, we need the animation to write more lines and dashes to the screen, so it looks like the "coding" is ongoing indefinitely.

To do this, we simply copy and paste the id="group1" element, and paste it below, renaming it to group2 so that we can target it specifically with CSS rules.

We then add a new "scroll" animation to that element, very similar to the one we made before, except the values are a little different this time which is why we can't reuse the previous rule.

#group2 {
  transform: translateY(165px);
  animation: scroll2 1s linear infinite;
  animation-delay: 1s
}

@keyframes scroll2 {
  0% {transform: translateY(165px)}
  100% {transform: translateY(0px)}
}

With this code we're pushing the group2 element down the page by 165px and then animating it back to 0px in the scroll2 animation. We're also delaying the animation by 1s again.

See the Pen CSS SVG coding animation - 4 by Chris Dermody (@ChipD) on CodePen.


We're oh so close, we just need the lines and dashes in group to to animate with an expand animation.

The tricky part

This is the part that hurts my brain, and took me the longest to figure out about this animation. The problem is that there's no way (without JavaScript) to delay a CSS animation that's looping.

Of course we have the animation-delay property that we've used already, but all this does is delay the start of the animation. We want ours to loop indefinitely, but also have delays in between each loop. That just isn't possible without some messsing about with keyframes, which is what I eventually settled on, so, brace yourselves...

We basically have to simulate a delay, using keyframes percentages. Consider the code below

#group2 #line1 #dash-1 {
  animation: line1dash1 1s linear infinite;
  opacity:1;
}

@keyframes line1dash1 {
  0% {width: 0}
  20% {width: 40px}
  100% {width: 40px} /* <--20% and 100% are the same, giving us that "delayed effect*/
}

This is the code for the first dash on the first line. With this code, we're telling the browser to animate the #dash-1 element infinitely, and have it take 1s to complete. However, we need that delay, so to achieve that effect, we're telling the browser to animate it to its full width (40px), at 20% of the animation.

20% of 1s is 0.2s, so effectively, the "expanding" of the dash is only taking 0.2s, but the animation is running for a full 1s. This gives us that delayed effect we need.

For each subsequent dash or line, we increment the percentages. For instance, the second dash on line1 has a slight delay:

#group2 #line1 #dash-2 {
  animation: line1dash2 1s linear infinite;
  opacity:1;
}

@keyframes line1dash2 {
  0% {width: 0px;}
  10% { width: 0px;} /*<-- we start the second dash at 10% instead of the 0% above */
  30% { width: 60px;}
  100% { width: 60px;}
}

All we do now is create an animation specific to each line or dash, and increment the delay all the way down.

I'm not gonna lie, this took me a lot of trial and error to get right, so don't feel bad if you try this and fuck it up, I stumbled my way over the finish line with this one, but we got there in the end.

The final product

See the Pen CSS SVG coding animation - 5 by Chris Dermody (@ChipD) on CodePen.


What do you think? Have I missed something obvious? I know the CSS structure is a little horrific, I really should refactor the element names and classes etc, but for the academic purpose of this post, I'll leave it be for now.

Let me know on Twitter @cderm your thoughts, or if you subscribe to my email list below, I read and reply to every single reply that comes in. Would you like to see more of these types of animations? Let me know :)

You've successfully subscribed to Chris Dermody
Great! Next, complete checkout to get full access to all premium content.
Error! Could not sign up. invalid link.
Welcome back! You've successfully signed in.
Error! Could not sign in. Please try again.
Success! Your account is fully activated, you now have access to all content.
Error! Stripe checkout failed.
Success! Your billing info is updated.
Error! Billing info update failed.