Banyak konflik CSS tidak selesai di source order karena dua selector yang bersaing jelas-jelas tidak setara — satu memakai class, satu lagi memakai id. Untuk situasi ini browser memakai kriteria ketiga: specificity, yaitu bobot numerik yang dihitung dari susunan selector. Dengan formula sederhana (a, b, c), kita bisa membandingkan dua aturan mana pun sebelum menjalankannya, sehingga hasilnya tidak perlu ditebak di browser.

Formula (a, b, c) #

Specificity ditulis sebagai tiga angka yang dihitung per selector, bukan per rule. Ketiga angkanya dihitung dari komponen yang berbeda:

KomponenDihitung dariContoh yang menambah angka
ajumlah selector id#header
bjumlah class, attribute selector, dan pseudo-class.tombol, [type="checkbox"], :hover
cjumlah element type dan pseudo-elementul, a, ::before

Mari kita latih pada selector yang sering muncul:

SelectorabcCatatan
p001satu element type
.nav a011satu class, satu element
ul li.active012satu class, dua element
#header .tombol110satu id, satu class

Perhatikan ul li.active: class memberi angka pada kolom b, sementara ul dan li mengisi kolom c. Tiga angka ini ditulis berurutan sebagai (0, 1, 2) dan menjadi identitas bobot selector tersebut.

Membandingkan Dua Selector #

Perbandingan dilakukan dari kiri ke kanan. Bandingkan angka a terlebih dahulu; bila seri, lanjut ke b; bila masih seri, lanjut ke c; bila ketiganya seri, keputusan jatuh ke source order. Tidak ada penghitungan total — angka (1, 0, 0) selalu mengalahkan (0, 12, 5) karena kolom a lebih dulu dinilai. Inilah sebabnya satu id mampu mengalahkan belasan class sekaligus.

Sebagai latihan, bandingkan #header .tombol (1, 1, 0) dengan .nav ul li a.aktif (0, 2, 3). Selector kedua memakai lebih banyak komponen, tetapi kolom a-nya nol, sehingga selector pertama menang tanpa perlu melihat kolom lain:

css
#header .tombol {      /* (1, 1, 0) — kolom a langsung memutuskan */
  color: #14503a;
}

.nav ul li a.aktif {   /* (0, 2, 3) — banyak komponen, tetapi tanpa id */
  color: #475569;
}

Tautan di atas ditarget kedua rule sekaligus; menambah tiga komponen lagi ke selector kedua pun tidak mengubah hasil, karena perbandingan berhenti di kolom a.

Beberapa komponen selector punya perlakuan khusus dalam perhitungan:

SelectorBobotCatatan
*0, 0, 0universal selector tidak menambah angka apa pun
.menu :not(.aktif)0, 2, 0:not() sendiri bernilai nol; argumen di dalamnya yang dihitung
:is(#promo, .kartu) p1, 0, 1:is() dan :has() mengambil bobot tertinggi dari argumennya

Pada baris terakhir, :is() menampung dua kandidat: #promo (1, 0, 0) dan .kartu (0, 1, 0). Browser memakai yang tertinggi, yaitu #promo, sehingga bobot keseluruhan menjadi (1, 0, 1). Menambahkan pilihan lemah ke dalam :is() tidak menurunkan bobotnya.

Dua tombol berikut memakai class yang sama, tetapi tombol kedua berada di dalam elemen ber-id undangan sehingga selector #undangan .tombol ikut menargetkannya:

Kedua deklarasi bersaing pada property yang sama, tetapi source order tidak sempat bekerja karena specificity sudah memisahkan keduanya.

Inline Style Ada di Tingkatan Sendiri #

Deklarasi yang ditulis langsung pada atribut style elemen berada di atas seluruh selector:

html
<p style="color: #b45309;">Status pembayaran tertunda.</p>

Pada demo berikut, stylesheet menargetkan paragraf yang sama lewat class, tetapi deklarasi inline tetap yang menang:

Browser menempatkan deklarasi inline pada tingkatan tersendiri yang melampaui semua selector, dan DevTools menampilkannya sebagai element.style. Satu-satunya yang mampu mengalahkannya dari stylesheet adalah deklarasi !important, seperti akan kita lihat pada chapter !important.

Ringkasan #

Specificity memberi bobot (a, b, c) pada setiap selector: id dihitung di kolom a, class-attribute-pseudo-class di kolom b, dan element-pseudo-element di kolom c. Perbandingan berjalan dari kiri ke kanan sehingga satu id selalu mengalahkan segudang class, universal selector tidak menambah apa pun, dan :not() hanya menghitung argumennya. Deklarasi inline berada di atas semua selector dan hanya ditundukkan oleh !important. Setelah menguasai penentu ketiga ini, kita beralih ke mekanisme yang bekerja ketika tidak ada deklarasi sama sekali: inheritance.