Animating Gradients with Pure CSS
Tags: css, html
Published: 2020-07-28 18:25:24

In this article, I will demonstrate how to animate gradients in an infinite, linear fashion using nothing but HTML and CSS. This is not the only animation technique for gradients, but I've found it's one of the more elegant and versatile.

{% codepen https://codepen.io/quadbaup/pen/yLewNZO %}

## Part 1: Understanding the Technique

This animation technique uses several elements to achieve this effect. 

First, let's establish those elements and their vocabulary in a scene:

{% codepen https://codepen.io/quadbaup/pen/dyGaqOG %}

* **Stage** - A parent container that designates where the visible area of the animation begins and ends. Think of this like a theatre stage, where the left and right sides are blocked by curtains. Anything that goes from the visible area of the stage to behind the curtains _will be_ invisible to the audience. For now, everything is visible for demonstration purposes.
* **Belt** - A child container nested within the stage to house visually-distinguishable elements. Think of this like a portion of a conveyor belt at the grocery store. Items on top of it are stationary but the belt moves items through space. This is what actually performs the animation.
* **Aspects** - A sequence of visually-distinguishable elements within the belt. This example uses dots. Later, we'll use gradients. Whatever elements you choose, there are several rules:

### Rules of Aspects

1. There must be at least two aspects in the sequence. 
2. The first and last aspect(s) must be identical. 
3. All aspects must be evenly spaced along the belt.
    * Note: even spacing is achieved here with Flexbox's `justify-content: space-around`.

In this example, the intention is to animate the aspects from the left to the right of the stage, so we start with the position of the belt offset to the left of the stage. The width and the offset distance of the belt are significant. They determine how many aspects will appear within the visible area at any given moment of the animation.

This animation has two aspects where one aspect will be visible at any given time. That means the last aspect of the sequence (Aspect #2) must visually appear in the exact same position on the screen in which the first aspect of the sequence (Aspect #1) will appear at the end of a single iteration of the animation. 

{% codepen https://codepen.io/quadbaup/pen/xxZMarx %}

---

### Basic Idea

**Start with the right side of the belt at the right-most edge of the stage. Animate the belt until its left side reaches the left-most edge of the stage.**

---

When the belt completes a single iteration of its animation, it instantly teleports back to its original location, where the last aspect of the sequence (Aspect #2) visually replaces the first aspect of the sequence (Aspect #1) and continues its journey on the next iteration of the animation. 

This is what that looks like: 

{% codepen https://codepen.io/quadbaup/pen/eYJxLZb %}

Then, just apply an `overflow: hidden` to the stage and its sides begin to behave correctly as theatre curtains to complete the illusion of perpetual motion within the visible area: 

{% codepen https://codepen.io/quadbaup/pen/eYJxMyM %}

As you can see, one of two aspects is fully visible at any given time.

#### Bonus Example

Here's what that same setup looks like with three aspects:

{% codepen https://codepen.io/quadbaup/pen/YzwgPxW %}

After the `overflow: hidden` is applied, two of the three aspects are fully visible at any given time.

{% codepen https://codepen.io/quadbaup/pen/OJMqPjj %}

Note that the second and third aspects at the beginning of the animation appear identical to the first and second aspects at the end of the animation.

---

## Part 2: Animating Gradients

Let's begin by establishing the first fundamental HTML element: the stage. We're using an `article` tag here, but any HTML element can be substituted.

```html
<article class="stage"></article>
```

Give it some arbitrary dimensions in CSS to designate the visible area:

```css
.stage {
  height: 200px;
  width: 200px;
}
```

Add some optional presentational styles:

```css
body {
  background-color: #8fb6ff;
  margin: 0;
  padding: 3rem;
}
.stage {
  background-color: #fff;
  box-shadow: 0 3px 14px #000;
  border-radius: 1rem;
  height: 200px;
  width: 200px;
}
```

{% codepen https://codepen.io/quadbaup/pen/rNxbKNg %}

Since we're using gradients as the aspects, we can take advantage of CSS3 and utilize `background-image: linear-gradient()` to make the stage's background act as both the belt and aspects and avoid superfluous HTML. The stage's `background-image` will establish the belt and the `linear-gradient()` will define aspects.

### Adding Gradient Colors

For the stage's gradient, we'll use the colors [Cerise](http://chir.ag/projects/name-that-color/#DA3287) and [Mustard](http://chir.ag/projects/name-that-color/#FFDE5E).

```css
background-image: linear-gradient(90deg, #da3287, #ffde5e);
```

A convenient thing about `linear-gradient` is that browser technology has advanced so much that browser vendor prefixes for this CSS function [are no longer necessary](https://caniuse.com/#search=linear-gradient) for this to work in all contemporary browsers.

{% codepen https://codepen.io/quadbaup/pen/abdxKvb %}

In this animation technique, a gradient is an aspect. It's easier to abstract gradients as aspects when you think of each pair of colors in a gradient as a single element.

At the moment there's only one aspect, a gradient of Cerise to Mustard, which violates the rules of aspects because there needs to be at least two aspects in a sequence.

Let's duplicate this color pair to create two more aspects.

```css
background-image: linear-gradient(90deg, #da3287, #ffde5e, #da3287, #ffde5e);
```

Now there's three aspects:

1. A gradient of Cerise to Mustard `#da3287, #ffde5e`
2. A gradient of Mustard to Cerise `#ffde5e, #da3287`
3. A gradient of Cerise to Mustard `#da3287, #ffde5e`
 
{% codepen https://codepen.io/quadbaup/pen/NWxmzdB %}

### Setting the Width of the Belt

Right now all the aspects are visible on the stage. A change needs to be made to ensure that only one aspect will be visible at a time during the animation.

This particular animation will animate on the X-axis which means the _width_ of the stage's belt needs to be modified. This is achieved by multiplying the X-axis of the `background-size` (100%) by the number of aspects (3).

```css
background-size: 300% 100%;
```

The Y-axis is set to 100% because we want the gradient to cover the entirety of the stage's height.

{% codepen https://codepen.io/quadbaup/pen/dyGLKRN %}

Although it looks as it did before, the background of the stage (the belt) has actually been extended three times beyond the bounds of its parent container in the horizontal direction. This isn't visible because the stage is doing its job and limiting the visible area.

With the belt and aspects all ready to go, all that remains is animating.

### Animating the Belt

The `background-position` of the stage's `background-image` (its belt) is set by the browser to `0 0` by default. This means the gradient is positioned at the top left of the stage.

We want the position of the background to animate from its left-most edge (where it is currently) to its right-most edge.

These `@keyframes` describe that animation behavior:

```css
@keyframes animateBg {
  0% {
    background-position: 0 0;
  }
  100% {
    background-position: 100% 0;
  }
}
```

Lastly, give this behavior to the stage.

```css
.stage {
  animation: animateBg 2s infinite linear;
  ...
```

And voila, a linearly-animated gradient that moves infinitely to the left.

{% codepen https://codepen.io/quadbaup/pen/rNxbKzN %}

## Tools

---

### CSS Gradient Animator

[Website Link](https://www.gradient-animator.com/)

This is the classic tool from [Ian Forrest](https://www.ianforrest.com/) that uses a slightly different technique to achieve an eased, oscillating animated effect.

---

### Pure CSS Animated Gradient Generator

[CodePen Link](https://codepen.io/quadbaup/full/ZEQqzqM)

I recently wrote a tool in React to generate the same CSS we just used to create infinitely linear animated gradients. It allows you to customize colors, duration, and the direction.

I've also open sourced this tool on GitHub and GitLab for anyone who wants to contribute features or improvements: 

* [GitHub repository](https://github.com/gmeben/pure-css-animated-gradient-generator)
* [GitLab repository](https://gitlab.com/gmeben/pure-css-animated-gradient-generator)

---

Playing around with these tools should give you a deeper understanding of the mechanics behind animating gradients.

Critique and suggestions are welcome.

Thank you for reading. 👍