Skip to main content
Skill web app · Pelajaran 9 dari 11 · 14 menit

Dasar Aksesibilitas Web untuk Pemula di Situs Next.js

Belajar dasar aksesibilitas web dari situs Next.js nyata: HTML semantik, fokus keyboard, nama aksesibel, ambang kontras, dan reduced motion, dengan kode.

Oleh Matthews WongDitinjau pada

Poin utama

Aksesibilitas web berarti membangun halaman yang bisa dilihat dan dioperasikan lewat keyboard, screen reader, atau pengaturan reduced motion. Mulailah dari HTML native, yang memberi setiap kontrol nama, peran, dan dukungan keyboard, lalu jaga fokus tetap terlihat, penuhi ambang kontras, dan matikan gerakan saat pengunjung memintanya.

Setelah pelajaran ini, Anda bisa

  • Memilih elemen native yang memberi kontrol nama, peran, dan perilaku keyboard secara gratis.
  • Menambahkan skip link dan ring fokus yang terlihat, serta memeriksa ambang kontras.
  • Menghormati prefers-reduced-motion di CSS maupun JavaScript tanpa menyembunyikan informasi.
Lihat cara kerjanya

Ikuti diagramnya langkah demi langkah

Tekan Putar, atau melangkah dengan panah. Setiap langkah juga ditulis di bawah diagram.

Satu jalur keyboard di sebuah halaman, lalu pengaturan gerakan

Fokus berpindah dari tombol Tab ke skip link, lalu ke landmark main dan tombol kuis native yang memunculkan nama dan peran. Reduced motion adalah preferensi terpisah.

Langkah 1 dari 6. Pengunjung keyboard tidak punya pointer. Fokus adalah kursornya, dan harus cepat mencapai konten, mendarat di kontrol asli, serta tidak pernah tersembunyi.

Tombol: ← → langkah · Home / End · K putar atau jeda

  1. Pengunjung keyboard tidak punya pointer. Fokus adalah kursornya, dan harus cepat mencapai konten, mendarat di kontrol asli, serta tidak pernah tersembunyi.

  2. Perhentian Tab pertama adalah link yang hanya terlihat saat fokus. Link ini memungkinkan pengunjung melewati header yang berulang di setiap halaman.

    href="#main-content"
  3. Menekan Enter memindahkan fokus ke elemen main. Navigasi berulang dilewati, dan screen reader juga bisa mendaftar main sebagai landmark.

    main id="main-content"
  4. Opsi kuis adalah elemen button asli, jadi Tab menjangkaunya dan Enter atau Spasi menekannya tanpa skrip tambahan.

    type="button"Tebak langkah berikutnya Berikutnya, screen reader menemui tombol ini. Apa yang harus bisa ditentukannya? (Hanya warnanya · Nama, peran, dan statusnya · Posisi terakhir mouse)
  5. Button native sudah memunculkan perannya. Teks yang terlihat menjadi namanya, dan aria-pressed melaporkan apakah tombol itu sudah dipilih.

    aria-pressedTebak langkah berikutnya Pengunjung mengaktifkan reduce motion. Apa yang harus dilakukan explainer dengan geraknya? (Menyembunyikan informasinya · Menjaga informasi, membuang geraknya · Memutarnya lebih cepat)
  6. Gerakan adalah preferensi terpisah. Saat reduce aktif, explainer memperpendek transisi dan menyembunyikan ring dekoratif, tetapi setiap langkah dan caption tetap terbaca.

    @media (prefers-reduced-motion: reduce)

Kenapa pakai HTML native sebelum ARIA?

Elemen native membawa maknanya sendiri. MDN menyatakannya lugas: button punya aksesibilitas keyboard bawaan, sehingga pengguna berpindah antar tombol dengan Tab dan mengaktifkannya dengan Spasi, Return, atau Enter, dan sarannya adalah memakai elemen yang tepat sejak awal. Div yang didandani seperti tombol kehilangan semuanya, dan Anda harus membangunnya ulang dengan tabindex, role, dan penangan tombol.

Baca selengkapnya (2)

Kriteria WCAG 2.2 bernama Name, Role, Value (level A) meminta setiap komponen antarmuka punya nama dan peran yang bisa ditentukan perangkat lunak, dan status yang bisa diubah pengguna ikut diekspos. W3C mencatat bahwa kontrol HTML standar sudah memenuhinya bila dipakai sesuai spesifikasi. Itulah alasan utama memakai elemen native: browser yang mengekspos untuk Anda.

Struktur sama pentingnya dengan kontrol. MDN menjelaskan bahwa heading memberi pengguna screen reader daftar isi yang bisa dilompati, dan elemen seksi seperti nav, main, dan footer memberi petunjuk tambahan tentang konten. Tanpa itu halaman terbaca sebagai satu blok raksasa. Di layout Next.js ini murah: satu h1 per halaman, heading berurutan, dan satu elemen main yang membungkus isi halaman.

Bagaimana pengguna keyboard bergerak di halaman Anda?

Tiga kriteria WCAG 2.2 mencakup dasarnya. Keyboard (2.1.1, level A) menyatakan semua fungsi harus bisa dioperasikan lewat keyboard. Focus Visible (2.4.7, level AA) menyatakan indikator fokus keyboard harus terlihat. Bypass Blocks (2.4.1, level A) meminta cara melewati blok yang berulang di banyak halaman, dan itulah fungsi skip link.

Baca selengkapnya (1)

Kegagalan yang umum adalah menghapus outline karena terlihat kurang rapi. Perbaikannya: ubah gayanya, jangan dihapus. Satu aturan :focus-visible global memberi setiap elemen fokus ring yang jelas, dan komponen bisa memperhalusnya. Hati-hati juga dengan tabindex. MDN menyebut hampir selalu ide buruk, dan menyimpan tabindex 0 serta -1 untuk kasus langka ketika elemen harus masuk urutan tab atau menerima fokus dari skrip.

Apa sebenarnya isi ambang kontras?

WCAG 2.2 Contrast (Minimum), 1.4.3 di level AA, meminta rasio minimal 4,5:1 untuk teks, dan minimal 3:1 untuk teks berukuran besar, yang didefinisikan sebagai 18 point atau 14 point tebal. Non-text Contrast, 1.4.11 di level AA, meminta 3:1 terhadap warna di sekitarnya untuk komponen antarmuka dan objek grafis yang dibutuhkan untuk memahami konten, misalnya ring fokus atau border input.

Baca selengkapnya (1)

Jangan menilai kontras dengan mata, terutama di tema gelap dengan lapisan transparan, karena latar efektifnya adalah campuran warna. Ukur warna akhir yang dirender dengan contrast checker atau developer tools browser, dan ukur ulang saat token berubah. Pelajaran ini sengaja tidak menyebut rasio untuk palet situs sendiri: sebuah rasio baru menjadi klaim setelah ada yang mengukurnya.

Bagaimana menghormati prefers-reduced-motion?

prefers-reduced-motion adalah media feature dengan dua nilai, no-preference dan reduce. MDN menyebut reduce berarti pengunjung meminta sistem untuk antarmuka yang menghapus, mengurangi, atau mengganti animasi berbasis gerak, dan memperingatkan bahwa animasi seperti menskalakan atau menggeser objek besar bisa memicu rasa tidak nyaman bagi penderita gangguan gerak vestibular. Hormati di CSS dengan media query, dan di JavaScript dengan bertanya ke window.matchMedia sebelum memulai animasi lewat skrip.

Baca selengkapnya (2)

Reduced motion tidak berarti konten dikurangi. Ganti gerakan dengan perubahan instan atau fade, dan pertahankan semua informasi. Kriteria WCAG 2.2 Animation from Interactions (2.3.3) berada di level AAA, tingkat paling ketat, dan meminta gerakan yang dipicu interaksi bisa dimatikan kecuali esensial. Jadikan target yang baik walau Anda tidak mengejar level itu.

Semua ini bukan sertifikat. Pemeriksa otomatis hanya menemukan sebagian masalah, jadi tekan Tab di sepanjang halaman sendiri, dengarkan lewat screen reader, dan aktifkan reduced motion. Standar menjelaskan apa yang dituju; ini bukan janji bahwa halaman mana pun memenuhi persyaratan hukum.

Cara situs ini melakukannya

Konfigurasi nyata di balik pelajaran ini

Kutipan dari repositori yang membangun dan men-deploy situs ini, apa adanya.

app/components/SiteChrome.tsx:40-55
function SkipLink(): JSX.Element {
  return (
    <a
      href="#main-content"
      className="sr-only focus:not-sr-only focus:fixed focus:top-4 focus:left-4 focus:z-[100] focus:px-4 focus:py-2 focus:font-bold focus:border-2"
      style={{
        backgroundColor: "var(--primary)",
        color: "var(--text-dark)",
        borderColor: "var(--border-color)",
      }}
    >
      Skip to main content
    </a>
  );
}

sr-only menyembunyikan link secara visual tetapi tetap ada di urutan tab, dan focus:not-sr-only memunculkannya begitu menerima fokus. Link ini menuju elemen main yang dirender SiteChrome dengan id yang sama. Labelnya string bahasa Inggris literal di komponen ini, jadi halaman berbahasa lain perlu menerjemahkannya.

app/components/learn/LessonQuiz.tsx:148-157
                      <button
                        key={option}
                        type="button"
                        className="quiz-option"
                        data-state={state}
                        aria-pressed={choice === oi}
                        disabled={locked}
                        onClick={() => choose(qi, oi)}
                      >

Setiap jawaban adalah button asli, jadi dukungan keyboard dan perannya datang dari browser. Teks yang terlihat menjadi nama aksesibel, dan aria-pressed menambahkan satu hal yang tidak bisa dikatakan HTML sendiri: opsi mana yang dipilih. Badge huruf diberi aria-hidden agar tidak dibaca dua kali, dan tiap pertanyaan berada dalam fieldset dengan legend.

app/globals.css:1255-1258
*:focus-visible {
  outline: 3px solid var(--accent-yellow);
  outline-offset: 2px;
}

Satu aturan memberi setiap elemen yang bisa difokuskan outline tebal, dengan offset dari tepi agar tidak bertabrakan dengan border. Opsi kuis menambahkan aturan :focus-visible sendiri di atasnya, dengan outline oranye, sehingga komponen bisa memperhalus ring tanpa menghapusnya.

app/components/learn/explainer.css:803-822
@media (prefers-reduced-motion: reduce) {
  .mx-node,
  .mx-edge path,
  .mx-zone rect,
  .mx-zone ellipse,
  .mx-step,
  .mx-progress-dot {
    transition-duration: 0.01ms;
  }
  .mx-ring,
  .mx-signal {
    display: none;
  }
  .mx-predict,
  .mx-predict-option,
  .mx-predict-result {
    animation: none;
  }
}

Transisi dipersingkat hingga nyaris nol dan tidak dihapus, jadi perubahan status tetap terjadi dan setiap elemen tetap berakhir di tempat yang benar. Hanya ring dan sinyal dekoratif yang disembunyikan, yang murni gerakan. Komentar di bagian itu merangkumnya: informasi tetap, gerakan hilang.

app/components/learn/MotionExplainer.tsx:144-148
  return (
    typeof window !== "undefined" &&
    window.matchMedia("(prefers-reduced-motion: reduce)").matches
  );
}

CSS tidak bisa menghentikan animasi yang digerakkan skrip, jadi komponen menanyakan media query yang sama dari JavaScript. Pengecekan typeof window menjaga fungsi aman saat render server, ketika tidak ada window dan tidak ada preferensi untuk dibaca.

Yang sering rusak

Kesalahan yang benar-benar membuang waktu

  • Div dengan onClick, bukan button

    Pengguna mouse bisa mengkliknya, tetapi Tab melewatinya, Enter tidak berbuat apa-apa, dan screen reader mengumumkannya sebagai teks biasa. Ganti dengan elemen button dan simpan gayanya di CSS. Hanya bila elemen native benar-benar tidak bisa dipakai, tambahkan sendiri role, tabindex 0, dan penangan tombolnya.

  • outline: none tanpa pengganti

    Halaman terlihat lebih rapi, tetapi pengguna keyboard kehilangan posisi mereka, yang gagal di Focus Visible. Pertahankan ring dan ubah gayanya: outline tebal dengan offset terbaca jelas di permukaan terang maupun gelap.

  • Skip link yang menunjuk ke ketiadaan

    Jika href-nya #main-content tetapi tidak ada elemen dengan id itu, link tidak melakukan apa pun yang terlihat. Simpan link dan targetnya di komponen yang sama atau konstanta bersama, lalu tekan Tab dan Enter di halaman nyata untuk memastikan fokus berpindah.

  • Menghapus konten saat reduced motion

    Menyembunyikan seluruh panel beranimasi karena pengunjung meminta gerakan lebih sedikit akan mengambil informasi mereka. Ganti animasi dengan fade atau perubahan status instan dan pertahankan teks, caption, dan kontrolnya.

Coba sendiri

Lakukan di komputer Anda sendiri

Uji halaman Anda sendiri tanpa mouse dan dengan satu pengaturan browser. Butuh sepuluh menit dan menemukan masalah yang tidak dilaporkan linter.

  1. Klik address bar, lalu tekan Tab sekali. Apakah skip link muncul, dan apakah Enter memindahkan fokus ke konten utama?

  2. Terus tekan Tab di seluruh halaman. Setiap kontrol harus menampilkan ring yang terlihat, dan tidak ada elemen yang bisa diklik yang terlewat.

  3. Cari di komponen Anda onClick pada div atau span. Setiap temuan adalah kandidat untuk button atau link native.

    grep -rn "<div[^>]*onClick" app
  4. Aktifkan pengaturan reduce motion di sistem operasi Anda lalu muat ulang. Pastikan tidak ada yang bergeser atau berdenyut, dan tidak ada informasi yang hilang.

Kuis singkat

Tiga pertanyaan sebelum lanjut

Pilih jawaban untuk melihat kenapa benar atau salah. Tidak ada yang disimpan atau dinilai, jadi tebak saja dengan bebas.

  1. 1.Sebuah kartu harus bekerja seperti tombol. Apa pilihan pertama terbaik?
  2. 2.Rasio kontras berapa yang diminta WCAG 2.2 level AA untuk teks isi biasa?
  3. 3.Pengunjung mengatur prefers-reduced-motion ke reduce. Apa respons yang tepat?
Pikirkan dulu

Jawab dalam hati, lalu cek

  1. 1.Apa fungsi skip link, dan kriteria WCAG mana yang dipenuhinya?Lihat jawabannya

    Jawaban:Ia adalah elemen pertama yang bisa difokuskan dan melompatkan fokus keyboard melewati header berulang ke konten utama. Ia memenuhi Bypass Blocks, 2.4.1, kriteria level A.

  2. 2.Kenapa outline: none tanpa pengganti jadi masalah?Lihat jawabannya

    Jawaban:Pengguna keyboard kehilangan jejak posisi fokus, yang gagal di Focus Visible (2.4.7, level AA). Ubah gaya ring dengan aturan seperti yang global di globals.css, jangan dihapus.

  3. 3.Kenapa explainer memeriksa reduced motion di CSS dan JavaScript?Lihat jawabannya

    Jawaban:CSS menangani transisi dan keyframe, tetapi gerakan yang digerakkan skrip tidak terlihat oleh stylesheet. Komponen membaca media query yang sama lewat window.matchMedia agar gerakan lewat skrip juga bisa berhenti.

Pelajari lebih dalam

Bahasan mendalam terkait di situs ini