Animations and responsive
CSS admits substantial animation and responsive design through several mechanisms: transitions (transition) admit substantial smooth state-change animations; keyframe animations (@keyframes + animation) admit substantial complex multi-stage animations; transforms (transform) admit translation, rotation, scaling, skewing of elements without affecting layout; media queries admit responsive design via viewport-size and device-feature checks; prefers-reduced-motion admits accessibility-aware animation. The combination — transitions for state changes, keyframe animations for complex motion, transforms for performant manipulation, media queries for responsive design — is the substance of CSS’s animation and adaptive-design surface.
Transitions
Animate property changes on state transitions:
.button {
background: blue;
transition: background 0.3s; /* property duration */
}
.button:hover {
background: red;
}
The full transition syntax:
.element {
transition: property duration timing-function delay;
transition: background 0.3s ease-in-out 0.1s;
/* Multiple properties: */
transition: background 0.3s, color 0.2s, transform 0.5s;
/* All properties: */
transition: all 0.3s; /* avoid; performance burden */
}
The principal timing functions:
transition-timing-function: linear;
transition-timing-function: ease; /* default */
transition-timing-function: ease-in;
transition-timing-function: ease-out;
transition-timing-function: ease-in-out;
transition-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1);
transition-timing-function: steps(5, end); /* discrete steps */
The conventional discipline:
- Specify which properties to transition — avoid
allfor performance. - Use
cubic-bezierfor substantial custom curves. - Use
0.2s–0.4sfor substantial conventional durations.
Keyframe animations
For complex multi-stage animations:
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideIn {
0% { transform: translateX(-100%); opacity: 0; }
50% { opacity: 1; }
100% { transform: translateX(0); opacity: 1; }
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.1); }
}
.element {
animation: fadeIn 0.3s ease-in-out;
/* Full syntax: */
animation:
name /* keyframes name */
1s /* duration */
ease-in-out /* timing function */
0.5s /* delay */
infinite /* iteration count (number or 'infinite') */
alternate /* direction (normal, reverse, alternate, alternate-reverse) */
forwards /* fill mode (none, forwards, backwards, both) */
running; /* play state (running, paused) */
}
/* Or individual properties: */
.element {
animation-name: fadeIn;
animation-duration: 0.3s;
animation-timing-function: ease-in-out;
animation-delay: 0.5s;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-fill-mode: forwards;
animation-play-state: running;
}
The principal fill modes:
none— element returns to original state after animation.forwards— keeps the final state.backwards— applies the first frame during delay.both— both forwards and backwards.
Transforms
Transforms admit translation, rotation, scaling, skewing — without affecting layout:
.element {
transform: translate(50px, 100px); /* x, y */
transform: translateX(50px);
transform: translateY(100px);
transform: translateZ(50px); /* requires perspective */
transform: rotate(45deg);
transform: rotateX(45deg); /* X axis */
transform: rotateY(45deg);
transform: rotateZ(45deg);
transform: scale(1.5); /* both axes */
transform: scale(1.5, 2); /* x, y */
transform: scaleX(2);
transform: skew(10deg);
/* Multiple (applied right-to-left): */
transform: translate(50px, 0) rotate(45deg) scale(1.2);
/* Origin: */
transform-origin: center; /* default */
transform-origin: top left;
transform-origin: 50% 50%;
transform-origin: 0 100%;
/* 3D (with perspective): */
perspective: 1000px; /* on parent */
transform-style: preserve-3d;
transform: rotate3d(1, 1, 0, 45deg);
transform: matrix3d(...); /* substantial */
}
The mechanism admits hardware-accelerated animation — substantial performance for transform and opacity over top, left, width, height.
Performance
The browser admits substantial optimisation for animating:
transform— admits GPU acceleration.opacity— admits GPU acceleration.
Animating other properties (width, height, top, left, padding, etc.) admits layout/paint, substantial slower:
/* Slow: */
.element { transition: width 0.3s; }
.element:hover { width: 200px; }
/* Fast (using transform): */
.element { transition: transform 0.3s; transform-origin: left; }
.element:hover { transform: scaleX(2); }
The will-change hint admits substantial preparation:
.element {
will-change: transform, opacity;
}
The conventional discipline applies will-change temporarily (during interaction) and removes it after — substantial memory cost.
Common animation patterns
Fade in
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.fade-in {
animation: fadeIn 0.3s ease-out;
}
Slide in from left
@keyframes slideInLeft {
from { transform: translateX(-100%); }
to { transform: translateX(0); }
}
.slide-in-left {
animation: slideInLeft 0.3s ease-out;
}
Fade in with movement
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in-up {
animation: fadeInUp 0.4s ease-out;
}
Pulse
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
.pulse {
animation: pulse 2s ease-in-out infinite;
}
Spin
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.spinner {
animation: spin 1s linear infinite;
}
Shake
@keyframes shake {
0%, 100% { transform: translateX(0); }
20%, 60% { transform: translateX(-10px); }
40%, 80% { transform: translateX(10px); }
}
.shake {
animation: shake 0.5s;
}
Bounce
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-30px); }
}
.bounce {
animation: bounce 0.5s;
}
Hover effects
.button {
transition: transform 0.2s, box-shadow 0.2s;
}
.button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.button:active {
transform: translateY(0);
}
Loading spinner
.spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top-color: #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
}
Skeleton placeholder
@keyframes skeleton-pulse {
0% { opacity: 1; }
50% { opacity: 0.4; }
100% { opacity: 1; }
}
.skeleton {
background: #e0e0e0;
border-radius: 4px;
animation: skeleton-pulse 1.5s ease-in-out infinite;
}
Stagger animation
.list-item {
opacity: 0;
animation: fadeInUp 0.4s ease-out forwards;
}
.list-item:nth-child(1) { animation-delay: 0.0s; }
.list-item:nth-child(2) { animation-delay: 0.1s; }
.list-item:nth-child(3) { animation-delay: 0.2s; }
.list-item:nth-child(4) { animation-delay: 0.3s; }
Media queries
Responsive design via viewport conditions:
/* Mobile-first (preferred): */
.container {
padding: 1rem;
}
@media (min-width: 768px) {
.container {
padding: 2rem;
}
}
@media (min-width: 1200px) {
.container {
padding: 3rem;
max-width: 1200px;
margin: 0 auto;
}
}
/* Multiple conditions: */
@media (min-width: 768px) and (max-width: 1199px) {
/* tablet only */
}
@media (min-width: 768px), (orientation: landscape) {
/* either */
}
/* Negation: */
@media not (min-width: 768px) {
/* below 768 */
}
Media features
The principal media features:
/* Viewport size: */
@media (min-width: 768px) { ... }
@media (max-width: 1199px) { ... }
@media (width: 1024px) { ... } /* exact */
/* Aspect ratio: */
@media (aspect-ratio: 16/9) { ... }
@media (min-aspect-ratio: 16/9) { ... }
/* Orientation: */
@media (orientation: landscape) { ... }
@media (orientation: portrait) { ... }
/* Resolution: */
@media (min-resolution: 2dppx) { ... } /* high-DPI displays */
/* Color scheme: */
@media (prefers-color-scheme: dark) { ... }
@media (prefers-color-scheme: light) { ... }
/* Motion preferences: */
@media (prefers-reduced-motion: reduce) { ... }
@media (prefers-reduced-motion: no-preference) { ... }
/* Contrast preferences: */
@media (prefers-contrast: more) { ... }
@media (prefers-contrast: less) { ... }
/* Print: */
@media print { ... }
@media screen { ... }
/* Pointer: */
@media (pointer: coarse) { ... } /* touch */
@media (pointer: fine) { ... } /* mouse */
/* Hover capability: */
@media (hover: hover) { ... } /* admits hover */
@media (hover: none) { ... } /* touch-only */
Common responsive patterns
Mobile-first
/* Base styles for mobile */
.container {
padding: 1rem;
}
.grid {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
/* Tablet */
@media (min-width: 768px) {
.container {
padding: 2rem;
}
.grid {
grid-template-columns: 1fr 1fr;
}
}
/* Desktop */
@media (min-width: 1024px) {
.grid {
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
}
}
Dark mode
:root {
--bg: white;
--text: black;
--accent: blue;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #1a1a1a;
--text: #e0e0e0;
--accent: #6cf;
}
}
body {
background: var(--bg);
color: var(--text);
}
Reduced motion
.element {
animation: slideIn 0.3s ease-out;
transition: transform 0.2s;
}
@media (prefers-reduced-motion: reduce) {
.element {
animation: none;
transition: none;
}
}
/* Or globally: */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
The mechanism admits substantial accessibility — users with vestibular disorders or substantial motion sensitivity.
Print styles
@media print {
nav, footer, .no-print {
display: none;
}
body {
font-size: 11pt;
color: black;
background: white;
}
a::after {
content: " (" attr(href) ")";
}
.page-break {
page-break-after: always;
}
}
High-DPI
.icon {
background: url("icon.png");
}
@media (min-resolution: 2dppx) {
.icon {
background: url("icon@2x.png");
background-size: 24px 24px;
}
}
Hover capability
.card {
/* base styles */
}
@media (hover: hover) {
.card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
}
@media (hover: none) {
/* touch-only adjustments */
.card {
padding: 1.5rem; /* larger tap targets */
}
}
Container queries
For component-level responsive design:
.card {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 400px) {
.card__title {
font-size: 1.5rem;
}
}
@container card (min-width: 600px) {
.card {
display: grid;
grid-template-columns: 1fr 2fr;
}
}
The mechanism admits substantial component-driven responsiveness — substantial improvement over media-query-only design.
CSS custom properties for animation
:root {
--transition-fast: 0.15s;
--transition-base: 0.3s;
--transition-slow: 0.5s;
--easing-default: cubic-bezier(0.25, 0.1, 0.25, 1);
}
.button {
transition: background var(--transition-fast) var(--easing-default);
}
.modal {
transition: opacity var(--transition-base) var(--easing-default);
}
The mechanism admits substantial design-system consistency.
Animation event hooks (JavaScript)
const element = document.querySelector(".animated");
element.addEventListener("animationstart", (e) => {
console.log(`animation ${e.animationName} started`);
});
element.addEventListener("animationend", (e) => {
console.log(`animation ${e.animationName} ended`);
});
element.addEventListener("animationiteration", (e) => {
console.log(`animation iteration`);
});
element.addEventListener("transitionend", (e) => {
console.log(`transition on ${e.propertyName} ended`);
});
Common patterns
View transition (modern)
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 0.3s;
}
async function navigate(url) {
if (!document.startViewTransition) {
location.href = url;
return;
}
document.startViewTransition(async () => {
// update DOM
await updateContent(url);
});
}
The View Transitions API admits substantial substantial cross-page animations.
Smooth scrolling
html {
scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
}
Scroll-triggered animation
.fade-in {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.6s, transform 0.6s;
}
.fade-in.visible {
opacity: 1;
transform: translateY(0);
}
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add("visible");
}
});
}, { threshold: 0.1 });
document.querySelectorAll(".fade-in").forEach((el) => observer.observe(el));
Parallax (modern)
CSS scroll-timeline admits substantial scroll-driven animations:
@keyframes slide-up {
from { transform: translateY(100px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.element {
animation: slide-up linear;
animation-timeline: view();
animation-range: entry 0% cover 50%;
}
The mechanism admits substantial scroll-tied animation declaratively.
Theme transition
* {
transition: background-color 0.3s, color 0.3s;
}
The transition admits substantial theme-switch smoothness — applied to all elements when switching dark/light mode.
Modal entrance
.modal-backdrop {
background: rgba(0, 0, 0, 0);
transition: background 0.3s;
}
.modal-backdrop.visible {
background: rgba(0, 0, 0, 0.5);
}
.modal {
transform: translateY(20px) scale(0.95);
opacity: 0;
transition: transform 0.3s ease-out, opacity 0.3s ease-out;
}
.modal-backdrop.visible .modal {
transform: translateY(0) scale(1);
opacity: 1;
}
Fluid typography
:root {
--font-size-base: clamp(1rem, 2vw, 1.25rem);
--font-size-h1: clamp(2rem, 5vw, 4rem);
}
body { font-size: var(--font-size-base); }
h1 { font-size: var(--font-size-h1); }
The clamp(min, ideal, max) admits substantial fluid sizing.
A note on the conventional discipline
The contemporary CSS animation/responsive advice:
- Use transitions for state changes; animations for complex motion.
- Animate
transformandopacity— substantial GPU acceleration. - Avoid animating layout properties — substantial paint cost.
- Use
will-changesparingly — only during interaction. - Respect
prefers-reduced-motion— substantial accessibility. - Design mobile-first — base styles for small screens, expand up.
- Use
prefers-color-schemefor dark mode. - Use container queries for component-level responsiveness.
- Use
clamp()for fluid sizing. - Use the View Transitions API for substantial cross-page animations.
- Test with reduced motion — substantial accessibility verification.
The combination — transitions for state changes, keyframe animations for complex motion, transforms for performant manipulation, media queries for viewport-based responsive design, container queries for component-level responsiveness, the substantial accessibility integration via prefers-reduced-motion — is the substance of CSS’s animation and adaptive-design surface. The discipline produces substantial smooth, accessible, performant, responsive interfaces.