CSS Download Loading Animation (@keyframes, clip-path)

CSS Download Loading Animation (@keyframes, clip-path)

Today let's work with CSS animations & clip-path to create a nice little download animation with an indefinite and a definite mode.

Read the full article or watch me code this on Youtube:

Result

Markup

The markup of this loader is quite simple as it consists of one div.downloader for each loading animation and either the additional .indefinite class or the .definite class with a variable set to the percentage the loader should display.

.downloader.indefinite
  .arrow
  .bar

.downloader.definite(style="--percentage: 60%;")
  .arrow
  .bar

Variable Setup

The size of the loader is controller through the --size CSS custom property - give it any value you want. The height of the bar and the space between the arrow and the bar is specified in percent whereas the size of the arrow is then derived through the remaining space.

The background color of the loader is given through --inactive-color and the actual animation color is defined through --active-color.

.downloader {
  --size: 5rem;
  --bar-height: 6%;
  --bar-arrow-spacing: 10%;
  --arrow-height: calc(100% - var(--bar-height) - var(--bar-arrow-spacing));

  --inactive-color: rgba(255, 255, 255, 0.2);
  --active-color: white;

  width: var(--size);
}

The basic layout for the downloader is defined as a flex box operating in column mode:

.downloader {
  ...

  display: flex;
  flex-direction: column;
}

Creating the Arrow Shape

The arrow shape is created by using a clip-path that crops the .arrow element to have it's arrowy shape. So it's also easy to customize and you can give it almost every shape you want.

.downloader {
  ...

  .arrow {
    background-color: var(--inactive-color);
    padding-top: var(--arrow-height);
    position: relative;

    clip-path: polygon(
      47% 0%, 
      53% 0%, 
      53% 85%, 
      80% 56%, 
      80% 67%, 
      50% 100%, 
      20% 67%, 
      20% 56%, 
      47% 85%
    );
  }
}

In order to create the animation an :after pseudo element is created inside the arrow, so it's also affected by the clip-path and it will be running from top to bottom, which is why it's fixed to the top (see bottom: 100%;) at the beginning.

.downloader {
  ...

  .arrow {
    ...

    &:after {
      content: "";
      position: absolute;
      background-color: var(--active-color);
      left: 0;
      right: 0;
      bottom: 100%;
      top: 0;
    }
  }
}

Configuring the Indefinite Mode

So, for the indefinite mode the arrow's :after element is given an animation, that makes it run from top to bottom:

.downloader {
  ...

  &.indefinite {
    .arrow:after {
      animation: 2s loading-vertical ease-in-out infinite;
    }
  }
}

@keyframes loading-vertical {
  0% {
    top: 0%;
    bottom: 100%;
  }
  30% {
    top: 0%;
    bottom: 100%;
  }
  50% {
    top: 0%;
    bottom: 0%;
  }
  70% {
    top: 0%;
    bottom: 0%;
  }
  100% {
    top: 100%;
    bottom: 0%;
  }
}

And same goes for the bar

Now, for the bar we're going to do the same, except that its size and animation direction differs:

.downloader {
  ...

  .bar {
    position: relative;
    width: 100%;
    padding-top: var(--bar-height);
    margin-top: var(--bar-arrow-spacing);
    background-color: var(--inactive-color);

    &:after {
      content: "";
      position: absolute;
      background: var(--active-color);
      left: 0;
      right: 100%;
      bottom: 0;
      top: 0;
    }
  }

  &.indefinite {
    ...

    .bar:after {
      animation: 2s loading-horizontal ease-in-out infinite;
    }
  }
}

...

@keyframes loading-horizontal {
  0% {
    left: 0%;
    right: 100%;
  }
  30% {
    left: 0%;
    right: 100%;
  }
  50% {
    left: 0%;
    right: 0%;
  }
  70% {
    left: 0%;
    right: 0%;
  }
  100% {
    left: 100%;
    right: 0%;
  }
}

Making the Definite Mode Work

Now, all that's required for the definite mode to work is the configuration of the .definite class. The arrow will keep its animation and the .bar:after's right property is calculated through the --percentage custom property.

.downloader {
  ...


  &.definite {
    .arrow:after {
      animation: 2s loading-vertical ease-in-out infinite;
    }

    .bar:after {
      transition: 250ms right ease-in-out;
      right: calc(100% - var(--percentage));
    }
  }
}