Hanya 4,1% situs web yang memenuhi standar WCAG 2.2 Level AA. Laporan WebAIM Million 2025 menemukan 95,9% situs web gagal persyaratan aksesibilitas dasar, rata-rata 51 kesalahan per halaman. Dengan 4.605 gugatan ADA yang diajukan terhadap situs web di 2024 dan European Accessibility Act yang berlaku dari Juni 2025, aksesibilitas kini merupakan risiko hukum dan bisnis, bukan hanya praktik terbaik. Axe-core, mesin aksesibilitas open-source Deque (diunduh 4 miliar kali), dapat mendeteksi secara otomatis 57% pelanggaran WCAG.
Alat aksesibilitas otomatis unggul dalam masalah struktural: teks alt yang hilang pada gambar, input formulir tanpa label, rasio kontras warna yang tidak cukup, peran ARIA yang hilang, pelanggaran hierarki heading, masalah urutan fokus keyboard, dan region landmark yang hilang. Apa yang tidak dapat ditangkap: apakah teks alt bermakna, apakah widget kustom benar-benar dapat dinavigasi keyboard oleh pengguna pembaca layar.
Axe-core menjalankan pemeriksaan aksesibilitas dalam konteks browser dan mengembalikan hasil terstruktur: pelanggaran (masalah pasti), tidak lengkap (pemeriksaan yang memerlukan tinjauan manual), lulus, dan tidak berlaku. Setiap pelanggaran mencakup elemen HTML yang menyebabkan masalah, kriteria sukses WCAG yang dilanggar, rating dampak (critical/serious/moderate/minor), dan panduan remediasi.
Tes aksesibilitas manual paling sederhana: cabut mouse Anda dan coba gunakan aplikasi Anda sendiri hanya dengan keyboard. Tab untuk menavigasi antara elemen interaktif. Enter/Space untuk mengaktifkan tombol. Tombol panah untuk menu dan slider. Escape untuk menutup modal. Jika Anda tidak dapat menjangkau fitur dengan keyboard saja, pengguna keyboard-only dan pengguna pembaca layar juga tidak dapat mengaksesnya.
Accessibility Testing Coverage:
────────────────────────────────────────────────────────────
Automated (axe-core catches ~57% of WCAG violations):
✓ Missing alt text on images
✓ Form inputs without labels
✓ Color contrast failures (4.5:1 for text, 3:1 for large)
✓ Missing ARIA landmarks (main, nav, header, footer)
✓ Heading hierarchy violations (h1 → h3, skipping h2)
✓ Empty buttons/links (no accessible name)
✓ Missing lang attribute on <html>
Manual review required (43% of violations):
✗ Alt text accuracy (exists but meaningless "image.jpg")
✗ Keyboard navigation logic and flow order
✗ Screen reader announcement quality
✗ Context-dependent ARIA usage
✗ Cognitive accessibility (plain language, readability)
WCAG 2.2 Compliance Stats (WebAIM Million 2025):
Websites passing Level AA: 4.1%
Average errors per page: 51 (down from 56.8 in 2024)
Most common failures:
1. Low contrast text (83% of sites)
2. Missing alt text (55% of sites)
3. Empty links (50% of sites)
4. Missing form labels (48% of sites)Dari pengalaman saya mengaudit matthewswong.com untuk aksesibilitas: pelanggaran paling umum yang saya temukan adalah 1) tombol ikon tanpa nama yang dapat diakses (tombol X untuk menutup modal tanpa aria-label atau teks sr-only), 2) kegagalan kontras warna pada teks di atas latar belakang gradien, dan 3) indikator fokus yang hilang pada elemen interaktif yang bergaya kustom. Semua tiga mudah diperbaiki setelah Anda tahu keberadaannya.
Paket @axe-core/playwright menambahkan metode `checkA11y()` yang menjalankan axe dalam konteks browser saat ini dan menggagalkan tes jika pelanggaran ditemukan. Jalankan setelah menavigasi ke setiap halaman dalam suite tes E2E Anda. Ini menangkap regresi aksesibilitas yang diperkenalkan oleh perubahan kode apa pun.
Palet warna default Tailwind dapat diakses — Tailwind 3+ menyertakan informasi rasio kontras dalam dokumen untuk setiap kombinasi warna. Risikonya adalah warna kustom yang ditambahkan ke tailwind.config.ts: warna merek yang terlihat baik secara visual tetapi gagal rasio kontras 4.5:1 untuk teks normal (WCAG AA). Periksa semua warna kustom dengan pemeriksa kontras WebAIM.
// npm install @axe-core/playwright
// accessibility.spec.ts
import { test, expect } from '@playwright/test'
import AxeBuilder from '@axe-core/playwright'
const criticalPages = ['/', '/blog', '/about', '/contact']
for (const path of criticalPages) {
test(`${path} has no critical accessibility violations`, async ({ page }) => {
await page.goto(path)
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
.analyze()
// Fail on critical violations, warn on moderate
const criticalViolations = results.violations.filter(
v => v.impact === 'critical' || v.impact === 'serious'
)
if (criticalViolations.length > 0) {
console.log('Critical violations:', JSON.stringify(criticalViolations, null, 2))
}
expect(criticalViolations, `Critical a11y violations on ${path}`).toHaveLength(0)
})
}
// Component-level accessibility test
test('icon buttons have accessible names', async ({ page }) => {
await page.goto('/dashboard')
// Find all buttons and verify they have accessible names
const buttons = page.getByRole('button')
const buttonCount = await buttons.count()
for (let i = 0; i < buttonCount; i++) {
const button = buttons.nth(i)
const name = await button.getAttribute('aria-label') ||
await button.textContent()
expect(name?.trim(), `Button #${i} has no accessible name`).toBeTruthy()
}
})
// Keyboard navigation test
test('modal is keyboard accessible', async ({ page }) => {
await page.goto('/invoices')
await page.keyboard.press('Tab') // focus first interactive element
await page.getByRole('button', { name: 'New Invoice' }).focus()
await page.keyboard.press('Enter') // open modal
await expect(page.getByRole('dialog')).toBeVisible()
await page.keyboard.press('Escape') // close modal
await expect(page.getByRole('dialog')).not.toBeVisible()
})Alat otomatis dan pengujian keyboard mencakup banyak hal, tetapi pengujian pembaca layar mengungkapkan cara konten Anda mengalir saat tata letak visual dilucuti. Tes dengan NVDA (gratis, Windows), JAWS (pembaca layar enterprise paling umum, Windows), atau VoiceOver (bawaan di macOS dan iOS). Hal utama yang harus diverifikasi: heading menciptakan garis besar yang logis, link memiliki teks deskriptif, bidang formulir mengumumkan label mereka saat difokuskan.
Menambahkan atribut ARIA untuk memperbaiki masalah aksesibilitas tanpa memahaminya berbahaya — ARIA yang salah dapat membuat halaman kurang dapat diakses daripada tanpa ARIA sama sekali. Aturan praktik penulisan ARIA: 'Tidak ada ARIA lebih baik dari ARIA yang buruk.' Kesalahan umum: menambahkan role='button' ke div alih-alih menggunakan elemen button nyata (Anda kemudian harus mengimplementasikan fokus keyboard, kunci Enter, dan kunci Space secara manual).
Pendekatan dengan leverage tertinggi: perbaiki aksesibilitas di tingkat komponen sehingga setiap halaman yang menggunakan komponen dapat diakses secara default. Saya menggunakan primitif Radix UI dalam proyek Next.js saya — mereka headless, unstyled, dan sepenuhnya dapat diakses keyboard dan pembaca layar berdasarkan spesifikasi.
Alur kerja aksesibilitas CI saya: Tes Playwright menjalankan axe-core pada semua halaman kritis (beranda, login, dashboard, alur pengguna utama) pada setiap PR. Pelanggaran dilaporkan sebagai anotasi GitHub Actions. Pelanggaran kritis menggagalkan build. Pelanggaran serius memposting peringatan tetapi tidak memblokir merge. Bulanan, saya menjalankan pemindaian axe-cli penuh di semua halaman.