CSS animations let you add motion to your pages without JavaScript. Firefox has excellent support for both CSS animations and the Web Animations API, and DevTools includes an animation inspector that makes debugging smooth.
CSS Transitions
Transitions animate property changes when a trigger fires (hover, class change, focus):
/* Smooth hover effect */
.button {
background-color: #3B82F6;
transform: translateY(0);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: background-color 0.2s ease,
transform 0.15s ease,
box-shadow 0.15s ease;
}
.button:hover {
background-color: #2563EB;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3);
}
Transition shorthand: transition: property duration timing-function delay
/* Multiple transitions */
transition: color 0.2s ease, opacity 0.3s ease 0.1s;
/* ^ delay */
CSS Animation (@keyframes)
For looping or complex multi-step animations:
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes pulse {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.05); opacity: 0.8; }
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateX(-20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.spinner {
animation: spin 1s linear infinite;
}
.notification {
animation: slideIn 0.3s ease-out both;
}
Animation properties:
.element {
animation-name: slideIn;
animation-duration: 0.3s;
animation-timing-function: ease-out;
animation-delay: 0.1s;
animation-iteration-count: 1; /* or: infinite */
animation-direction: normal; /* or: reverse, alternate, alternate-reverse */
animation-fill-mode: both; /* or: none, forwards, backwards */
animation-play-state: running; /* or: paused */
/* Shorthand */
animation: slideIn 0.3s ease-out 0.1s 1 normal both;
}
Timing Functions
The timing function controls the animation curve:
animation-timing-function: linear; /* constant speed */
animation-timing-function: ease; /* slow-fast-slow (default) */
animation-timing-function: ease-in; /* slow start */
animation-timing-function: ease-out; /* slow end */
animation-timing-function: ease-in-out; /* slow both ends */
animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1); /* spring */
animation-timing-function: steps(4, end); /* discrete steps */
Firefox DevTools shows a visual curve editor when you click a timing function value.
The will-change Property
Hint to the browser that an element will animate — allows GPU compositing:
.animated-element {
will-change: transform, opacity;
}
/* Remove after animation to free GPU memory */
.animated-element.animation-done {
will-change: auto;
}
Use sparingly — every will-change element gets its own compositor layer.
Web Animations API
For programmatic animation control, use the Web Animations API (WAAPI):
// Same as CSS: @keyframes + animation
const element = document.querySelector('.box');
const animation = element.animate([
{ transform: 'translateX(0)', opacity: 1 },
{ transform: 'translateX(200px)', opacity: 0.5 },
{ transform: 'translateX(400px)', opacity: 1 },
], {
duration: 1000,
easing: 'ease-in-out',
iterations: Infinity,
direction: 'alternate',
fill: 'both',
});
// Control playback
animation.pause();
animation.play();
animation.reverse();
animation.cancel();
// Synchronize multiple animations
const a1 = el1.animate([...], { duration: 500 });
const a2 = el2.animate([...], { duration: 500, delay: 250 });
// Wait for animations to finish
await Promise.all([a1.finished, a2.finished]);
console.log('All done');
Firefox Animation Inspector
Firefox DevTools has a dedicated Animation panel:
- Open DevTools (F12)
- Click the Animations tab (or find it under the three-dot menu)
- The timeline shows all animations on the page
- Click any animation to inspect its keyframes
- Drag the scrubber to step through frames
- Click an animation name to jump to the element in the DOM
This makes debugging "why is my animation janky?" much easier — you can see exactly when each keyframe fires and the computed easing curve.
Performance: Animating the Right Properties
Only animate these properties for smooth 60fps — they run on the GPU compositor:
/* GPU-composited: fast */
transform: translate(), scale(), rotate(), skew()
opacity
filter (in most cases)
/* NOT composited: triggers layout (slow) */
width, height, top, left, margin, padding, border
font-size, line-height
/* Slow: animates a layout property */
.bad {
transition: height 0.3s ease;
}
/* Fast: animate transform instead */
.good {
transform-origin: top;
transition: transform 0.3s ease;
}
.good.collapsed {
transform: scaleY(0);
}
Reduced Motion Accessibility
Always respect the user's preference:
/* Default: animate freely */
.animated {
animation: spin 2s linear infinite;
}
/* Reduced motion: disable or simplify */
@media (prefers-reduced-motion: reduce) {
.animated {
animation: none;
}
/* Or keep a subtle version */
.animated {
animation-duration: 10s; /* much slower */
}
}
// Check in JavaScript
const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!prefersReduced) {
element.animate([...], { duration: 300 });
} else {
element.classList.add('target-state'); // instant change
}
Firefox users can set this preference in Settings → General → Reduce animations.
CSS Custom Properties in Animations
:root {
--animation-duration: 0.3s;
--animation-delay: 0s;
}
.card {
animation: slideIn var(--animation-duration) ease var(--animation-delay) both;
}
/* Stagger children */
.card:nth-child(1) { --animation-delay: 0.0s; }
.card:nth-child(2) { --animation-delay: 0.1s; }
.card:nth-child(3) { --animation-delay: 0.2s; }
.card:nth-child(4) { --animation-delay: 0.3s; }
CSS custom properties can be set in JavaScript too:
card.style.setProperty('--animation-delay', `${index * 0.1}s`);
Top comments (0)