D
P
0

HTML & CSS

Override CSS Page-Scoped Diam-Diam Mengalahkan Komponen 'Global' Saya di Specificity

18 Juni 2026·3 menit baca
Override CSS Page-Scoped Diam-Diam Mengalahkan Komponen 'Global' Saya di Specificity

Saya merapikan style checkbox di sebuah situs yang saya bangun: memindahkan aturan styled-checkbox dari file CSS per-halaman ke stylesheet komponen global, supaya bisa dipakai konsisten di mana saja. Idenya bagus — kecuali di halaman /checkout, di mana state custom checkbox saya (kotak terisi oranye dengan centang putih) tetap tidak pernah muncul. Yang tampil di sana adalah checkbox bawaan browser, kotak abu-abu standar.

Anehnya, di halaman lain styling-nya sempurna. Hanya di /checkout yang gagal — persis halaman yang paling tidak ingin saya rusak.

Komponen global-nya seperti ini:

.checkbox input[type="checkbox"] {
    position: absolute;
    opacity: 0; /* sembunyikan input bawaan */
}
.checkbox .box {
    width: 18px; height: 18px;
    border: 1px solid #ccc;
}
.checkbox input:checked + .box {
    background: #f97316; /* oranye */
    /* centang putih dirender di sini */
}

Aturan opacity: 0 itu seharusnya menyembunyikan input bawaan supaya .box custom bisa menggantikannya. Di kebanyakan halaman, itu berhasil. Di /checkout, input bawaan tetap terlihat — yang artinya opacity: 0 saya kalah.

Akar masalahnya: selector lama yang lebih spesifik masih menang

Saya buka DevTools, klik input bawaan, dan lihat panel Computed → Styles. Di situ jelas: aturan opacity: 0 saya dicoret (strike-through), dan satu aturan lebih lama menang di atasnya:

.checkout-page .checkbox input[type="checkbox"] {
    opacity: 1;       /* ← yang menang */
    position: static;
}

Aturan ini sisa dari lokasi lama style checkbox — dulu ada di file per-halaman checkout, sengaja memaksa input bawaan tetap terlihat. Saya memindahkan style yang baru ke stylesheet global, tapi lupa menghapus override page-scoped ini.

Dan inilah inti masalahnya — specificity. Hitung kedua selector:

.checkbox input[type="checkbox"]                 → (0,2,1) = 1 class + 1 attr + 1 elemen
.checkout-page .checkbox input[type="checkbox"]  → (0,3,1) = 2 class + 1 attr + 1 elemen

Selector lama membawa ancestor .checkout-page ekstra, jadi specificity-nya lebih tinggi. Di cascade, specificity tertinggi menang — tidak peduli stylesheet mana yang dimuat belakangan. Jadi opacity: 1 lama mengalahkan opacity: 0 global saya, input bawaan tidak pernah disembunyikan, dan state custom tidak punya ruang untuk dirender.

Yang menipu: aturan global saya dimuat lebih akhir di dokumen. Insting umum adalah "yang terakhir menang", tapi urutan sumber hanya jadi penentu ketika specificity sama. Di sini specificity tidak sama — jadi .checkout-page menang meskipun didefinisikan lebih dulu.

Cara melacaknya di DevTools

Ini polanya, supaya Anda bisa mengenalinya dalam hitungan detik bukan jam:

  1. Inspect elemen yang salah (di sini, input bawaan yang muncul).
  2. Buka tab Computed, cari properti yang bermasalah (opacity), klik panahnya untuk expand.
  3. DevTools mengurut semua aturan yang berkontribusi dari pemenang ke pecundang. Pemenang di atas; yang kalah dicoret (strike-through).
  4. Lihat aturan teratas yang tidak dicoret — itu yang sedang menang. Di sini: .checkout-page .checkbox input[type="checkbox"].
  5. Selector dengan ancestor ekstra (.checkout-page) hampir selalu jadi tersangka pada bug "kenapa cuma di halaman ini".

Begitu Anda melihat aturan global Anda dicoret sementara aturan ber-ancestor lebih panjang yang menang, diagnosisnya sudah selesai: ini perang specificity, bukan masalah loading order.

Perbaikannya: hapus override page-scoped yang lama

Tidak perlu !important, tidak perlu menaikkan specificity aturan global (yang justru menyebarkan masalah). Perbaikan yang benar adalah menghapus selector page-scoped yang dulu bersaing dengan aturan di lokasi lamanya:

/* HAPUS dari file checkout — peninggalan dari lokasi lama */
.checkout-page .checkbox input[type="checkbox"] {
    opacity: 1;
    position: static;
}

Begitu override ini hilang, aturan .checkbox global akhirnya menang di /checkout seperti di halaman lain. Input bawaan tersembunyi, dan kotak oranye dengan centang putih akhirnya muncul.

Pelajaran

Ketika Anda mempromosikan CSS sebuah komponen dari file per-halaman ke stylesheet "global", jangan cuma memindahkan aturan baru — buru juga selector page-scoped lama yang dulu bersaing dengannya. Selector itu sering membawa ancestor ekstra (.checkout-page, .single-product, dan sebagainya) yang memberinya specificity lebih tinggi, sehingga mereka terus menang persis di halaman yang paling Anda pedulikan. Loading order tidak akan menyelamatkan Anda — specificity yang menentukan. Buka panel Computed, lihat aturan mana yang menang dan mana yang dicoret, lalu hapus override yang sudah tidak berguna itu, bukan menumpuk !important di atasnya.