kodingup.camp

Beranda/Tailwind Mastery

Efek Gerak & Transisi

Modul 15 dari 163 menit bacaMotion & Transition Effects

Isi modul

    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).

    Memilih properti spesifik (misalnya transition-colors) sedikit lebih ringan untuk browser daripada transition-all.

    Duration, ease, delay

    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

    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

    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

    1. Lupa transition, sehingga hover: langsung meloncat. Tambahkan transition di keadaan normal, bukan hanya di hover:.
    2. Menganimasikan width, height, atau top. Ini memicu layout ulang dan terasa patah-patah. Gunakan transform dan opacity.
    3. Durasi terlalu lama (1000ms untuk hover tombol). Pengguna merasa aplikasi lambat. Gunakan 150-300ms.
    4. Mengabaikan motion-reduce. Animasi besar yang terus berulang bisa membuat sebagian orang tidak nyaman. Bungkus dengan motion-safe: atau matikan dengan motion-reduce:animate-none.
    5. Menulis @keyframes di luar @theme lalu bingung kenapa animate-* tidak ada. Utility animate-* berasal dari variabel --animate-* di @theme; keyframes-nya juga sebaiknya diletakkan di dalam @theme agar ikut dikeluarkan saat dipakai.

    Latihan

    1. Mudah, Buat tautan yang warnanya berubah dari gray-600 ke indigo-600 saat hover dengan transisi 200ms.
    2. Sedang, Buat tombol "Suka" yang membesar ke scale-110 saat hover dan mengecil ke scale-90 saat ditekan, dengan kurva ease-out, dan tidak membesar kalau pengguna memilih kurangi gerakan.
    3. 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
    1. Tautan:
    <a href="#" class="text-gray-600 transition-colors duration-200 hover:text-indigo-600">Pelajari lebih lanjut</a>
    
    1. 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>
    
    1. 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

    Kuis pemahaman

    Pilih satu jawaban per soal. Penjelasan muncul setelah kamu menjawab.