Orientamento dei componenti web

I componenti web sono elementi personalizzati, Shadow DOM e template HTML: le primitive della piattaforma per componenti riutilizzabili e indipendenti dal framework, il cui markup e i cui stili sono incapsulati nel documento. Utilizzali per sistemi di progettazione, widget incorporabili e UI autonome.

Le regole riportate di seguito sono trasversali. Recupera la guida specifica per l'attività in questione; ognuna ribadisce il contesto di cui ha bisogno e documenta le API e le insidie specifiche.

Decidi se hai bisogno di un elemento personalizzato

Utilizza un elemento personalizzato solo quando hai bisogno di un comportamento incapsulato, di un ciclo di vita o di un'API riutilizzabile basata su JS in un tag. Non crearne uno quando:

  • Un semplice elemento HTML con CSS sarebbe sufficiente. Un <button> o <details> con stile è più accessibile e più economico di una nuova implementazione.
  • L'elemento è puramente di presentazione e non ha alcun comportamento. Una classe su un elemento nativo è più efficiente e può essere limitata con @layer o @scope.
  • Ti serve solo lo scoping dello stile sul server senza interattività; un foglio di stile con ambito o un DOM shadow dichiarativo (senza un upgrade) potrebbe essere sufficiente.

La ricostruzione della semantica nativa (un <my-button> personalizzato che ricrea lo stato attivo, l'attivazione e ARIA) è l'uso improprio più comune: l'accessibilità della piattaforma viene ereditata senza costi solo se riutilizzi l'elemento nativo. Quando è giustificato un wrapper in stile <my-button> , di solito per hook di stile condivisi, markup aggiuntivo o un comportamento che un elemento nativo non può esprimere, esegui il wrapping del <button> reale anziché implementarlo di nuovo (consulta Applicazione di stili ai componenti web per l'inoltro degli stili a un elemento nativo interno).

Il test consiste nel verificare se hai bisogno di uno stato incapsulato, di un'API con script o di un comportamento che l'elemento nativo non può esprimere, non solo di un aspetto diverso. Un pulsante di attivazione/disattivazione Mi piace/Preferito è idoneo: contiene lo stato del dominio bloccato, espone questo stato come hook di stile per la pagina circostante e invia il proprio evento change, nessuna di queste funzionalità è fornita da un <button> con stile. Un pulsante che cambia colore solo quando viene premuto non lo fa: utilizza <button> + CSS. Quando lo crei, modella il suo stato con uno stato o un attributo personalizzato, a seconda dei casi (consulta Elementi personalizzati).

Abbina lo strumento al codebase

La scelta di utilizzare o meno un elemento personalizzato dipende da ciò che il progetto utilizza già. Gli agenti tendono ad astrarre eccessivamente e a cercare un nuovo elemento con troppa foga. In caso di dubbi, l'opzione più leggera (elemento nativo, CSS, un modello server) è di solito quella giusta e puoi passare a un elemento personalizzato in un secondo momento se il riutilizzo o il comportamento lo richiedono effettivamente. Euristiche, in ordine di priorità approssimativo:

  • Il codebase ha già un modello di componente (React, Vue, Svelte…). Utilizza per impostazione predefinita il componente di quel framework per la UI in-app. Utilizza un elemento personalizzato solo quando l'elemento deve uscire o sopravvivere al framework: una primitiva del sistema di progettazione condivisa tra app o framework, un widget inserito nel markup che il framework non controlla o un output che deve funzionare come HTML semplice (email, CMS, frammenti SSR). Non creare un sistema di componenti parallelo per le normali viste app.
  • Nessuno strumento, markup per lo più statico. Preferisci HTML + CSS semplice o un template/parziale del server. Un elemento personalizzato è utile solo quando hai effettivamente bisogno di un comportamento, di un ciclo di vita o di un'API basata su JS nel tag, non solo per condividere il markup, cosa che un'inclusione lato server o <template> fa senza inviare JS.
  • Generatore di siti statici con isole di interattività. Esegui il rendering del markup in modo statico; riserva l'elemento personalizzato per la parte interattiva. Per "molto markup, poca interattività", mantieni il markup in Light DOM (o Declarative Shadow DOM) e lascia che un piccolo elemento personalizzato lo migliori con il potenziamento progressivo, anziché eseguire nuovamente il rendering di tutto da JS.
  • In alternativa, puoi spingere oltre un elemento integrato. Sfruttare al massimo un elemento nativo (CSS, alcuni listener, l'occasionale pseudo-elemento) è solitamente preferibile a un elemento personalizzato, perché mantieni l'accessibilità e il comportamento della piattaforma senza costi aggiuntivi. Passa a un elemento personalizzato quando le soluzioni alternative non sono più gestibili, quando devi lottare contro il comportamento integrato dell'elemento o l'esperienza di creazione diventa peggiore di quella di un'API di componente pulita. La necessità di un po' di JS su un elemento integrato non è, di per sé, un motivo per sostituirlo.

Regole trasversali

Queste regole si applicano a tutte le guide riportate di seguito:

  • OBBLIGATORIO: il nome del tag deve essere in formato kebab case con almeno un trattino (ad es. my-element). Questo è ciò che distingue gli elementi personalizzati da quelli integrati attuali e futuri.
  • OBBLIGATORIO: chiama prima super() nel costruttore, prima di toccare this; la chiamata dopo qualsiasi istruzione che legge o scrive this genera un errore.
  • OBBLIGATORIO: utilizza elementi semantici (<button>, <nav>, <ul>) all'interno dei modelli shadow. La maggior parte degli errori di accessibilità negli shadow tree deriva da un markup non semantico; uno slot non modifica il ruolo di ciò che proietta.
  • OBBLIGATORIO: i riferimenti ARIA id (aria-labelledby, aria-describedby, aria-controls) non possono superare un confine shadow; l'elemento di riferimento e l'elemento id a cui punta devono trovarsi nello stesso albero.
  • Preferisci attachShadow({ mode: 'open' }) in modo che gli strumenti di sviluppo, gli script di pagina e l'idratazione del server possano raggiungere element.shadowRoot. Utilizza 'closed' solo quando devi nascondere la struttura interna agli script della pagina.
  • Evita gli elementi integrati personalizzati (extends HTMLButtonElement con is="…"); Safari ha rifiutato definitivamente di supportarli.

Matrice delle guide ai casi d'uso

Identifica l'attività e recupera la guida corrispondente:

Se devi… Recuperare
Definisci un nuovo tag semantico con comportamento e ciclo di vita Elementi personalizzati
Definisci l'ambito di stili e markup per un componente e proietta i contenuti del consumer al suo interno Shadow DOM, modelli e slot
Eseguire il rendering dello shadow tree di un componente sul server (SSR) senza un flash di contenuti non formattati Pre-rendering di elementi personalizzati
Stile oltre il confine dello shadow DOM (temi, ::part, proprietà personalizzate) Stilizzazione dei componenti web
Invia il valore di un controllo personalizzato a un elemento <form> circostante Elementi personalizzati associati ai moduli
Rendi accessibile un elemento personalizzato (ruoli, stato attivo, ARIA tra shadow root) Componenti web accessibili