Specificity

Perhitungan bobot selector dan cara menghindari perang specificity.

Daftar isi

Apabila beberapa aturan menargetkan elemen yang sama dengan deklarasi yang berkonflik, cascade memerlukan ukuran untuk menentukan pemenangnya. Ukuran itu bernama specificity, yaitu bobot yang dihitung dari susunan selector. Topik ini membahas sistem tiga angka yang digunakan browser, perhitungan pada selector yang sering muncul, dan kebiasaan yang menjaga stylesheet tetap mudah diatur.

Sistem tiga angka#

Browser menghitung bobot selector dengan tiga angka yang ditulis berurutan, misalnya 0-0-0. Angka pertama menghitung jumlah selector id, angka kedua menghitung jumlah selector class, atribut, dan pseudo-class, sedangkan angka ketiga menghitung jumlah selector elemen dan pseudo-element. Tanda pisah pada penulisan 0-1-0 hanyalah notasi, bukan operasi pengurangan. Perbandingan dilakukan kolom demi kolom dari kiri ke kanan, dan bukan sebagai bilangan desimal.

Sifat per kolom itu berarti satu bobot id tidak dapat dikalahkan oleh class berapa pun. Bobot 0-1-0 lebih tinggi daripada 0-0-13, dan bobot 1-0-0 lebih tinggi daripada 0-99-99. Dengan demikian, banyak kasus gaya yang terlihat tidak berlaku sebenarnya adalah deklarasi yang kalah pada satu kolom saja.

css Contoh: lima class tetap kalah dari satu id
/* bobot 0-5-0, lima class dijumlahkan pada kolom kedua */
.kartu.isi.teks.utama.sorot { color: #15803d; }

/* bobot 1-0-0, deklarasi inilah yang diterapkan */
#konten { color: #1e40af; }

Diagram berikut merangkum ketiga kolom bobot beserta satu contoh selector untuk masing-masing kolom.

id1-0-0#utamaclass, atribut, pseudo-class0-1-0.kartuelemen, pseudo-element0-0-1p
Tiga kolom bobot specificity

Menghitung bobot selector#

Perhitungan dimulai dari nol pada ketiga kolom, lalu setiap bagian selector menambah satu pada kolomnya. Selector sederhana hanya menyentuh satu kolom, sedangkan selector majemuk menjumlahkan seluruh bagiannya. Tabel berikut memuat contoh yang sering ditemui.

SelectorSusunanBobot
psatu elemen0-0-1
.kartusatu class0-1-0
#utamasatu id1-0-0
ul li.aktifdua elemen dan satu class0-1-2
a:hoversatu elemen dan satu pseudo-class0-1-1

Selector ul li.aktif memperlihatkan pola penjumlahan, yaitu dua elemen mengisi kolom ketiga dan satu class mengisi kolom kedua, sehingga bobotnya 0-1-2. Selector a:hover menggunakan pseudo-class, yang dihitung pada kolom kedua sebagaimana class. Pseudo-element seperti ::before berbeda, karena dihitung pada kolom ketiga sebagaimana elemen. Selector universal * dan kombinator seperti > tidak menambah bobot apa pun.

Ketika bobot sama, urutan menentukan#

Dua selector yang berbeda bentuk dapat memiliki bobot yang sama. Selector .kartu dan .judul sama-sama bernilai 0-1-0, demikian pula p dan li. Kesamaan bobot membuat cascade berhenti membandingkan selector dan melanjutkan ke jenjang terakhir, yaitu urutan sumber. Apabila keduanya memuat deklarasi yang berkonflik untuk elemen yang sama, deklarasi pada aturan yang ditulis belakangan yang diterapkan.

Sifat ini sering dimanfaatkan secara sadar untuk membuat variasi. Class dasar ditulis lebih dulu, kemudian class varian ditulis sesudahnya dengan bobot yang sama, sehingga varian menggantikan nilai dasar tanpa selector yang lebih rumit.

css Contoh: varian menang karena urutan, bukan bobot
/* class dasar, ditulis lebih dulu */
.tombol { background: #1e40af; }

/* varian dengan bobot sama, ditulis belakangan */
.tombol.hijau { background: #15803d; }

Kebalikannya juga berlaku, yaitu varian yang tertulis sebelum dasar akan kalah dan tampak tidak berfungsi. Karena itu, memeriksa urutan aturan menjadi langkah awal yang murah ketika sebuah varian tidak muncul.

Menghindari perang specificity#

Perang specificity terjadi ketika selector dibuat semakin panjang dan semakin berbobot hanya untuk mengalahkan selector yang sudah ada. Gejalanya berupa rantai selector seperti aside .kartu .isi p yang lahir untuk menandingi selector sebelumnya. Dengan pola seperti ini, setiap tambahan gaya menuntut selector yang lebih berat lagi, sampai !important terasa menjadi satu-satunya jalan keluar.

Tiga kebiasaan mencegah kondisi tersebut. Pertama, hindari selector id pada gaya penulis, karena bobotnya sulit dikalahkan tanpa selector yang lebih rumit lagi. Kedua, jaga selector tetap pendek, idealnya satu class per aturan, sehingga seluruh gaya bermain pada bobot yang seimbang. Ketiga, susun lapisan gaya yang konsisten, misalnya gaya dasar, komponen, lalu utilitas, agar penggantian nilai dilakukan lewat urutan seksi dan bukan lewat bobot selector.

Selector yang pendek juga mempermudah penggunaan ulang. Komponen dengan gaya 0-1-0 dapat ditempatkan di mana saja tanpa menuntut struktur induk tertentu, sedangkan komponen dengan rantai selector panjang hanya bekerja pada susunan HTML yang persis sama. Disiplin ini terasa memberatkan di awal, tetapi memangkas banyak pekerjaan ketika proyek berkembang.

Kesalahan umum#

Kesalahan pertama adalah menambah elemen induk atau class yang tidak diperlukan hanya untuk menaikkan bobot, misalnya menulis div .kartu padahal .kartu sudah cukup. Selector yang panjang menuntut aturan berikutnya lebih panjang lagi. Tulis selector sependek mungkin sejak awal, dan panjangkan hanya ketika pembatasan sasaran memang diperlukan.

Kesalahan kedua adalah menggandakan selector yang sama untuk menaikkan bobot, misalnya menulis .kartu.kartu. Trik ini memang berfungsi karena class yang sama dihitung dua kali, tetapi niatnya tersembunyi dan menyesatkan pembaca kode. Apabila penggantian gaya bersifat darurat, !important yang disertai komentar penjelas lebih jelas menyatakan maksudnya.

Kesalahan ketiga adalah membangun seluruh gaya dengan selector id karena terasa aman dari penggantian. Yang terjadi justru sebaliknya, yaitu setiap varian dan keadaan baru memerlukan selector yang masih lebih berat. Cadangkan bobot id untuk keperluan skrip atau jangkar, bukan untuk penataan gaya.

Ringkasan#

  • Specificity dihitung sebagai tiga angka, yaitu jumlah id, jumlah class, atribut, dan pseudo-class, serta jumlah elemen dan pseudo-element.
  • Perbandingan dilakukan kolom demi kolom dari kiri, sehingga satu id mengalahkan class berapa pun.
  • Apabila bobot sama, deklarasi yang ditulis lebih akhir menang.
  • Perang specificity dicegah dengan menghindari selector id, menjaga selector tetap pendek, dan mengatur lapisan gaya secara konsisten.

Referensi#