Skillversitas

CSS Transitions

CSS transitions allow a property to change from one value to another over a specified duration with a controllable speed curve. Transitions make property changes happen gradually instead of instantly — creating simple, smooth animations.

A transition becomes active when an element's property changes — it can be triggered by pseudo-classes such as :hover, :focus, :active, or by class changes via JavaScript.

The main transition components:

  • transition-property — which property to transition
  • transition-duration — how long the transition lasts
  • transition-timing-function — the speed curve of the transition
  • transition-delay — how long to wait before the transition starts

Code Example

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>CSS Transitions</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    body {
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
      background: #f0f2f5;
      padding: 40px 20px;
    }

    h1 {
      text-align: center;
      margin-bottom: 10px;
      color: #1a1a2e;
    }

    .subtitle {
      text-align: center;
      color: #888;
      margin-bottom: 50px;
    }

    .section {
      max-width: 800px;
      margin: 0 auto 60px;
    }

    .section h2 {
      color: #1a1a2e;
      margin-bottom: 25px;
      text-align: center;
    }

    /* ===== Timing Functions ===== */
    .timing-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
      gap: 20px;
    }

    .timing-item {
      background: white;
      border-radius: 12px;
      padding: 20px;
      text-align: center;
      box-shadow: 0 2px 10px rgba(0,0,0,0.06);
    }

    .timing-item h4 {
      font-size: 0.8rem;
      color: #555;
      margin-bottom: 15px;
    }

    .timing-track {
      width: 100%;
      height: 4px;
      background: #e9ecef;
      border-radius: 2px;
      position: relative;
      margin-bottom: 10px;
    }

    .timing-ball {
      width: 20px;
      height: 20px;
      background: #667eea;
      border-radius: 50%;
      position: absolute;
      top: 50%;
      left: 0;
      transform: translateY(-50%);
      transition-property: left;
      transition-duration: 2s;
    }

    .timing-item:hover .timing-ball {
      left: calc(100% - 20px);
    }

    .timing-ease .timing-ball { transition-timing-function: ease; }
    .timing-linear .timing-ball { transition-timing-function: linear; }
    .timing-ease-in .timing-ball { transition-timing-function: ease-in; }
    .timing-ease-out .timing-ball { transition-timing-function: ease-out; }
    .timing-ease-in-out .timing-ball { transition-timing-function: ease-in-out; }
    .timing-cubic .timing-ball { transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55); }

    /* ===== Button Hover Examples ===== */
    .button-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
      gap: 20px;
    }

    .btn {
      padding: 14px 28px;
      border: none;
      border-radius: 8px;
      font-size: 1rem;
      font-weight: 600;
      cursor: pointer;
      text-align: center;
    }

    .btn-fill {
      background: #667eea;
      color: white;
      transition-property: background, transform, box-shadow;
      transition-duration: 0.3s;
      transition-timing-function: ease;
    }

    .btn-fill:hover {
      background: #764ba2;
      transform: translateY(-2px);
      box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
    }

    .btn-outline {
      background: transparent;
      color: #667eea;
      border: 2px solid #667eea;
      transition-property: background, color, border-color;
      transition-duration: 0.3s;
    }

    .btn-outline:hover {
      background: #667eea;
      color: white;
    }

    .btn-scale {
      background: #43e97b;
      color: #1a1a2e;
      transition-property: transform, box-shadow;
      transition-duration: 0.2s;
      transition-timing-function: ease-in-out;
    }

    .btn-scale:hover {
      transform: scale(1.08);
      box-shadow: 0 8px 25px rgba(67, 233, 123, 0.4);
    }

    .btn-delay {
      background: #f5576c;
      color: white;
      transition-property: transform, box-shadow;
      transition-duration: 0.3s;
      transition-delay: 0s;
    }

    .btn-delay:hover {
      transform: translateY(-2px) scale(1.02);
      box-shadow: 0 6px 20px rgba(245, 87, 108, 0.4);
      transition-delay: 0.1s;
    }

    /* ===== Card Lift ===== */
    .card-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
      gap: 25px;
    }

    .lift-card {
      background: white;
      border-radius: 12px;
      padding: 30px 20px;
      text-align: center;
      box-shadow: 0 2px 8px rgba(0,0,0,0.06);
      transition-property: transform, box-shadow;
      transition-duration: 0.4s;
      transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
    }

    .lift-card:hover {
      transform: translateY(-12px);
      box-shadow: 0 12px 40px rgba(0,0,0,0.12);
    }

    .lift-card-icon {
      font-size: 2.5rem;
      margin-bottom: 15px;
    }

    .lift-card h3 {
      color: #1a1a2e;
      margin-bottom: 8px;
    }

    .lift-card p {
      color: #666;
      font-size: 0.85rem;
      line-height: 1.5;
    }

    /* ===== Color Change ===== */
    .color-grid {
      display: flex;
      gap: 20px;
      justify-content: center;
      flex-wrap: wrap;
    }

    .color-box {
      width: 120px;
      height: 120px;
      border-radius: 16px;
      display: flex;
      align-items: center;
      justify-content: center;
      color: white;
      font-weight: bold;
      cursor: pointer;
      transition-property: background-color, border-radius, transform;
      transition-duration: 0.5s;
      transition-timing-function: ease-in-out;
    }

    .color-box-1 { background: #667eea; }
    .color-box-2 { background: #f5576c; }
    .color-box-3 { background: #43e97b; }
    .color-box-4 { background: #fa709a; }

    .color-box-1:hover { background: #764ba2; border-radius: 50%; transform: rotate(10deg); }
    .color-box-2:hover { background: #667eea; border-radius: 50%; transform: rotate(-10deg); }
    .color-box-3:hover { background: #00f2fe; border-radius: 50%; transform: scale(1.1); }
    .color-box-4:hover { background: #fee140; border-radius: 50%; transform: scale(1.1); }

    /* ===== Expand Animation ===== */
    .expand-section {
      background: white;
      border-radius: 12px;
      overflow: hidden;
      box-shadow: 0 2px 10px rgba(0,0,0,0.06);
    }

    .expand-item {
      border-bottom: 1px solid #eee;
    }

    .expand-item:last-child {
      border-bottom: none;
    }

    .expand-header {
      padding: 18px 24px;
      cursor: pointer;
      display: flex;
      justify-content: space-between;
      align-items: center;
      font-weight: 600;
      color: #1a1a2e;
      transition-property: background;
      transition-duration: 0.2s;
    }

    .expand-header:hover {
      background: #f8f9fa;
    }

    .expand-arrow {
      font-size: 1.2rem;
      transition-property: transform;
      transition-duration: 0.3s;
    }

    .expand-item.active .expand-arrow {
      transform: rotate(180deg);
    }

    .expand-content {
      max-height: 0;
      overflow: hidden;
      transition-property: max-height, padding;
      transition-duration: 0.4s;
      transition-timing-function: ease-in-out;
    }

    .expand-item.active .expand-content {
      max-height: 200px;
      padding: 0 24px 18px;
    }

    .expand-content p {
      color: #666;
      line-height: 1.6;
      font-size: 0.9rem;
    }

    /* ===== Multi-Property Transition ===== */
    .multi-prop-section {
      display: flex;
      justify-content: center;
      gap: 30px;
      flex-wrap: wrap;
    }

    .multi-box {
      width: 200px;
      height: 200px;
      border-radius: 16px;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      font-weight: bold;
      transition-property: all;
      transition-duration: 0.5s;
    }

    .multi-box-1 {
      background: #667eea;
      color: white;
      transition-delay: 0s;
    }

    .multi-box-2 {
      background: #f5576c;
      color: white;
      transition-delay: 0.1s;
    }

    .multi-box-3 {
      background: #43e97b;
      color: #1a1a2e;
      transition-delay: 0.2s;
    }

    .multi-box:hover {
      transform: rotate(5deg) scale(1.05);
      box-shadow: 0 10px 30px rgba(0,0,0,0.2);
    }

    .multi-box-1:hover { background: #764ba2; }
    .multi-box-2:hover { background: #667eea; }
    .multi-box-3:hover { background: #00f2fe; }
  </style>
</head>
<body>

  <h1>CSS Transitions</h1>
  <p class="subtitle">Interaction: hover, click, and watch the smooth changes</p>

  <div class="section">
    <h2>Timing Functions</h2>
    <div class="timing-grid">
      <div class="timing-item timing-ease">
        <h4>ease (default)</h4>
        <div class="timing-track">
          <div class="timing-ball"></div>
        </div>
      </div>
      <div class="timing-item timing-linear">
        <h4>linear</h4>
        <div class="timing-track">
          <div class="timing-ball"></div>
        </div>
      </div>
      <div class="timing-item timing-ease-in">
        <h4>ease-in</h4>
        <div class="timing-track">
          <div class="timing-ball"></div>
        </div>
      </div>
      <div class="timing-item timing-ease-out">
        <h4>ease-out</h4>
        <div class="timing-track">
          <div class="timing-ball"></div>
        </div>
      </div>
      <div class="timing-item timing-ease-in-out">
        <h4>ease-in-out</h4>
        <div class="timing-track">
          <div class="timing-ball"></div>
        </div>
      </div>
      <div class="timing-item timing-cubic">
        <h4>cubic-bezier (bounce)</h4>
        <div class="timing-track">
          <div class="timing-ball"></div>
        </div>
      </div>
    </div>
  </div>

  <div class="section">
    <h2>Button Hover Effects</h2>
    <div class="button-grid">
      <button class="btn btn-fill">Fill Change</button>
      <button class="btn btn-outline">Outline to Fill</button>
      <button class="btn btn-scale">Scale Up</button>
      <button class="btn btn-delay">With Delay</button>
    </div>
  </div>

  <div class="section">
    <h2>Card Lift Effect</h2>
    <div class="card-grid">
      <div class="lift-card">
        <div class="lift-card-icon">🚀</div>
        <h3>Kecepatan</h3>
        <p>Website loads in milliseconds</p>
      </div>
      <div class="lift-card">
        <div class="lift-card-icon">🔒</div>
        <h3>Security</h3>
        <p>End-to-end data encryption</p>
      </div>
      <div class="lift-card">
        <div class="lift-card-icon">📱</div>
        <h3>Responsive</h3>
        <p>Looks perfect on all devices</p>
      </div>
    </div>
  </div>

  <div class="section">
    <h2>Color Change</h2>
    <div class="color-grid">
      <div class="color-box color-box-1">Change</div>
      <div class="color-box color-box-2">Change</div>
      <div class="color-box color-box-3">Change</div>
      <div class="color-box color-box-4">Change</div>
    </div>
  </div>

  <div class="section">
    <h2>Expand Animation (Accordion)</h2>
    <div class="expand-section">
      <div class="expand-item active" onclick="this.classList.toggle('active')">
        <div class="expand-header">
          What are CSS Transitions?
          <span class="expand-arrow">▼</span>
        </div>
        <div class="expand-content">
          <p>CSS Transitions allow properties to change from one value to another gradually over a specified duration, creating a smooth, natural animation.</p>
        </div>
      </div>
      <div class="expand-item" onclick="this.classList.toggle('active')">
        <div class="expand-header">
          When to use Transitions?
          <span class="expand-arrow">▼</span>
        </div>
        <div class="expand-content">
          <p>Transitions are perfect for simple state changes such as hover effects, focus states, and toggle animations. For complex animations with many keyframes, use CSS Animations.</p>
        </div>
      </div>
      <div class="expand-item" onclick="this.classList.toggle('active')">
        <div class="expand-header">
          How to control the speed?
          <span class="expand-arrow">▼</span>
        </div>
        <div class="expand-content">
          <p>Use transition-timing-function to control the speed curve. cubic-bezier() gives the most flexible control over the acceleration and deceleration of a transition.</p>
        </div>
      </div>
    </div>
  </div>

  <div class="section">
    <h2>Multi-Property Transition with Delay</h2>
    <div class="multi-prop-section">
      <div class="multi-box multi-box-1">Box 1</div>
      <div class="multi-box multi-box-2">Box 2</div>
      <div class="multi-box multi-box-3">Box 3</div>
    </div>
  </div>

</body>
</html>

Code Explanation

transition-property

This property determines which CSS properties will be transitioned. It can be a single property, multiple properties separated by commas, or all for every property that changes.

.box {
  transition-property: background;          /* one property */
  transition-property: background, color;   /* two properties */
  transition-property: all;                 /* all properties */
}

transition-duration

Determines how long a transition lasts. Its value is given in seconds (s) or milliseconds (ms).

.box {
  transition-duration: 0.3s;     /* 300 milliseconds */
  transition-duration: 500ms;    /* same as 0.5s */
  transition-duration: 0s, 0.5s; /* delay 0 for the first property, 0.5s for the second */
}

transition-timing-function

Controls the speed curve of a transition. Some built-in values:

  • ease — default. Slow at the start, fast in the middle, slow at the end
  • linear — constant speed from start to finish
  • ease-in — slow at the start, fast at the end
  • ease-out — fast at the start, slow at the end
  • ease-in-out — slow at the start and end, fast in the middle
  • cubic-bezier(x1, y1, x2, y2) — full control over the curve
.box {
  transition-timing-function: ease;  /* default */
  transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55); /* bounce effect */
}

transition-delay

Determines the delay before a transition starts. Useful for creating sequential effects.

.box {
  transition-delay: 0.2s;  /* wait 200ms before starting */
}

Shorthand transition

All transition properties can be written on a single line using the shorthand:

/* transition: property duration timing-function delay; */
.box {
  transition: all 0.3s ease 0s;
  transition: background 0.3s ease, color 0.5s ease-in-out;
  transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.1s;
}

Transitioning Multiple Properties

You can transition several properties at once with different durations and timings:

.card {
  transition:
    transform 0.3s ease,
    box-shadow 0.4s ease,
    background 0.5s ease-in-out;
}

.card:hover {
  transform: translateY(-10px);
  box-shadow: 0 10px 30px rgba(0,0,0,0.2);
  background: #667eea;
}

Cubic Bezier

cubic-bezier() lets you create custom curves. Parameters: (x1, y1, x2, y2) where x is time and y is progress. Tools like cubic-bezier.com help you visualize the curve.

/* Bounce effect */
transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);

/* Elastic effect */
transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);

Everyday Use Cases

1. CTA Buttons on a Landing Page

Call-to-Action buttons use transitions to give visual feedback on hover — shifting upward, changing color, and adding a shadow:

.cta-button {
  transition: transform 0.3s ease, background 0.3s ease, box-shadow 0.3s ease;
}

.cta-button:hover {
  transform: translateY(-3px);
  background: #764ba2;
  box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
}

2. Form Input Focus

Input fields whose border and shadow change when they receive focus — giving users a clear visual indication:

.input {
  border: 2px solid #ddd;
  transition: border-color 0.3s ease, box-shadow 0.3s ease;
}

.input:focus {
  border-color: #667eea;
  box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2);
}

3. Navbar Background Change

A transparent navbar that becomes solid when the user scrolls — the transition on the background makes the change feel natural:

.navbar {
  background: transparent;
  transition: background 0.4s ease, box-shadow 0.4s ease;
}

.navbar.scrolled {
  background: rgba(255, 255, 255, 0.95);
  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}

4. Hamburger Menu to X

A hamburger icon that morphs into an X icon when the menu opens:

.hamburger-line {
  transition: transform 0.3s ease, opacity 0.3s ease;
}

.menu.active .hamburger-line:nth-child(1) {
  transform: rotate(45deg) translate(5px, 5px);
}

.menu.active .hamburger-line:nth-child(2) {
  opacity: 0;
}

.menu.active .hamburger-line:nth-child(3) {
  transform: rotate(-45deg) translate(5px, -5px);
}

5. Tooltip Appearance

A tooltip that fades in gradually when the user hovers over an element:

.tooltip-text {
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease, visibility 0.3s ease;
}

.tooltip:hover .tooltip-text {
  opacity: 1;
  visibility: visible;
}

Conclusion

CSS transitions make property changes happen gradually over a specified duration. transition-property determines which properties are transitioned, transition-duration determines how long, transition-timing-function controls the speed curve, and transition-delay adds a waiting time. The transition shorthand makes it easy to write all properties on a single line. Transitions are best suited for simple animations triggered by state changes such as hover, focus, or class toggles. For complex, self-running animations, use CSS animations with @keyframes.