Sampai sini, setiap selector yang kamu tulis menyasar elemen berdasarkan apa dia — sebuah tag, class, atau id. Tapi elemen itu tidak diam. Sebuah link bisa disentuh kursor, sebuah input bisa difokuskan, sebuah checkbox bisa dicentang, dan sebuah item daftar bisa jadi yang pertama atau yang terakhir di antara saudaranya. Bukankah enak kalau kamu bisa memberi style ke elemen berdasarkan apa yang sedang terjadi padanya, atau di mana posisinya di antara saudara-saudaranya?
Nah, di situlah pseudo-class berperan. Pseudo-class adalah kata kunci yang kamu tempelkan ke sebuah selector untuk menyasar elemen pada keadaan atau posisi tertentu — saat disentuh kursor, saat dia jadi anak ketiga, saat checkbox-nya dicentang — tanpa menulis satu baris JavaScript pun. Inilah salah satu hal yang bikin CSS terasa hidup, dan begitu kamu paham, stylesheet kamu jadi jauh lebih bertenaga.
Apa itu pseudo-class?
Pseudo-class adalah kata kunci yang diawali dengan satu titik dua (:) dan ditempelkan ke sebuah selector. Fungsinya menyempitkan pilihan ke elemen yang sedang berada pada keadaan tertentu atau memenuhi syarat tertentu:
a:hover {
color: orange;
}
Coba baca pelan-pelan: “sebuah elemen <a>, selagi disentuh kursor, teksnya jadi oranye.” Bagian a adalah selector biasa yang sudah kamu kenal. Bagian :hover itulah pseudo-class-nya — dialah syaratnya. Digabung, keduanya berarti “terapkan ini hanya ketika kursor sedang berada di atas link.”
Kata kuncinya adalah pseudo, alias “semu”. Tidak ada elemen :hover di HTML kamu; kamu tidak pernah menulis <a:hover>. Browser sendirilah yang mendeteksi keadaannya, lalu menerapkan aturan begitu syaratnya terpenuhi. Jadi kamu cuma menjelaskan sebuah situasi, dan CSS yang bereaksi terhadapnya.
Pseudo-class satu titik dua, pseudo-element dua
Pseudo-class memakai satu titik dua: :hover, :focus, :first-child. Sepupu dekatnya, yaitu pseudo-element (seperti ::before dan ::after), memakai dua titik dua. Keduanya bersaudara tapi beda fungsi — pseudo-class menyasar elemen yang sudah ada pada keadaan tertentu, sedangkan pseudo-element justru menciptakan bagian baru dari sebuah elemen untuk diberi style. Pegang jumlah titik dua ini, maka kamu pun tidak akan tertukar antara dua konsepnya.
Karena pseudo-class cuma bagian dari sebuah selector, dia menumpang di atas semua yang sudah kamu pelajari tentang selector CSS. Kamu bisa menumpuknya ke sebuah class, id, atau descendant selector — di mana pun sebuah selector boleh berada.
Keadaan interaksi: yang sehari-hari kepakai
Pseudo-class yang paling sering kamu pakai adalah yang merespons interaksi pengguna. Merekalah yang bikin tombol dan link terasa responsif, bukannya mati.
:hover
:hover aktif saat pointer pengguna berada di atas sebuah elemen. Inilah umpan balik klasik yang berkata “ini bisa diklik”:
.button {
background: #00B8E6;
color: white;
}
.button:hover {
background: #0095bb;
}
Tombolnya mulai dengan warna cyan, lalu begitu kursor lewat di atasnya, warnanya sedikit menggelap. Pergeseran kecil itu memberi tahu pengguna “ya, ini merespons kamu.” Hampir setiap elemen interaktif di situs yang rapi punya style hover.
:focus
:focus berlaku ketika sebuah elemen sedang difokuskan — dipilih lewat keyboard (dengan menekan Tab ke arahnya) atau dengan mengklik ke dalam sebuah input. Pseudo-class ini penting banget untuk aksesibilitas, sebab pengguna keyboard bergantung pada penanda fokus yang terlihat untuk tahu mereka sedang ada di bagian mana halaman:
input:focus {
outline: 2px solid #00B8E6;
border-color: #00B8E6;
}
Jangan hapus outline fokus tanpa gantinya
Kesalahan yang umum adalah menulis :focus { outline: none; } untuk membunuh outline bawaan karena dianggap “jelek”. Jangan — itu membuat pengguna keyboard dan pembaca layar terdampar tanpa petunjuk visual posisi fokus. Kalau kamu tidak suka outline bawaan, ganti dengan style buatanmu sendiri yang tetap terlihat (outline custom, border yang menyala, atau perubahan background). Jangan sekadar dihapus.
:active
:active adalah keadaan sesaat selagi sebuah elemen sedang diklik — di antara saat tombol mouse ditekan dan saat dilepas. Inilah yang memberi sensasi “tertekan” yang memuaskan:
.button:active {
transform: translateY(1px);
}
Itu menggeser tombol turun satu piksel saat diklik, meniru tombol sungguhan yang sedang ditekan masuk. Kecil, tapi cukup untuk membuat antarmuka terasa nyata.
:visited
:visited menyasar link yang sudah pernah dikunjungi pengguna. Secara tradisional, browser menampilkan link yang sudah dikunjungi dengan warna berbeda supaya kamu bisa membedakan mana yang sudah diklik:
a:visited {
color: #8b5cf6;
}
Urutan LVHA penting untuk link
Saat kamu memberi style keempat keadaan link, deklarasikan dalam urutan ini: :link, :visited, :hover, :active — diingat dengan singkatan LVHA (“LoVe HAte”). Karena keempatnya bisa berlaku sekaligus dan punya specificity yang sama, yang terakhir ditulislah yang menang. Kalau urutannya kacau, bisa-bisa :hover tidak pernah muncul karena ditimpa :visited. Pegang urutan LVHA, dan semuanya bekerja sesuai harapan.
Pseudo-class struktural: menyasar berdasarkan posisi
Keluarga besar kedua menyasar elemen berdasarkan di mana posisinya di antara saudara-saudaranya — pertama, terakhir, genap, ganjil, atau urutan ke-n. Ini berguna sekali untuk memberi style ke daftar, tabel, dan grid tanpa perlu menambahkan class tambahan ke setiap item.
:first-child dan :last-child
Keduanya menyasar elemen yang jadi anak pertama (atau terakhir) di dalam induknya:
li:first-child {
font-weight: bold;
}
li:last-child {
border-bottom: none;
}
Item daftar pertama dibuat tebal; yang terakhir dihilangkan garis bawahnya (berguna untuk membuang pemisah yang menggantung di ujung daftar). Kamu tidak perlu menambahkan class first atau last di mana pun — CSS yang menghitung posisinya untukmu.
:nth-child()
:nth-child() adalah jagoan di keluarga ini. Dia menerima sebuah angka, kata kunci, atau rumus, lalu menyasar elemen pada posisi-posisi itu. Bentuk paling sederhananya adalah odd (ganjil) dan even (genap):
tr:nth-child(even) {
background: #f3f4f6;
}
Itulah tabel “loreng zebra” yang klasik — setiap baris genap dapat background abu-abu muda, sehingga tabel yang panjang jauh lebih enak dibaca. Kamu juga bisa memberikan angka biasa untuk menyasar satu elemen tertentu:
li:nth-child(3) {
color: red;
}
Dan keajaiban sebenarnya ada pada rumus an + b, di mana n dihitung mulai dari 0. Misalnya, 3n menyasar setiap elemen ketiga (yang ke-3, ke-6, ke-9, dan seterusnya):
li:nth-child(3n) {
margin-right: 0;
}
Pola ini pas banget untuk grid — misalnya kamu punya grid 3 kolom dan ingin menghapus margin kanan dari setiap item ketiga supaya barisnya rapi sejajar. Rumusnya mengurus itu tanpa menyentuh HTML.
Cara membaca rumus an+b
Pada :nth-child(an + b), masukkan n = 0, 1, 2, 3… lalu baca hasilnya. 2n menghasilkan 0, 2, 4, 6 → setiap item genap. 2n + 1 menghasilkan 1, 3, 5, 7 → setiap item ganjil. 3n + 1 menghasilkan 1, 4, 7, 10. Begitu kamu melihatnya sebagai “melangkah sebanyak a, mulai dari geseran b,” rumusnya berhenti terlihat seperti sandi.
:nth-of-type()
:nth-child() menghitung semua anak tanpa peduli tag-nya. Sebaliknya, :nth-of-type() hanya menghitung saudara dengan jenis elemen yang sama. Kalau di dalam induk kamu bercampur tag <p> dan <img>, maka p:nth-of-type(2) menemukan paragraf kedua secara spesifik — mengabaikan gambar yang menyelip di antaranya, yang justru tetap akan dihitung oleh :nth-child.
Pseudo-class form: bereaksi pada keadaan input
Form punya kelompok pseudo-class tersendiri yang merespons keadaan input — dicentang, dinonaktifkan, valid, dan lainnya. Berkat ini, kamu bisa membangun form yang kaya dan responsif dengan CSS murni.
:checked
:checked menyasar checkbox atau radio button yang sedang dipilih. Pasangkan dengan sibling selector, dan kamu bisa bereaksi terhadap sebuah toggle tanpa JavaScript:
input:checked {
accent-color: #00B8E6;
}
Inilah tulang punggung toggle berbasis CSS, checkbox custom, dan widget “klik untuk membuka” — checkbox-nya menyimpan keadaan, lalu CSS memberi style berdasarkan apakah dia dicentang atau tidak.
:disabled dan :enabled
:disabled menyasar kontrol form yang dimatikan (lewat atribut disabled), sedangkan :enabled menyasar yang masih aktif:
button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
Membuat tombol yang nonaktif jadi pudar dan memunculkan kursor “not-allowed” adalah cara yang jelas dan baku untuk memberi sinyal “yang ini belum bisa kamu pakai sekarang.”
:required, :valid, dan :invalid
Ketiganya bereaksi terhadap validasi form bawaan HTML. :required menyasar input yang ditandai wajib, sementara :valid dan :invalid mencerminkan apakah nilai saat ini lolos aturan validasi field-nya:
input:invalid {
border-color: #ef4444;
}
input:valid {
border-color: #22c55e;
}
Selagi pengguna mengetik email, border-nya merah saat formatnya masih salah, lalu hijau begitu alamatnya valid — umpan balik instan, tanpa script sama sekali. (Tapi hati-hati sedikit: :invalid sudah berlaku bahkan sebelum pengguna mengetik apa pun, jadi untuk pengalaman yang lebih lembut, biasanya kamu menggabungkannya dengan :focus atau baru menampilkan error setelah ada interaksi.)
Beberapa tambahan yang berguna
Ada beberapa pseudo-class lagi yang layak kamu tahu karena benar-benar menghemat usaha.
:not() adalah pseudo-class negasi — dia menyasar semuanya kecuali yang ada di dalamnya. Ingin semua tombol kecuali yang utama?
.button:not(.primary) {
background: #e5e7eb;
}
:first-of-type dan :last-of-type bekerja seperti :first-child/:last-child, tapi terbatas pada jenis elemen tertentu — cermin dari ide nth-of-type.
Lalu :root menyasar elemen akar dokumen (tag <html>), yang jadi tempat lazim untuk mendeklarasikan CSS custom property:
:root {
--brand: #00B8E6;
}
Kamu akan terus berjumpa :root begitu mulai bekerja dengan variabel CSS — di situlah nilai-nilai global bersemayam.
Merangkai pseudo-class
Pseudo-class tidak saling meniadakan — kamu bisa menggabungkannya satu sama lain dan dengan selector biasa untuk hasil yang sangat presisi. Setiap satu yang kamu tambahkan akan menyempitkan pilihan lebih jauh:
.menu li:first-child a:hover {
color: #00B8E6;
}
Itu dibaca sebagai: “elemen <a> di dalam <li> pertama milik .menu, tapi hanya selagi disentuh kursor.” Kamu menumpuk pseudo-class struktural (:first-child) dan pseudo-class interaksi (:hover) dalam satu aturan. Kemampuan dirangkai inilah yang bikin pseudo-class begitu ekspresif — kamu menggambarkan elemen yang persis pada keadaan yang persis, lalu CSS mengurus sisanya.
Penutup
Pseudo-class membuatmu bisa memberi style ke elemen berdasarkan keadaan dan posisi, tanpa butuh JavaScript — dan itu kekuatan sungguhan untuk membangun antarmuka yang terasa responsif. Inilah inti yang perlu kamu bawa pulang:
- Sebuah pseudo-class memakai satu titik dua dan menyasar elemen pada suatu keadaan atau posisi:
a:hover,li:first-child. - Keadaan interaksi —
:hover,:focus,:active,:visited— bikin link dan tombol terasa hidup. Selalu pertahankan style:focusyang terlihat demi aksesibilitas, dan urutkan keadaan link sebagai LVHA. - Pseudo-class struktural —
:first-child,:last-child,:nth-child(),:nth-of-type()— menyasar elemen berdasarkan posisi.:nth-child()dengan rumusan + bmengurus loreng zebra, grid, dan pola berulang. - Pseudo-class form —
:checked,:disabled,:required,:valid,:invalid— bereaksi pada keadaan input untuk form yang kaya tanpa script. - Tambahan berguna:
:not()untuk mengecualikan,:rootuntuk variabel global. - Kamu bisa merangkai pseudo-class untuk aturan yang presisi dan ekspresif.
Pseudo-class menyasar elemen yang sudah ada pada suatu keadaan. Kerabat dekatnya, pseudo-element, melangkah satu tahap lebih jauh: dia membuat dan memberi style ke bagian-bagian baru dari sebuah elemen — seperti menyisipkan konten sebelum atau sesudahnya, atau memberi style hanya pada baris pertama sebuah paragraf. Itulah kelanjutan yang alami, dan ke sanalah kita menuju berikutnya.