Website exist to communication. Mostly they communicate using words but they can also communicate in more “non-verbal” ways. Sometimes an intuitive animation or properly colored icon can express an idea or feeling quicker, more effectively, and with less clutter than text can.

Part of creating a considerate user experience (UX) is clearly communicating what every action the user can perform means and the state of the action. They should never click the wrong button out of confusion or be left wondering if they clicked it. The UI should indicate that they can’t perform actions rather than letting them know after they attempt to. Any feedback that is provided or interaction that is prevented should be at the lowest level of granularity so that its context is clear and the user is given the most freedom.

For an application I am working on I wanted to provide “processing” feedback at different levels of granularity on the page. I wanted to indicate that the page was loading , that a widget or data table was fetching data, that the request initiated by a button click was being processed, and similar activities. The theme for the application makes use of sharp edges on the elements (no rounded corners). After playing around with some animations I landed on doing a standard loading bar that eased out.

Here is the final product so you can follow along with the steps bellow:

So for starters we need a container:

<div class="example"></div>

<style>
.example {
    width: 10em;
    height: 5em;
    margin: 1em;
    border: 1px solid black;
}
</style>

Now we add the loading bar using a pseudo-element. Pseudo-elements are good to use to add elements to the page that are purely for stylistic purposes. It is also cleaner and more reusable than manually modifying the DOM.

$loader-height: 0.25em;
$loader-width: 25%;
$loader-color: #00BCD4;

.is-processing::after {
    content: '';
    display: block;
    height: $loader-height;
    width: $loader-width;
    background-color: $loader-color;
}

For pseudo-elements you have to set the content property so that it will be generated. We set it do display block with an explicit width and height since it has no content to give it shape. Then we add a background color to make it standout from the component.

The next step is to make it move. We will do this by absolutely positioning it relative to the box and gradually changing its position. The difficult part comes when we get to the edges. The simple solution would be to put overflow: hidden on the container and start the animation at left: -$loader-width and end at left: $width + $loader-width. So basically the container would be a window and the slider would start to the left, slide past the window, and end to the right of it. Then you repeat that animation to infinity.

The problem I ran into with that approach is that the styles of some components didn’t play well when I put overflow: hidden on them. An alternate approach is to use a combination of changing the position and the width of the loading bar. At the beginning and end of the animation the bar’s width is expanding or collapsing respectively. It starts by expanding up to its length, then it moves across till its end hits the end of the container, and then it continues to move toward that end while shrinking its width at the same rate. It may sound complicated but it is actually really simple to implement with @keyframes. See bellow:

$loader-height: 0.25em;
$loader-width: 25%;
$loader-color: #00BCD4;

.is-processing {
    position: relative;
    &::after {
        content: '';
        display: block;
        height: $loader-height;
        position: absolute;
        background-color: $loader-color;
        animation: loading-bar 1s infinite linear;
    }
}

@keyframes loading-bar { 
    0% {
        width: 0;
        left: 0%;
    }
    18.75% {
        width: 30%;
        left: 0%;
    }
    62.5% {
        width: 30%;
        left: 70%;
    }
    100% {
        width: 0;
        left: 100%;
    }
}

The first thing we had to do was to make the container position: relative; and the loading bar position: absolute; so that we could absolutely position the loading bar relative to its container. Next we add the ‘animation’ property to the pseudo-element and reference the custom key-frames at-rule labeled ‘loading-bar’.

With key-frames you are defining the styles of the target element over time. The key-frames itself doesn’t know how much time your animation will take; you define that in the animation property. It just allows you to define percent based breakpoints and it will handle animating properties between those breakpoints.

I decided on a bar width of 30% of its parent. The following is conceptually how the animation needs to work for a consistent speed:

  1. 0-30%: increase the width of the bar to its full width.
  2. 30-70%: move the bar to the right till it hits the end.
  3. 70-100%: shrink the bar’s width to 0 while moving it right.

Initially you may think that my breakpoints should be at 0, 30, 70, and 100. The tricky part is that the track is bigger than the window. If we were doing this with overflow hidden then we would start at left: -30% and end at left: 100% with the right edge of the bar at left: 130%. So the track distance is 160% the length of the window. To avoid confusion I will drop the % sign. Just think of the track as 160 units. Since our bar is 30 units we need to travel 130 units.

We will obviously still start at 0% and end at 100% but the intermediate steps will need to be calculated based on the track length. The bar should stop expanding when its left would be at 30 on the track. So we divide 30 by 130 to get the amount of time to travel that distance and we get 23%. So that is our first break-point. We need to start shrinking when its left would be at 100 on the track. So we divide 100 by 130 and get 76.9%. Here is a picture to help explain it:

alt text

Then we just set the animation shorthand properties so that it last for 1 second and repeats for infinity. We also add ‘linear’ to smooth out the animation. To cause it to ease out we just have to shift the 3rd break-point backwards. I chose to move it -15% so that it was at 61.9%.

Now with this base you can play with it to make your own unique animation. You could change the width of the element as it moves across. You could change the other pseudo-element to be another bar and animate them at different speeds or make one stack up. The possibilities are endless!

So that’s it, and here are some examples of some components that I integrated it with:

Tags:
  1. CSS
  2. UX

Erik Murphy

Erik is an agile software developer in Charlotte, NC. He enjoys working full-stack (CSS, JS, C#, SQL) as each layer presents new challenges. His experience involves a variety of applications ranging from developing brochure sites to high-performance streaming applications. He has worked in many domains including military, healthcare, finance, and energy.

Copyright © 2023 Induro, LLC All Rights Reserved.