Kenapa ini penting
Gerakan yang halus membuat antarmuka terasa hidup dan memberi umpan balik: tombol yang berubah warna perlahan saat hover, menu yang muncul dengan meluncur, ikon loading yang berputar. Tanpa gerakan, perubahan terasa kaku dan "meloncat". Tapi gerakan berlebihan juga mengganggu, bahkan bisa membuat sebagian orang pusing atau mual (gangguan vestibular).
Tailwind menyediakan utility transisi, durasi, kurva percepatan (easing), animasi siap pakai, cara membuat keyframes kustom di @theme, serta variant motion-reduce: dan motion-safe: untuk menghormati pengguna yang memilih mengurangi gerakan di pengaturan sistemnya.
Konsep inti
Transition
Transisi menghaluskan perubahan antar dua keadaan (misalnya normal ke hover).
transitionmentransisikan properti umum (warna, opacity, shadow, transform, dll).transition-colors,transition-opacity,transition-transform,transition-shadow,transition-alluntuk properti tertentu.transition-nonemematikan transisi.
Memilih properti spesifik (misalnya transition-colors) sedikit lebih ringan untuk browser daripada transition-all.
Duration, ease, delay
duration-150,duration-300,duration-700dalam milidetik (angka bebas di v4).ease-linear,ease-in,ease-out,ease-in-out. Untuk elemen yang masuk layar,ease-outterasa paling alami (cepat di awal, melambat di akhir). Kurva kustom:ease-[cubic-bezier(0.34,1.56,0.64,1)]atau token--ease-*di@theme.delay-100,delay-300untuk menunda awal transisi.
Panduan praktis: interaksi kecil (hover tombol) 150-200ms; panel atau modal 200-300ms. Lebih dari 500ms biasanya terasa lambat.
Transform
scale-95, hover:scale-105, -translate-y-1, rotate-45, rotate-180, skew-x-6. Di v4 transform langsung bisa dipakai tanpa class transform. Animasi pada transform dan opacity adalah yang paling mulus karena tidak memicu perhitungan ulang layout.
Animasi bawaan
animate-spinuntuk ikon loading.animate-pinguntuk efek riak/notifikasi.animate-pulseuntuk skeleton loader.animate-bounceuntuk panah "gulir ke bawah".
Keyframes kustom di @theme
Di v4, animasi kustom didefinisikan dengan variabel --animate-* dan @keyframes di dalam blok @theme. Variabel --animate-muncul menghasilkan class animate-muncul.
@starting-style lewat variant starting:
Untuk animasi saat elemen pertama kali muncul (misalnya popover), v4 menyediakan variant starting: yang memakai @starting-style CSS. Contoh: transition opacity-100 starting:opacity-0.
Menghormati preferensi gerak
motion-reduce:aktif saat pengguna mengaktifkan "kurangi gerakan" di sistem operasi.motion-safe:aktif hanya kalau pengguna tidak meminta pengurangan gerak.
Analogi: motion-safe: itu seperti bertanya dulu "boleh saya putar musik?" sebelum menyalakannya. Animasi dekoratif besar sebaiknya selalu dibungkus motion-safe:.
Contoh kode
/* src/style.css */
@import "tailwindcss";
@theme {
/* Kurva percepatan kustom → ease-pegas */
--ease-pegas: cubic-bezier(0.34, 1.56, 0.64, 1);
/* Animasi kustom → animate-muncul */
--animate-muncul: muncul 0.4s var(--ease-pegas) both;
@keyframes muncul {
from {
opacity: 0;
transform: translateY(12px) scale(0.97);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
/* Animasi kustom → animate-goyang */
--animate-goyang: goyang 0.5s ease-in-out;
@keyframes goyang {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-6px); }
75% { transform: translateX(6px); }
}
}
<!-- Tombol dengan transisi warna dan efek tekan -->
<button class="cursor-pointer rounded-lg bg-indigo-600 px-5 py-2.5 text-white transition duration-150 ease-out hover:bg-indigo-700 active:scale-95">
Simpan
</button>
<!-- Kartu yang terangkat saat hover, hanya kalau pengguna tidak membatasi gerak -->
<article class="rounded-xl bg-white p-6 shadow-sm transition duration-300 motion-safe:hover:-translate-y-1 hover:shadow-lg">
Kartu interaktif
</article>
<!-- Ikon loading berputar; berhenti berputar untuk motion-reduce -->
<button disabled class="inline-flex items-center gap-2 rounded-lg bg-gray-900 px-4 py-2 text-white">
<svg class="size-4 animate-spin motion-reduce:animate-none" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" class="opacity-25" />
<path d="M4 12a8 8 0 018-8" stroke="currentColor" stroke-width="4" class="opacity-75" />
</svg>
Memproses...
</button>
<!-- Skeleton loader dengan pulse -->
<div class="max-w-sm animate-pulse space-y-3">
<div class="h-40 rounded-lg bg-gray-200"></div>
<div class="h-4 w-3/4 rounded bg-gray-200"></div>
<div class="h-4 w-1/2 rounded bg-gray-200"></div>
</div>
<!-- Titik notifikasi dengan ping -->
<span class="relative flex size-3">
<span class="absolute inline-flex size-full animate-ping rounded-full bg-red-400 opacity-75"></span>
<span class="relative inline-flex size-3 rounded-full bg-red-500"></span>
</span>
<!-- Animasi kustom dari @theme -->
<div class="motion-safe:animate-muncul rounded-xl bg-white p-6 shadow">Saya muncul dengan pegas</div>
<!-- Panah FAQ yang berputar saat <details markdown="1"> terbuka -->
<details class="group rounded-lg border border-gray-200 p-4">
<summary class="flex cursor-pointer list-none items-center justify-between font-medium">
Apakah ada sertifikat?
<span class="transition-transform duration-200 group-open:rotate-180">▾</span>
</summary>
<p class="mt-3 text-gray-600">Ya, setelah menyelesaikan proyek akhir.</p>
</details>
// Memicu animasi goyang saat input salah, lalu melepasnya agar bisa diulang
const input = document.querySelector("#kodeOtp");
function tandaiSalah() {
input.classList.remove("animate-goyang");
void input.offsetWidth; // paksa reflow agar animasi bisa diputar ulang
input.classList.add("animate-goyang", "border-red-500");
}
// tandaiSalah() → input bergoyang kiri-kanan selama 0,5 detik
Kesalahan umum
- Lupa
transition, sehinggahover:langsung meloncat. Tambahkantransitiondi keadaan normal, bukan hanya dihover:. - Menganimasikan
width,height, atautop. Ini memicu layout ulang dan terasa patah-patah. Gunakantransformdanopacity. - Durasi terlalu lama (1000ms untuk hover tombol). Pengguna merasa aplikasi lambat. Gunakan 150-300ms.
- Mengabaikan
motion-reduce. Animasi besar yang terus berulang bisa membuat sebagian orang tidak nyaman. Bungkus denganmotion-safe:atau matikan denganmotion-reduce:animate-none. - Menulis
@keyframesdi luar@themelalu bingung kenapaanimate-*tidak ada. Utilityanimate-*berasal dari variabel--animate-*di@theme; keyframes-nya juga sebaiknya diletakkan di dalam@themeagar ikut dikeluarkan saat dipakai.
Latihan
- Mudah, Buat tautan yang warnanya berubah dari
gray-600keindigo-600saat hover dengan transisi 200ms. - Sedang, Buat tombol "Suka" yang membesar ke
scale-110saat hover dan mengecil kescale-90saat ditekan, dengan kurvaease-out, dan tidak membesar kalau pengguna memilih kurangi gerakan. - Menantang, Buat animasi kustom
animate-geser-masuk(elemen masuk dari kiri 24px sambil memudar masuk, 300ms) di@theme, lalu terapkan ke tiga item daftar dengan jeda bertahap menggunakan arbitrary property[animation-delay:...].
Lihat solusi
- Tautan:
<a href="#" class="text-gray-600 transition-colors duration-200 hover:text-indigo-600">Pelajari lebih lanjut</a>
- Tombol suka:
<button class="cursor-pointer rounded-full bg-rose-100 p-3 text-2xl transition-transform duration-150 ease-out motion-safe:hover:scale-110 active:scale-90">
❤️
</button>
- Animasi geser masuk:
@import "tailwindcss";
@theme {
--animate-geser-masuk: geser-masuk 0.3s ease-out both;
@keyframes geser-masuk {
from { opacity: 0; transform: translateX(-24px); }
to { opacity: 1; transform: translateX(0); }
}
}
<ul class="space-y-2">
<li class="motion-safe:animate-geser-masuk">Langkah 1</li>
<li class="motion-safe:animate-geser-masuk [animation-delay:100ms]">Langkah 2</li>
<li class="motion-safe:animate-geser-masuk [animation-delay:200ms]">Langkah 3</li>
</ul>
<!-- → ketiga item muncul berurutan dari kiri -->
Ringkasan
transition+duration-*+ease-*menghaluskan perubahan state; tulis di keadaan normal.- Animasikan
transformdanopacityuntuk hasil paling mulus; 150-300ms untuk interaksi. - Animasi bawaan:
animate-spin,animate-ping,animate-pulse,animate-bounce. - Animasi kustom:
--animate-*plus@keyframesdi dalam@theme. - Hormati pengguna dengan
motion-safe:danmotion-reduce:.