🎨 Discover CSS Keyframes
Tags: css, html, keyframes, animation
Published: 2022-12-24 13:59:26

*The purpose of this article is to share how I solved this problem. Copying/pasting each block of code will not work.*

## Let's have fun with CSS !
👋 This morning I gave me a challenge : making a custom spinner only with HTML and CSS (JS is allowed but only for creating a module for reusability) 

💼 My objective was to animate the logo of [Pit](https://www.pit.fr), the company in which I'm involved.

![PIT Logo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c8h9dsjjj03im5l7ayf7.png)

## First of all, the mockup
💡 I had a general idea of ​​what I wanted to do, so I decided to draw each step of the animation.

![Animation steps](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dyb8u4ddwmg51d8sakjv.png)

% Percentages you can see are the progression of the animation. Each frame lasts a different duration.

⏰ Here is the final timeline , with mathematics for calculating some areas (thank you **Pit**agore)

![Timeline and calculus](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c7ie8lf28b0yj1294rl8.png)

Do you need a translation of the timeline ?
- 0% -> 10% : Increased logo size
- 10% -> 15% : Appearance of the square n°2 (left one)
- 15% -> 20% : Appearance of the square n°3 (top one)
- 20% -> 25% : Appearance of the square n°1 (right one)
- 25% -> 35% : Appearance of the "addons", small squares next to each big square
- 35% -> 90% : Rotation (because it's a spinner !)
- 90% -> 100% : Decreased logo size

Can you imagine the result ?

## Now, let's code !
🏛 For the **HTML**, I wanted to have the small import possible of the spinner, because I can imagine use it for projects later.
I wrote the HTML first and forbade myself to edit it, because that's what I wanted it to look like.
```html
<div
  id="pit-loader"
  data-color="#E9AE24"
  data-size="150"
  data-duration="3s">
</div>
```

👨‍💻 Now, a pinch of **JavaScript**, I split the logo by 4 (yes, there are 4 squares).
```js
const squareCount = 4;

for (var i = 0; i < squareCount; i++) {
  var squareNode = document.createElement("div");
  squareNode.classList.add("pit-square");
  loader.appendChild(squareNode);
}
```
I used this way to keep the call of the spinner in HTML as simple as possible.

👩‍🎨 The core of the animation is in the **CSS**.
The logo is composed by 4 squares, and for each square, another small square. The logo is easy to reproduce, that's why I draw it in CSS

```css
#pit-loader {
    position: absolute;
    display: flex;
    flex-wrap: wrap;
    gap: calc(var(--gap) * 1px);
    width: calc(var(--canvas) * 1px);
    height: calc(var(--canvas) * 1px);
    transform: rotate(225deg);
}

.pit-square {
    position: absolute;
    width: calc(var(--square) * 0.4px);
    height: calc(var(--square) * 0.4px);
    border: calc(var(--square) * 0.3px) solid var(--primary);
}

.pit-square::after {
    content: "";
    position: absolute;
    width: calc(var(--square) * 0.3px);
    height: calc(var(--square) * 0.3px);
    background-color: var(--primary);
    bottom: calc(var(--square) * 0.4px);
    right: calc(var(--square) * 0.4px * 2);
}
```
Basically, this creates the logo you see before, nothing is relevant here.

🎞 To animate the logo, I used CSS **Keyframes** (Who guessed it ?) And it's now my split with percentages is useful !

🏞 When we focus on the whole logo, the "only" animation is the size and the spinning, that's it !
```css
@keyframes loader {
    0% {
        opacity: 0;   
        transform: scale(0); 
    }
    10% {
        opacity: 1;
        transform: scale(1);
    }
    35% {
        transform: rotate(225deg);
    }
    80% {
        transform: rotate(-135deg);
        opacity: 1;
    }
    100% {
        transform: rotate(-135deg) scale(0);
        opacity: 0;
    }
}
```

🤔 However the difficulty is the animation of the 4 squares, because I wished every square to appears after the previous one. So I created 4 keyframes

```css
/* Animation of the right square */
@keyframes square-1 {
    0% {
        opacity: 0;
        right: 0;
    }
    20% {
        opacity: 0;
        right: 0;
    }
    25% {
        opacity: 1;
        right: calc((var(--square) + var(--gap)) * 1px);
    }
}

/* Animation of the left square */
@keyframes square-2 {
    0% {
        opacity: 0;
        left: 0;
    }
    10% {
        opacity: 0;
        left: 0;
    }
    15% {
        opacity: 1;
        left: calc((var(--square) + var(--gap)) * 1px);
    }
}

/* Animation of the top square */
@keyframes square-3 {
    0% {
        opacity: 0;
        top: 0;
    }
    15% {
        opacity: 0;
        top: 0;
    }
    20% {
        opacity: 1;
        top: calc((var(--square) + var(--gap)) * 1px);
    }
}

/* Animation of the bottom square */
@keyframes square-4 {
    0% {
        opacity: 0;
    }
    10% {
        opacity: 1;
    }
}
```

➕ Finally the "addon" square is so easy
```css
@keyframes addon {
    0% {
        opacity: 0;
    }
    25% {
        opacity: 0;
        right: 0;
    }
    35% {
        opacity: 1;
        right: calc(var(--square) * 0.4px * 2);
    }
}
```

➡️ To give a specific keyframe to each square, I used the `nth-child()` way. Example for the right square.

```css
.pit-square:nth-child(1) {
    top: calc(var(--square) * 1px + var(--gap) * 1px);
    animation: var(--animation-duration) ease-in infinite square-1;
}
```

⏮ I talked before of `data-attributes`, I discovered them in this project, so here is a small explanation.
In HTML, we can add `data-*` attribute to any component, and retrieve them back in JS.
```js
var loader = document.getElementById("pit-loader");
var color = loader.getAttribute("data-color");
loader.style.setProperty("--primary", color);
```

## It makes my head spin !
🥁 Drum roll ... the final result ... *TADA!*

![GIF result of the logo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aqzq4d0ooctbnxhmgibq.gif)