Skip to content

Mengubah 113 Logo Klien Jadi Infinite Marquee Murni CSS

Adityo Guni Waluyo

Grid 113 logo klien bikin homepage panjang tiada ujung. Saya rombak jadi marquee CSS murni dua baris: loop -50 persen, gerbang reduced-motion.

Ringkasan

Homepage penuh 113 logo klien bikin konten penting kebawa jauh ke bawah. Daripada pasang library JS yang overkill, ganti pake marquee CSS murni: dua track diduplikasi, dianimasikan pake translate3d, dan jebakan margin di tile terakhir diganti padding biar loop mulus. Bonusnya performa ngebut plus aksesibilitas aman lewat aria-hidden dan prefers-reduced-motion.

Saya buka halaman utama versi dev, scroll ke bawah, dan langsung terhempas. Ada 113 kartu logo klien berjejer dalam grid responsif, membentuk tembok putih tanpa ujung yang mendorong konten penting jauh ke bawah layar. Pengunjung harus scroll melewati lautan putih ini sebelum bisa melihat nilai utama yang ingin saya sampaikan di homepage.

Awalnya saya pikir jalan pintasnya gampang. Tinggal pasang library marquee JavaScript populer, atur sedikit konfigurasi, beres. Tapi setelah membayangkan ukuran bundle yang membengkak cuma untuk menggeser gambar statis, saya sadar itu overkill banget.

Solusi yang lebih masuk akal ternyata marquee logo CSS murni. Saya ubah grid statis itu menjadi dua track infinite yang berjalan tanpa JavaScript sama sekali.

Jebakan loop dan solusi CSS

Struktur akhirnya menggunakan dua elemen unordered list. Track pertama berisi 57 tile pertama yang diduplikasi persis dua kali. Track kedua berisi 56 tile sisanya dengan perlakuan yang sama. Totalnya tetap 113 tile unik, tapi direplikasi untuk kebutuhan looping.

Animasinya mengandalkan @keyframes yang mendefinisikan gaya waypoint animasi CSS dan mengikatnya ke elemen [1]. Track pertama bergerak menggunakan translate3d dari 0 ke -50 persen. Track kedua menjalankan keyframes cermin dari -50 persen kembali ke 0, sehingga arah geraknya berlawanan, semua dengan durasi 90 detik linear infinite.

Di sinilah jebakan utamanya. Endpoint -50 persen hanya akan looping mulus jika track memegang tepat dua salinan identik dan celah antar tile sudah dilipat ke titik lompatan. Saya sempat kena animasi yang melompat kasar tiap kali loop berulang. Ternyata penyebabnya margin di item terakhir, bukan lipatan flex gap ke dalam padding-right yang setara. Begitu diganti ke padding, loop mendarat tepat di sambungan tanpa kedip sedikit pun.

@media (prefers-reduced-motion: no-preference) {
  .marquee-track-1 {
    animation: scroll-left 90s linear infinite;
  }
  .marquee-track-2 {
    animation: scroll-right 90s linear infinite;
  }
}

@keyframes scroll-left {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(-50%, 0, 0); }
}

@keyframes scroll-right {
  from { transform: translate3d(-50%, 0, 0); }
  to { transform: translate3d(0, 0, 0); }
}

Keuntungan besar dari pendekatan ini adalah performa. Animasi yang dibatasi hanya pada transform dan opacity tetap berada di tahap compositing pada jalur rendering [4]. Properti yang memicu layout atau paint justru bikin animasi lambat, jadi left atau margin gak boleh ikut dianimasikan.

Aksesibilitas dan detail visual

Efek visual di tepi container juga butuh perhatian. Saya menambahkan edge fade menggunakan inline mask-image dengan linear-gradient: transparan, hitam 8 persen, hitam 92 persen, transparan. Properti ini menerima gambar yang dihasilkan termasuk gradien dan menyembunyikan region elemen per saluran alpha mask [3]. Hasilnya, logo muncul dan hilang secara halus di tepi layar, bukan terpotong mendadak.

Dari sisi aksesibilitas, setiap tile saya tandai sebagai item daftar dekoratif dengan aria-hidden="true". Atribut ini menghapus konten murni dekoratif atau duplikat dari pohon aksesibilitas dan tidak boleh ditempatkan pada elemen yang bisa difokuskan [5]. Karena logo-logo ini cuma hiasan untuk membangun kredibilitas visual, membacanya berulang kali lewat screen reader justru mengganggu.

Selain itu, seluruh blok animasi saya bungkus ketat dalam query media prefers-reduced-motion: no-preference. Ini bukan sekadar hiasan kode. Scaling atau panning objek besar adalah pemicu gerak vestibular, jadi gerakan marquee ambient harus berada di balik preferensi pengguna tersebut [2]. Kalau pengguna mengatur sistem operasinya untuk mengurangi gerakan, mereka lihat strip logo statis yang rapi, bukan animasi yang memaksa.

Kontainer strip-nya sendiri saya kasih role presentation, biar pembaca layar nggak menebak-nebak struktur daftar yang panjangnya dobel. Ukuran tile-nya responsif, dari 112 piksel di ponsel sampai 144 piksel di desktop, jadi sekitar delapan logo terlihat per baris tanpa perlu breakpoint tambahan.

Saya akhirnya memutuskan untuk tidak pernah lagi menarik dependensi eksternal cuma untuk efek visual yang bisa diselesaikan browser dengan beberapa baris CSS. Kadang solusi paling elegan emang yang paling dekat dengan mesinnya, asalkan kita paham betul cara kerjanya.

## Sources [1] https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@keyframes [2] https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@media/prefers-reduced-motion [3] https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/mask-image [4] https://web.dev/articles/animations-guide [5] https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-hidden

Artikel terkait