Komponen Web yang Dapat Diakses

Batas shadow mengubah cara kerja semantik dan ARIA. Dua kegagalan yang mendominasi: semantik yang hilang di dalam shadow tree, dan referensi ARIA yang rusak secara diam-diam karena tidak dapat melewati shadow root.

Mulai dari markup semantik: gunakan <button>, <nav>, <ul> yang sebenarnya di dalam template sehingga sebagian besar semantik tersedia secara gratis, dan simpan ElementInternals/ARIA untuk celah.

Semantik default melalui mixin ARIA ElementInternals

Tetapkan peran elemen kustom dan status ARIA di JS melalui ElementInternals, bukan menulis atribut ARIA ke host. Hal ini mempertahankan semantik sebagai default yang dimiliki komponen, sekaligus tetap memungkinkan konsumen menggantinya dengan atribut di Light DOM.

class ToggleSwitch extends HTMLElement {
  #internals;
  constructor() {
    super();
    this.#internals = this.attachInternals();
    this.#internals.role = 'switch';          // default role, no host attribute needed
    this.#internals.ariaChecked = 'false';    // exposed to the accessibility tree
    this.addEventListener('click', () => this.toggle());
    this.addEventListener('keydown', (e) => {
      if (e.key === ' ') { e.preventDefault(); this.toggle(); }
    });
  }
  connectedCallback() {
    // Put the host in the tab order on connect (setting tabIndex in the constructor throws on createElement).
    if (!this.hasAttribute('tabindex')) this.tabIndex = 0;
  }
  toggle() {
    const on = this.#internals.ariaChecked !== 'true';
    this.#internals.ariaChecked = String(on);
  }
}
customElements.define('toggle-switch', ToggleSwitch);

Hal ini menghindari pengotoran DOM dengan atribut ARIA dan mencegah konsumen menimpanya secara tidak sengaja, sementara hierarki aksesibilitas masih melihat peran dan status yang benar.

Pengelolaan fokus dan delegatesFocus

  • Jika elemen host itu sendiri adalah kontrol interaktif dan membawa peran serta status melalui ElementInternals (seperti ToggleSwitch di atas), tempatkan host dalam urutan tab di dalam connectedCallback (if (!this.hasAttribute('tabindex')) this.tabIndex = 0 — jangan pernah di konstruktor, yang akan menampilkan error di document.createElement) sehingga fokus mendarat di host dan teknologi pendukung mengumumkan peran dan statusnya. Jangan tetapkan tabindex pada host untuk komponen statis non-interaktif (seperti info atau kartu).
  • Lampirkan shadow root dengan delegatesFocus: true saat komponen menggabungkan kontrol internal yang dapat difokuskan yang memiliki semantik (atau pada elemen kustom yang terkait dengan formulir dengan static formAssociated = true). Memfokuskan host (dan mengklik <label> terkait untuk elemen yang terkait dengan formulir) kemudian meneruskan fokus ke turunan pertama yang dapat difokuskan, dan :focus cocok dengan host. Jangan gabungkan delegatesFocus: true dengan peran/status di host, atau fokus akan mendarat di elemen dalam dan melewati status host.
  • Untuk fokus berpindah atau memindahkan fokus ke area yang baru ditampilkan, berikan tabindex="-1" ke target dan panggil .focus(); buat target yang dapat difokuskan secara terprogram menjadi eksplisit, bukan mengandalkan urutan tab.

Hubungan ARIA di seluruh shadow root

  • WAJIB: id yang direferensikan oleh aria-labelledby, aria-describedby, aria-controls, dll. hanya di-resolve dalam hierarki milik elemen yang mereferensikan. Atribut berbasis id di Light DOM tidak dapat mengarah ke id di dalam shadow tree, dan sebaliknya; referensi akan diabaikan secara otomatis.
  • Untuk melewati batas, gunakan properti elemen ARIA yang direfleksikan (misalnya, ariaLabelledByElements, ariaDescribedByElements, dll.), yang menggunakan referensi elemen JS langsung, bukan ID. Referensi ini di-resolve ke luar: elemen di dalam shadow tree dapat mengarah ke target di Light DOM atau ancestor tree mana pun (termasuk konten yang di-slot), sehingga Anda dapat memberi label pada kontrol internal shadow dari konten Light DOM — sesuatu yang tidak dapat dinyatakan oleh referensi id.
  • ID tersebut tidak di-resolve ke dalam: elemen tidak dapat mereferensikan ke dalam hierarki shadow turunan (misalnya, host tidak dapat diberi label oleh konten shadow-nya sendiri). Untuk arah tersebut, mekanisme Reference Target yang muncul adalah arah standar, tetapi belum tersedia secara luas.
  • Jika hubungan tidak dapat dinyatakan dengan cara apa pun, pertahankan dalam satu hierarki (render label dalam hierarki yang sama) atau gunakan teks aria-label, bukan referensi id.

Strategi penggantian

Jika mixin ARIA ElementInternals tidak tersedia, tetapkan peran dan status ARIA sebagai atribut pada host (this.setAttribute('role', 'switch')). Hal ini berfungsi di mana saja, dengan konsekuensi menempatkan atribut di DOM yang dapat diganti oleh konsumen.

Jika properti elemen ARIA yang direfleksikan tidak tersedia, gunakan atribut aria-* berbasis id untuk hubungan hierarki yang sama; label lintas batas tidak memiliki padanan berbasis id, jadi gunakan teks aria-label sebagai pengganti.