Sebuah komponen tidak hanya punya satu tampilan — tombol punya wajah saat diarahkan kursor, saat ditekan, saat menerima fokus keyboard, dan saat tidak dapat dipakai. Pada sistem desain, setiap wajah itu bukan improvisasi per komponen, melainkan kontrak visual: daftar state yang wajib ada, beserta perubahan gaya yang disepakati untuk masing-masing. Mekanisme pseudo-classnya sudah kita kuasai pada Chapter Hover dan Active serta Focus dan Focus-visible ; chapter ini menyusunnya menjadi kontrak sistem dan menerapkannya pada tombol serta input.
Kontrak State untuk Tombol dan Input #
Kontrak yang baik mencakup empat state interaksi untuk elemen yang dapat dipakai, dan kontrak itu berlaku sama untuk seluruh komponen sejenis: dua tombol apa pun di sistem harus merespons hover, fokus, tekan, dan kondisi mati dengan pola yang identik. Konsistensi inilah yang membuat pengguna berhenti perlu menebak perilaku:
| State | Selector | Perubahan pada tombol |
|---|---|---|
| Istirahat | .tombol | Latar --biru-600, teks putih |
| hover | .tombol:hover | Latar melangkah ke tingkatan lebih gelap |
| focus | .tombol:focus-visible | Outline 2px biru langit dengan offset 2px |
| active | .tombol:active | Latar paling gelap pada skala |
| disabled | .tombol:disabled | Opacity 0,5 dan kursor not-allowed |
File yang dipakai demo menerapkan kontrak itu pada dua komponen:
.tombol {
--tombol-latar: rgb(29, 78, 216);
--tombol-teks: rgb(255, 255, 255);
background-color: var(--tombol-latar);
color: var(--tombol-teks);
padding: 8px 16px;
border: 0;
border-radius: 8px;
font-size: 14px;
transition: background-color 0.15s ease;
}
.tombol:hover { background-color: rgb(30, 64, 175); }
.tombol:active { background-color: rgb(30, 58, 138); }
.tombol:focus-visible { outline: 2px solid rgb(2, 132, 199); outline-offset: 2px; }
.tombol:disabled { opacity: 0.5; cursor: not-allowed; }
.input {
border: 1px solid rgb(203, 213, 225);
border-radius: 8px;
padding: 8px 12px;
font-size: 14px;
}
.input:focus-visible {
outline: 2px solid rgb(2, 132, 199);
border-color: rgb(29, 78, 216);
}Mari kita telaah keputusannya:
- State sebagai aturan terpisah, bukan penulisan ulang:
.tombolmendeklarasikan gaya istirahat sekali; setiap state hanya menimpa properti yang berubah. Perubahan latar mengikuti urutan ramp dari Chapter Color Palettes — hover dan active adalah tingkatan yang lebih gelap dari token istirahat. :focus-visible, bukan:focus: outline hanya muncul saat fokus datang dari navigasi keyboard, sehingga pengguna mouse tidak melihat cincin yang tidak perlu; pengguna keyboard tetap mendapat petunjuk fokus sebagaimana dibahas pada Chapter Visible Focus .:disabledmemakai opacity: nilai computedbackground-colorpada tombol mati tidak berubah — peredupan terjadi pada tingkat penggambaran. Ini membuat state mati murah dan berlaku pada semua isi tombol sekaligus.
Perhatikan juga satu detail pada transisi: transition di .tombol membuat perpindahan antar-state bergerak halus, tetapi pergerakan itu bukan untuk semua orang. Pengguna yang mengaktifkan preferensi gerak minim perlu dihormati dengan membungkus transisi dalam @media (prefers-reduced-motion: no-preference) — pola lengkapnya kita bahas pada Chapter Reduced Motion
.
Menguji Kontrak secara Otomatis #
Tidak semua state dapat diprobe secara otomatis: :hover dan :active menuntut interaksi pointer yang tidak bisa dilakukan harness, sehingga keduanya diverifikasi dengan mencoba manual di browser. Yang bisa diverifikasi otomatis adalah state yang berlaku statis: :disabled pada tombol yang beratribut disabled, dan :focus-visible pada input yang menerima fokus saat halaman dimuat. Demo chapter ini memprobe ketiganya — dua tombol dan satu input yang otomatis difokuskan oleh skrip kecil.
Hasil di Browser:
Tombol istirahat tampil berlatar rgb(29, 78, 216).
Tombol mati tercatat opacity 0.5 dengan kursor not-allowed,
sementara computed background-color-nya tetap rgb(29, 78, 216).
Input yang difokuskan saat halaman dimuat membawa outline-width 2px,
outline-color rgb(2, 132, 199), dan border-color rgb(29, 78, 216).Nilai-nilai tersebut diverifikasi otomatis melalui penanda data-expected. Saat membangun komponen baru, salin kontraknya terlebih dahulu sebagai komentar, lalu isi implementasinya — pola state yang sama berlaku untuk elemen bentuk pada Chapter Checked dan Disabled
serta Focus States
.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
contoh-kontrak-state.css
Unduh berkas.tombol {
--tombol-latar: rgb(29, 78, 216);
--tombol-teks: rgb(255, 255, 255);
background-color: var(--tombol-latar);
color: var(--tombol-teks);
padding: 8px 16px;
border: 0;
border-radius: 8px;
font-size: 14px;
transition: background-color 0.15s ease;
}
.tombol:hover { background-color: rgb(30, 64, 175); }
.tombol:active { background-color: rgb(30, 58, 138); }
.tombol:focus-visible { outline: 2px solid rgb(2, 132, 199); outline-offset: 2px; }
.tombol:disabled { opacity: 0.5; cursor: not-allowed; }
.input {
border: 1px solid rgb(203, 213, 225);
border-radius: 8px;
padding: 8px 12px;
font-size: 14px;
}
.input:focus-visible {
outline: 2px solid rgb(2, 132, 199);
border-color: rgb(29, 78, 216);
}