Accessibility - Keyboard Navigation
Semua fitur interaktif harus dapat dioperasikan menggunakan keyboard saja, tanpa mouse.
Tab Order
Urutan fokus harus logis dan mengikuti alur visual:
<!-- Salah — tab tidak mengikuti urutan visual -->
<div>
<input tabindex="3" placeholder="Email" />
<input tabindex="1" placeholder="Nama" />
<input tabindex="2" placeholder="Telepon" />
</div>
<!-- Benar — urutan DOM natural sudah benar -->
<div>
<input placeholder="Nama" />
<input placeholder="Email" />
<input placeholder="Telepon" />
</div>Hindari tabindex > 0. Gunakan:
tabindex="0"— elemen non-interaktif dapat difokuskantabindex="-1"— dapat difokuskan secara programatik, tapi tidak di tab order
Fokus Visual
Fokus harus selalu terlihat jelas:
/* Jangan menghapus outline tanpa pengganti */
button:focus {
outline: none; /* ❌ Buruk */
}
/* Gunakan :focus-visible untuk pengganti yang baik */
button:focus-visible {
outline: 3px solid #005fcc;
outline-offset: 2px;
}Keyboard Shortcuts Umum
| Key | Fungsi |
|---|---|
Tab | Pindah ke elemen fokus berikutnya |
Shift + Tab | Pindah ke elemen fokus sebelumnya |
Enter / Space | Aktifkan button, link, atau control |
Escape | Tutup modal, menu, atau dialog |
Arrow Keys | Navigasi dalam widget (tabs, menu, grid) |
Home / End | Ke awal / akhir dalam list |
Focus Trap untuk Modal
Pengguna tidak boleh keluar dari modal sebelum menutupnya:
function trapFocus(element) {
const focusable = element.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const first = focusable[0];
const last = focusable[focusable.length - 1];
element.addEventListener("keydown", (e) => {
if (e.key !== "Tab") return;
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
});
}Skip Link
Memungkinkan screen reader user melewati navigasi berulang:
<a href="#main" class="skip-link">Lewati ke konten utama</a>
<nav>...</nav>
<main id="main">...</main>.skip-link {
position: absolute;
top: -40px;
left: 0;
background: #000;
color: #fff;
padding: 8px;
z-index: 100;
}
.skip-link:focus {
top: 0;
}Focus Management pada SPA
Saat navigasi halaman di SPA, pindahkan fokus ke konten baru:
// Setelah route change
function onRouteChange() {
const main = document.querySelector("main");
main.setAttribute("tabindex", "-1");
main.focus();
main.removeAttribute("tabindex");
}