Automated accessibility testing artinya menyuruh program memeriksa halaman dengan daftar aturan yang bisa dicek mesin: gambar tanpa alt, kontras kurang, kolom tanpa label, bahasa yang gak disebut. Bayangin spellchecker di Google Docs. Dia langsung menggarisbawahi "kamr", tapi diam saja saat kamu menulis "kamar ini menghadap laut" padahal kamarnya menghadap bukit. Ejaannya benar, isinya salah. Alat otomatis seperti axe dan Lighthouse adalah spellchecker untuk aksesibilitas: cepat, gak pernah capek, dan buta terhadap makna. Tim aksesibilitas pemerintah Inggris (GOV.UK) memakai perumpamaan yang sama.
🧪 Tebakan cepat: Setelah lima perbaikan, halaman pesan Bu Sri dapat 0 violation di axe dan skor 100 di Lighthouse. Tombol "Pesan kamar, bayar di tempat" masih
<div onclick>. Bisa Pak Hadi (58), yang memesan pakai keyboard, menekan tombol itu? Jawabannya ada di bagian "Skor 100 bukan berarti accessible".
Halaman yang kita uji
Kita pakai halaman pesan Kamar Teras yang sama dengan Lab di halaman ini, dan sengaja menanam 9 masalah. Semua angka di pelajaran ini berasal dari menjalankan alatnya sungguhan di halaman itu: axe-core 4.14.0 dan Lighthouse 13.5.0 di Chromium 147.
| Masalah | Kriteria WCAG | Ketahuan oleh |
|---|---|---|
<html> tanpa lang |
3.1.1 | axe, Lighthouse |
foto kamar tanpa alt |
1.1.1 | axe, Lighthouse |
| tombol hati (favorit) cuma berisi ikon, tanpa nama | 4.1.2 | axe, Lighthouse |
kolom Nomor WhatsApp tanpa <label> |
4.1.2, 1.3.1 | axe, Lighthouse |
catatan "Gratis batal..." #94a3b8 di atas putih |
1.4.3 | axe, Lighthouse |
tombol Pesan dari <div onclick> |
2.1.1 | cek keyboard |
peta rute dengan alt="gambar" |
1.1.1 | orang yang membaca alt-nya |
:focus { outline: none; } |
2.4.7 | cek keyboard |
kartu width: 480px |
1.4.10 | cek lebar 320px |
Lima ketahuan mesin, empat cuma ketahuan orang.
axe: aturan yang dijalankan di halaman
axe-core adalah kumpulan aturan open source buatan Deque. Versi 4.14 punya 105 aturan, 70 di antaranya bertanda WCAG level A atau AA. Kamu bisa memakainya lewat ekstensi axe DevTools (tab "axe" di DevTools, lalu scan), lewat Lighthouse (yang menjalankan sebagian aturan axe), atau dari kode, misalnya @axe-core/playwright. Di halaman yang sudah memuat axe-core:
const results = await axe.run();
for (const v of results.violations) {
console.log(`${v.id} (${v.impact}): ${v.help}`);
for (const node of v.nodes) console.log(` ${node.target.join(' ')}`);
}
console.log(`${results.violations.length} violations`);
Hasilnya di halaman Bu Sri, persis seperti yang tercetak:
button-name (critical): Buttons must have discernible text
.fav
color-contrast (serious): Elements must meet minimum color contrast ratio thresholds
.note
html-has-lang (serious): <html> element must have a lang attribute
html
image-alt (critical): Images must have alternative text
img[height="150"]
label (critical): Form elements must have labels
#wa
5 violations
results.violations: aturan yang gagal.v.idnama aturannya; penjelasan lengkapnya ada didequeuniversity.com/rules/axe/4.14/<id>.v.impact: seberapa berat untuk pengguna, dariminor,moderate,serious, sampaicritical.node.target: selector CSS elemen yang gagal.node.failureSummarymenjelaskan kenapa, misalnya untuk.note: "Element has insufficient color contrast of 2.56 (foreground color: #94a3b8, background color: #ffffff, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1".results.incomplete: needs review, hal yang axe curigai tapi gak bisa diputuskan sendiri. Harus dicek orang, dan tes yang cuma memeriksaviolationsgak melihatnya.
Satu contoh yang sering disalahpahami: id ganda. Mendeteksinya pekerjaan mesin, cukup membandingkan nilai id. Tapi di axe-core 4.14, aturan duplicate-id sudah deprecated dan mati secara bawaan, karena WCAG 2.2 menghapus kriteria 4.1.1 (Parsing). Dua <p id="total"> gak dilaporkan, kecuali kamu menyalakannya: axe.run({ rules: { 'duplicate-id': { enabled: true } } }). Kalau id ganda itu dipakai aria-describedby atau <label for>, aturan duplicate-id-aria menangkapnya, sebagai needs review.
Lighthouse: skor dari aturan axe yang diberi bobot
Lighthouse (DevTools, tab Lighthouse, kategori Accessibility) menjalankan sebagian aturan axe, memberi tiap audit bobot dari impact-nya, lalu menghitung bobot audit yang lolos dibagi bobot audit yang berlaku. Gak ada nilai setengah: satu tombol tanpa nama membuat seluruh audit button-name gagal.
| Audit yang gagal di halaman Bu Sri | Bobot |
|---|---|
image-alt, button-name, label |
10 masing-masing |
html-has-lang, color-contrast |
7 masing-masing |
Halaman awal: 18 audit berlaku dengan total bobot 136, yang gagal berbobot 44, jadi 92 / 136 = 68. Setelah lima perbaikan, dua audit baru ikut berlaku (html-lang-valid dan aria-hidden-focus, 7 masing-masing), semuanya lolos: 150 / 150 = 100. Di Lab, skornya naik bertahap 68, 74, 81, 89, 95, 100, sama dengan Lighthouse sungguhan.
Skor 100 bukan berarti accessible
Di halaman yang skornya 100 itu, Tab berhenti di 9 tempat: empat link navigasi, tombol hati, tiga tombol malam, dan kolom WhatsApp. Tombol Pesan gak pernah kena, karena <div> gak bisa difokus. Jadi jawaban tebakannya: gak bisa. Gak ada aturan axe yang memeriksa onclick di <div>, dan Lighthouse menaruh "Interactive controls are keyboard focusable" di daftar Additional items to manually check dengan bobot 0. Itu jawaban tebakannya. Lighthouse sendiri menulis di laporannya: "Automatic detection can only detect a subset of issues and does not guarantee the accessibility of your web app, so manual testing is also encouraged."
Berapa besar bagian yang ditangkap mesin? Tergantung cara menghitungnya:
- GOV.UK ("What we found when we tested tools on the world's least-accessible webpage", Mehmet Duran, 2017) menanam 143 masalah dan menguji 10 alat. Alat terbaik menemukan 41% (termasuk peringatan yang minta dicek manual), yang terlemah 17%, dan gabungan kesepuluhnya 71%. Sebanyak 29% gak ditemukan alat mana pun.
- Deque ("The Automated Accessibility Coverage Report", 2021), pembuat axe sendiri, menghitung dari lebih dari 2.000 audit, 13.000 halaman, dan hampir 300.000 masalah: tes otomatis axe menemukan 57,38% dari jumlah masalah. Dihitung per kriteria, cuma 16 dari 50 kriteria WCAG 2.1 AA yang tercakup. Angka per jumlah itu tinggi karena masalah yang paling sering muncul justru yang paling mudah dicek mesin: kontras sendirian 30,08% dari semua masalah.
Angka "30 sampai 40%" yang sering dikutip ada di antara keduanya. Intinya sama: alat otomatis menangkap masalah yang paling sering dan paling mudah ditangkap, di setiap build, tanpa capek. Sisanya tetap butuh orang.
eslint-plugin-jsx-a11y: cek sejak di editor
axe memeriksa halaman yang sudah jadi. Untuk React, eslint-plugin-jsx-a11y memeriksa JSX-nya, jadi garis merah muncul di editor sebelum halamannya dibuka.
// eslint.config.mjs
import jsxA11y from 'eslint-plugin-jsx-a11y';
export default [
{ files: ['**/*.{js,jsx}'], ...jsxA11y.flatConfigs.recommended },
];
Di komponen KartuKamar.jsx dengan <img src="/kamar-teras.jpg" /> (baris 5), <img src="/peta.png" alt="gambar" /> (baris 6), <div className="book" onClick={pesan}> (baris 7), dan catatan berwarna #94a3b8, npx eslint KartuKamar.jsx (plugin 6.10.2) mencetak:
5:7 error img elements must have an alt prop, either with meaningful text, or an empty string for decorative images jsx-a11y/alt-text
7:7 error Visible, non-interactive elements with click handlers must have at least one keyboard listener jsx-a11y/click-events-have-key-events
7:7 error Avoid non-native interactive elements. ... jsx-a11y/no-static-element-interactions
Linter melihat onClick di <div>, yang lolos dari axe. Tapi dia gak tahu warna akhirnya (kontras gak dicek), dan img-redundant-alt cuma mencari kata Inggris "image", "photo", "picture", jadi alt="gambar" lolos. Tiap alat melihat lapisan yang berbeda.
Di CI: gagal kalau ada violation baru
Pasang axe di tes Playwright, lalu jalankan di CI (misalnya GitHub Actions) setiap ada pull request. Situs lama biasanya sudah punya utang violation; simpan daftarnya di a11y-baseline.json, dan buat tes gagal cuma kalau muncul violation yang baru.
// tests/a11y.spec.js
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
import baseline from './a11y-baseline.json' with { type: 'json' };
test('halaman pesan Kamar Teras', async ({ page }) => {
await page.goto('/kamar/teras');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa'])
.analyze();
const found = results.violations.flatMap((v) => v.nodes.map((n) => `${v.id} ${n.target.join(' ')}`));
const baru = found.filter((f) => !baseline.includes(f));
expect(baru).toEqual([]);
});
withTagsmembatasi aturan ke WCAG A dan AA, dari 2.0 sampai 2.2.foundberisi baris seperti"label #wa": id aturan dan selector elemennya.- Baseline berisi lima violation lama halaman Bu Sri. Saat seseorang menambah kolom "Nama tamu" tanpa label, tesnya gagal dengan
+ "label #nama", dan job CI merah. Jadikan job itu wajib di branch protection, supaya PR-nya gak bisa di-merge sebelum diperbaiki. - Setiap kali utang lama dibayar, hapus barisnya dari baseline, supaya violation itu gak bisa kembali diam-diam.
Cek manual yang wajib menyusul
Setelah axe bersih, lakukan tiga cek ini. Masing-masing menemukan masalah di halaman Bu Sri yang skornya 100:
- Keyboard. Tab dan Shift+Tab sampai ujung halaman, Enter dan Spasi di tiap tombol, Escape di dialog. Apakah semua bisa dicapai (2.1.1), urutannya masuk akal (2.4.3), dan fokusnya kelihatan (2.4.7)? Di sini: Pesan gak bisa dicapai, dan fokusnya gak terlihat. Polanya ada di pelajaran Keyboard Navigation Patterns.
- Zoom dan lebar sempit. Zoom 200%: teks harus tetap terbaca tanpa ada yang hilang (1.4.4 Resize Text). Lalu sempitkan jendela ke 320 CSS px, setara layar 1.280px yang di-zoom 400%: halaman gak boleh perlu digeser ke samping (1.4.10 Reflow). Dengan kartu 480px, halaman Bu Sri jadi 538px lebarnya, jadi di layar 320px harus digeser 218px.
- Screen reader. Dengarkan halamannya dengan NVDA dan Chrome, atau VoiceOver dan Safari. Peta dibacakan "gambar", dan cuma telinga orang yang tahu itu gak berguna. Skrip tesnya ada di pelajaran Screen Reader Testing.
⚠️ Jebakan yang sering ketemu
- Menganggap skor 100 sama dengan accessible. Lighthouse sendiri bilang gak menjamin. Halaman dengan tombol yang gak bisa di-Tab tetap dapat 100.
- Tes CI yang cuma memeriksa
violations. Hal yang masukincomplete(needs review), sepertiduplicate-id-aria, lewat tanpa ada yang melihat. - Mengira axe menangkap semua yang bisa dicek mesin. Di axe-core 4.14,
duplicate-idmati secara bawaan; nyalakan kalau kamu butuh. - Baseline yang gak pernah dikurangi. Utang lama jadi permanen, dan baseline-nya berubah jadi daftar pengecualian.
TL;DR: Alat otomatis itu spellchecker aksesibilitas: menangkap yang bisa dicek mesin, buta terhadap makna. axe-core (Deque, 105 aturan di 4.14) melaporkan violations dengan id, impact (minor sampai critical), help, dan selector target; incomplete berarti needs review. Di halaman Bu Sri, axe menemukan 5 dari 9 masalah: html-has-lang, image-alt, button-name, label, color-contrast (2,56:1). Id ganda bisa dicek mesin, tapi duplicate-id mati secara bawaan sejak WCAG 2.2 menghapus 4.1.1. Lighthouse menghitung bobot audit yang lolos dibagi yang berlaku (68 jadi 100), tanpa nilai setengah, dan menyebut sendiri bahwa skornya gak menjamin halaman accessible: tombol Pesan dari <div onclick> tetap gak bisa di-Tab di halaman yang skornya 100. Cakupan alat: GOV.UK 2017, satu alat 17 sampai 41%; Deque 2021, 57,38% dari jumlah masalah tapi cuma 16 dari 50 kriteria AA, karena masalah yang paling sering (kontras) paling mudah dicek. eslint-plugin-jsx-a11y menangkap onClick di <div> sejak di editor. Di CI, @axe-core/playwright dengan baseline membuat build gagal cuma untuk violation baru. Setelah itu wajib cek manual: keyboard (2.1.1, 2.4.3, 2.4.7), zoom 200% (1.4.4) dan lebar 320 CSS px (1.4.10), dan screen reader.