Dostępne komponenty internetowe

Granica shadow zmienia sposób działania semantyki i ARIA. Najczęściej występują 2 błędy: brak semantyki w drzewie shadow i odwołania ARIA, które nie działają, ponieważ nie mogą przekraczać korzeni shadow.

Zacznij od znaczników semantycznych: używaj w szablonie prawdziwych elementów <button>, <nav> i <ul>, aby większość semantyki była dostępna bezpłatnie, a atrybuty ElementInternals/ARIA zostaw na luki.

Domyślna semantyka za pomocą domieszki ARIA ElementInternals

Ustaw rolę i stan ARIA elementu niestandardowego w JS za pomocą ElementInternals, zamiast zapisywać atrybuty ARIA na hoście. Dzięki temu semantyka pozostaje domyślną wartością komponentu, ale konsument może ją zastąpić atrybutami w 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);

Dzięki temu unikniesz zaśmiecania modelu DOM atrybutami ARIA i przypadkowego ich uszkodzenia przez użytkowników, a drzewo ułatwień dostępu będzie nadal widzieć prawidłową rolę i stan.

Zarządzanie skupieniem uwagi i delegatesFocus

  • Gdy element hosta jest elementem interaktywnym i przenosi rolę oraz stan za pomocą ElementInternals (jak ToggleSwitch powyżej), umieść hosta w kolejności tabulacji wewnątrz connectedCallback (if (!this.hasAttribute('tabindex')) this.tabIndex = 0 – nigdy w konstruktorze, który zgłasza błąd w document.createElement), aby fokus trafiał na hosta, a technologia wspomagająca osoby z niepełnosprawnością ogłaszała jego rolę i stan. Nie ustawiaj atrybutu tabindex na hoście w przypadku statycznych, nieinteraktywnych komponentów (takich jak objaśnienie lub karta).
  • Dołącz shadow root z delegatesFocus: true, gdy komponent zawiera wewnętrzny element sterujący, który można zaznaczyć i który ma semantykę (lub w przypadku elementu niestandardowego powiązanego z formularzem z static formAssociated = true). Zaznaczenie hosta (i kliknięcie powiązanego elementu <label> w przypadku elementów powiązanych z formularzem) powoduje przeniesienie zaznaczenia na pierwszy element podrzędny, który można zaznaczyć, a :focus pasuje do hosta. Nie łącz delegatesFocus: true z rolą/stanem na hoście, bo fokus zostanie przeniesiony na element wewnętrzny i pominie stan hosta.
  • W przypadku zmiennego fokusu lub przenoszenia fokusu do nowo wyświetlonego obszaru nadaj celowi atrybut tabindex="-1" i wywołaj .focus(). Cele, które można zaznaczyć programowo, powinny być wyraźnie określone, a nie zależne od kolejności kart.

Relacje ARIA między elementami shadow root

  • OBOWIĄZKOWY: element id, do którego odwołuje się aria-labelledby, aria-describedby, aria-controls itd., jest rozpoznawany tylko w własnym drzewie elementu odwołującego się. Atrybut oparty na id w Light DOM nie może wskazywać na id w drzewie Shadow DOM i odwrotnie. Odwołanie jest ignorowane bez powiadomienia.
  • Aby przekroczyć granicę, użyj odzwierciedlonych właściwości elementu ARIA (np. ariaLabelledByElements, ariaDescribedByElements), które przyjmują bezpośrednie odwołania do elementów JS zamiast identyfikatorów. Są one rozwiązywane na zewnątrz: element w drzewie shadow może wskazywać na cel w Light DOM lub dowolnym drzewie nadrzędnym (w tym na treści w przedziałach), dzięki czemu można oznaczyć element sterujący wewnątrz shadow z poziomu treści Light DOM – czego nie można wyrazić za pomocą odniesień id.
  • Nie są rozwiązywane do wewnątrz: element nie może odwoływać się do podrzędnego drzewa shadow (np. host nie może być oznaczony etykietą przez własną zawartość shadow). W tym kierunku standardem jest nowy mechanizm Reference Target, ale nie jest on jeszcze powszechnie dostępny.
  • Jeśli relacji nie można wyrazić w żaden sposób, zachowaj ją w jednym drzewie (wyrenderuj etykietę w tym samym drzewie) lub użyj tekstu aria-label zamiast odwołania id.

Strategie zastępcze

Jeśli domieszka ARIA ElementInternals jest niedostępna, ustaw rolę i stan ARIA jako atrybuty na hoście (this.setAttribute('role', 'switch')). Działa to wszędzie, ale kosztem umieszczenia w DOM atrybutów, które konsument może zastąpić.

Jeśli odzwierciedlone właściwości elementu ARIA są niedostępne, użyj atrybutu aria-* opartego na id w przypadku relacji w tym samym drzewie. Etykieta między granicami nie ma odpowiednika opartego na id, więc użyj tekstu aria-label.