CSS - Animations

Mastering CSS Animations: The Complete Guide

CSS - Animations

What is CSS Animation?

CSS animation allows HTML elements to gradually change from one style to another without requiring JavaScript. These animations create smooth transitions between states and can significantly enhance user experience.

CSS animations work by defining keyframes that specify the styles an element will have at various points during the animation sequence. This gives developers precise control over the element's appearance throughout the entire animation timeline.

Unlike simple hover effects, CSS animations can include multiple steps, timing functions, and can be triggered automatically when a page loads.

Syntax:

@keyframes animation-name {
  from {
    /* initial styles */
  }
  to {
    /* final styles */
  }
}

.element {
  animation: animation-name duration timing-function delay iteration-count direction fill-mode;
}

Example 1:

@keyframes slideIn {
  from {
    transform: translateX(-100%);
    opacity: 0;
  }
  to {
    transform: translateX(0);
    opacity: 1;
  }
}

.code-subtle-example {
  animation: slideIn 1s ease-out 0.5s 1 forwards;
}

Output: An element with the class code-subtle-example will slide in from the left after a 0.5s delay, taking 1 second to complete the animation once.

Example 2:

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

.code-subtle-button:hover {
  animation: pulse 1.5s infinite ease-in-out;
}

Output: When a user hovers over an element with class code-subtle-button, it will pulse slightly larger and smaller continuously while the hover state remains.

Tips:

  1. Use descriptive names for your keyframes to make your code more maintainable.

  2. Consider using CSS variables for animation values to easily adjust animations site-wide.

  3. For performance reasons, stick to animating only transform and opacity properties when possible.

CSS Animations vs. Transitions

Understanding when to use animations versus transitions is crucial for efficient development. Let's explore the key differences between these two powerful CSS features.

Transitions are ideal for simple state changes triggered by user interactions like hover or focus, while animations offer more complex multi-step animations that can run automatically.

Animations provide greater control with keyframes allowing multiple intermediate states, whereas transitions only allow for start and end states.

Syntax Comparison:

/* Transition Syntax */
.element {
  transition: property duration timing-function delay;
}

/* Animation Syntax */
.element {
  animation: name duration timing-function delay iteration-count direction fill-mode;
}

Example 1 - Transition:

.code-subtle-button {
  background-color: #3498db;
  transition: background-color 0.3s ease;
}

.code-subtle-button:hover {
  background-color: #2980b9;
}

Output: When hovering over the button, its background color smoothly changes from blue to a darker blue over 0.3 seconds.

Example 2 - Animation:

@keyframes rainbow {
  0% { background-color: red; }
  25% { background-color: yellow; }
  50% { background-color: green; }
  75% { background-color: blue; }
  100% { background-color: purple; }
}

.code-subtle-header {
  animation: rainbow 5s linear infinite;
}

Output: The header cycles through a rainbow of colors continuously, changing colors every 1.25 seconds with smooth transitions between each.

Tips:

  1. Use transitions for simple interaction-based effects and animations for more complex or automatic effects.

  2. Combine both techniques—use transitions for hover states and animations for attention-grabbing elements.

  3. When in doubt, start with a transition and upgrade to an animation if you need intermediate states.

Basic Animation Properties

Let's dive into the essential properties that control how your CSS animations behave. Understanding these properties gives you precise control over your animations.

The animation shorthand property combines all animation-related properties into a single declaration, making your code cleaner and more concise.

Each animation property controls a specific aspect of the animation, from its timing to how many times it repeats.

Basic Animation Properties Breakdown:

  1. animation-name: Specifies the name of the @keyframes rule to use

  2. animation-duration: Sets how long the animation takes to complete one cycle

  3. animation-timing-function: Defines how the animation progresses over time

  4. animation-delay: Determines when the animation starts

  5. animation-iteration-count: Sets how many times the animation should repeat

  6. animation-direction: Specifies whether the animation should play forwards, backwards, or alternate

  7. animation-fill-mode: Defines what styles apply before/after the animation

  8. animation-play-state: Controls whether the animation is running or paused

Example 1:

.code-subtle-notification {
  animation-name: bounce;
  animation-duration: 0.5s;
  animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
  animation-delay: 1s;
  animation-iteration-count: 3;
  animation-direction: alternate;
  animation-fill-mode: forwards;
}

Output: A notification element that waits 1 second, then bounces three times with a snappy cubic-bezier timing function, alternating direction each time, and stays in its final state after completing.

Example 2 (Shorthand):

.code-subtle-loading-indicator {
  animation: spin 1.2s ease-in-out infinite;
}

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

Output: A loading indicator that continuously rotates 360 degrees, taking 1.2 seconds per rotation with an ease-in-out timing function.

Tips:

  1. Use shorthand notation for cleaner code, but use individual properties when you need to override specific aspects.

  2. Set animation-fill-mode: forwards when you want elements to retain their final animated state.

  3. For subtle UI animations, keep durations short (0.2s-0.5s) to avoid frustrating users.

Transform Property in CSS Animations

The transform property is the backbone of modern CSS animations, allowing you to manipulate elements in 2D or 3D space without affecting document flow.

Transform operations can be combined to create complex animations by applying multiple transformations such as rotation, scaling, and translation simultaneously.

When animating with transform, you're changing the visual rendering of an element rather than layout properties, which makes animations smoother and more performant.

Syntax:

transform: function(value);
/* Or multiple functions */
transform: function1(value1) function2(value2);

Example 1:

@keyframes code-subtle-wobble {
  0% {
    transform: rotate(0deg) scale(1);
  }
  25% {
    transform: rotate(5deg) scale(1.1);
  }
  75% {
    transform: rotate(-5deg) scale(0.9);
  }
  100% {
    transform: rotate(0deg) scale(1);
  }
}

.code-subtle-logo {
  animation: code-subtle-wobble 2s ease-in-out infinite;
}

Output: A logo that wobbles by rotating slightly back and forth while simultaneously scaling larger and smaller, creating an organic, breathing effect.

Example 2:

@keyframes code-subtle-flip {
  0% {
    transform: perspective(800px) rotateY(0);
  }
  100% {
    transform: perspective(800px) rotateY(180deg);
  }
}

.code-subtle-card:hover {
  animation: code-subtle-flip 1s forwards;
}

Output: When hovered, a card element flips horizontally 180 degrees with perspective, creating a 3D card-flip effect.

Tips:

  1. Always include non-prefixed transform property after vendor-prefixed versions for better browser compatibility.

  2. Use the transform-origin property to control the pivot point for rotations and scaling.

  3. For smoother animations, combine multiple transforms in a single declaration rather than animating them separately.

Translation, Rotation, and Scaling

These transform functions form the core toolkit for creating dynamic CSS animations. Understanding how each works is essential for creating sophisticated effects.

Translation moves elements along the X, Y, or Z axis without affecting the document flow, making it ideal for animations that need elements to move around the page.

Rotation and scaling allow you to create effects like spinning loaders, pulsing buttons, or attention-grabbing elements that grow when interacted with.

1. Translate

The translate function moves an element from its current position along the X and/or Y axis.

Syntax:

transform: translate(x, y);
transform: translateX(x);
transform: translateY(y);
transform: translateZ(z); /* Requires 3D transforms */
transform: translate3d(x, y, z);

Example 1:

@keyframes code-subtle-float {
  0% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-10px);
  }
  100% {
    transform: translateY(0);
  }
}

.code-subtle-cloud-icon {
  animation: code-subtle-float 3s ease-in-out infinite;
}

Output: A cloud icon that gently floats up 10px and back down in a continuous cycle, creating a peaceful floating effect.

Example 2:

@keyframes code-subtle-entrance {
  from {
    transform: translate(-50px, 20px);
    opacity: 0;
  }
  to {
    transform: translate(0, 0);
    opacity: 1;
  }
}

.code-subtle-menu-item {
  opacity: 0;
  animation: code-subtle-entrance 0.4s forwards;
  animation-delay: calc(var(--item-index) * 0.1s);
}

Output: Menu items that enter the screen diagonally one after another with staggered timing based on their index variable.

2. Rotate

The rotate function rotates an element around a fixed point (determined by transform-origin).

Syntax:

transform: rotate(angle);
transform: rotateX(angle);
transform: rotateY(angle);
transform: rotateZ(angle);

Example 1:

@keyframes code-subtle-spinner {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

.code-subtle-loading {
  width: 40px;
  height: 40px;
  border: 4px solid #f3f3f3;
  border-top: 4px solid #3498db;
  border-radius: 50%;
  animation: code-subtle-spinner 1.2s linear infinite;
}

Output: A spinner loading indicator that rotates continuously in a clockwise direction, completing a full rotation every 1.2 seconds.

Example 2:

@keyframes code-subtle-shake {
  0%, 100% {
    transform: rotate(0deg);
  }
  20%, 60% {
    transform: rotate(-6deg);
  }
  40%, 80% {
    transform: rotate(6deg);
  }
}

.code-subtle-error-icon {
  animation: code-subtle-shake 0.6s ease-in-out;
}

Output: An error icon that shakes back and forth by rotating slightly in alternating directions, drawing attention to the error.

3. Scale

The scale function changes the size of an element, either uniformly or along specific axes.

Syntax:

transform: scale(x, y);
transform: scaleX(x);
transform: scaleY(y);
transform: scaleZ(z); /* Requires 3D transforms */
transform: scale3d(x, y, z);

Example 1:

@keyframes code-subtle-pulse {
  0%, 100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.1);
  }
}

.code-subtle-notification-badge {
  background-color: #e74c3c;
  border-radius: 50%;
  animation: code-subtle-pulse 1.5s ease-in-out infinite;
}

Output: A notification badge that pulses by growing 10% larger and returning to normal size in a continuous heartbeat-like effect.

Example 2:

@keyframes code-subtle-squish {
  0%, 100% {
    transform: scaleY(1);
  }
  50% {
    transform: scaleY(0.8);
  }
}

.code-subtle-button:active {
  animation: code-subtle-squish 0.2s linear;
}

Output: When clicked, a button compresses vertically to 80% of its height and returns to normal, creating a satisfying "squish" effect that mimics physical button presses.

Tips:

  1. Use translate3d() or translateZ() to force hardware acceleration for smoother animations.

  2. When scaling elements, consider how text might become distorted and adjust accordingly.

  3. Combine rotation with other transforms to create more organic, natural-looking animations.

Advanced Properties: Opacity, Skew, and Visibility

These additional properties complement transform functions to create more nuanced and polished animations. They control aspects like transparency, angular distortion, and element visibility.

Combining these properties with basic transforms allows for complex effects like elements that fade in while sliding, or text that skews for a dynamic typography effect.

Properly timing these properties in your keyframes is crucial for creating cohesive animations that feel natural and intentional.

1. Opacity

The opacity property specifies the transparency of an element, ranging from 0 (completely transparent) to 1 (fully opaque).

Syntax:

opacity: value; /* 0 to 1 */

Example 1:

@keyframes code-subtle-fade {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.code-subtle-content-block {
  animation: code-subtle-fade 0.7s ease-out forwards;
}

Output: Content blocks that fade in while moving upward slightly, creating a smooth entrance effect commonly used in modern websites.

Example 2:

@keyframes code-subtle-blink {
  0%, 100% {
    opacity: 1;
  }
  50% {
    opacity: 0.3;
  }
}

.code-subtle-cursor {
  display: inline-block;
  width: 2px;
  height: 1.2em;
  background-color: #000;
  animation: code-subtle-blink 1s step-end infinite;
}

Output: A text cursor that blinks by changing opacity abruptly, mimicking the blinking cursor in text editors.

2. Skew

The skew function tilts an element along the X and/or Y axis by the specified angle, creating a parallelogram-like distortion.

Syntax:

transform: skew(x-angle, y-angle);
transform: skewX(angle);
transform: skewY(angle);

Example 1:

@keyframes code-subtle-windblown {
  0%, 100% {
    transform: skewX(0deg);
  }
  50% {
    transform: skewX(-10deg);
  }
}

.code-subtle-flag {
  transform-origin: left;
  animation: code-subtle-windblown 3s ease-in-out infinite;
}

Output: A flag element that skews horizontally back and forth, creating the impression of being blown by wind.

Example 2:

@keyframes code-subtle-perspective-shift {
  from {
    transform: skewY(0deg);
  }
  to {
    transform: skewY(5deg);
  }
}

.code-subtle-card-container:hover .code-subtle-card {
  animation: code-subtle-perspective-shift 0.4s ease forwards;
}

Output: When the container is hovered, cards shift their vertical skew to create a subtle perspective change, as if the viewing angle changed.

3. Visibility

The visibility property determines whether an element is visible, while still taking up space in the layout, unlike display: none.

Syntax:

visibility: visible | hidden | collapse;

Example 1:

@keyframes code-subtle-reveal {
  0% {
    visibility: hidden;
    opacity: 0;
  }
  1% {
    visibility: visible;
    opacity: 0;
  }
  100% {
    visibility: visible;
    opacity: 1;
  }
}

.code-subtle-tooltip {
  visibility: hidden;
  animation: code-subtle-reveal 0.3s ease-out forwards;
}

.code-subtle-trigger:hover .code-subtle-tooltip {
  animation-play-state: running;
}

Output: A tooltip that becomes visible and fades in when its trigger is hovered, using visibility to ensure screen readers can access it appropriately.

Example 2:

@keyframes code-subtle-disappear {
  0%, 80% {
    visibility: visible;
    opacity: 1;
  }
  99% {
    visibility: visible;
    opacity: 0;
  }
  100% {
    visibility: hidden;
    opacity: 0;
  }
}

.code-subtle-notification {
  animation: code-subtle-disappear 5s linear forwards;
}

Output: A notification that stays visible for 4 seconds, fades out, then becomes hidden to prevent interactions while not changing layout.

Tips:

  1. Use opacity instead of visibility for smooth transitions, but combine with visibility for better accessibility.

  2. Apply skew transforms sparingly as they can make text difficult to read if overused.

  3. When animating opacity, consider setting a visible background to prevent text from becoming illegible during the transition.

Practical Applications: Loaders and Card Flips

Now let's explore how to create practical, everyday UI elements using the animation concepts we've covered. These examples demonstrate how to combine multiple properties for polished, production-ready animations.

Animated loaders provide visual feedback during wait times, improving perceived performance and user patience during necessary delays.

Card flip animations create engaging interactions for revealing additional information or transitioning between related content views.

1. Animated Loading Spinner

HTML:

<div class="code-subtle-loader"></div>

CSS:

@keyframes code-subtle-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

.code-subtle-loader {
  width: 40px;
  height: 40px;
  border: 4px solid rgba(0, 0, 0, 0.1);
  border-top: 4px solid #3498db;
  border-radius: 50%;
  animation: code-subtle-spin 1s linear infinite;
}

Output: A circular spinner with a transparent border and a solid blue segment on top that rotates continuously, creating a clean loading indicator.

Advanced Loader with Multiple Elements:

@keyframes code-subtle-pulse-ring {
  0% {
    transform: scale(0.33);
    opacity: 1;
  }
  80%, 100% {
    transform: scale(1);
    opacity: 0;
  }
}

.code-subtle-loader-container {
  position: relative;
  width: 100px;
  height: 100px;
}

.code-subtle-loader-ring {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border: 4px solid #3498db;
  border-radius: 50%;
  opacity: 0;
}

.code-subtle-loader-ring:nth-child(1) {
  animation: code-subtle-pulse-ring 2s ease infinite;
}

.code-subtle-loader-ring:nth-child(2) {
  animation: code-subtle-pulse-ring 2s ease 0.5s infinite;
}

.code-subtle-loader-ring:nth-child(3) {
  animation: code-subtle-pulse-ring 2s ease 1s infinite;
}

Output: Three concentric rings that pulse outward with staggered timing, creating a ripple effect that suggests ongoing processing.

2. Card Flip Animation

HTML:

<div class="code-subtle-card-container">
  <div class="code-subtle-card">
    <div class="code-subtle-card-front">Front Content</div>
    <div class="code-subtle-card-back">Back Content</div>
  </div>
</div>

CSS:

.code-subtle-card-container {
  perspective: 1000px;
  width: 300px;
  height: 200px;
}

.code-subtle-card {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transition: transform 0.8s;
}

.code-subtle-card-front,
.code-subtle-card-back {
  position: absolute;
  width: 100%;
  height: 100%;
  backface-visibility: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
}

.code-subtle-card-front {
  background-color: #f8f9fa;
  color: #333;
}

.code-subtle-card-back {
  background-color: #3498db;
  color: white;
  transform: rotateY(180deg);
}

.code-subtle-card-container:hover .code-subtle-card {
  transform: rotateY(180deg);
}

Output: A card that flips 180 degrees on hover, revealing its back face with a smooth 3D rotating effect.

Advanced Interactive Card Flip with Animation:

@keyframes code-subtle-flip-in {
  from {
    transform: rotateY(180deg);
  }
  to {
    transform: rotateY(0deg);
  }
}

@keyframes code-subtle-flip-out {
  from {
    transform: rotateY(0deg);
  }
  to {
    transform: rotateY(180deg);
  }
}

.code-subtle-interactive-card {
  transform-style: preserve-3d;
  animation: code-subtle-flip-in 0.6s ease-out forwards;
}

.code-subtle-interactive-card.flipped {
  animation: code-subtle-flip-out 0.6s ease-in forwards;
}

Output: A card that can be toggled between front and back states with smooth animations in both directions when a "flipped" class is added/removed via JavaScript.

Tips for Creating Loaders and Cards:

  1. For loaders, keep animations smooth and continuous—linear timing functions work well here.

  2. When creating 3D card flips, always set perspective on the parent container and transform-style: preserve-3d on the flipping element.

  3. Use backface-visibility: hidden to ensure the back of cards isn't visible through the front face during rotations.

  4. For accessibility, ensure loading animations don't flash or strobe in ways that could trigger photosensitive conditions.

CSS Filters and Blend Modes

These advanced CSS features allow you to apply visual effects similar to those found in image editing software. They can dramatically enhance your animations with minimal code.

Filters apply visual effects like blur, brightness adjustment, and shadows directly to elements without requiring complex SVG filters.

Blend modes determine how elements interact visually with layers beneath them, creating effects like overlays, screen blending, and color mixing.

1. CSS Filters

Syntax:

filter: function(value);
/* Multiple filters */
filter: function1(value1) function2(value2);

Common Filter Functions:

  • blur(px)

  • brightness(%)

  • contrast(%)

  • grayscale(%)

  • hue-rotate(deg)

  • invert(%)

  • opacity(%)

  • saturate(%)

  • sepia(%)

  • drop-shadow(h-offset v-offset blur color)

Example 1:

@keyframes code-subtle-focus {
  from {
    filter: blur(5px) brightness(50%);
  }
  to {
    filter: blur(0) brightness(100%);
  }
}

.code-subtle-hero-image {
  animation: code-subtle-focus 1.2s ease-out forwards;
}

Output: A hero image that starts blurry and dark, then gradually comes into focus and full brightness, creating a dramatic entrance effect.

Example 2:

@keyframes code-subtle-color-shift {
  0% {
    filter: hue-rotate(0deg) saturate(100%);
  }
  50% {
    filter: hue-rotate(180deg) saturate(150%);
  }
  100% {
    filter: hue-rotate(360deg) saturate(100%);
  }
}

.code-subtle-profile-picture:hover {
  animation: code-subtle-color-shift 3s linear infinite;
}

Output: A profile picture that cycles through the color spectrum when hovered, shifting hues while slightly increasing saturation in the middle of the animation.

2. Mix Blend Modes

Mix blend mode defines how an element's content blends with its background and the content/background of its parent element.

Syntax:

mix-blend-mode: normal | multiply | screen | overlay | darken | lighten | color-dodge | color-burn | difference | exclusion | hue | saturation | color | luminosity;

Example 1:

@keyframes code-subtle-reveal {
  from {
    background-position: 0% 50%;
  }
  to {
    background-position: 100% 50%;
  }
}

.code-subtle-overlay-text {
  font-size: 4rem;
  font-weight: bold;
  color: white;
  background: linear-gradient(90deg, #ff7e5f, #feb47b);
  background-size: 200% 100%;
  mix-blend-mode: overlay;
  animation: code-subtle-reveal 3s ease infinite alternate;
}

Output: Bold text with a gradient background that moves from left to right and blends with the content beneath it using overlay mode, creating a colorful, dynamic heading.

Example 2:

@keyframes code-subtle-multiply-shift {
  0%, 100% {
    background-color: rgba(255, 0, 0, 0.5);
  }
  33% {
    background-color: rgba(0, 255, 0, 0.5);
  }
  66% {
    background-color: rgba(0, 0, 255, 0.5);
  }
}

.code-subtle-blend-layer {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  mix-blend-mode: multiply;
  animation: code-subtle-multiply-shift 6s ease-in-out infinite;
}

Output: An overlay layer that cycles through red, green, and blue semi-transparent colors, using multiply blend mode to create color-filtere