Membuat Navbar yang Aman untuk Hover, Sentuhan, dan Keyboard
Studi kasus navigasi NgodingSantai: dropdown desktop, panel mobile, details native, Escape, fokus, target sentuh, dan status aria-expanded.
Navbar NgodingSantai pernah mempunyai perilaku berbeda antarlaman: kategori tampak aktif pada halaman yang bukan kategori, dropdown mengandalkan hover, dan susunan mobile menampilkan tujuh kategori sebagai teks yang rapat. Perbaikannya bukan menambahkan ARIA sebanyak mungkin, melainkan memilih elemen native, menyatukan markup, dan mendukung beberapa cara input.
Gunakan link untuk berpindah dan button untuk membuka
“Kategori” pada desktop membuka menu sehingga implementasi akhirnya memakai button. Item di dalam menu tetap berupa link menuju halaman kategori. Pemisahan ini membuat Space dan Enter mengaktifkan tombol secara native, sedangkan link tetap mempunyai tujuan yang dapat disalin atau dibuka pada tab baru.
<button type="button"
class="nav-link category-toggle"
aria-haspopup="true"
aria-expanded="false">
Kategori
</button>
<div class="category-menu" role="menu">...</div>
Role menu digunakan bersama perilaku tombol panah yang diterapkan. Jika tidak menerapkan pola keyboard menu, daftar link biasa tanpa role kompleks sering lebih tepat.
Hover bukan satu-satunya jalan
Pengguna mouse mendapat pembukaan saat pointer masuk. Pengguna touch tidak memicu logika hover tersebut dan dapat menekan tombol. Pengguna keyboard mendapat pembukaan pada focusin. Pointer event memeriksa pointerType agar sentuhan tidak membuka lalu langsung menutup menu karena simulasi mouse.
categoryToggle.addEventListener("click", toggleMenu);
categoryWrap.addEventListener("pointerenter", event => {
if (event.pointerType !== "touch") openMenu();
});
categoryWrap.addEventListener("focusin", openMenu);
Sediakan cara menutup yang dapat diprediksi
Menu menutup ketika pointer keluar setelah jeda singkat, fokus meninggalkan wrapper, pengguna menekan Escape, atau mengeklik area luar. Jeda mencegah celah kecil antara tombol dan popup membuat menu hilang sebelum pointer mencapai link.
Tombol memperbarui aria-expanded pada setiap perubahan. Escape mengembalikan fokus ke toggle agar pengguna tidak kehilangan posisi. ArrowDown dan ArrowUp memindahkan fokus secara melingkar antaritem.
Gunakan details untuk kategori mobile
Panel mobile memakai details dan summary. Elemen native sudah menyediakan toggle yang dapat dioperasikan keyboard dan state terbuka. Daftar kategori di dalamnya menjadi kartu dua kolom pada ponsel yang cukup lebar dan satu kolom di bawah 390 piksel, sehingga label “Web Performance” atau “Karier Developer” tidak bertabrakan.
Jangan membangun tujuh link dalam satu baris
Tampilan lama membiarkan kategori membungkus seperti teks inline. Hasilnya tidak mempunyai batas target yang jelas dan garis bawah tampak acak. Grid sekarang menggunakan minmax(0,1fr), gap konsisten, border per item, serta teks yang dapat membungkus di dalam kartunya sendiri.
.mobile-category-list {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: .5rem;
}
@media (max-width: 390px) {
.mobile-category-list { grid-template-columns: 1fr; }
}
Ukuran target dan fokus
Tombol header serta link panel mempunyai tinggi minimum 44–48 piksel. Ukuran ini bukan berarti setiap ikon terlihat besar; area klik dapat lebih luas dari garis SVG. :focus-visible memakai outline aksen yang tidak dihapus oleh komponen.
Status aktif berasal dari URL
Markup header pada halaman lama tidak selalu sama. app.js kini membangun ulang navigasi berdasarkan data-root dan pathname. Beranda, Artikel, Kategori, Tentang, dan Kontak masing-masing hanya memiliki satu status aktif. Profil redaksi dan kebijakan editorial dikelompokkan di bawah Tentang.
Panel mobile mempunyai batas viewport
Pada layar pendek, menu panjang tidak memaksa dokumen melebar atau membuat item paling bawah tidak dapat dicapai. Panel memakai max-height berdasarkan dynamic viewport dan overflow-y:auto. Saat berpindah ke desktop, listener media query menutup panel serta mengembalikan ikon menu.
Matriks pengujian manual
| Input | Langkah | Hasil |
|---|---|---|
| Mouse | Hover Kategori lalu pindah ke item | Menu tetap terbuka dan link dapat dipilih |
| Touch | Tap tombol, tap kategori | Tidak membutuhkan tap hover tambahan |
| Keyboard | Tab, Enter, panah, Escape | State dan fokus dapat diprediksi |
| Zoom 200% | Buka header dan panel | Item reflow dan dapat discroll |
| Reduced motion | Aktifkan preferensi | Transisi tidak menghambat operasi |
Referensi primer
Uji urutan fokus saat panel berubah
Membuka panel mobile tidak otomatis menjebak fokus karena panel bukan dialog modal. Pengguna tetap dapat menavigasi dokumen, tetapi link panel muncul pada urutan yang logis sesudah tombol. Ketika panel ditutup dengan Escape, fokus kembali ke tombol menu. Ketika link dipilih, navigasi halaman mengambil alih.
Nama aksesibel tidak bergantung pada bentuk ikon
Search, tema, menu, close, dan kembali ke atas menggunakan SVG dekoratif serta label teks melalui aria-label. Label tema berubah berdasarkan aksi berikutnya. Tooltip title membantu sebagian pengguna pointer, tetapi bukan pengganti nama aksesibel.
Audit tanpa mouse
Dari address bar, tekan Tab melewati skip link, brand, navigasi, dropdown, search, theme, dan menu. Pastikan fokus selalu terlihat, tidak masuk ke item tersembunyi, serta tidak terjebak. Sesudah itu uji dengan zoom dan pembesaran teks karena reflow adalah bagian pengalaman aksesibel, bukan hanya desain responsive.
Skenario keyboard yang dapat dicoba pada situs ini
- Gunakan Tab untuk mencapai pemicu Kategori di navbar desktop.
- Buka daftar, lanjutkan fokus ke tautan kategori, lalu tekan Escape. Periksa apakah menu tertutup dan fokus kembali ke pemicu.
- Pada viewport ponsel, buka menu utama dan kelompok Kategori. Pastikan tautan dapat dicapai tanpa hover.
- Ulangi pada tema gelap dan perbesaran 200%. Fokus harus tetap terlihat.
Catat kegagalan pada checklist frontend dengan browser dan langkah reproduksi. Menu disclosure biasa tidak harus memakai pola keyboard menu aplikasi desktop. Atribut role dan aria harus sesuai perilaku yang benar-benar diterapkan.
Gunakan pemeriksa kontras untuk pasangan warna teks. Pengujian rasio tidak menggantikan pemeriksaan fokus, nama tombol, urutan Tab, atau pembaca layar.
