Recraftory

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 difokuskan
  • tabindex="-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

KeyFungsi
TabPindah ke elemen fokus berikutnya
Shift + TabPindah ke elemen fokus sebelumnya
Enter / SpaceAktifkan button, link, atau control
EscapeTutup modal, menu, atau dialog
Arrow KeysNavigasi dalam widget (tabs, menu, grid)
Home / EndKe 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();
    }
  });
}

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");
}