12 Commit Cuma Buat Nempel Badge di Dada Maskot
Tantangan teknis menempatkan badge ISO pada maskot: positioning, border-radius, dan animasi CSS yang tidak saling menimpa.
Ringkasan
Nempel badge di maskot ternyata nggak semudah itu, butuh 12 commit gara-gara anchor absolute positioning-nya ngaco. Solusinya bungkus artwork pakai wrapper relative w-fit biar badge nggak ikut geser pas ukuran layar berubah. Badge juga sempat lonjong gara-gara rounded-full di kotak non-persegi, untung centering pakai translate Tailwind v4 aman dari keyframe animasi.
12 Commit Cuma Buat Nempel Badge di Dada Maskot
Jam 01:23 pagi, saya menatap layar. Proses menempatkan iso stamp di atas maskot seharusnya simpel, tapi badge-nya justru terlihat kayak ditempelin paksa di pinggulnya.
Awalnya saya kira masalahnya cuma di koordinat. Saya coba geser-geser nilai left dan bottom pakai absolute positioning di kolom terluar. Seharusnya cukup, kan? Ternyata nggak. Badge itu tetap ikut-ikutan bergeser aneh tiap ukuran layar berubah, atau malah ketutupan elemen lain.
Setelah 12 commit, saya belajar tiga hal mendasar tentang gimana browser sebenarnya merender posisi dan animasi.
Anchor yang Salah Kaprah
Masalah pertama ternyata ada di anchor-nya. Absolute positioning itu selalu mencari ancestor terdekat yang punya posisi relative [4]. Karena wrapper gambar maskotnya nggak punya properti itu, badge malah ngacu ke kolom utama yang jauh lebih lebar.
Solusinya simpel. Saya bungkus artwork maskot dengan wrapper relative w-fit. Jadi anchor-nya benar-benar ngepas sama ukuran gambar, bukan ukuran kolom kontainer yang luas. Badge sekarang punya batas area yang jelas.
Lingkaran yang Ternyata Elips
Setelah posisinya aman, saya notice badge-nya nggak bulat sempurna. Bentuknya agak lonjong.
Ternyata rounded-full pada kotak yang bukan persegi akan merender elips. Persentase border-radius itu pakai lebar kotak untuk radius horizontal dan tinggi kotak untuk radius vertikal [3].
Biar jadi lingkaran beneran, kotaknya harus persegi tetap. Saya ubah jadi pakai size-20 (atau size-14 di ukuran layar lebih kecil), ditambah flex untuk centering isinya. Hasilnya bulat sempurna, nggak lagi penyok.
Dua Channel CSS yang Selamatkan Animasi
Bagian paling tricky justru di animasi. Saya pengen badge-nya punya efek membal ala gravitasi, bukan sekadar squash-and-stretch yang kaku. Saya tulis keyframe stamp-bounce di frontend/src/app/globals.css.
Di keyframe 0% dan 100%, saya set translateY(-25%) dengan cubic-bezier(.8,0,1,1) buat efek jatuh yang makin cepat. Di 50%, saya set translateY(0) dengan cubic-bezier(0,0,.2,1) buat efek naik yang melambat. Timing function ini hidup di tiap keyframe, bukan di shorthand animasinya [2].
Tapi pas dijalanin, badge-nya malah lepas dari posisi tengah saat animasi berjalan. Kenapa?
Karena saya pakai -translate-x-1/2 dari Tailwind buat ngetengahin badge secara horizontal. Di Tailwind v4, class ini dikompilasi jadi properti translate yang individual, bukan properti transform [5].
Kabar baiknya, properti translate individual ini memang didesain bekerja secara independen dari properti transform [1]. Jadi, animasi transform di keyframe saya nggak menimpa offset centering tadi. Mereka berjalan di dua channel CSS yang berbeda. Posisi tengah tetap aman walau animasi jalan.
Secara geometris, left-1/2 digabung -translate-x-1/2 udah tengah banget. Tapi mata saya bilang lain. Gambar maskotnya agak condong ke satu sisi. Akhirnya saya nambahin ml-[-80px] di class final. Ini murni judgment editorial setelah tiga kali live nudge dari 0px, 25px, 50px, sampai mentok di 80px. Tujuannya biar kelihatan pas di mata, bukan pas di penggaris.
Hasil akhirnya di frontend/src/components/home/hero.tsx jadi kayak gini:
``html
Kadang fitur yang kelihatan sepele secara visual justru butuh pemahaman mendalam soal gimana browser merender properti CSS. Nggak ada jalan pintas, cuma ada proses ngecek detail kecil sampai semuanya klik di layar.
Sumber
1. MDN Web Docs. *translate*. Properti individual yang bekerja independen dari transform.
2. MDN Web Docs. *animation-timing-function*. Penerapan nilai timing per keyframe.
3. MDN Web Docs. *border-radius*. Perhitungan persentase radius berdasarkan lebar dan tinggi kotak.
4. MDN Web Docs. *position*. Resolusi absolute positioning terhadap ancestor terdekat yang berposisi.
5. Tailwind CSS Documentation. *Translate*. Kompilasi class ke properti translate individual di v
4.