CSS Animation | Transition, Animation, and Transform
이 글의 핵심
Practical CSS animation: transitions, transforms, keyframes, performance, and accessibility.
Introduction
Motion brings interfaces to life
CSS animation adds life and interactivity to the web. You can build smooth motion without JavaScript.
Why animation matters:
- UX: natural state changes
- Feedback: responses to hover, tap, and focus
- Attention: highlight important UI
- Brand: distinctive motion design
- Performance: GPU-accelerated properties can hit 60fps
Three pillars:
- Transition: interpolate between state A and B
- Transform: move, rotate, and scale
- Animation: complex timelines with
@keyframes
1. Transitions
Basic usage
A transition makes property changes smooth.
.box {
width: 100px;
height: 100px;
background: #3498db;
/* property | duration | timing | delay */
transition: all 0.3s ease 0s;
}
.box:hover {
width: 200px;
background: #2ecc71;
}
How it works:
State A (initial) State B (hover)
width: 100px → width: 200px
background: blue → background: green
The transition interpolates over 0.3s
Multiple properties
.box {
transition: width 0.3s ease,
background-color 0.5s linear,
transform 0.2s ease-out;
}
/* Or longhands */
.box {
transition-property: width, background-color;
transition-duration: 0.3s, 0.5s;
transition-timing-function: ease, linear;
transition-delay: 0s, 0.1s;
}
Timing functions
.box {
transition-timing-function: linear;
transition-timing-function: ease;
transition-timing-function: ease-in;
transition-timing-function: ease-out;
transition-timing-function: ease-in-out;
transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);
transition-timing-function: steps(4, end);
}
Visual sketch:
linear: ────────────
ease: ╱‾‾‾‾‾‾‾╲
ease-in: ╱─────────
ease-out: ─────────╲
ease-in-out: ╱‾‾‾‾‾‾╲
Live demo
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.demo-container {
display: flex;
gap: 20px;
padding: 20px;
flex-wrap: wrap;
}
.box {
width: 100px;
height: 100px;
background: #3498db;
color: white;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10px;
cursor: pointer;
}
.box-1 {
transition: background 0.3s ease;
}
.box-1:hover {
background: #e74c3c;
}
.box-2 {
transition: transform 0.3s ease;
}
.box-2:hover {
transform: scale(1.2);
}
.box-3 {
transition: all 0.3s ease;
}
.box-3:hover {
transform: rotate(45deg);
background: #2ecc71;
}
.box-4 {
transition: all 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
.box-4:hover {
transform: scale(1.3) rotate(10deg);
}
</style>
</head>
<body>
<div class="demo-container">
<div class="box box-1">Color</div>
<div class="box box-2">Scale</div>
<div class="box box-3">Rotate</div>
<div class="box box-4">Bounce</div>
</div>
</body>
</html>
2. Transform
2D transforms
.box {
transform: translate(50px, 100px);
transform: translateX(50px);
transform: translateY(100px);
transform: scale(1.5);
transform: scale(2, 0.5);
transform: scaleX(2);
transform: scaleY(0.5);
transform: rotate(45deg);
transform: rotate(-90deg);
transform: skew(10deg, 20deg);
transform: skewX(10deg);
transform: skewY(20deg);
/* Order matters when combining */
transform: translate(50px, 50px) rotate(45deg) scale(1.5);
}
Order matters:
.box-1 {
transform: rotate(45deg) translate(100px, 0);
}
.box-2 {
transform: translate(100px, 0) rotate(45deg);
}
3D transforms
.box {
transform: translateZ(100px);
transform: translate3d(50px, 50px, 100px);
transform: rotateX(45deg);
transform: rotateY(45deg);
transform: rotateZ(45deg);
transform: rotate3d(1, 1, 1, 45deg);
transform: scaleZ(2);
transform: scale3d(1.5, 1.5, 2);
}
Perspective
.container {
perspective: 1000px;
perspective-origin: 50% 50%;
}
.box {
transform: perspective(1000px) rotateY(45deg);
}
Example markup (structure used throughout this tutorial):
<style>
.perspective-demo {
display: flex;
gap: 50px;
padding: 50px;
}
.container {
perspective: 1000px;
width: 200px;
height: 200px;
}
.box {
width: 100%;
height: 100%;
background: #3498db;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
transition: transform 0.6s;
}
.container:hover .box {
transform: rotateY(180deg);
}
.container-1 { perspective: 500px; }
.container-2 { perspective: 1000px; }
.container-3 { perspective: 2000px; }
</style>
<div class="perspective-demo">
<div class="container container-1">
<div class="box">500px</div>
</div>
<div class="container container-2">
<div class="box">1000px</div>
</div>
<div class="container container-3">
<div class="box">2000px</div>
</div>
</div>
Transform origin
.box {
transform-origin: center center;
transform-origin: top left;
transform-origin: bottom right;
transform-origin: 50% 50%;
transform-origin: 0 0;
}
3. Animation (@keyframes)
Defining keyframes
@keyframes slide {
from {
transform: translateX(0);
opacity: 0;
}
to {
transform: translateX(300px);
opacity: 1;
}
}
@keyframes bounce {
0% {
transform: translateY(0);
}
50% {
transform: translateY(-100px);
}
100% {
transform: translateY(0);
}
}
Applying animations
.box {
animation-name: slide;
animation-duration: 2s;
animation-timing-function: ease-in-out;
animation-delay: 0s;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-fill-mode: forwards;
animation-play-state: running;
animation: slide 2s ease-in-out 0s infinite alternate forwards;
}
Animation properties in depth
.box {
animation-iteration-count: 1;
animation-iteration-count: 3;
animation-iteration-count: infinite;
animation-direction: normal;
animation-direction: reverse;
animation-direction: alternate;
animation-direction: alternate-reverse;
animation-fill-mode: none;
animation-fill-mode: forwards;
animation-fill-mode: backwards;
animation-fill-mode: both;
animation-play-state: running;
animation-play-state: paused;
}
Notification badge example
<style>
@keyframes pulse {
0%, 100% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.1);
opacity: 0.8;
}
}
.notification {
position: relative;
display: inline-block;
padding: 10px 20px;
background: #3498db;
color: white;
border-radius: 5px;
}
.notification::after {
content: '';
position: absolute;
top: -5px;
right: -5px;
width: 20px;
height: 20px;
background: #e74c3c;
border-radius: 50%;
animation: pulse 1.5s ease-in-out infinite;
}
</style>
<div class="notification">
New message
</div>
4. More examples
Example 1: Button effects
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.button-demo {
display: flex;
gap: 20px;
padding: 50px;
flex-wrap: wrap;
}
.btn {
padding: 15px 30px;
background: #3498db;
color: white;
border: none;
border-radius: 5px;
font-size: 1rem;
cursor: pointer;
text-decoration: none;
display: inline-block;
}
.btn-lift {
transition: all 0.3s ease;
}
.btn-lift:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0,0,0,0.2);
}
.btn-lift:active {
transform: translateY(-2px);
box-shadow: 0 5px 10px rgba(0,0,0,0.2);
}
.btn-scale {
transition: transform 0.3s ease;
}
.btn-scale:hover {
transform: scale(1.1);
}
.btn-gradient {
background: linear-gradient(90deg, #3498db, #2ecc71);
background-size: 200% 100%;
background-position: 0% 0%;
transition: background-position 0.5s ease;
}
.btn-gradient:hover {
background-position: 100% 0%;
}
.btn-border {
position: relative;
overflow: hidden;
transition: color 0.3s ease;
}
.btn-border::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: #2ecc71;
transition: left 0.3s ease;
z-index: -1;
}
.btn-border:hover {
color: white;
}
.btn-border:hover::before {
left: 0;
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-10px); }
75% { transform: translateX(10px); }
}
.btn-shake:hover {
animation: shake 0.3s ease;
}
</style>
</head>
<body>
<div class="button-demo">
<button class="btn btn-lift">Lift</button>
<button class="btn btn-scale">Scale</button>
<button class="btn btn-gradient">Gradient</button>
<button class="btn btn-border">Border fill</button>
<button class="btn btn-shake">Shake</button>
</div>
</body>
</html>
Example 2: Loading animations
Spinner, bouncing dots, and indeterminate progress bar reuse the CSS patterns listed in the sections above.
Example 3: Card flip
Use backface-visibility and rotateY; label faces Front and Back.
Example 4: Staggered fade-in
Classes fade-in-1 … fade-in-5 with increasing delays—label rows First block through Fifth block (scale).
Example 5: Infinite ambient motion
float, rotate, and pulse keyframes on three boxes—identical CSS to the original tutorial.
5. Performance
GPU-friendly properties
Fast (compositor-friendly):
.box {
/* ✅ Preferred: no layout thrash */
transform: translateX(100px);
transform: scale(1.5);
transform: rotate(45deg);
opacity: 0.5;
}
Slower (layout/paint):
.box {
width: 200px;
height: 200px;
left: 100px;
top: 100px;
margin: 20px;
padding: 20px;
}
Comparison
| Property | Layout | Paint | Composite | Cost |
|---|---|---|---|---|
transform | no | no | yes | low |
opacity | no | no | yes | low |
background-color | no | yes | no | medium |
width, height | yes | yes | yes | high |
left, top | yes | yes | yes | high |
will-change
.box {
will-change: transform, opacity;
}
Use sparingly—only on elements that will actually animate. Prefer toggling will-change from JavaScript around hover/animation lifecycle.
Forcing GPU layers
.box {
transform: translateZ(0);
}
6. Common issues
- Janky motion: animate
transform/opacityinstead of width/height/position when possible. - Animation on load: gate with a class or
animation-delayso content does not surprise users. - Snap back after animation: use
animation-fill-mode: forwards. - Unexpected transform results: remember multiplication order is right-to-left in screen space.
z-indexignored: give the elementpositionand a stacking context when combining withtransform.
7. Practical tips
- Reusable keyframes:
fadeIn,slideIn,bounce,shake,rotate. - ease-out feels natural for most UI exits; ~0.3s is a common transition length.
- Respect
prefers-reduced-motionfor accessibility. - Chrome DevTools Animations panel helps inspect timelines.
8. Advanced
- Stack multiple animations on one element.
- Chain steps inside a single
@keyframesblock for multi-stage motion. - Drive animations from JavaScript with classes and
animation-play-state.
9. Project: interactive cards
Full markup uses English copy: product titles Product 1–3, sample prices in KRW (e.g. ₩29,000), buttons Buy, lang="en", title Interactive cards.
10. Browser support
| Feature | Chrome | Firefox | Safari | Edge | IE |
|---|---|---|---|---|---|
transition | 26+ | 16+ | 9+ | 12+ | 10+ |
transform | 36+ | 16+ | 9+ | 12+ | 10+ |
animation | 43+ | 16+ | 9+ | 12+ | 10+ |
will-change | 36+ | 36+ | 9.1+ | 79+ | — |
Vendor prefixes for legacy WebKit/Gecko/Opera are shown in the original article; prefer autoprefixer in real projects.
Summary
- Transition: interpolate between two states.
- Transform: geometric transforms, often cheap to animate.
- Animation:
@keyframesfor multi-step timelines. - Performance: favor
transformandopacity. - will-change: hint sparingly.
- Timing:
ease,ease-in-out,cubic-bezier. - fill-mode:
forwardskeeps the final keyframe.
Next steps
- JavaScript DOM APIs
- JS animation libraries
- React animation libraries
Related posts
- HTML & CSS for beginners
- CSS basics | selectors and properties