CSS Animation Techniques: From Transitions to Keyframes

February 14, 2026 · 10 min read · Developer

CSS animations bring websites to life. A subtle hover effect, a smooth page transition, or a loading spinner — these small details transform a static page into an engaging experience. The best part? You don't need JavaScript for most animations. CSS provides two powerful mechanisms: transitions for simple state changes and keyframe animations for complex, multi-step sequences. This guide covers both, plus performance optimization techniques to keep your animations silky smooth at 60fps.

CSS Transitions: The Basics

Transitions are the simplest form of CSS animation. They automatically animate the change between two states of a property. Instead of a button color snapping from blue to green instantly, a transition makes it smoothly fade over a specified duration.

.button {
    background: #4A90D9;
    color: white;
    padding: 12px 24px;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    transition: background 0.3s ease, transform 0.2s ease;
}

.button:hover {
    background: #2ECC71;
    transform: translateY(-2px);
}

Transition Properties

The transition shorthand combines four sub-properties:

Timing Functions

The timing function controls the animation's acceleration curve. The built-in options are:

For natural-feeling motion, cubic-bezier(0.4, 0, 0.2, 1) (Material Design's standard curve) is an excellent default.

CSS Transforms

Transforms are the workhorses of CSS animation. They modify an element's visual rendering without affecting layout, making them extremely performant:

/* Move */
transform: translateX(100px);
transform: translateY(-50%);
transform: translate(100px, 50px);

/* Scale */
transform: scale(1.5);
transform: scaleX(2);

/* Rotate */
transform: rotate(45deg);
transform: rotate3d(1, 1, 0, 45deg);

/* Skew */
transform: skewX(10deg);

/* Combine multiple transforms */
transform: translateY(-10px) rotate(5deg) scale(1.1);

Keyframe Animations

For animations that need more than two states, or that should play automatically (not just on hover), use @keyframes:

@keyframes pulse {
    0% {
        transform: scale(1);
        opacity: 1;
    }
    50% {
        transform: scale(1.05);
        opacity: 0.8;
    }
    100% {
        transform: scale(1);
        opacity: 1;
    }
}

.pulsing-element {
    animation: pulse 2s ease-in-out infinite;
}

Animation Properties

The animation shorthand combines these sub-properties:

Practical Animation Examples

Loading Spinner

@keyframes spin {
    to { transform: rotate(360deg); }
}

.spinner {
    width: 40px;
    height: 40px;
    border: 4px solid rgba(255,255,255,0.2);
    border-top-color: #4A90D9;
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
}

Fade In on Scroll

@keyframes fadeInUp {
    from {
        opacity: 0;
        transform: translateY(30px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.fade-in {
    animation: fadeInUp 0.6s ease-out forwards;
}

Skeleton Loading

@keyframes shimmer {
    0% { background-position: -200% 0; }
    100% { background-position: 200% 0; }
}

.skeleton {
    background: linear-gradient(
        90deg,
        #1a1a2e 25%,
        #2a2a4e 50%,
        #1a1a2e 75%
    );
    background-size: 200% 100%;
    animation: shimmer 1.5s ease-in-out infinite;
    border-radius: 4px;
}

Bounce Effect

@keyframes bounce {
    0%, 100% { transform: translateY(0); }
    25% { transform: translateY(-20px); }
    50% { transform: translateY(0); }
    75% { transform: translateY(-10px); }
}

.bouncing {
    animation: bounce 1s ease infinite;
}

Performance Best Practices

Not all CSS properties are equal when it comes to animation performance. Animating certain properties triggers expensive browser operations:

The Golden Rule: Only Animate Transform and Opacity

These two properties are handled by the GPU compositor and don't trigger layout or paint operations:

Avoid animating these properties (they trigger layout recalculation):

Instead of animating width, use transform: scaleX(). Instead of animating top, use transform: translateY().

Use will-change Sparingly

.animated-element {
    will-change: transform;  /* Hints browser to optimize */
}

Don't apply will-change to everything — it uses GPU memory. Apply it only to elements that will actually animate, and remove it after animation completes if possible.

Respect User Preferences

@media (prefers-reduced-motion: reduce) {
    * {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
    }
}

Some users experience motion sickness or discomfort from animations. Always include a reduced-motion media query to respect their system preferences.

CSS vs JavaScript Animation

When should you use CSS vs JavaScript for animations?

For most UI animations — button hovers, modal appearances, skeleton loaders, tab switches — CSS is sufficient and preferred. Explore interactive examples with our Flexbox Playground and CSS Gradient Generator.

Conclusion

CSS animations are a powerful tool that every web developer should master. Start with transitions for simple hover effects, graduate to keyframes for multi-step animations, and always follow the performance golden rule: animate only transform and opacity. Respect user motion preferences with prefers-reduced-motion. Use our Box Shadow Generator and Border Radius Preview to design the elements you'll be animating, and check out our Flexbox vs Grid guide for layout techniques.

🔧 Related Wootils Tools:
CSS Gradient Generator · Box Shadow Generator · Border Radius Preview · Flexbox Playground