Web Development

CSS Animations & Transitions: Panduan Lengkap

Pelajari CSS Animations dan Transitions secara mendalam โ€” keyframes, transition properties, 2D/3D transforms, timing functions, animation sequencing, dan teknik optimasi performa animasi untuk web yang smooth

1. Pengenalan Animasi CSS

CSS menyediakan tiga cara utama untuk membuat gerakan: Transitions (perubahan halus antar state), Transforms (perubahan bentuk/posisi tanpa animasi), dan Animations (animasi berulang atau kompleks menggunakan keyframes). Ketiganya bisa digabungkan untuk menciptakan pengalaman visual yang menarik.

Kapan Menggunakan Apa?

Teknik Kapan Digunakan Contoh
TransitionPerubahan state sederhana (hover, focus, click)Button hover, dropdown show/hide
TransformUbah posisi, rotasi, skala (tanpa trigger)Icon rotate, card scale on hover
AnimationGerakan kompleks, looping, multi-stepLoading spinner, entrance animation

Ringkasan Properti

Kategori Properti
Transitiontransition-property, transition-duration, transition-timing-function, transition-delay
Transformtransform, transform-origin, transform-style, perspective
Animationanimation-name, animation-duration, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, animation-fill-mode, animation-play-state

2. CSS Transitions

Transition memungkinkan perubahan properti CSS terjadi secara halus selama durasi tertentu. Transitions di-trigger saat properti berubah (melalui :hover, class change, JavaScript, dll).

Dasar Transition

/* Shorthand: property duration timing-function delay */
.btn {
  background-color: #3b82f6;
  color: white;
  padding: 12px 24px;
  border: none;
  border-radius: 8px;
  cursor: pointer;

  /* Transisi untuk semua properti yang berubah */
  transition: all 0.3s ease;
}

.btn:hover {
  background-color: #2563eb;
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
}

/* Longhand (lebih kontrol) */
.card {
  transition-property: transform, box-shadow;
  transition-duration: 0.3s, 0.5s;
  transition-timing-function: ease-out, ease-in-out;
  transition-delay: 0s, 0.1s;
}

Transition pada Berbagai Properti

/* Transition warna */
.link {
  color: #3b82f6;
  text-decoration: none;
  transition: color 0.2s ease;
}
.link:hover { color: #1d4ed8; }

/* Transition ukuran */
.avatar {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  transition: transform 0.3s ease;
}
.avatar:hover { transform: scale(1.1); }

/* Transition opacity */
.tooltip {
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease, visibility 0.3s;
}
.tooltip.visible {
  opacity: 1;
  visibility: visible;
}

/* Transition height (perlu max-height trick) */
.accordion-content {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.4s ease;
}
.accordion-content.open {
  max-height: 500px;  /* Nilai harus cukup besar */
}

/* Transition untuk border-radius (morph effect) */
.shape {
  width: 100px;
  height: 100px;
  background: #8b5cf6;
  border-radius: 10px;
  transition: border-radius 0.5s ease;
}
.shape:hover {
  border-radius: 50%;  /* Kotak โ†’ Lingkaran */
}
๐Ÿ’ก Tips Performance

Untuk performa terbaik, animasikan hanya transform dan opacity. Kedua properti ini bisa di-handle oleh GPU tanpa memicu repaint/reflow. Hindari animasi height, width, margin, padding secara langsung.

3. Timing Functions

Timing function menentukan bagaimana kecepatan animasi berubah selama durasinya. Ini sangat mempengaruhi "feel" dari animasi.

Keyword Timing Functions

Keyword Cubic Bezier Karakter Cocok Untuk
ease(0.25, 0.1, 0.25, 1.0)Mulai lambat, cepat di tengah, lambat di akhirUmum (default)
ease-in(0.42, 0, 1.0, 1.0)Mulai lambat, makin cepatElemen masuk
ease-out(0, 0, 0.58, 1.0)Mulai cepat, makin lambatElemen keluar
ease-in-out(0.42, 0, 0.58, 1.0)Lambat di awal dan akhirTransisi halus
linear(0, 0, 1.0, 1.0)KonstanProgress bar, loading
step-startโ€”Langsung ke akhirText typewriter effect
step-endโ€”Tetap di awal, lalu loncatFrame-by-frame animation

Custom Cubic Bezier

/* Custom cubic-bezier(x1, y1, x2, y2) */
.bouncy {
  transition: transform 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
  /* Efek "bounce" โ€” overshoot lalu settle */
}
.bouncy:hover { transform: scale(1.1); }

/* Smooth masuk */
.smooth-enter {
  transition: opacity 0.4s cubic-bezier(0.0, 0.0, 0.2, 1);
}

/* Quick keluar */
.quick-exit {
  transition: opacity 0.2s cubic-bezier(0.4, 0.0, 1, 1);
}

/* iOS-like spring animation */
.ios-spring {
  transition: transform 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}

/* Swoop in dari kiri */
.swoop {
  transition: transform 0.5s cubic-bezier(0.55, 0.085, 0.68, 0.53);
  transform: translateX(-100%);
}
.swoop.visible {
  transform: translateX(0);
}

/* CSS Variable untuk timing function */
:root {
  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-back: cubic-bezier(0.6, -0.28, 0.735, 0.045);
  --ease-out-back: cubic-bezier(0.175, 0.885, 0.32, 1.275);
}

.element {
  transition: transform 0.4s var(--ease-out-expo);
}
Diagram: Timing Function Curves
  Kecepatan
  โ†‘
  โ”‚    โ•ฑโ”€โ”€โ”€ linear โ”€โ”€โ”€โ”€โ”€โ”€
  โ”‚   โ•ฑ
  โ”‚  โ•ฑ     โ•ญโ”€โ”€ ease โ”€โ”€โ•ฎ
  โ”‚ โ•ฑ     โ•ฑ             โ•ฒ
  โ”‚โ•ฑ     โ•ฑ               โ•ฒ
  โ”œโ”€โ”€โ”€โ”€ โ•ฑโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฒโ”€โ”€โ†’ Waktu
  โ”‚    ease-in    ease-out
  โ”‚    (cepat di akhir)  (cepat di awal)
  โ”‚
  โ”‚    โ•ญโ”€โ”€ ease-in-out โ”€โ”€โ”€โ•ฎ
  โ”‚   โ•ฑ                     โ•ฒ
  โ”‚  โ•ฑ                       โ•ฒ
  โ”‚ โ•ฑ                         โ•ฒ
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ†’ Waktu
  
  Cubic bezier: (x1, y1, x2, y2)
  x = waktu, y = progress

4. 2D Transforms

Transform memungkinkan kamu memanipulasi elemen dalam 2D dan 3D: translasi (geser), rotasi, skala, dan skew. Transform tidak mempengaruhi layout dokumen โ€” elemen tetap mempertahankan ruang aslinya.

Fungsi Transform 2D

Fungsi Deskripsi Contoh
translate(x, y)Geser elementranslate(20px, -10px)
translateX(x)Geser horizontaltranslateX(100%)
translateY(y)Geser vertikaltranslateY(-50%)
rotate(angle)Putar elemenrotate(45deg)
scale(x, y)Ubah skalascale(1.5, 1.5)
scaleX(x)Skala horizontalscaleX(-1) โ† mirror
scaleY(y)Skala vertikalscaleY(0.5)
skew(x, y)Miringkan elemenskew(10deg, 5deg)
matrix(a,b,c,d,e,f)Kombinasi semua 2D transformmatrix(1,0,0,1,20,30)

Contoh Transform 2D

/* Hover effects dengan transform */
.card {
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
  transform: translateY(-8px);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
}

/* Rotate icon pada hover */
.icon-arrow {
  display: inline-block;
  transition: transform 0.3s ease;
}
a:hover .icon-arrow {
  transform: translateX(4px);
}

/* Centering dengan translate */
.centered {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

/* Flip card */
.flip-card {
  perspective: 1000px;
}
.flip-card-inner {
  transition: transform 0.6s;
  transform-style: preserve-3d;
}
.flip-card:hover .flip-card-inner {
  transform: rotateY(180deg);
}
.flip-card-back {
  transform: rotateY(180deg);
}

/* Multiple transforms โ€” ORDER MATTERS! */
.element {
  /* Rotasi dulu, baru geser (rotasi mengubah sumbu) */
  transform: rotate(45deg) translateX(100px);
  /* โ‰  translateX(100px) rotate(45deg) โ† Geser dulu, baru rotasi */
}

/* Skew effect untuk pricing table */
.pricing-highlight {
  transform: skewX(-5deg);
  transition: transform 0.3s ease;
}
.pricing-highlight:hover {
  transform: skewX(0deg);
}

Transform Origin

/* transform-origin mengubah titik pusat transform */
.element {
  /* Default: center center (50% 50%) */
  transform-origin: center center;

  /* Bisa pakai keyword, px, atau % */
  transform-origin: top left;        /* 0% 0% */
  transform-origin: bottom right;    /* 100% 100% */
  transform-origin: 20px 50px;
  transform-origin: 50% 0;           /* Tengah atas */
}

/* Contoh: Menu dropdown dari pojok kanan atas */
.dropdown {
  transform-origin: top right;
  transform: scale(0);
  transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.dropdown.open {
  transform: scale(1);
}

/* Contoh: Kartu yang flip dari kiri */
.flip-left {
  transform-origin: left center;
}

/* Contoh: Jam analog (jarum dari bawah) */
.clock-hand {
  transform-origin: bottom center;
  transform: rotate(0deg);
}

5. 3D Transforms

3D transforms memungkinkan manipulasi elemen dalam ruang tiga dimensi. Kamu memerlukan perspective untuk memberikan efek kedalaman.

Fungsi 3D

/* 3D translate */
.element-3d {
  transform: translate3d(20px, 30px, 50px);
  transform: translateZ(100px);  /* Mendekati/menjauhi penonton */
}

/* 3D rotate */
.element-rotate {
  transform: rotateX(45deg);    /* Putar horizontal (atas-bawah) */
  transform: rotateY(45deg);    /* Putar vertikal (kiri-kanan) */
  transform: rotateZ(45deg);    /* Putar di sumbu Z (sama dengan rotate) */
  transform: rotate3d(1, 1, 0, 45deg);  /* Putar di sumbu custom */
}

/* 3D scale */
.element-scale {
  transform: scale3d(1.5, 1.5, 1.5);
  transform: scaleZ(2);
}

/* Perspective โ€” memberikan efek kedalaman */
.container {
  perspective: 1000px;  /* Semakin kecil, semakin dramatis */
}
.element {
  transform: rotateY(30deg);
}

/* Atau perspective() sebagai fungsi transform */
.element {
  transform: perspective(1000px) rotateY(30deg);
}

3D Card Flip

<div class="flip-container">
  <div class="flipper">
    <div class="front">
      <h3>Depan</h3>
      <p>Klik untuk membalik</p>
    </div>
    <div class="back">
      <h3>Belakang</h3>
      <p>Konten tersembunyi!</p>
    </div>
  </div>
</div>

<style>
.flip-container {
  perspective: 1000px;
  width: 300px;
  height: 200px;
}

.flipper {
  position: relative;
  width: 100%;
  height: 100%;
  transition: transform 0.6s;
  transform-style: preserve-3d;
}

.flip-container:hover .flipper {
  transform: rotateY(180deg);
}

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

.front {
  background: linear-gradient(135deg, #667eea, #764ba2);
  color: white;
}

.back {
  background: linear-gradient(135deg, #f093fb, #f5576c);
  color: white;
  transform: rotateY(180deg);
}
</style>

3D Cube

.cube-container {
  perspective: 800px;
  width: 200px;
  height: 200px;
  margin: 100px auto;
}

.cube {
  width: 100%;
  height: 100%;
  position: relative;
  transform-style: preserve-3d;
  animation: rotateCube 8s infinite linear;
}

.cube-face {
  position: absolute;
  width: 200px;
  height: 200px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2rem;
  font-weight: bold;
  border: 2px solid rgba(255,255,255,0.3);
  background: rgba(99, 102, 241, 0.7);
  color: white;
}

.front  { transform: translateZ(100px); }
.back   { transform: rotateY(180deg) translateZ(100px); }
.right  { transform: rotateY(90deg) translateZ(100px); }
.left   { transform: rotateY(-90deg) translateZ(100px); }
.top    { transform: rotateX(90deg) translateZ(100px); }
.bottom { transform: rotateX(-90deg) translateZ(100px); }

@keyframes rotateCube {
  0%   { transform: rotateX(0deg) rotateY(0deg); }
  100% { transform: rotateX(360deg) rotateY(360deg); }
}

6. CSS Keyframes Animations

Keyframes memungkinkan kamu mendefinisikan animasi multi-step yang kompleks. Berbeda dengan transition yang hanya Aโ†’B, keyframes bisa Aโ†’Bโ†’Cโ†’Dโ†’A.

Sintaksis Dasar

/* Deklarasi keyframes */
@keyframes fadeInUp {
  0% {
    opacity: 0;
    transform: translateY(30px);
  }
  100% {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Menggunakan animasi */
.element {
  animation-name: fadeInUp;
  animation-duration: 0.6s;
  animation-timing-function: ease-out;
  animation-delay: 0.2s;
  animation-fill-mode: forwards;
  /* Shorthand: name duration timing delay count direction fill play */
  animation: fadeInUp 0.6s ease-out 0.2s forwards;
}

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

/* Multi-step keyframes */
@keyframes bounce {
  0%, 20%, 53%, 80%, 100% {
    animation-timing-function: ease;
    transform: translateY(0);
  }
  40%, 43% {
    animation-timing-function: ease;
    transform: translateY(-30px);
  }
  70% {
    animation-timing-function: ease;
    transform: translateY(-15px);
  }
  90% {
    transform: translateY(-4px);
  }
}

Contoh Keyframes Lengkap

/* Pulse effect */
@keyframes pulse {
  0% { transform: scale(1); }
  50% { transform: scale(1.05); }
  100% { transform: scale(1); }
}
.pulse { animation: pulse 2s infinite ease-in-out; }

/* Shake effect */
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  10%, 30%, 50%, 70%, 90% { transform: translateX(-8px); }
  20%, 40%, 60%, 80% { transform: translateX(8px); }
}
.shake { animation: shake 0.8s ease-in-out; }

/* Spin */
@keyframes spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}
.spinner { animation: spin 1s linear infinite; }

/* Gradient animation */
@keyframes gradientShift {
  0% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}
.gradient-bg {
  background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
  background-size: 400% 400%;
  animation: gradientShift 15s ease infinite;
}

/* Typewriter effect */
@keyframes typing {
  from { width: 0; }
  to { width: 100%; }
}
@keyframes blink {
  50% { border-color: transparent; }
}
.typewriter {
  overflow: hidden;
  white-space: nowrap;
  border-right: 3px solid #3b82f6;
  width: fit-content;
  animation:
    typing 3.5s steps(40, end),
    blink 0.75s step-end infinite;
}

/* Floating effect */
@keyframes float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-20px); }
}
.floating { animation: float 3s ease-in-out infinite; }

/* Ripple effect */
@keyframes ripple {
  0% {
    transform: scale(0);
    opacity: 1;
  }
  100% {
    transform: scale(4);
    opacity: 0;
  }
}

7. Animation Properties Detail

Semua Properti Animation

Properti Nilai Default Fungsi
animation-namenama @keyframesnoneKeyframes yang digunakan
animation-durationwaktu (s/ms)0sDurasi satu siklus
animation-timing-functionease, linear, cubic-bezier(), steps()easeKurva kecepatan
animation-delaywaktu (s/ms)0sWaktu tunggu sebelum mulai
animation-iteration-countangka, infinite1Jumlah pengulangan
animation-directionnormal, reverse, alternate, alternate-reversenormalArah animasi
animation-fill-modenone, forwards, backwards, bothnoneState sebelum/sesudah animasi
animation-play-staterunning, pausedrunningPlay atau pause
animation-compositionreplace, add, accumulatereplaceCara menggabungkan animasi

Animation Direction

/* normal: 0% โ†’ 100%, 0% โ†’ 100%, ... */
.anim-normal { animation-direction: normal; }

/* reverse: 100% โ†’ 0%, 100% โ†’ 0%, ... */
.anim-reverse { animation-direction: reverse; }

/* alternate: 0% โ†’ 100%, 100% โ†’ 0%, 0% โ†’ 100%, ... */
.anim-alternate { animation-direction: alternate; }

/* alternate-reverse: 100% โ†’ 0%, 0% โ†’ 100%, ... */
.anim-alt-reverse { animation-direction: alternate-reverse; }

/* Contoh: Loading bar yang bolak-balik */
@keyframes loadBar {
  0% { width: 0%; }
  100% { width: 100%; }
}
.loader {
  height: 4px;
  background: #3b82f6;
  animation: loadBar 1.5s ease-in-out infinite alternate;
}

Animation Fill Mode

/* none: Element kembali ke state awal setelah animasi */
/* forwards: Element mempertahankan state akhir animasi */
/* backwards: Element menerapkan state awal animasi saat delay */
/* both: forwards + backwards */

/* Contoh: Elemen muncul dan tetap terlihat */
.fade-in-element {
  opacity: 0;
  animation: fadeIn 0.5s ease-out forwards;
  /* forwards: setelah animasi selesai, opacity tetap 1 */
}

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

/* Staggered animation dengan delay */
.stagger-item {
  opacity: 0;
  animation: fadeInUp 0.5s ease-out both;
  /* both: langsung apply state awal (opacity:0) saat delay */
}
.stagger-item:nth-child(1) { animation-delay: 0.1s; }
.stagger-item:nth-child(2) { animation-delay: 0.2s; }
.stagger-item:nth-child(3) { animation-delay: 0.3s; }
.stagger-item:nth-child(4) { animation-delay: 0.4s; }
.stagger-item:nth-child(5) { animation-delay: 0.5s; }

/* JavaScript: play/pause animation */
<script>
const elem = document.querySelector('.animated');
elem.addEventListener('click', () => {
  if (elem.style.animationPlayState === 'paused') {
    elem.style.animationPlayState = 'running';
  } else {
    elem.style.animationPlayState = 'paused';
  }
});
</script>

Multiple Animations

/* Gabungkan beberapa animasi dengan koma */
.complex-animation {
  animation:
    fadeInUp 0.6s ease-out forwards,
    pulse 2s ease-in-out 0.6s infinite;
  /* fadeInUp dulu (0.6s), lalu pulse mulai setelah delay 0.6s */
}

/* Animasi berbeda untuk property berbeda */
.card-entrance {
  animation:
    slideIn 0.5s ease-out,
    colorShift 3s ease-in-out infinite;
}

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

@keyframes colorShift {
  0%, 100% { border-color: #3b82f6; }
  33% { border-color: #8b5cf6; }
  66% { border-color: #ec4899; }
}

8. Pola Animasi Populer

Scroll-Triggered Animation (dengan CSS)

/* Entrance animation saat scroll (dengan Intersection Observer) */
.animate-on-scroll {
  opacity: 0;
  transform: translateY(40px);
  transition: opacity 0.6s ease, transform 0.6s ease;
}

.animate-on-scroll.visible {
  opacity: 1;
  transform: translateY(0);
}

/* CSS-only dengan animation-timeline (Experimental) */
@keyframes scrollFadeIn {
  from { opacity: 0; transform: translateY(50px); }
  to { opacity: 1; transform: translateY(0); }
}

.scroll-animated {
  animation: scrollFadeIn linear both;
  animation-timeline: view();
  animation-range: entry 0% entry 100%;
}

Loading Skeleton

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

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

.skeleton-text {
  height: 16px;
  margin-bottom: 12px;
  width: 80%;
}

.skeleton-avatar {
  width: 48px;
  height: 48px;
  border-radius: 50%;
}

/* Dark mode skeleton */
[data-theme="dark"] .skeleton {
  background: linear-gradient(
    90deg,
    #1a1a2e 25%,
    #16213e 50%,
    #1a1a2e 75%
  );
  background-size: 200% 100%;
}

Modal Entrance/Exit

.modal-overlay {
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease, visibility 0.3s;
}

.modal-overlay.active {
  opacity: 1;
  visibility: visible;
}

.modal {
  transform: scale(0.8) translateY(20px);
  opacity: 0;
  transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275),
              opacity 0.3s ease;
}

.modal-overlay.active .modal {
  transform: scale(1) translateY(0);
  opacity: 1;
}

/* Stagger children animation */
.modal-overlay.active .modal-content > * {
  animation: fadeInUp 0.4s ease forwards;
}
.modal-overlay.active .modal-content > *:nth-child(1) { animation-delay: 0.1s; }
.modal-overlay.active .modal-content > *:nth-child(2) { animation-delay: 0.2s; }
.modal-overlay.active .modal-content > *:nth-child(3) { animation-delay: 0.3s; }

Notification Toast

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

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

.toast {
  position: fixed;
  top: 20px;
  right: 20px;
  padding: 16px 24px;
  border-radius: 12px;
  background: #10b981;
  color: white;
  animation: slideInRight 0.4s ease forwards;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
}

.toast.hiding {
  animation: slideOutRight 0.3s ease forwards;
}

9. Optimasi Performa Animasi

Animasi yang buruk dapat menyebabkan jank (frame drop) dan menguras baterai. Berikut panduan untuk animasi yang smooth dan efisien.

Rendering Pipeline Browser

Diagram: Browser Rendering Pipeline
  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ”‚  STYLE  โ”‚ โ†’ โ”‚ LAYOUT  โ”‚ โ†’ โ”‚  PAINT  โ”‚ โ†’ โ”‚COMPOSITEโ”‚
  โ”‚ (Recalc)โ”‚   โ”‚(Reflow) โ”‚   โ”‚(Repaint)โ”‚   โ”‚ (GPU)   โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ†‘              โ†‘             โ†‘             โ†‘
  Color, font    Width, height   Background,    Transform,
  display,       margin, padding  box-shadow     opacity
  visibility     position         border
  
  โ† MAHAL โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ MURAH โ†’
  
  โœ… HANYA composite: transform, opacity (16ms budget)
  โš ๏ธ + paint: warna, shadow
  โŒ + layout: ukuran, posisi (paling mahal!)

Tips Performa

โœ… DO โŒ DON'T
Animasi transformAnimasi left, top, margin
Animasi opacityAnimasi width, height
Gunakan will-change dengan bijakOveruse will-change pada banyak elemen
GPU-accelerated propertiesAnimasi filter tanpa perlu
CSS animations untuk simpleJavaScript animations untuk simple
requestAnimationFrame untuk JSsetInterval untuk animasi JS

will-change Property

/* will-change: beritahu browser bahwa properti akan berubah */
.animated-element {
  /* Tambahkan SEBELUM animasi dimulai */
  will-change: transform, opacity;
  /* Browser akan memisahkan layer ke GPU */
}

/* โŒ JANGAN: Terlalu banyak will-change */
.too-many {
  will-change: transform, opacity, left, top, width, height;
  /* Ini kontra-produktif! Habis memori GPU */
}

/* โœ… DO: Tambah via JS sebelum animasi, hapus setelah */
<script>
const el = document.querySelector('.animate-me');
el.addEventListener('mouseenter', () => {
  el.style.willChange = 'transform';
});
el.addEventListener('transitionend', () => {
  el.style.willChange = 'auto';
});
</script>

/* Promote to GPU layer dengan translateZ(0) */
.gpu-accelerated {
  transform: translateZ(0);
  /* Atau */
  transform: translate3d(0, 0, 0);
  /* Ini memaksa browser membuat layer terpisah */
}

Contoh: Animasi yang Efisien

/* โœ… EFEKTIF: Menggunakan transform + opacity */
.smooth-card {
  will-change: transform, opacity;
  transform: translateY(0);
  opacity: 1;
  transition: transform 0.3s ease, opacity 0.3s ease;
}
.smooth-card:hover {
  transform: translateY(-8px) scale(1.02);
  opacity: 0.95;
}

/* โŒ TIDAK EFEKTIF: Trigger layout + repaint */
.janky-card {
  margin-top: 0;
  transition: margin-top 0.3s ease;
}
.janky-card:hover {
  margin-top: -8px;  /* Triggers layout! */
}

/* โœ… Menggunakan transformไปฃๆ›ฟmargin/padding */
/* Bad: padding animation */
.element { padding: 20px; transition: padding 0.3s; }
.element:hover { padding: 30px; }

/* Good: scale animation */
.element { transform: scale(1); transition: transform 0.3s; }
.element:hover { transform: scale(1.05); }

10. Accessibility: prefers-reduced-motion

Beberapa pengguna memiliki gangguan vestibular (sensitif terhadap gerakan) atau epilepsi photosensitive. prefers-reduced-motion adalah media query yang mendeteksi preferensi ini.

/* Deteksi preferensi pengguna */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Alternatif: Ganti animasi dengan transisi sederhana */
@media (prefers-reduced-motion: reduce) {
  .fade-in-element {
    animation: none;
    opacity: 1;
    transform: none;
  }

  .parallax-section {
    transform: none !important;
  }

  .loading-spinner {
    animation: none;
    /* Tampilkan indicator statis */
    border: 3px solid #3b82f6;
  }
}

/* Progressive: Default animasi, kurangi jika diminta */
.animated {
  animation: bounce 2s ease infinite;
}

@media (prefers-reduced-motion: reduce) {
  .animated {
    animation: none;
    /* Atau ganti dengan animasi yang lebih halus */
    animation: subtlePulse 3s ease infinite;
  }
}

@keyframes subtlePulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.7; }
}
โš ๏ธ Aksesibilitas Animasi

Selalu berikan alternatif untuk pengguna yang sensitif terhadap gerakan. Gunakan @media (prefers-reduced-motion: reduce) untuk menonaktifkan atau mengurangi animasi. Ini bukan hanya best practice โ€” ini merupakan bagian dari WCAG 2.3.3 (Animation from Interactions).

11. Quiz Pemahaman

Uji pemahamanmu tentang CSS Animations & Transitions:

Pertanyaan 1: Properti CSS apa yang paling efisien untuk animasi dari segi performa?

a) width dan height
b) margin dan padding
c) transform dan opacity
d) left dan top

Pertanyaan 2: Apa fungsi animation-fill-mode: forwards?

a) Memutar animasi ke depan
b) Mempertahankan state akhir animasi setelah selesai
c) Memutar animasi berulang-ulang
d) Menunda animasi

Pertanyaan 3: Apa bedanya transition dan animation?

a) Tidak ada perbedaan
b) Transition butuh trigger perubahan state, animation bisa berjalan otomatis dengan keyframes
c) Animation hanya untuk 3D
d) Transition lebih cepat dari animation

Pertanyaan 4: Media query apa yang mendeteksi preferensi pengguna untuk mengurangi animasi?

a) @media (animation: none)
b) @media (prefers-reduced-motion: reduce)
c) @media (motion: disabled)
d) @media (accessibility: true)

Pertanyaan 5: transform-origin: top left dan rotate(45deg) akan menghasilkan apa?

a) Elemen berputar 45ยฐ dari pusat
b) Elemen berputar 45ยฐ dari pojok kiri atas
c) Elemen geser ke kiri 45px
d) Elemen miring 45ยฐ ke kiri
๐Ÿ” Zoom
100%
๐ŸŽจ Tema