Blinking dots: a quick intro to CSS animation
Tags: css, beginners
Published: 2020-05-18 17:37:26

⚠️ __Motion Warning:__ The Glitch page preview contains animated elements.

---

The CSS `animation` property makes it possible to create _simple UI animation_ with CSS only. No library to install, import, or load! In addition, most animation libraries build on, or use similar functionalities as, CSS animation; knowing the basic principles of CSS animation helps you learn these animation libraries more efficiently.

This post is a quick introduction to CSS animation to accompany [this Glitch page](https://glitch.com/~css-animation-dots), which I created to demonstrate simple CSS animation in the form of single blinking dots. I'm also including references if you'd like to learn more.

You can view the source code below and/or click "Remix" to try it yourself.

{% glitch css-animation-dots app %}

## Write your first CSS animation

To animate an element, we need to declare the following:

__1) The `@keyframes` at-rule__

```css
@keyframes blink {
  0% { opacity: 0; }
  50% { opacity: 1; }
  100% { opacity: 0; }
}
```

- `blink` is the animation name. You can use any name—just make sure you use the correct name in the `animation` property.
- I animate the opacity from `0` to `1` and back to `0`. We can[⚠️] animate most CSS properties (except for ones that don't make sense like `background-image`, that is). 
    - If you're curious, here is MDN's [list of animatable CSS properties](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties).
    - ⚠️ …But doesn’t mean we should. Animating properties other than `transform` and `opacity` is taxing on the browser and may hurt performance. See [Chen Hui Jing’s post](https://dev.to/huijing/on-fixed-elements-and-backgrounds-515p) for a good explanation on how browser rendering engines process styles.

We define animation frames/states either as percentage (like the above example) or `from` (0%) and `to` (100%). If several frames have the same styles (like `0%` and `100%` above), we can group them together as with regular CSS selectors.

Other ways to define animation frames:

```css
/* Use from - to */
@keyframes choppyBlink {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* Group same frames together */
@keyframes blink {
  0%, 100% { opacity: 0; } /* more concise! */
  50% { opacity: 1; }
}
```

This alone will not do anything, though. We need to assign the animation to our element.

__2) The `animation` property__

```css
.dot {
  animation: blink 2s infinite;
}
```

- Apply the animation named `blink` to elements with the class `.dot`.  
    - If there is no `@keyframes blink` in our stylesheets, nothing happens.
- The __animation-duration__ is `2s`. With the code samples above, the element starts with `opacity: 0`, the opacity gradually increases until it reaches `1` within one second, and goes back to `0` within another second (total of two seconds). 
    - Longer duration = slower animation, shorter = faster.
- The __animation-iteration-count__ is `infinite`, so it loops forever. 
    - If we replace it with `3`, our element animates 3 times.
- I don't declare __animation-delay__ here. By default the delay is 0 second, ie. the animation runs directly as the element is rendered. 
    - If we replace it with `4s`, the animation starts running 4 seconds after the element is rendered.

Common basic variations:

```css
/* Minimal example */
.dot--basic {
  animation: blink 2s infinite;
}

/* Run animation once */
.dot--once {
  animation: blink 2s 1;
}

/* Wait 4s before running the animation */
.dot--delayed {
  animation: blink 2s infinite 4s;
}
```

Putting it together, here's the minimal CSS animation example.

```css
@keyframes blink {
  0%, 100% { opacity: 0; }
  50% { opacity: 1; }
}

.dot {
  animation: blink 2s infinite;
}
```

- Animate elements with the class name `.dot` immediately after render
- Animate from __zero to full and back to zero opacity__ within __two seconds__ (one second each way) forever

But wait... what values exactly can we use in the `animation` property? Read on!

## CSS animation properties

The `animation` property is a [shorthand property](https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties) to define multiple animation sub-properties, similar to the `background` property.

So, our example above

```css
.dot--delayed {
  animation: blink 2s infinite 4s;
}
```

is identical to

```css
.dot--delayed {
  animation-name: blink;
  animation-duration: 2s;
  animation-iteration-count: infinite;
  animation-delay: 4s;
}
```

There are a total of __eight (8) animation sub-properties__:

- ⭐️ animation-name
- ⭐️ animation-duration (example: `2s`)
- animation-timing-function (example: `ease`, `ease-in-out`, `linear`)
- ⭐️ animation-delay (example: `2s`)
- ⭐️ animation-iteration-count (`infinite` or any integer)
- animation-direction (`normal` | `reverse` | `alternate` | `alternate-reverse`)
- animation-fill-mode (`none` | `forwards` | `backwards` | `both`)
- animation-play-state (`paused` | `running`)

I only discussed the four most common sub-properties here (marked with the ⭐️ emoji), but you can see examples for all sub-properties in [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations).

Interestingly, the order of these properties _does not matter_ when you use them in the `animation` shorthand, except for one rule: `animation-duration` has to be _before_ `animation-delay`.

## Browser support and vendor prefixes

You may come across CSS animation code that contains vendor prefixes such as `-webkit-`, `-moz-`, and so on.

With vendor prefixes, the code from the first example looks like this.

```css
@-webkit-keyframes blink {
  0%, 100% { opacity: 0; }
  50% { opacity: 1; }
}
@-moz-keyframes blink {
  0%, 100% { opacity: 0; }
  50% { opacity: 1; }
}
@-o-keyframes blink {
  0%, 100% { opacity: 0; }
  50% { opacity: 1; }
}
@keyframes blink {
  0%, 100% { opacity: 0; }
  50% { opacity: 1; }
}

.dot {
  -webkit-animation: blink 2s infinite;
  -moz-animation: blink 2s infinite;
  -o-animation: blink 2s infinite;
  animation: blink 2s infinite;
}
```

At the time of writing, around _96.82%_ of users worldwide are on browsers that support unprefixed CSS animation, according to [caniuse.com](https://caniuse.com/#feat=css-animation). Unsupported browsers _will not_ get any error; they simply will not get the animation (CSS is awesome this way! 😉).

Use your own discretion based on your target users to decide whether to include vendor prefixes. (If you use any kind of preprocessor, these will be automatically added based on your configuration.)


## Make sure it's accessible

### 1. Turn off our animation for users who prefer reduced motion

Users may specify their preference for reduced motion in their device's settings, for example because it triggers motion sickness. 

If your animation is not crucial to the UI, disable it. If it's crucial (ie. conveys something meaningful), define an animation with the minimum movement possible.

```css
@media (prefers-reduced-motion: reduce) {
  .dot {
    animation: none; /* or define an alternate animation */
  }
}
```

### 2. Add appropriate ARIA role/attribute if necessary

If you create an extra element for animation that has no meaningful content, add `aria-hidden="true"` so screen readers could skip it.

If the animated element functions as an image, for instance a pulsating font icon or element with background image, add `role="img"` and `aria-label` attributes, for example:

```html
<div class="icon icon-star-bg" role="img" aria-label="Favorite"></div>
```

## Conclusion

I'm only covering the surface here, but hopefully enough to get you familiar with CSS animation. My Glitch demo page only contains basic examples using `opacity` and `transform`. Remix it (or create a new one) and get creative with the animations!

---

## Further reads

- CSS animation
    - [Using CSS animations](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations) - MDN Web Docs
    - [animation property](https://css-tricks.com/almanac/properties/a/animation/) by Chris Coyier, CSS Tricks
    - [caniuse.com browser support](https://caniuse.com/#feat=css-animation)
- Reduced motion
    - [An Introduction to the Reduced Motion Media Query](https://css-tricks.com/introduction-reduced-motion-media-query/) by Eric Bailey, CSS Tricks
    - [prefers-reduced-motion: Sometimes less movement is more](https://web.dev/prefers-reduced-motion/) by Thomas Steiner, web.dev
- ARIA roles/attributes
    - [Introduction to ARIA](https://developers.google.com/web/fundamentals/accessibility/semantics-aria) in web.dev
    - [img role](https://www.w3.org/TR/wai-aria/#img) - W3C Recommendation
    - [aria-hidden state](https://www.w3.org/TR/wai-aria/#aria-hidden) - W3C Recommendation
    - [ARIA: img role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/Role_Img) - MDN Web Docs 
    - [Using the aria-hidden attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-hidden_attribute) - MDN Web Docs