Formulir adalah komponen yang paling menuntut aksesibilitas, karena formulir memerlukan isian yang tepat dari pengguna dan menampilkan error apabila isian itu keliru. Topik ini membahas label yang terhubung ke kontrolnya, petunjuk yang dihubungkan dengan aria-describedby, penandaan masukan wajib, pengumuman error yang dapat didengar pengguna pembaca layar, dan rekap seluruh prasyarat formulir yang dapat diakses. Fondasinya adalah label dan validasi bawaan yang sudah dibahas pada modul Formulir dan Masukan, sedangkan topik ini menelaah keduanya dari sisi alat bantu.
Label yang terhubung ke kontrol#
Setiap kontrol formulir memerlukan label yang terhubung, yaitu elemen label yang berpasangan dengan kontrolnya melalui atribut for pada label dan id pada kontrol. Pasangan itu memberi kontrol sebuah nama aksesibel yang dibacakan pembaca layar, sekaligus memperluas sasaran klik karena menekan label memindahkan fokus ke kontrolnya. Asosiasi eksplisit semacam ini sudah diperkenalkan pada modul formulir, dan yang ditinjau ulang di sini adalah akibatnya bagi alat bantu.
<label for="nama">Nama peminjam</label>
<input type="text" id="nama" name="nama">Tanpa asosiasi tersebut, pembaca layar membacakan masukan sebagai kotak teks kosong tanpa nama, dan pengguna tidak tahu isian apa yang diminta. Pada pembaca layar, kontrol yang berfokus diumumkan dengan jenisnya beserta nama labelnya, misalnya Nama peminjam, kotak teks. Mengganti label dengan atribut placeholder tidak menyelesaikan masalah, karena teks placeholder hilang begitu kotak terisi dan tidak semua pembaca layar membacakannya sebagai nama. Satu label terhubung satu kontrol, dan keduanya sebaiknya berdampingan dalam urutan DOM agar urutan Tab dan urutan pembacaan tetap selaras.
Petunjuk dan deskripsi dengan aria-describedby#
Petunjuk seperti format yang benar perlu dihubungkan ke kontrolnya secara eksplisit, karena kedekatan posisi di layar tidak menjamin keduanya dibacakan bersama oleh pembaca layar. Atribut aria-describedby menghubungkan kontrol dengan satu atau beberapa id elemen yang isinya menjadi deskripsi kontrol itu, dan teksnya dibacakan setelah nama dari label.
<label for="email">Surel</label>
<input type="email" id="email" name="email"
aria-describedby="petunjuk-email error-email">
<p id="petunjuk-email">Format yang benar, misalnya [email protected]</p>
<p id="error-email" class="error" role="alert" hidden>Surel belum lengkap.</p>Atribut itu menerima beberapa id sekaligus yang dibacakan sesuai urutannya, dan contoh di atas sengaja memasukkan error-email ke dalam daftarnya sehingga pesan error juga menjadi deskripsi kontrol. Atribut ini dapat ditulis pada posisi mana pun di dalam tag kontrol, tetapi id yang dirujuk harus ada di halaman yang sama, sebagaimana pasangan for dan id pada label. Tanpa penghubung ini, petunjuk hanya sekelompok teks biasa yang tidak terkait dengan apa pun menurut alat bantu. Dengan pola ini, petunjuk selalu terdengar ketika kontrol difokuskan, dan pesan error ikut terbaca ketika isian keliru. Bagian berikut membahas cara pesan error itu muncul dan diumumkan.
Menandai masukan yang wajib diisi#
Atribut required adalah penanda wajib yang paling bermanfaat, karena browser menolak pengiriman tanpa isian dan pembaca layar mengumumkan kontrol itu sebagai wajib. Untuk kontrol yang tidak mendukung required, seperti widget buatan sendiri, atribut aria-required="true" menyatakan hal yang sama kepada alat bantu.
<label for="nama">Nama peminjam (wajib)</label>
<input type="text" id="nama" name="nama" required>Tanda bintang yang hanya ditegaskan dengan warna tidak cukup, karena makna simbol itu tidak otomatis dibacakan dan warna tidak terlihat oleh semua pengguna. Kewajiban isian perlu dinyatakan dengan teks yang jelas, seperti kata wajib pada label di atas, disertai atribut required. Pembaca layar umumnya menambahkan kata wajib ketika membacakan kontrol beratribut required, sehingga syarat itu terdengar tanpa usaha tambahan. Kombinasi teks pada label dan atribut tersebut membuat penandaan ini lengkap bagi pengguna yang melihat maupun yang mendengarkan.
Pengumuman error yang dapat diakses#
Error yang hanya diperlihatkan secara visual gagal bagi pengguna pembaca layar, karena tidak ada pengumuman bahwa pengiriman ditolak dan di mana letak kesalahannya. Dua hal harus dikerjakan sekaligus, yaitu mengumumkan dan menghubungkan. Pengumuman menggunakan role="alert", yang menyiratkan wilayah pengumuman (live region) dengan sifat aria-live="assertive". Pengumuman yang lebih tenang menggunakan wilayah aria-live="polite". Penghubungan menggunakan aria-invalid="true" pada kontrol yang keliru beserta aria-describedby yang menunjuk pesan errornya.
// tidak-dijalankan
const input = document.querySelector("#email");
const pesan = document.querySelector("#error-email");
input.addEventListener("change", () => {
const sah = input.checkValidity();
input.setAttribute("aria-invalid", String(!sah));
pesan.hidden = sah;
});Skrip di atas menggunakan API browser sehingga tidak dieksekusi di sandbox, tetapi polanya terbaca, yaitu status keliru dinyatakan pada kontrol dan pesannya diperlihatkan pada elemen yang sudah dirujuk aria-describedby. Apabila pengiriman ditolak, fokus juga dipindahkan ke kontrol pertama yang keliru agar pengguna tidak mencarinya ulang.
Rekap prasyarat formulir yang dapat diakses#
Sebelum formulir dinyatakan selesai, seluruh butir berikut diperiksa satu per satu pada setiap kontrolnya.
- Setiap kontrol memiliki label yang terhubung melalui pasangan
fordanid. - Petunjuk format terhubung melalui
aria-describedbydan dibacakan setelah nama label. - Masukan wajib ditandai
requireddan dinyatakan dengan teks, bukan hanya warna atau simbol. - Error diumumkan melalui
role="alert"atau wilayaharia-liveyang sudah berada di DOM. - Kontrol yang keliru diberi
aria-invalid="true"dan pesan errornya dirujuk oleharia-describedby. - Tombol kirim memiliki teks yang menyatakan akibatnya, misalnya Kirim peminjaman.
- Kelompok kontrol pilihan terkemas dalam
fieldsetdenganlegendyang menjelaskan pertanyaannya. - Formulir dapat diselesaikan hanya dengan papan ketik, termasuk perpindahan fokus ke kontrol pertama yang keliru.
Daftar ini memadukan materi modul formulir dengan materi topik ini, sehingga dapat digunakan sebagai daftar periksa saat mengaudit halaman. Butir pertama sampai kelima menyangkut isian dan error, butir keenam dan ketujuh menyangkut pengiriman beserta kelompok kontrol, sedangkan butir terakhir menutup jalur papan ketik.
Berkas utuh formulir-aksesibel.html#
Berkas berikut menerapkan seluruh prasyarat itu pada formulir peminjaman buku yang memuat label terhubung, petunjuk ber-aria-describedby, penanda wajib, pesan error ber-role="alert", serta aria-invalid yang diperbarui skrip. Atribut novalidate digunakan agar jalur error kustom selalu berjalan, dan fokus dipindahkan ke kontrol pertama yang keliru saat pengiriman ditolak.
Buka berkasnya di browser, kosongkan nama, isi surel dengan teks sembarangan, lalu kirim. Pesan error muncul di bawah masing-masing kontrol dan dibacakan pembaca layar berkat role="alert", sedangkan pembetulan menghapus penanda aria-invalid beserta pesannya. Kedua pesan error tersebut berada di DOM dalam keadaan tersembunyi sejak halaman dimuat, dan skrip hanya mengisi teksnya lalu memperlihatkannya, sesuai pola wilayah pengumuman pada bagian sebelumnya. Fokus yang dipindahkan skrip ke kontrol pertama yang keliru membuat pembetulan dapat dimulai tanpa mencari ulang letak kesalahannya.
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<title>Formulir yang dapat diakses</title>
<style>
body { font-family: sans-serif; margin: 0 auto; max-width: 32rem; padding: 0 1rem; }
.kolom { margin-bottom: 1rem; }
label { display: block; font-weight: 600; }
input { width: 100%; padding: 8px; box-sizing: border-box; }
.petunjuk { color: #555555; font-size: 0.9rem; margin: 0.25rem 0 0; }
.error { color: #b00020; font-weight: 600; margin: 0.25rem 0 0; }
input[aria-invalid="true"] { border: 2px solid #b00020; }
button { padding: 8px 16px; }
</style>
</head>
<body>
<h1>Formulir peminjaman buku</h1>
<form id="form-pinjam" novalidate>
<div class="kolom">
<label for="nama">Nama peminjam (wajib)</label>
<input type="text" id="nama" name="nama" required
aria-describedby="error-nama">
<p class="error" id="error-nama" role="alert" hidden></p>
</div>
<div class="kolom">
<label for="email">Surel</label>
<input type="email" id="email" name="email" required
aria-describedby="petunjuk-email error-email">
<p class="petunjuk" id="petunjuk-email">Format yang benar, misalnya [email protected]</p>
<p class="error" id="error-email" role="alert" hidden></p>
</div>
<button type="submit">Kirim peminjaman</button>
</form>
<script>
const form = document.getElementById("form-pinjam");
form.addEventListener("submit", (event) => {
event.preventDefault();
let sah = true;
const nama = document.getElementById("nama");
const errorNama = document.getElementById("error-nama");
if (nama.value.trim() === "") {
errorNama.textContent = "Nama peminjam wajib diisi.";
errorNama.hidden = false;
nama.setAttribute("aria-invalid", "true");
sah = false;
} else {
errorNama.hidden = true;
nama.removeAttribute("aria-invalid");
}
const email = document.getElementById("email");
const errorEmail = document.getElementById("error-email");
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value)) {
errorEmail.textContent = "Surel belum memuat tanda @ dan alamat yang lengkap.";
errorEmail.hidden = false;
email.setAttribute("aria-invalid", "true");
sah = false;
} else {
errorEmail.hidden = true;
email.removeAttribute("aria-invalid");
}
if (!sah) {
const pertama = form.querySelector('[aria-invalid="true"]');
pertama.focus();
} else {
form.reset();
}
});
</script>
</body>
</html>Kesalahan umum#
Menyandikan label pada placeholder. Teks placeholder menghilang saat kotak terisi dan tidak dijamin dibacakan sebagai nama, sehingga isian menjadi tanpa nama. Gunakan elemen label yang terhubung, dan perlakukan placeholder hanya sebagai pelengkap.
Menggunakan warna merah sebagai satu-satunya penanda error. Warna tidak terlihat oleh semua pengguna, dan pembaca layar tidak membacakan warna. Tambahkan teks pesan error yang diumumkan role="alert" dan dirujuk aria-describedby.
Menulis aria-describedby yang menunjuk id yang tidak ada. Atribut yang menunjuk elemen kosong atau salah id menghasilkan deskripsi yang hilang, dan petunjuknya tidak pernah terdengar. Periksa kembali setiap id yang dirujuk, termasuk saat markup dipindahkan.
Ringkasan#
- Label yang terhubung memberi nama aksesibel dan memperluas sasaran klik, sedangkan placeholder tidak dapat menggantikannya.
aria-describedbymenghubungkan petunjuk dan pesan error ke kontrolnya, dan teksnya dibacakan setelah nama label.- Error diumumkan dengan
role="alert"atau wilayaharia-live, lalu dihubungkan ke kontrolnya denganaria-invaliddanaria-describedby. - Formulir yang dapat diakses menuntut label, deskripsi, penanda wajib, pengumuman error, dan jalur papan ketik yang lengkap sekaligus.
Referensi#
- Forms Tutorial — W3C WAI — Panduan resmi formulir yang dapat diakses beserta daftar periksanya.
- Labels — W3C WAI — Teknik asosiasi label pada setiap jenis kontrol.
- Notifications — W3C WAI — Pola pemberitahuan error pada formulir.
- aria-describedby — MDN Web Docs — Dokumentasi atribut penghubung deskripsi.
- aria-invalid — MDN Web Docs — Dokumentasi atribut penanda nilai keliru.
- aria-live — MDN Web Docs — Dokumentasi wilayah live dan tingkat kesopanannya.
- Elemen label — MDN Web Docs — Dokumentasi asosiasi label dan kontrol.
- Learn Accessibility: Forms — web.dev — Bab formulir pada kursus aksesibilitas web.dev.