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(jakToggleSwitchpowyżej), umieść hosta w kolejności tabulacji wewnątrzconnectedCallback(if (!this.hasAttribute('tabindex')) this.tabIndex = 0– nigdy w konstruktorze, który zgłasza błąd wdocument.createElement), aby fokus trafiał na hosta, a technologia wspomagająca osoby z niepełnosprawnością ogłaszała jego rolę i stan. Nie ustawiaj atrybututabindexna 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 zstatic 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:focuspasuje do hosta. Nie łączdelegatesFocus: truez 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-controlsitd., jest rozpoznawany tylko w własnym drzewie elementu odwołującego się. Atrybut oparty naidw Light DOM nie może wskazywać naidw 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-labelzamiast odwołaniaid.
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.