Un confine shadow modifica il funzionamento della semantica e di ARIA. I problemi principali sono due: la semantica mancante all'interno dello shadow tree e i riferimenti ARIA che si interrompono senza preavviso perché non possono attraversare le shadow root.
Inizia dal markup semantico: utilizza i tag <button>, <nav>, <ul> reali all'interno del template in modo che la maggior parte della semantica sia già presente e riserva ElementInternals/ARIA per le lacune.
Semantica predefinita tramite il mixin ARIA ElementInternals
Imposta il ruolo e lo stato ARIA di un elemento personalizzato in JS tramite ElementInternals, anziché scrivere attributi ARIA sull'host. In questo modo, la semantica viene mantenuta come impostazione predefinita del componente, consentendo comunque a un consumer di eseguirne l'override con gli attributi nel DOM Light.
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);
In questo modo si evita di inquinare il DOM con attributi ARIA e si impedisce ai consumatori di sovrascriverli accidentalmente, mentre l'albero di accessibilità vede comunque il ruolo e lo stato corretti.
Gestione del focus e delegatesFocus
- Quando l'elemento host stesso è il controllo interattivo e trasporta il ruolo e lo stato tramite
ElementInternals(comeToggleSwitchsopra), inserisci l'host nell'ordine di tabulazione all'interno diconnectedCallback(if (!this.hasAttribute('tabindex')) this.tabIndex = 0, mai nel costruttore, che genera un errore sudocument.createElement) in modo che lo stato attivo si trovi sull'host e la tecnologia per la disabilità annunci il suo ruolo e il suo stato. Non impostaretabindexsull'host per i componenti statici non interattivi (come un callout o una scheda). - Collega la shadow root con
delegatesFocus: truequando il componente include un controllo attivabile interno che possiede la semantica (o su un elemento personalizzato associato al modulo constatic formAssociated = true). L'attivazione dell'host (e il clic su un<label>associato per gli elementi associati al modulo) inoltra l'attivazione al primo elemento secondario attivabile e:focuscorrisponde all'host. Non combinaredelegatesFocus: truecon un ruolo/stato sull'host, altrimenti lo stato attivo verrà applicato all'elemento interno e lo stato dell'host verrà ignorato. - Per lo stato attivo mobile o per spostare lo stato attivo in una regione appena visualizzata, assegna al target
tabindex="-1"e chiama.focus(); rendi espliciti i target attivabili tramite programmazione anziché fare affidamento sull'ordine di tabulazione.
Relazioni ARIA tra shadow root
- OBBLIGATORIO: un
ida cui fanno riferimentoaria-labelledby,aria-describedby,aria-controlse così via viene risolto solo all'interno dell'albero proprio dell'elemento di riferimento. Un attributo basato suidnel light DOM non può puntare a unidall'interno di uno shadow tree e viceversa; il riferimento viene ignorato senza avvisi. - Per superare il confine, utilizza le proprietà ARIA element riflesse (ad es.
ariaLabelledByElements,ariaDescribedByElementse così via), che accettano riferimenti diretti agli elementi JS anziché agli ID. Vengono risolti verso l'esterno: un elemento all'interno di uno shadow tree può puntare a un target nel Light DOM o in qualsiasi albero predecessore (inclusi i contenuti con slot), in modo da poter etichettare un controllo interno allo shadow dai contenuti del Light DOM, un'operazione che i riferimentiidnon possono esprimere. - Non si risolvono verso l'interno: un elemento non può fare riferimento all'interno di uno shadow tree secondario (ad esempio, un host non può essere etichettato dai propri contenuti shadow). In questa direzione, il meccanismo emergente Reference Target è la direzione degli standard, ma non è ancora ampiamente disponibile.
- Quando una relazione non può essere espressa in nessuno dei due modi, mantienila all'interno di un albero (esegui il rendering dell'etichetta nello stesso albero) o utilizza il testo
aria-labelanziché un riferimentoid.
Strategie di riserva
Se il mixin ARIA ElementInternals non è disponibile, imposta il ruolo e lo stato ARIA come attributi sull'host (this.setAttribute('role', 'switch')). Funziona ovunque, al costo di inserire nel DOM attributi che un consumer potrebbe sostituire.
Se le proprietà degli elementi ARIA riflesse non sono disponibili, utilizza un attributo aria-* basato su id per le relazioni nello stesso albero. Un'etichetta tra confini non ha un equivalente basato su id, quindi utilizza il testo aria-label.