Form modern tidak butuh JavaScript untuk mengetahui bahwa sebuah isian salah. Cukup dengan attribute HTML seperti required dan type="email", browser sudah menjalankan constraint validation dan menandai setiap kolom sebagai valid atau tidak valid — dan CSS bisa bereaksi terhadap keadaan itu lewat pseudo-class khusus. Masalahnya, versi paling kasarnya menyalakan tanda merah sebelum pengguna sempat mengetik apa pun. Pada chapter ini kita lihat pseudo-class yang tersedia, mengapa :invalid saja tidak cukup, dan bagaimana :user-valid serta :user-invalid memperbaiki pengalamannya.

Pseudo-class Validasi dari HTML #

Empat pseudo-class dasar membaca keadaan validasi yang dihitung browser:

Pseudo-classCocok ketika
:requiredkolom memiliki attribute required
:optionalkolom tidak memiliki attribute required
:validnilai kolom memenuhi seluruh batasan yang berlaku
:invalidnilai kolom melanggar minimal satu batasan

Batasan itu datang dari HTML, bukan CSS: required menuntut kolom terisi, type="email" menuntut pola alamat email, dan attribute minlength menuntut panjang minimal. CSS hanya membaca hasil perhitungannya:

css
.kolom-email:invalid {
  border-color: #b3362b;
}

.kolom-email:valid {
  border-color: #1f6f54;
}

.kolom-email:required {
  border-left: 4px solid #f4b942;
}

Demo berikut belum disentuh apa pun, tetapi kolomnya sudah merah — isi alamat sah untuk melihatnya berubah hijau:

Masalah: Merah yang Muncul Terlalu Dini #

Perhatikan kejanggalan pada contoh di atas. Halaman baru saja dibuka, pengguna belum menyentuh apa pun, tetapi kolom email sudah merah — karena kolom kosong yang required memang cocok dengan :invalid sejak awal. Form yang menghukum pengguna sebelum mereka melakukan kesalahan memberi kesan buruk dan membuat tanda merah kehilangan maknanya.

Solusinya adalah pasangan pseudo-class yang lebih cerdas: :user-valid dan :user-invalid. Keduanya hanya cocok setelah pengguna benar-benar berinteraksi dengan kolom — mengetik lalu berpindah kolom, misalnya. Sebelum sentuhan pertama, kedua pseudo-class itu tidak mencocokkan apa pun:

css
.kolom-email:user-invalid {
  border-color: #b3362b;
}

.kolom-email:user-valid {
  border-color: #1f6f54;
}

Perbandingan perilakunya:

Pseudo-classMuncul kapanCocok untuk
:invalidLangsung, sejak halaman dimuatDeteksi teknis, menandai kolom wajib sebelum submit
:user-invalidSetelah pengguna mengubah isi kolomUmpan balik kesalahan yang adil

Perlu diketahui, styling ini berjalan di atas mekanisme submit bawaan browser: form yang masih memiliki kolom :invalid tidak akan terkirim, dan browser menampilkan gelembung pesan miliknya sendiri. CSS kita tidak mengubah perilaku itu — ia menambahkan umpan balik visual yang lebih awal dan lebih lembut sebelum pengguna sampai ke tombol submit. Bila validasi kustom penuh dibutuhkan, attribute novalidate pada <form> mematikan mekanisme bawaan tersebut dan pengurusannya pindah ke JavaScript, yang berada di luar cakupan course CSS ini.

Pesan Error dan Sukses dengan Sibling Selector #

Border berwarna bagus, tetapi pengguna butuh kalimat yang menjelaskan. Pola yang umum: letakkan elemen pesan tepat setelah input, sembunyikan secara bawaan, lalu tampilkan lewat general sibling selector ~ dari section Selectors:

html
<label for="demo-email">Email</label>
<input id="demo-email" class="kolom-email" type="email" required>
<p class="pesan-email">Tulis alamat email yang sah, misalnya [email protected]</p>
css
.pesan-email {
  display: none;
  margin: 6px 0 0;
  font-size: 0.875rem;
  color: #b3362b;
}

.kolom-email:user-invalid ~ .pesan-email {
  display: block;
}

.kolom-email:user-invalid {
  border-color: #b3362b;
}

Karena pesannya paragraf sungguhan, ia bisa dibacakan pembaca layar dan tidak bergantung pada warna border semata. Seluruh perpindahan state pada demo ini dihitung browser dari attribute type="email" dan required — tanpa JavaScript.

Ringkasan #

HTML constraint validation menyediakan state-nya, CSS menyediakan tampilannya: :required dan :optional menandai kebijakan kolom, sedangkan :valid dan :invalid membaca hasil perhitungan browser. Untuk umpan balik yang adil, :user-valid dan :user-invalid menunda reaksi sampai pengguna benar-benar menyunting kolom, dan pesan kesalahan tampil lewat sibling selector alih-alih bergantung pada warna border. Chapter berikutnya membahas dua state yang arahnya berbeda — bukan tentang isi yang salah, melainkan tentang kolom yang sengaja dikunci: disabled dan readonly.