Menghidupkan antarmuka dengan animasi yang halus dan cepat: prinsip animasi GPU-friendly, CSS transitions & keyframes, Web Animations API, micro-interactions, dan menghormati prefers-reduced-motion di TokoKita

Setelah memastikan TokoKita accessible di episode 15, saatnya membuat antarmuka terasa hidup: animasi. Animasi yang baik bukan hiasan — ia memberi feedback (tombol menekan), membangun konteks (menu muncul dari arah yang logis), dan memandu perhatian. Animasi yang buruk terasa lambat dan mengganggu.
Episode 16 menggabungkan apa yang sudah kalian pelajari: performa (episode 12), UX (episode 1), dan aksesibilitas (episode 15). Aturan utamanya: animasi harus halus (60fps), bermakna, dan hormat pada preferensi pengguna.
Ini kunci yang membedakan animasi profesional dari animasi "janky": browser merender tiga properti di GPU — tanpa memicu layout/paint ulang:
transform: translate(...) scale(...) rotate(...);
opacity: 0 → 1;Hindari menganimasikan width, height, top, left, margin — semuanya memicu layout (ingat reflow di episode 2) dan pasti jank pada perangkat menengah. Kalau harus menggeser elemen, pakai transform: translate, bukan top/left.
.btn-add {
transition: transform 0.15s ease, opacity 0.15s ease;
}
.btn-add:active {
transform: scale(0.96);
}Transition — animasi dari satu kondisi ke kondisi lain (hover, active):
.btn-add:hover {
transform: translateY(-2px);
}Keyframes — animasi multi-tahap:
@keyframes shimmer {
0% { background-position: -400px 0; }
100% { background-position: 400px 0; }
}
.skeleton {
background: linear-gradient(90deg, #eee 25%, #ddd 37%, #eee 63%);
background-size: 800px 100%;
animation: shimmer 1.4s infinite;
}Skeleton loading adalah micro-interaction yang "mengisi" waktu tunggu fetch (episode 11) tanpa memberi kesan halaman macet.
Saat animasi butuh kontrol dari JavaScript (play/pause, reverse, sinkron dengan state), gunakan Web Animations API (WAAPI) — alternatif native yang ringan, tanpa library:
export function animateCartItem(el: HTMLElement) {
el.animate(
[
{ transform: "scale(0.8)", opacity: 0 },
{ transform: "scale(1)", opacity: 1 },
],
{ duration: 250, easing: "ease-out" }
);
}element.animate() mengembalikan animasi yang bisa di-cancel() dan reverse() — ideal untuk micro-interaction tanpa menambah dependency.
Micro-interactions adalah umpan balik kecil pada aksi pengguna — dan inilah "rasa" sebuah produk:
| Aksi | Micro-interaction |
|---|---|
| Tombol ditekan | Scale mengecil lalu kembali |
| Item ditambahkan | Badge keranjang "melompat" |
| Form salah | Input bergetar halus + pesan |
| Toggle | Knob bergeser mulus |
| Data dimuat | Skeleton shimmer |
Durasi ideal: 150-300ms. Lebih cepat terasa menyentak, lebih lambat terasa lelet. Konsistenkan easing (mis. cubic-bezier(0.2, 0, 0, 1)) di seluruh aplikasi — bisa disimpan sebagai design token (episode 10).
Banyak pengguna (migrain, gangguan vestibular) membutuhkan animasi dikurangi. CSS menyediakan media query khusus:
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}Important
prefers-reduced-motion adalah syarat aksesibilitas, bukan saran — sama seriusnya dengan alt pada gambar (episode 3 dan 15). Animasi yang mengabaikannya berpotensi membuat pengguna sakit. Selalu sediakan fallback statis.
Rancang tiga micro-interaction untuk alur utama:
0.96 saat ditekan; badge keranjang bounce via WAAPI.isLoading dari TanStack Query (episode 11).opacity + transform), diikuti prefers-reduced-motion.import { useEffect, useRef } from "react";
export function CartBadge({ count }: { count: number }) {
const ref = useRef<HTMLSpanElement>(null);
useEffect(() => {
if (count > 0) ref.current?.animate(
[{ transform: "scale(1)" }, { transform: "scale(1.4)" }, { transform: "scale(1)" }],
{ duration: 300, easing: "ease-in-out" }
);
}, [count]);
return <span ref={ref} className="cart-badge">{count}</span>;
}| Kesalahan | Dampak | Perbaikan |
|---|---|---|
Animasi width/top/left | Jank & reflow berulang | transform + opacity |
Durasi 1s+ | Terasa lambat | 150-300ms |
| Animasi tak berhenti | Mengganggu & boros baterai | Sesuai konteks, prefers-reduced-motion |
| Semua elemen bergerak | "Huru-hara visual" | Satu fokus gerakan per waktu |
| Animasi saat halaman load | LCP/CLS buruk | Animasi setelah render stabil |
Inti yang harus dibawa pulang:
transform dan opacity — keduanya berjalan di GPU tanpa reflow.prefers-reduced-motion — ini syarat aksesibilitas.Di episode 17 selanjutnya kita menaikkan skala arsitektur: micro-frontends & module federation — memecah aplikasi besar menjadi beberapa tim yang deploy secara independen, pendekatan yang makin matang di 2026. Sampai jumpa di episode 17!