C
HTML/CSS/Advanced/Lesson 11

CSS Variables & Animations — Essential Vocabulary for Modern UI

45 min·theory

CSS Variables & Animations — Essential Vocabulary for Modern UI

🎯 After reading this lesson

Once you've finished this lesson, you'll be able to confidently do the following three things.

  • ✅ Implement dark mode using CSS Custom Properties (--var)
  • ✅ Understand the difference between transition and @keyframes
  • ✅ Handle accessibility with prefers-reduced-motion

Keep the learning objectives as a checklist, and close the lesson once you can answer all of them.

CSS Variables (Custom Properties) — `--name`

Basic Usage

css
:root {
    --primary: #00d066;
    --text-base: 16px;
    --radius: 8px;
}

.btn {
    background: var(--primary);
    border-radius: var(--radius);
    font-size: var(--text-base);
}

:root is the global scope. Reference it anywhere with var(--name).

Component Scope

css
.card {
    --card-padding: 16px;
    padding: var(--card-padding);
}
.card.large {
    --card-padding: 32px;        /* same variable, different value */
}

Variables can be redefined per element. Child elements can override variables defined by their parent.

Dark Mode — Just change one variable

css
:root {
    --bg: #ffffff;
    --fg: #000000;
}
[data-theme="dark"] {
    --bg: #1a1a1a;
    --fg: #e6e6e6;
}

body {
    background: var(--bg);
    color: var(--fg);
}
javascript
// Switch theme with JS
document.documentElement.dataset.theme = 'dark';

No need to touch dozens of colors individually — theme switching made simple. Tailwind's dark: variant automates exactly this.

prefers-color-scheme — Follow System Settings

css
@media (prefers-color-scheme: dark) {
    :root {
        --bg: #1a1a1a;
        --fg: #e6e6e6;
    }
}

Automatically detects the user's OS setting. Use when a manual toggle isn't needed.

Integration with JS

javascript
// Read
getComputedStyle(document.documentElement).getPropertyValue('--primary');

// Write
document.documentElement.style.setProperty('--primary', '#ff0000');

Tailwind + CSS Variables

css
@theme {
    --color-primary: #00d066;
    --radius-card: 12px;
}

Tailwind v4 is entirely CSS variable-based. bg-primary automatically becomes bg: var(--color-primary).

transition — Smooth State Changes

The Simplest Form

css
.btn {
    background: blue;
    transition: background 0.3s ease;
}
.btn:hover {
    background: red;     /* smoothly over 0.3 seconds */
}

Property / duration / timing function — these three are the core of transition.

Multiple Properties at Once

css
.card {
    transform: translateY(0);
    box-shadow: 0 1px 2px rgba(0,0,0,0.1);
    transition: transform 0.3s, box-shadow 0.3s;
}
.card:hover {
    transform: translateY(-4px);
    box-shadow: 0 8px 16px rgba(0,0,0,0.15);
}

Card lifts slightly on hover + deeper shadow — the standard pattern in modern web design.

Timing Functions

  • linear — constant speed
  • ease (default) — accelerate then decelerate
  • ease-in — starts slow
  • ease-out — starts fast, ends gently (UI standard)
  • cubic-bezier(...) — custom

UI transitions almost always use ease-out. The naturalness of "quick start + smooth landing."

The Pitfall of transition: all

css
.btn {
    transition: all 0.3s;   /* ❌ all properties — hurts performance */
}

Explicitly listing properties is better:

css
.btn {
    transition: background-color 0.2s, transform 0.2s;
}

Which Properties Are Animatable

Only those that can be interpolated numerically. display, visibility, and background-image are not animatable. opacity, transform, color, width, and height are animatable.

Properties with good GPU acceleration: transform and opacity (performance winners). Changing width/height triggers layout recalculation, making it slower.

transform — translate / scale / rotate

css
.el:hover {
    transform: translateX(20px) scale(1.05) rotate(5deg);
}

Faster than changing margin/padding — composited directly by the GPU.

@keyframes — Complex Animations

Basic Structure

css
@keyframes fadeIn {
    from { opacity: 0; transform: translateY(20px); }
    to   { opacity: 1; transform: translateY(0); }
}

.modal {
    animation: fadeIn 0.3s ease-out;
}

Define 0% → 100% (from → to). CSS handles the interpolation automatically.

When You Need Intermediate Steps

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

.icon { animation: bounce 1s infinite; }

Loading Spinner — Real-world Example

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

.spinner {
    width: 24px;
    height: 24px;
    border: 3px solid #eee;
    border-top-color: #00d066;
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
}

This is the spinner used on every SaaS site.

Full animation Shorthand

css
.el {
    animation:
        fadeIn          /* name */
        0.3s            /* duration */
        ease-out        /* timing */
        0.1s            /* delay */
        forwards        /* fill-mode: retain end state */
        infinite;       /* iteration-count */
}

Shorthand. The expanded form uses 6 individual properties:

css
.el {
    animation-name: fadeIn;
    animation-duration: 0.3s;
    animation-timing-function: ease-out;
    animation-delay: 0.1s;
    animation-fill-mode: forwards;
    animation-iteration-count: infinite;
}

prefers-reduced-motion — Accessibility Essential

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

Disabling motion for users with vertigo or vestibular disorders. Directly impacts your accessibility score.

🤖 Try asking AI like this

  • "Make this button float up slightly and deepen the shadow on hover."
  • "Create a CSS loading spinner. 360-degree rotation, 0.8 seconds, infinite loop."
  • "Add prefers-reduced-motion support."
Read this first: Media Queries
CSS Variables & Animations — Essential Vocabulary for Modern UI - HTML/CSS