Animation
Animation in CSS can be done with transitions and transforms OR using the @keyframes
rule. This page is only concerned with the latter.
Animations also need a trigger to start them. Typical ways to trigger an animation are using some simple Javascript, CSS :hover
pseudo class, or (as in the case of this page) the page load itself. Another way is to use the :target
pseudo class
Getting Started
There are two basic steps in CSS animations. First you write and name your animation. Secondly you add it an element.
To write an animation in the CSS you use the @keyframes declaration and right after that you write the name you want to give it. I made up the name headingSize:
@keyframes heading-size {
0% {
transform: scale(0.1);
opacity: 0%;
}
60% {
transform: scale(1.8);
opacity: 80%;
}
100% {
transform: scale(1);
opacity: 100%;
}
}
Instead of using percentage values you can use the from
and to
keywords where from
is the same as 0%
and to
is 100%
:
@keyframes heading-size {
from {
scale: 0.1;
}
to {
scale: 1;
}
}
Also it’s not necessary to have a start and end definition for example:
@keyframes bg-color {
50% {
background-color: red;
}
}
The second thing is you need to write that animation into whichever element/s you wish to animate.
For instance to write the animation I named headingSize
above into any h1 headers:
h1 {
animation: headingSize 7s;
animation-iteration-count: 3;
animation-direction: alternate;
}
Iterations
The iteration-count
is how many times the animation plays. The default is 1, so the animation plays just once. You can set it to infinite
if you want it to carry on indefinitely.
Animation direction
The animation-direction
can have the following values:
normal
reverse
alternate
alternate-reverse
The value of reverse
play it backwards.
alternate
means it first goes forward then backwards. So it begins an 0% through the timeline plays to 100%, then plays from 100% back to 0%. This is very useful for any animation that plays more than once and as different start and end points.
alternate-reverse
is the same as alternate except that it begins by playing backwards.
Animation timing
The rate of change does not have to be constant. The animation-timing-function
takes the following values:
ease
(the default) a slow start, then fast, before it ends slowlyease-in
slow startease-out
slow finishease-in-out
a slow start and finishlinear
same speed throughoutsteps()
the animation unfolds in discrete steps. The number of steps is set in the brackets eg.steps(12)
. A second optional parameter uses key termsjump-start
,jump-end
,jump-none
,jump-both
,step-start
andstep-end
. Thejump-start
andjump-end
can be abbreviated to juststart
andend
.cubic-bezier(n, n, n, n)
the possible values are from 0 to 1.
See cubic-bezier.com to understand and set up a cubic bezier function. It’s easier to see and play with than explain in words.
steps()
I couldn’t find what the default jump
is but from below it appears to be jump-end
.
All the following use transform: scaleX()
from an initial setting of 0
then up to 1
.
with just steps(6)
with steps(6, jump-start)
with steps(6, jump-end)
with steps(6, jump-both)
Animation delay
Delaying the animation is easy with the property animation-delay:
animation-delay: 5s
Fill Mode
animation-fill-mode
is not the most intuitive name. The values in an animation only persist while the animation is running. Once it stops or before it starts the keyframe values are ignored. Changing this is what animation-fill-mode
does. The default value, normal
, is that an animation will not affect the styles before or after an animation.
This can be changed:
The possible values are:
normal
- the default (above)forwards
- when the animation is finished it stops retaining the styles set in the final keyframe.backwards
- before the animation starts (during the delay stage) the styles of the initial keyframe are appliedboth
- retains both
For further info on this see the explantion by Josh Comeau.
Play State
animation-play-state is simply whether the animation is playing or paused. There are two values:
animation-play-state: playing
animation-play-state: paused
You might leave an animation in the paused state ready when someone hovers over it. Alternatively you might want the hover state to pause a running animation.
The shorthand
All these properties can go into the shorthand animation
The order doesn’t matter except for animation-duration
and animation-delay
with both have the same values (seconds). The animation-delay
MUST come after 'animation-duraction
in the list:
animation-name
- the name given in @keyframes declarationanimation-duration
- in secondsanimation-timing-function
- ease, ease-in, ease-out, ease-in-out, linear etc.animation-delay
- in secondsanimation-iteration-count
- how many times it plays, whole numbers or ‘infinite’animation-direction
- normal, reverse, alternate, alternate-reverseanimation-fill-mode
- normal, forwards, backwards, bothanimation-play-state
- playing (default) or paused
Finally you can add multiple animations to one selector simply splitting them with a commer:
div.move {
animation: bounceIn .5s, rotate 5s;
}
What can go into an animation?
The @keyframes declaration allows the different set points to be declared separately at different points through the animation. These can be defined in either percentages or using from and to. The key word from
is the same as 0%
and to
is the same as 100%
.
@keyframes new-animation {
from {
left: 0px;
}
to {
left: 350px;
}
}
See the Pen This Pen by Synphod (@Synphod) on CodePen.
Prefers reduced motion
Not everyone likes too much animation and it can give some people headaches and nausea. Fortunately they can set their machines to prefers-reduced-motion
. But this only works if the develper has incorporated this into the code. The works like other media queries by checking if a user has this turned on.
There are several ways this can be accomplished in CSS but the simplest is probably to wrap potentially offending animations in a media query like so:
@media (prefers-reduced-motion: no-prefereence) {
.some-div {
animation: enlarge 1s infinite alternate;
}
}
You can test whether this is working in Chrome dev tools using the emulation.
The prefers-reduce-motion takes two possible values: no-prefereence
and reduce
.
If set to reduce you can simply write:
@media (prefers-reduced-motion) {
/* code */
}
View the CSS for this page
@keyframes bodybg {
0% {
background: var(--MainBorderColor);
}
100% {
background: var(--container-bg)
}
}
.container {
animation: bodybg 5s;
animation-delay: 5s;
animation-fill-mode: backwards;
}
@keyframes h1 {
0% {
transform: scale(0.1);
opacity: 0%;
}
60% {
transform: scale(2.8);
opacity: 80%;
}
100% {
transform: scale(1);
opacity: 100%;
}
}
h1 {
animation: h1 7s;
animation-iteration-count: 1;
animation-direction: alternate;
animation-delay: 6s;
animation-fill-mode: backwards;
}
h2 {
animation: flipInY 3s;
animation-iteration-count: 2;
}
.resources {
animation: lightSpeedIn 3s;
}
header, nav {
animation: fadeInDownBig 2s;
}
code {
animation: bounceInRight 6s;
}
@keyframes expand {
to {
transform: scaleX(1);
}
}
.steps {
height: 2em;
position: relative;
margin-bottom: 0.5em;
background-color: #1113;
}
.steps::after {
content: '';
display: block;
position: absolute;
inset: 0;
transform: scaleX(0);
transform-origin: left;
animation: expand 10s infinite both;
animation-timing-function: steps(6);
background-color: #26b;
}
.steps.jstart::after {
animation-timing-function: steps(6, jump-start);
}
.steps.jend::after {
animation-timing-function: steps(6, jump-end);
}
.steps.jboth::after {
animation-timing-function: steps(6, jump-both);
}
.steps-p {
margin-bottom: 0.1em;
}
@keyframes change-bg {
50% {
background-color: red;
}
}
.color-swap {
width: min(100%, 200px);
aspect-ratio: 5 / 3;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
animation: change-bg 6s linear infinite;
font-weight: bold;
color: #fff9;
background-color: rgb(0, 98, 255);
}