Checklist CI untuk aksesibilitas frontend yang efektif bukan berarti semua pemeriksaan harus dijalankan di setiap langkah. Pendekatan yang sehat adalah membagi guardrail ke beberapa lapisan: cek cepat saat developer menulis kode, validasi yang sedikit lebih dalam sebelum push, lalu audit terpilih di CI berdasarkan risiko perubahan. Dengan cara ini, tim mendapatkan umpan balik cepat tanpa membuat pipeline lambat atau penuh false positive.
Dalam banyak diskusi komunitas developer, isu utamanya sering bukan sekadar alat apa yang dipakai, tetapi bagaimana menjaga proses kerja tim tetap waras saat standar kualitas bertambah. Untuk aksesibilitas, masalah umum adalah dua ekstrem: tidak ada cek sama sekali, atau semua hal dijadikan blocker sejak hari pertama. Artikel ini fokus pada jalan tengah yang praktis untuk tim web modern JavaScript: guardrail lokal + CI, severity gating, baseline pelanggaran lama, review PR yang terarah, dan rollout bertahap.
Mengapa checklist CI untuk aksesibilitas frontend sering gagal di tim
Banyak implementasi gagal bukan karena alatnya buruk, melainkan karena scope dan timing-nya salah. Beberapa pola yang sering terjadi:
- Semua cek dijalankan di setiap commit, termasuk audit browser penuh. Hasilnya lambat dan diabaikan.
- Semua temuan langsung memblokir merge, padahal proyek sudah punya utang aksesibilitas lama.
- Tidak ada pemisahan severity, sehingga isu minor diperlakukan sama dengan kegagalan keyboard navigation atau missing form label.
- Audit hanya di CI, sehingga feedback datang terlalu lambat dan konteks perbaikan sudah hilang.
- Terlalu bergantung pada skor tunggal, misalnya skor Lighthouse, tanpa melihat jenis masalah yang sebenarnya berdampak.
Tujuan yang lebih realistis adalah: mencegah regresi aksesibilitas baru, memperbaiki isu kritis secepat mungkin, dan mengurangi utang lama secara bertahap.
Prinsip desain workflow: cepat di lokal, selektif di CI, ketat pada regresi baru
Untuk tim frontend modern JavaScript, pembagian berikut biasanya paling masuk akal:
1. Pre-commit: hanya cek yang sangat cepat
Jalankan linting yang bisa mendeteksi masalah dari source code atau JSX/TSX tanpa perlu browser penuh. Fokus di sini adalah feedback dalam hitungan detik.
- ESLint dengan plugin aksesibilitas untuk JSX/template
- Format dan type check ringan bila memang sudah menjadi standar tim
- Scope file yang berubah saja
2. Pre-push: cek integrasi ringan
Pre-push cocok untuk tes yang lebih mahal daripada linting, tetapi masih cukup cepat dijalankan lokal. Misalnya smoke test halaman utama dengan Playwright dan audit axe di beberapa route penting.
- Render halaman target
- Jalankan audit otomatis terhadap komponen atau halaman yang berubah
- Batasi jumlah skenario agar tidak mengganggu flow kerja
3. CI pipeline: pemeriksaan bertingkat berdasarkan risiko
CI sebaiknya tidak menjadi tempat pertama menemukan masalah sederhana. Perannya adalah validasi konsisten di environment terkontrol, terutama untuk pull request dan branch utama.
- Stage cepat: lint + unit test + a11y lint
- Stage menengah: Playwright + axe pada halaman kritis
- Stage terjadwal atau selective: Lighthouse/audit lebih luas, tidak selalu memblokir PR
Pembagian ini bekerja karena biaya setiap jenis cek berbeda. Linting murah dan deterministik, sedangkan audit berbasis browser lebih mahal dan kadang lebih rentan terhadap noise.
Komponen inti checklist CI untuk aksesibilitas frontend
Linting aksesibilitas di level source code
Linting adalah guardrail paling murah untuk menangkap kesalahan umum sebelum aplikasi berjalan di browser. Contoh masalah yang biasanya bisa dideteksi:
- Elemen interaktif tanpa label yang dapat dibaca
- Penggunaan atribut ARIA yang tidak valid
- Elemen klik yang bukan button/link tanpa dukungan keyboard yang benar
- Input form tanpa label yang terasosiasi
- Gambar penting tanpa alternatif teks
Untuk stack React/JSX, tim biasanya memakai ESLint dengan aturan aksesibilitas JSX. Untuk framework lain, prinsipnya sama: pilih linter yang memahami template atau markup yang digunakan tim.
# contoh skrip package.json yang sederhana dan aman dijalankan lokal
{
"scripts": {
"lint": "eslint .",
"lint:a11y": "eslint src --ext .js,.jsx,.ts,.tsx"
}
}Kapan jadi blocker? Hampir selalu ya. Jika lint rule stabil dan jelas, sebaiknya kegagalan lint aksesibilitas memblokir merge karena biayanya rendah dan false positive relatif lebih sedikit dibanding audit dinamis.
Audit otomatis dengan axe di browser
Setelah linting, lapisan berikutnya adalah audit DOM yang benar-benar dirender. Di sini alat seperti axe berguna karena bisa menemukan isu yang tidak terlihat dari source code saja, misalnya struktur heading yang bermasalah, kontras tertentu, atau relasi ARIA yang rusak setelah komponen dirender.
Pendekatan yang praktis adalah menjalankan axe lewat end-to-end test runner seperti Playwright. Dengan begitu, tim tidak perlu membuat sistem audit terpisah; browser test dan audit aksesibilitas berbagi setup yang sama.
// contoh konsep test Playwright + axe
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('halaman checkout tidak memiliki pelanggaran a11y kritis', async ({ page }) => {
await page.goto('/checkout');
const results = await new AxeBuilder({ page })
.include('main')
.analyze();
const seriousOrCritical = results.violations.filter(v =>
['serious', 'critical'].includes(v.impact || '')
);
expect(seriousOrCritical).toEqual([]);
});Contoh di atas sengaja memfilter serious dan critical. Ini penting untuk severity gating: tidak semua temuan harus memblokir pipeline, terutama pada fase awal adopsi.
Playwright untuk skenario pengguna, bukan sekadar screenshot
Playwright tidak otomatis menjamin aksesibilitas, tetapi sangat cocok untuk memverifikasi alur yang sering rusak dalam UI modern:
- Fokus berpindah ke modal saat modal terbuka
- Urutan tab tetap masuk akal
- Escape menutup dialog
- Komponen custom dapat dioperasikan via keyboard
- Pesan error form muncul dan terkait dengan field yang benar
Ini melengkapi axe. Audit otomatis bagus untuk aturan umum, sedangkan Playwright berguna untuk behavior yang sering menjadi sumber bug nyata bagi pengguna keyboard atau screen reader.
test('focus masuk ke modal setelah tombol dibuka', async ({ page }) => {
await page.goto('/settings');
await page.getByRole('button', { name: 'Buka preferensi' }).click();
await expect(page.getByRole('dialog', { name: 'Preferensi' })).toBeVisible();
await expect(page.getByRole('dialog', { name: 'Preferensi' })).toBeFocused();
});Jika API spesifik berbeda di project Anda, pertahankan prinsip utamanya: uji perilaku fokus dan keyboard secara eksplisit, jangan diasumsikan benar hanya karena komponen terlihat benar.
Lighthouse sebagai audit tambahan, bukan satu-satunya pagar kualitas
Lighthouse bisa berguna untuk menambah sinyal, terutama untuk halaman publik yang penting. Namun untuk workflow tim, ada beberapa batasan:
- Skor tunggal mudah disalahartikan
- Hasil bisa berubah karena kondisi environment
- Tidak semua masalah aksesibilitas kompleks tertangkap
- Tidak cocok dijadikan blocker untuk semua PR kecil
Karena itu, Lighthouse lebih aman dipakai sebagai:
- Audit terjadwal di branch utama
- Cek tambahan untuk halaman prioritas tinggi
- Pelengkap tren kualitas, bukan satu sumber kebenaran
Contoh alur lokal + CI yang tidak berat
Pre-commit
Tujuan pre-commit adalah menghentikan kesalahan yang murah dideteksi. Jangan jalankan browser test penuh di tahap ini.
- ESLint termasuk aturan aksesibilitas
- Hanya file yang berubah
- Opsional: test komponen ringan bila cepat dan stabil
# contoh konsep dengan file yang berubah
npx eslint --cache srcJangan menaruh Lighthouse atau suite Playwright penuh di pre-commit. Developer akan mencari cara melewati hook jika waktu tunggu terlalu lama.
Pre-push
Pre-push adalah tempat yang lebih masuk akal untuk smoke test aksesibilitas lokal.
- Jalankan 2-5 skenario Playwright paling kritis
- Audit axe pada halaman yang paling sering disentuh atau paling penting secara bisnis
- Gunakan filter changed paths bila repository cukup besar
Jika tim merasa pre-push masih mengganggu, jadikan ini opsional untuk branch lokal tetapi wajib di CI PR. Yang penting adalah jangan menggandakan biaya secara tidak perlu.
Pipeline CI pull request
Pemisahan job akan memudahkan debugging dan memungkinkan eksekusi paralel.
jobs:
install:
steps:
- checkout
- install dependencies
- cache dependencies
lint_a11y:
needs: install
steps:
- run lint
- run lint:a11y
test_unit:
needs: install
steps:
- run unit tests
a11y_smoke:
needs: install
steps:
- start app
- run Playwright smoke tests
- run axe checks on critical routes
- upload report artifact
lighthouse_report:
needs: install
if: changed public pages or nightly build
steps:
- start app
- run lighthouse on selected pages
- upload report artifactStruktur di atas sengaja generik agar bisa diterapkan di GitHub Actions, GitLab CI, CircleCI, atau sistem lain. Intinya bukan nama platform, tetapi pemisahan biaya dan tujuan tiap job.
Aturan gagal/lolos yang realistis
Tanpa aturan yang jelas, tim akan bingung kapan PR harus diblokir dan kapan cukup diberi catatan. Berikut model yang cukup aman untuk banyak tim:
Rule 1: lint aksesibilitas selalu blocker
- Jika aturan lint terbukti stabil, gagal = merge ditolak
- Pengecualian harus jarang dan terdokumentasi
Rule 2: axe memblokir hanya untuk severity tertentu
- critical/serious pada route yang diuji = blocker
- moderate/minor = warning atau tiket perbaikan
Rule 3: baseline lama tidak langsung memblokir
- Temuan yang sudah ada sebelum PR tidak otomatis menggagalkan build
- Regresi baru pada area yang berubah harus gagal
Rule 4: Lighthouse lebih cocok sebagai threshold longgar atau non-blocking report
- Gunakan untuk halaman prioritas, bukan semua halaman
- Jangan pakai skor sebagai satu-satunya alasan menolak PR kecil
Catatan: Severity gating bekerja karena tim memisahkan masalah yang benar-benar menghambat penggunaan dari masalah yang perlu dibenahi tetapi tidak harus menghentikan rilis hari itu juga.
Baseline pelanggaran: cara berhenti mewarisi utang tanpa memblokir semua PR
Jika aplikasi sudah lama berjalan, hampir pasti ada pelanggaran aksesibilitas yang belum selesai. Memblokir semua PR sampai seluruh backlog beres biasanya tidak realistis. Solusi yang lebih sehat adalah baseline.
Cara kerja baseline
- Jalankan audit pada branch utama dan simpan daftar pelanggaran yang sudah diketahui.
- Di PR, bandingkan hasil baru dengan baseline tersebut.
- Build gagal jika ada pelanggaran baru atau jika jumlah pelanggaran pada route yang berubah bertambah.
- Perbaikan yang mengurangi baseline harus diterima sebagai kemajuan, walaupun belum nol.
Pendekatan ini membantu tim memfokuskan energi pada regresi baru tanpa pura-pura menghapus semua utang teknis sekaligus.
Hal yang perlu diperhatikan
- Baseline harus diperbarui secara sadar, bukan otomatis setiap build.
- Simpan baseline per halaman atau per komponen penting jika memungkinkan.
- Jangan masukkan issue yang berasal dari environment test yang tidak stabil.
Kesalahan umum adalah menjadikan baseline sebagai tempat menyembunyikan masalah baru. Karena itu, update baseline idealnya melalui review PR khusus atau approval dari maintainer frontend.
Mengurangi false positive tanpa menurunkan standar
False positive adalah alasan paling umum mengapa tim mulai mengabaikan guardrail. Beberapa strategi yang biasanya efektif:
Batasi scope audit
Audit seluruh halaman, termasuk area third-party, iklan, atau widget eksternal, sering menghasilkan noise. Lebih baik gunakan scope yang relevan, misalnya container main atau region aplikasi yang memang dimiliki tim.
Pastikan state UI stabil sebelum audit
Audit yang dijalankan saat skeleton loading, modal belum terbuka penuh, atau data belum selesai dimuat akan menghasilkan hasil yang tidak konsisten. Tunggu elemen utama siap sebelum memanggil axe atau Lighthouse.
Jangan audit komponen yang memang sengaja hidden
Beberapa komponen offscreen atau lazy-rendered bisa memicu temuan yang tidak relevan jika belum berada dalam state yang benar. Uji state yang nyata dipakai pengguna.
Dokumentasikan pengecualian yang sah
Bila ada rule yang memang tidak relevan untuk pola tertentu, dokumentasikan alasan dan ruang lingkupnya. Hindari menonaktifkan rule secara global hanya karena satu kasus sulit.
Pisahkan bug produk dari keterbatasan alat
Tidak semua isu aksesibilitas bisa diukur otomatis. Sebaliknya, tidak semua output alat harus dianggap bug produk tanpa verifikasi. Review teknis singkat tetap dibutuhkan untuk kasus ambigu.
Review PR: guardrail manusia tetap dibutuhkan
Checklist CI untuk aksesibilitas frontend tidak lengkap tanpa review PR yang fokus. Otomasi bisa menangkap banyak hal, tetapi belum cukup untuk menilai kualitas interaksi secara utuh.
Tambahkan checklist singkat di template PR untuk perubahan UI:
- Apakah komponen baru dapat dioperasikan dengan keyboard?
- Apakah nama tombol, link, dan input jelas?
- Apakah error form tersambung ke field terkait?
- Apakah dialog, menu, atau popover mengelola fokus dengan benar?
- Apakah perubahan ini menyentuh halaman prioritas yang wajib diuji dengan axe/Playwright?
Checklist review sebaiknya pendek. Jika terlalu panjang, reviewer akan melewatinya. Fokus pada pola yang paling sering rusak di codebase Anda.
Rollout bertahap untuk tim yang belum punya guardrail aksesibilitas
Jika tim mulai dari nol, jangan langsung menyalakan semua blocker. Rollout bertahap biasanya lebih berhasil:
Tahap 1: observability dulu
- Aktifkan lint aksesibilitas sebagai warning
- Tambahkan audit axe di CI sebagai report artifact
- Pilih 3-5 route paling penting untuk diukur
Tahap 2: blocker untuk masalah murah dan jelas
- Naikkan lint aksesibilitas menjadi blocking
- Gagal jika ada temuan serious/critical baru dari axe pada route prioritas
Tahap 3: baseline dan pengurangan utang
- Simpan baseline issue lama
- Blokir regresi baru, bukan seluruh backlog
- Buat target triwulanan untuk mengurangi baseline
Tahap 4: perluas coverage secara selektif
- Tambahkan flow keyboard dan focus management di Playwright
- Jalankan Lighthouse pada halaman publik tertentu
- Perluas route hanya jika sinyal tetap berkualitas
Rollout seperti ini bekerja karena tim belajar dari data nyata: durasi job, rasio false positive, pola bug yang berulang, dan area aplikasi yang paling rawan.
Contoh kebijakan sederhana yang bisa langsung dipakai
Aturan lokal
- Pre-commit: eslint + a11y lint pada file berubah
- Pre-push: smoke Playwright + axe untuk route kritis
Aturan PR CI
- Gagal jika lint:a11y gagal
- Gagal jika ada violation axe dengan impact serious/critical yang baru
- Warning jika ada moderate/minor
- Lighthouse hanya report untuk PR biasa
- Lighthouse bisa blocking untuk halaman publik prioritas jika tim sudah siap
Aturan main branch
- Jalankan audit lebih luas secara terjadwal
- Review tren dan kurangi baseline secara bertahapKesalahan umum yang perlu dihindari
- Mengukur terlalu banyak route sejak awal. Mulai dari halaman penting.
- Menggunakan skor sebagai pengganti analisis. Lihat jenis masalah, bukan hanya angka.
- Memblokir seluruh PR karena utang lama. Gunakan baseline.
- Mencampur semua cek ke satu job besar. Sulit didiagnosis dan lebih lambat.
- Tidak menguji keyboard dan fokus. Banyak bug aksesibilitas nyata ada di sini, bukan hanya di atribut ARIA.
- Menonaktifkan rule terlalu cepat. Verifikasi dulu apakah masalahnya memang false positive atau bug sungguhan.
Penutup
Checklist CI untuk aksesibilitas frontend yang baik bukan tentang menambah sebanyak mungkin alat, melainkan menempatkan cek yang tepat di tahap yang tepat. Linting menangkap masalah murah lebih awal, axe di browser mencegah regresi DOM yang nyata, Playwright memverifikasi perilaku keyboard dan fokus, sedangkan Lighthouse memberi sinyal tambahan tanpa harus membebani setiap PR.
Jika Anda ingin mulai tanpa memperlambat tim, urutan paling aman adalah: aktifkan a11y lint di lokal, jalankan smoke axe di CI untuk route kritis, terapkan severity gating, lalu pakai baseline agar utang lama tidak menghentikan semua pekerjaan baru. Dari situ, perluas coverage hanya ketika sinyal tetap berguna dan pipeline tetap cepat.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!