Ada bug yang tidak muncul di konsol, tidak bikin build gagal, dan tidak pernah dilaporkan siapa pun kecuali mata kamu sendiri waktu menatap layar terlalu lama. Saya kena satu di situs marketing sebuah klien: heading hero-nya terasa "salah" tapi saya butuh waktu lama untuk merumuskan salahnya di mana.
Gejalanya: ada yang kepotong, tapi tipis
Heading utamanya pakai text-5xl di mobile dan naik ke text-6xl di layar besar, dengan Quicksand sebagai font judul. Sekilas rapi. Tapi begitu saya zoom, kelihatan: ujung atas huruf-huruf tinggi seperti d, k, t kepangkas sedikit, dan ekor g sama y juga hilang ujungnya. Bukan blur, bukan ketutupan elemen lain. Benar-benar terpotong rata seperti ada kotak tak kelihatan yang lebih pendek dari hurufnya.
Yang bikin saya makin bingung, potongannya tidak konsisten antar heading. Kata yang isinya cuma huruf pendek seperti "semua" tampak baik-baik saja, jadi berkali-kali saya menutup devtools sambil berpikir ini cuma halusinasi.
Tersangka pertama saya jelas: font. Saya cek apakah Quicksand ter-load dengan benar, cek font-display, cek apakah ada fallback yang nyempil. Semua bersih. Saya ganti font sementara ke sans-serif sistem, dan potongannya berkurang tapi tidak hilang sepenuhnya. Itu petunjuk pertama yang benar: kalau ganti font cuma mengubah parahnya dan bukan menghilangkannya, masalahnya ada di kotak yang menampung teks, bukan di hurufnya.
Ada satu tempat lagi yang bikin ini kelihatan jauh lebih parah
Hero-nya juga punya animasi reveal per karakter. Polanya standar: tiap huruf dibungkus satu span dengan overflow-hidden, lalu span di dalamnya digeser naik dari bawah supaya hurufnya seperti "muncul" dari balik garis.
Di situ potongannya jadi brutal. Karena tiap karakter sekarang punya kotak klip sendiri yang mengikuti line box masing-masing, apa pun yang tadinya cuma sedikit meleber ke luar sekarang langsung dipangkas dengan bersih. Animasinya, ironisnya, jadi alat diagnosis terbaik saya: dia mengubah masalah yang samar jadi masalah yang jelas.
Akar masalahnya: line-height bawaan yang snap ke 1
Ini bagian yang bikin saya cukup lama nyari, karena tidak ada satu baris CSS pun di proyek saya yang menulis line-height.
Utility ukuran teks Tailwind bukan cuma mengatur font-size. Tiap ukuran datang berpasangan dengan line-height bawaan. Untuk ukuran kecil sampai sedang, pasangannya longgar dan proporsional. Tapi mulai dari text-5xl ke atas, pasangan itu snap ke line-height: 1. Persis satu kali ukuran font, tidak lebih.
Dan line-height: 1 itu bukan "pas". Di banyak font, terutama font geometris membulat seperti Quicksand, jumlah ascent dan descent bawaan font-nya lebih besar dari satu em. Artinya area yang butuh dilukis huruf itu lebih tinggi daripada line box yang kamu kasih. Sisanya meleber keluar. Kalau tidak ada yang mengklip, biasanya kamu selamat dan cuma dapat spacing yang terasa sesak. Tapi begitu ada overflow-hidden di jalur render, entah dari wrapper animasi, kartu, atau container mana pun, meleber itu langsung berubah jadi potongan.
Jadi rangkaiannya: Tailwind kasih line-height: 1 ke heading besar, font-nya butuh lebih dari 1em, dan wrapper overflow-hidden per karakter mengeksekusi hukumannya.
Cara paling cepat membuktikannya: tambahkan leading-normal sementara ke heading yang bermasalah. Kalau hurufnya langsung utuh, kamu sudah tahu penyebabnya, dan tinggal urusan menentukan angka yang tepat.
Perbaikannya: timpa tokennya, bukan tiap headingnya
Godaan pertama saya adalah menempelkan leading-[1.05] ke setiap heading. Itu jalan, tapi berarti setiap heading baru yang dibuat siapa pun setelah ini akan lahir dengan bug yang sama sampai ada yang ingat menambahkan class-nya.
Di Tailwind v4, line-height bawaan tiap ukuran itu variabel tema, jadi bisa ditimpa sekali di satu tempat:
@import "tailwindcss";
@theme {
--text-5xl--line-height: 1.05;
--text-6xl--line-height: 1.05;
}Sejak itu, text-5xl dan text-6xl di seluruh situs punya sedikit ruang napas tanpa siapa pun perlu mengingat apa pun. Angka 1.05 sengaja kecil. Tujuannya bukan bikin heading jadi longgar dan kehilangan kesan padat, cuma memberi cukup ruang supaya ascender dan descender tidak keluar dari kotaknya.
Satu tempat yang tetap saya set manual: wrapper per karakter di komponen reveal-nya. Token tadi cuma menyentuh elemen yang benar-benar memakai text-5xl atau text-6xl, sedangkan yang melakukan klip di sini adalah span pembungkusnya. Saya tulis nilainya eksplisit di situ supaya tinggi kotak klipnya dijamin sama dengan headingnya, di ukuran teks mana pun komponen itu dipakai:
<span className="inline-block overflow-hidden leading-[1.05]">
<span className="inline-block">{char}</span>
</span>Setelah wrapper klipnya ikut memakai line-height yang sama dengan headingnya, animasinya jalan mulus dan hurufnya utuh dari frame pertama sampai terakhir.
Yang saya bawa pulang
- Kalau huruf kepotong dan ganti font cuma mengubah tingkat parahnya, berhenti menyalahkan font. Curigai line box-nya.
- Utility ukuran teks itu paket dua isi. Kamu mengatur
font-size, tapi kamu juga diam-diam menerimaline-height, dan mulaitext-5xlnilainya1. line-height: 1aman sampai ketemuoverflow-hidden. Wrapper klip apa pun akan mengubah luberan yang tak terlihat jadi potongan yang terlihat.- Perbaiki di level token kalau masalahnya sistemik. Menempel class satu per satu cuma menunda bug yang sama muncul lagi di heading berikutnya.