D
P
0

CSS & Tailwind

Teks dengan `text-[oklch(0.97_0.01_260/0.80)]` Tampil Tanpa Warna di Tailwind v4? Trailing Zero Bikin Class Tidak Pernah Terkompilasi

4 Juli 2026·4 menit baca
Teks dengan `text-[oklch(0.97_0.01_260/0.80)]` Tampil Tanpa Warna di Tailwind v4? Trailing Zero Bikin Class Tidak Pernah Terkompilasi

Gejalanya terdengar seperti bug layout: satu subtitle section di landing page yang saya kerjakan tayang di production dalam keadaan tak terlihat. Bukan display: none, bukan ketutup elemen lain. Node-nya ada di DOM, mengambil tempatnya, teksnya bisa di-select kalau tahu harus drag di mana. Background section itu gelap, dan subtitle itu seharusnya dapat warna putih lembut dari satu class Tailwind:

<h2 class="text-[oklch(0.97_0.01_260/0.80)]">
  A subtitle that shipped invisible
</h2>

Yang bikin makin aneh: class OKLCH lain di halaman yang sama bekerja normal. Heading tepat di atasnya juga pakai arbitrary value OKLCH dan tampil sempurna. Build bersih, tidak ada error, tidak ada warning, deploy hijau. Cuma satu class ini yang seolah berhenti eksis.

Refleks pertama saya, seperti biasa, salah arah: saya kira ada perang specificity, ada rule lain yang menimpa warnanya. DevTools bilang lain. Tidak ada yang menimpa apa pun. Rule yang cocok memang tidak ada sama sekali. Class-nya tercantum rapi di atribut class, tapi panel styles menunjukkan nol rule untuk class itu. Warna computed-nya murni warisan dari parent, warna dasar section yang gelap, di atas background yang juga gelap. Ya jelas tidak kelihatan.

Saya cari oklch(0.97 di stylesheet hasil build. Rule-nya ternyata ada. Tapi selector-nya berakhir dengan /0.8), bukan /0.80):

.text-\[oklch\(0\.97_0\.01_260\/0\.8\)\] {
  color: oklch(0.97 0.01 260 / 0.8);
}

Satu karakter. Satu angka nol di belakang. Grep ke seluruh codebase menjelaskan sisanya:

grep -rn "oklch(0.97_0.01_260" src/

Warna yang sama ternyata hidup dalam dua ejaan. Di beberapa file, class-nya ditulis tangan sebagai /0.80; di file lain dia datang dari snippet yang di-paste sebagai /0.8. Secara matematis, alpha-nya identik. Sebagai string, itu dua class yang berbeda.

Kenapa ini terjadi

Tailwind v4 tidak mem-parse template kamu; dia men-scan source sebagai teks, mengumpulkan kandidat class, lalu men-generate CSS untuk yang dia temukan. Arbitrary value itu stringly-typed: text-[oklch(0.97_0.01_260/0.80)] dan text-[oklch(0.97_0.01_260/0.8)] mendeskripsikan warna yang persis sama, tapi bagi scanner dan bagi browser keduanya adalah dua nama class yang tidak ada hubungannya. Engine-nya juga menormalisasi arbitrary value saat menulis CSS, jadi yang mendarat di stylesheet adalah bentuk kanonis, dan string class di HTML kamu harus cocok dengan selector hasil generate itu karakter demi karakter.

Di codebase ini, stylesheet akhirnya hanya berisi rule untuk satu ejaan. Ejaan satunya, yang nyata-nyata ada di sebagian markup yang tayang, tidak cocok dengan rule mana pun. Dan utility class yang tidak match itu bukan error di model ini. Dia bukan apa-apa. Tidak ada build gagal, tidak ada warning di console, tidak ada laporan dead code. Browser melihat atribut class berisi token yang tidak cocok dengan selector mana pun lalu mengangkat bahu, persis seperti kalau kamu typo. Itu memang by design: scanning utility harus toleran terhadap string asing, jadi dia mustahil memberi tahu kamu bahwa salah satu string itu tadinya dimaksudkan jadi warna.

Yang bikin bug ini kejam adalah dua nama class itu identik secara visual. /0.8 dan /0.80 lolos dari code review, lolos dari setiap mata yang membaca diff, dan tentu saja lolos dari compiler yang memang tidak pernah berjanji membandingkan keduanya.

Perbaikannya

Perbaikan yang sebenarnya bukan membetulkan angka nolnya. Perbaikannya adalah berhenti menulis literal warna mentah di markup sama sekali. Tailwind v4 bikin ini gampang: definisikan warnanya sekali sebagai theme token di @theme, dan biarkan framework men-generate satu utility kanonis untuknya:

@theme {
  --color-ink-soft: oklch(0.97 0.01 260 / 0.8);
}

Lalu semua pemakaian menunjuk ke token itu:

<h2 class="text-ink-soft">
  A subtitle with exactly one spelling
</h2>

text-ink-soft tidak bisa bercabang jadi ejaan varian. Literalnya hidup di tepat satu tempat, dan token itu kanonis by construction: tidak ada trailing zero yang bisa berbeda antara yang kamu tulis dan yang di-generate.

Sambil migrasi, saya grep literal yang terduplikasi itu untuk menangkap semua call site: dua ejaan tadi, plus sepasang sepupunya yang hue-nya digeser tipis padahal jelas dimaksudkan sebagai warna yang sama. Semuanya diganti ke token.

Dan satu kebiasaan baru: kalau ada style yang hilang diam-diam di production padahal build hijau, tersangka pertama saya sekarang adalah mismatch scan atau kanonikalisasi, bukan cascade.

Pelajaran

Arbitrary value itu string, bukan nilai. Warna yang sama ditulis dua cara adalah dua class yang berbeda, dan compiler tidak akan pernah memperingatkanmu, karena utility yang tidak match memang gagal secara diam-diam by design. Theme token menghapus seluruh kelas masalah ini: satu literal, satu tempat, satu ejaan. Dan kalau sebuah style menghilang seperti hantu, grep dulu untuk near-duplicate arbitrary value sebelum menyalahkan specificity. Warna yang sama, string yang beda. Browser tidak melakukan matematika di nama class kamu.