Stile dei componenti Web utilizzando un foglio di stile condiviso

Pubblicato: 2022-03-10
Riepilogo rapido ↬ I componenti Web sono una nuova straordinaria funzionalità del Web, che consente agli sviluppatori di definire i propri elementi HTML personalizzati. Se combinati con una guida di stile, i componenti Web possono creare un'API componente, che consente agli sviluppatori di interrompere la copia e incollare frammenti di codice e utilizzare invece semplicemente un elemento DOM. Usando il DOM ombra, possiamo incapsulare il componente web e non doverci preoccupare di guerre di specificità con nessun altro foglio di stile sulla pagina. Tuttavia, i componenti Web e le guide di stile attualmente sembrano essere in contrasto tra loro.

I componenti Web sono una nuova straordinaria funzionalità del Web, che consente agli sviluppatori di definire i propri elementi HTML personalizzati. Se combinati con una guida di stile, i componenti Web possono creare un'API componente, che consente agli sviluppatori di interrompere la copia e incollare frammenti di codice e utilizzare invece semplicemente un elemento DOM. Usando il DOM ombra, possiamo incapsulare il componente web e non doverci preoccupare di guerre di specificità con nessun altro foglio di stile sulla pagina.

Tuttavia, i componenti Web e le guide di stile attualmente sembrano essere in contrasto tra loro. Da un lato, le guide di stile forniscono una serie di regole e stili che vengono applicati globalmente alla pagina e garantiscono la coerenza nel sito web. D'altra parte, i componenti web con il DOM ombra impediscono a qualsiasi stile globale di penetrare nel loro incapsulamento, impedendo così alla guida di stile di influenzarli.

Ulteriori letture su SmashingMag:

  • Applicazione delle migliori pratiche nei sistemi basati su componenti
  • Come utilizzare il preprocessore LESS CSS per fogli di stile più intelligenti
  • Un tuffo in profondità in Adobe Edge Reflow

Quindi, come possono i due coesistere, con le guide di stile globali che continuano a fornire coerenza e stili, anche ai componenti Web con il DOM ombra? Per fortuna, ci sono soluzioni che funzionano oggi e altre soluzioni in arrivo che consentono alle guide di stile globali di fornire uno stile ai componenti web. (Per il resto di questo articolo, userò il termine "componenti web" per fare riferimento a elementi personalizzati con il DOM ombra.)

Altro dopo il salto! Continua a leggere sotto ↓

Cosa dovrebbe fare uno stile di una guida di stile globale in un componente Web?

Prima di discutere come ottenere una guida di stile globale per lo stile di un componente Web, dovremmo discutere di cosa dovrebbe e non dovrebbe provare a modellare.

Prima di tutto, le attuali migliori pratiche per i componenti Web affermano che un componente Web, inclusi i suoi stili, dovrebbe essere incapsulato, in modo che non dipenda da alcuna risorsa esterna per funzionare. Ciò consente di utilizzarlo ovunque all'interno o all'esterno del sito Web, anche quando la guida allo stile non è disponibile.

Di seguito è riportato un semplice componente Web del modulo di accesso che incapsula tutti i suoi stili.

 <template> <style> :host { color: #333333; font: 16px Arial, sans-serif; } p { margin: 0; } p + p { margin-top: 20px; } a { color: #1f66e5; } label { display: block; margin-bottom: 5px; } input[type="text"], input[type="password"] { background-color: #eaeaea; border: 1px solid grey; border-radius: 4px; box-sizing: border-box; color: inherit; font: inherit; padding: 10px 10px; width: 100%; } input[type="submit"] { font: 16px/1.6 Arial, sans-serif; color: white; background: cornflowerblue; border: 1px solid #1f66e5; border-radius: 4px; padding: 10px 10px; width: 100%; } .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <div class="container"> <form action="#"> <p> <label for="username">User Name</label> <input type="text" name="username"> </p> <p> <label for="password">Password</label> <input type="password" name="password"> </p> <p> <input type="submit" value="Login"> </p> <p class="footnote">Not registered? <a href="#">Create an account</a></p> </form> </div> </template> <script> const doc = (document._currentScript || document.currentScript).ownerDocument; const template = doc.querySelector('#login-form-template'); customElements.define('login-form', class extends HTMLElement { constructor() { super(); const root = this.attachShadow({mode: 'closed'}); const temp = document.importNode(template.content, true); root.appendChild(temp); } }); </script>

Nota: gli esempi di codice sono scritti nella specifica della versione 1 per i componenti Web.

Modulo di accesso con nome utente e password.
Un semplice componente Web del modulo di accesso

Tuttavia, incapsulare completamente ogni componente web porterebbe inevitabilmente a molti CSS duplicati, specialmente quando si tratta di impostare la tipografia e lo stile degli elementi nativi. Se uno sviluppatore desidera utilizzare un paragrafo, un tag di ancoraggio o un campo di input nel suo componente web, dovrebbe avere uno stile come il resto del sito web.

Se incapsulamo completamente tutti gli stili di cui ha bisogno il componente Web, il CSS per lo stile di paragrafi, tag di ancoraggio, campi di input e così via verrebbe duplicato in tutti i componenti Web che li utilizzano. Ciò non solo aumenterebbe i costi di manutenzione, ma porterebbe anche a dimensioni di download molto maggiori per gli utenti.

Invece di incapsulare tutti gli stili, i componenti Web dovrebbero incapsulare solo i loro stili univoci e quindi dipendere da un insieme di stili condivisi per gestire gli stili per tutto il resto. Questi stili condivisi diventerebbero essenzialmente una sorta di Normalize.css, che i componenti Web potrebbero utilizzare per garantire che gli elementi nativi siano stilizzati secondo la guida di stile.

Nell'esempio precedente, il componente Web del modulo di accesso dichiarerebbe gli stili solo per le sue due classi univoche: .container e .footnote . Il resto degli stili apparterrebbe al foglio di stile condiviso e darebbe lo stile ai paragrafi, ai tag di ancoraggio, ai campi di input e così via.

In breve, la guida allo stile non dovrebbe provare a definire lo stile del componente Web, ma dovrebbe invece fornire una serie di stili condivisi che i componenti Web possono utilizzare per ottenere un aspetto coerente.

Come si faceva lo styling di Shadow DOM con fogli di stile esterni

La specifica iniziale per i componenti Web (nota come versione 0) consentiva a qualsiasi foglio di stile esterno di penetrare nel DOM ombra attraverso l'uso dei selettori ::shadow o /deep/ CSS. L'uso di ::shadow e /deep/ ti ha permesso di avere una guida di stile che penetra nel DOM ombra e imposta gli stili condivisi, indipendentemente dal fatto che il componente web lo desideri o meno.

 /* Style all p tags inside a web components shadow DOM */ login-form::shadow p { color: red; }

Con l'avvento della versione più recente delle specifiche dei componenti Web (nota come versione 1), gli autori hanno rimosso la capacità dei fogli di stile esterni di penetrare nel DOM ombra e non hanno fornito alternative. Invece, la filosofia è cambiata dall'uso dei draghi per lo stile dei componenti Web all'utilizzo dei bridge. In altre parole, gli autori dei componenti Web dovrebbero essere responsabili di quali regole di stile esterne sono autorizzate a definire lo stile del loro componente, piuttosto che essere obbligati a consentirle.

Sfortunatamente, quella filosofia non ha ancora raggiunto il web, il che ci lascia un po' in crisi. Fortunatamente, alcune soluzioni disponibili oggi e alcune in arrivo in un futuro non molto lontano consentiranno a un foglio di stile condiviso di modellare un componente web.

Cosa puoi fare oggi

Esistono tre tecniche che puoi utilizzare oggi che consentiranno a un componente Web di condividere stili: @import , elementi personalizzati e una libreria di componenti Web.

Usando @import

L'unico modo nativo oggi per portare un foglio di stile in un componente Web è utilizzare @import . Anche se funziona, è un anti-modello. Per i componenti Web, tuttavia, è un problema di prestazioni ancora più grande.

 <template> <style> @import "styleguide.css" </style> <style> .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- rest of template DOM --> </template>

Normalmente, @import è un anti-pattern perché scarica tutti i fogli di stile in serie, invece che in parallelo, specialmente se sono annidati. Nella nostra situazione, il download di un singolo foglio di stile in serie non può essere d'aiuto, quindi in teoria dovrebbe andare bene. Ma quando l'ho testato in Chrome, i risultati hanno mostrato che l'utilizzo di @import ha causato il rendering della pagina fino a mezzo secondo più lento rispetto a quando si incorporavano semplicemente gli stili direttamente nel componente Web.

Nota: a causa delle differenze nel modo in cui funziona il polyfill delle importazioni HTML rispetto alle importazioni HTML native, WebPagetest.org può essere utilizzato solo per fornire risultati affidabili nei browser che supportano nativamente le importazioni HTML (ad esempio Chrome).

Grafico a barre delle prestazioni dei componenti Web nativi.
I risultati di tre test sulle prestazioni mostrano che @import fa sì che il browser esegua un rendering fino a mezzo secondo più lento rispetto a quando si incorporano gli stili direttamente nel componente Web.

Alla fine, @import è ancora un anti-pattern e può essere un problema di prestazioni nei componenti web. Quindi, non è un'ottima soluzione.

Non utilizzare il DOM ombra

Poiché il problema con il tentativo di fornire stili condivisi ai componenti Web deriva dall'utilizzo del DOM ombra, un modo per evitare del tutto il problema consiste nel non utilizzare il DOM ombra.

Non utilizzando il DOM ombra, creerai elementi personalizzati anziché componenti Web (vedi a lato sotto), l'unica differenza è la mancanza del DOM ombra e dell'ambito. Il tuo elemento sarà soggetto agli stili della pagina, ma dobbiamo già affrontarlo oggi, quindi non è niente che non sappiamo già come gestire. Gli elementi personalizzati sono completamente supportati da webcomponentjs polyfill, che ha un ottimo supporto per il browser.

Il più grande vantaggio degli elementi personalizzati è che puoi creare una libreria di modelli utilizzandoli oggi e non devi aspettare che il problema dello stile condiviso sia risolto. E poiché l'unica differenza tra i componenti Web e gli elementi personalizzati è il DOM ombra, puoi sempre abilitare il DOM ombra nei tuoi elementi personalizzati una volta che è disponibile una soluzione per lo stile condiviso.

Se decidi di creare elementi personalizzati, tieni presente alcune differenze tra elementi personalizzati e componenti web.

Innanzitutto, poiché gli stili per l'elemento personalizzato sono soggetti agli stili di pagina e viceversa, ti consigliamo di assicurarti che i tuoi selettori non causino conflitti. Se le tue pagine utilizzano già una guida di stile, lascia gli stili per l'elemento personalizzato nella guida di stile e fai in modo che l'elemento restituisca il DOM previsto e la struttura della classe.

Lasciando gli stili nella guida di stile, creerai un percorso di migrazione fluido per i tuoi sviluppatori, perché possono continuare a utilizzare la guida di stile come prima, ma poi migrare lentamente all'utilizzo del nuovo elemento personalizzato quando sono in grado di farlo. Una volta che tutti hanno utilizzato l'elemento personalizzato, è possibile spostare gli stili in modo che risiedano all'interno dell'elemento in modo da tenerli insieme e consentire un refactoring più semplice sui componenti Web in un secondo momento.

In secondo luogo, assicurati di incapsulare qualsiasi codice JavaScript all'interno di un'espressione di funzione richiamata immediatamente (IFFE), in modo da non sanguinare alcuna variabile nell'ambito globale. Oltre a non fornire l'ambito CSS, gli elementi personalizzati non forniscono l'ambito JavaScript.

In terzo luogo, dovrai utilizzare la funzione connectedCallback dell'elemento personalizzato per aggiungere il modello DOM all'elemento. In base alle specifiche del componente Web, gli elementi personalizzati non devono aggiungere elementi figlio durante la funzione di costruzione, quindi dovrai rinviare l'aggiunta del DOM alla funzione connectedCallback .

Infine, l'elemento <slot> non funziona al di fuori del DOM ombra. Ciò significa che dovrai utilizzare un metodo diverso per fornire agli sviluppatori un modo per inserire il loro contenuto nel tuo elemento personalizzato. Di solito, ciò comporta semplicemente la manipolazione del DOM da soli per inserire il loro contenuto dove lo desideri.

Tuttavia, poiché non c'è separazione tra il DOM ombra e il DOM chiaro con elementi personalizzati, dovrai anche fare molta attenzione a non applicare uno stile al DOM inserito, a causa degli stili a cascata dei tuoi elementi.

 <!-- login-form.html --> <template> <style> login-form .container { max-width: 300px; padding: 50px; border: 1px solid grey; } login-form .footnote { text-align: center; } </style> <!-- Rest of template DOM --> </template> <script> (function() { const doc = (document._currentScript || document.currentScript).ownerDocument; const template = doc.querySelector('#login-form-template'); customElements.define('login-form', class extends HTMLElement { constructor() { super(); } // Without the shadow DOM, we have to manipulate the custom element // after it has been inserted in the DOM. connectedCallback() { const temp = document.importNode(template.content, true); this.appendChild(temp); } }); })(); </script>
 <!-- index.html --> <link rel="stylesheet" href="styleguide.css"> <link rel="import" href="login-form.html"> <login-form></login-form>

In termini di prestazioni, gli elementi personalizzati sono veloci quasi quanto i componenti Web non utilizzati (ad esempio collegando il foglio di stile condiviso nella head e utilizzando solo elementi DOM nativi). Di tutte le tecniche che puoi usare oggi, questa è di gran lunga la più veloce.

Grafico a barre delle prestazioni degli elementi personalizzati.
I risultati di due test sulle prestazioni mostrano che gli elementi personalizzati sono veloci quasi quanto non utilizzare affatto i componenti Web.

A parte: un elemento personalizzato è ancora un componente Web a tutti gli effetti. Il termine "componenti Web" viene utilizzato per descrivere quattro tecnologie separate: elementi personalizzati, tag modello, importazioni HTML e DOM ombra.

Sfortunatamente, il termine è stato usato per descrivere tutto ciò che utilizza qualsiasi combinazione delle quattro tecnologie. Ciò ha portato a molta confusione su ciò che le persone intendono quando dicono "componente web". Proprio come ha scoperto Rob Dodson, ho trovato utile usare termini diversi quando si parla di elementi personalizzati con e senza il DOM ombra.

La maggior parte degli sviluppatori con cui ho parlato tende ad associare il termine "componente web" a un elemento personalizzato che utilizza il DOM ombra. Quindi, ai fini di questo articolo, ho creato una distinzione artificiale tra un componente web e un elemento personalizzato.

Utilizzo di una libreria di componenti Web

Un'altra soluzione che puoi utilizzare oggi è una libreria di componenti Web, come Polymer, SkateJS o X-Tag. Queste librerie aiutano a colmare le lacune del supporto odierno e possono anche semplificare il codice necessario per creare un componente web. Di solito forniscono anche funzionalità aggiuntive che semplificano la scrittura di componenti Web.

Ad esempio, Polymer ti consente di creare un semplice componente Web in poche righe di JavaScript. Un ulteriore vantaggio è che Polymer fornisce una soluzione per l'utilizzo del DOM ombra e di un foglio di stile condiviso. Ciò significa che oggi puoi creare componenti Web che condividono gli stili.

Per fare ciò, crea quello che chiamano un modulo di stile, che contiene tutti gli stili condivisi. Può essere un tag <style> con gli stili condivisi incorporati o un <link rel=“import”> che punta a un foglio di stile condiviso. In entrambi i casi, includi gli stili nel tuo componente web con un <style include> , quindi Polymer analizzerà gli stili e li aggiungerà come tag <style> in linea al tuo componente web.

 <!-- shared-styles.html --> <dom-module> <!-- Link to a shared style sheet --> <!-- <link rel="import" href="styleguide.css"> --> <!-- Inline the shared styles --> <template> <style> :host { color: #333333; font: 16px Arial, sans-serif; } /* Rest of shared CSS */ </style> </template> </dom-module>
 <!-- login-form.html --> <link rel="import" href="../polymer/polymer.html"> <link rel="import" href="../shared-styles/shared-styles.html"> <dom-module> <template> <!-- Include the shared styles --> <style include="shared-styles"></style> <style> .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- Rest of template DOM --> </template> <script> Polymer({ is: 'login-form' }); </script> </dom-module>

L'unico aspetto negativo dell'utilizzo di una libreria è che può ritardare il tempo di rendering dei componenti Web. Ciò non dovrebbe sorprendere perché il download del codice della libreria e l'elaborazione dello stesso richiedono tempo. Qualsiasi componente Web sulla pagina non può iniziare il rendering finché la libreria non ha terminato l'elaborazione.

Nel caso di Polymer, può ritardare il tempo di rendering della pagina fino a mezzo secondo rispetto ai componenti Web nativi. Un modulo di stile che incorpora gli stili è leggermente più lento di un modulo di stile che collega gli stili e incorporare gli stili direttamente nel componente Web è veloce quanto l'utilizzo di un modulo di stile.

Ancora una volta, Polymer non fa nulla in particolare per rallentare il tempo di rendering. Il download della libreria Polymer e l'elaborazione di tutte le sue fantastiche funzionalità, oltre alla creazione di tutti i binding dei modelli, richiedono tempo. È solo il compromesso che dovrai fare per utilizzare una libreria di componenti Web.

Grafico a barre delle prestazioni del componente web Polymer.

I risultati dei test sulle prestazioni mostrano che, utilizzando Polymer, i componenti Web avranno un rendering fino a mezzo secondo più lento rispetto ai componenti Web nativi.

La promessa del futuro

Se nessuna delle soluzioni attuali funziona per te, non disperare. Se tutto va bene, entro pochi mesi o qualche anno, saremo in grado di utilizzare stili condivisi utilizzando alcuni approcci diversi.

Proprietà personalizzate

Le proprietà personalizzate (o variabili CSS, come sono state chiamate) sono un modo per impostare e utilizzare le variabili nei CSS. Questa nozione non è nuova ai preprocessori CSS, ma in quanto funzionalità CSS nativa, le proprietà personalizzate sono in realtà più potenti di una variabile del preprocessore.

Per dichiarare una proprietà personalizzata, utilizza la notazione della proprietà personalizzata di –my-variable: value e accedi alla variabile utilizzando la property: var(–my-variable) . Una proprietà personalizzata si sovrappone come qualsiasi altra regola CSS, quindi il suo valore eredita dal suo genitore e può essere sovrascritto. L'unico avvertimento sulle proprietà personalizzate è che devono essere dichiarate all'interno di un selettore e non possono essere dichiarate da sole, a differenza di una variabile del preprocessore.

 <style> /* Declare the custom property */ html { --main-bg-color: red; } /* Use the custom property */ input { background: var(--main-bg-color); } </style>

Una cosa che rende le proprietà personalizzate così potenti è la loro capacità di perforare il DOM ombra. Questa non è la stessa idea dei selettori /deep/ e ::shadow perché non si fanno strada con la forza nel componente web. Invece, l'autore del componente Web deve utilizzare la proprietà personalizzata nel proprio CSS per poterla applicare. Ciò significa che l'autore di un componente Web può creare un'API di proprietà personalizzata che i consumatori del componente Web possono utilizzare per applicare i propri stili.

 <template> <style> /* Declare the custom property API */ :host { --main-bg-color: brown; } .one { color: var(--main-bg-color); } </style> <div class="one">Hello World</div> </template> <script> /* Code to set up my-element web component */ </script> <my-element></my-element> <style> /* Override the custom variable with own value */ my-element { --main-bg-color: red; } </style>

Il supporto del browser per le proprietà personalizzate è sorprendentemente buono. L'unico motivo per cui non è una soluzione che puoi utilizzare oggi è che non esiste un polyfill funzionante senza Custom Elements versione 1. Il team dietro il polyfill di webcomponentjs sta attualmente lavorando per aggiungerlo, ma non è ancora stato rilasciato e in uno stato costruito, il che significa che se esegui l'hashing delle tue risorse per la produzione, non puoi usarle. Da quello che ho capito, dovrebbe uscire all'inizio del prossimo anno.

Anche così, le proprietà personalizzate non sono un buon metodo per condividere gli stili tra i componenti Web. Poiché possono essere utilizzati solo per dichiarare un singolo valore di proprietà, il componente Web dovrebbe comunque incorporare tutti gli stili della guida di stile, sebbene con i loro valori sostituiti con variabili.

Le proprietà personalizzate sono più adatte alle opzioni di tematica, piuttosto che agli stili condivisi. Per questo motivo, le proprietà personalizzate non sono una soluzione praticabile al nostro problema.

/* Usa la proprietà personalizzata */ input { background: var(–main-bg-color); } </stile>

Una cosa che rende le proprietà personalizzate così potenti è la loro capacità di perforare il DOM ombra. Questa non è la stessa idea dei selettori /deep/ e ::shadow perché non si fanno strada con la forza nel componente web. Invece, l'autore del componente Web deve utilizzare la proprietà personalizzata nel proprio CSS per poterla applicare. Ciò significa che l'autore di un componente Web può creare un'API di proprietà personalizzata che i consumatori del componente Web possono utilizzare per applicare i propri stili.

 <template> <style> /* Declare the custom property API */ :host { --main-bg-color: brown; } .one { color: var(--main-bg-color); } </style> <div class="one">Hello World</div> </template> <script> /* Code to set up my-element web component */ </script> <my-element></my-element> <style> /* Override the custom variable with own value */ my-element { --main-bg-color: red; } </style>

Il supporto del browser per le proprietà personalizzate è sorprendentemente buono. L'unico motivo per cui non è una soluzione che puoi utilizzare oggi è che non esiste un polyfill funzionante senza Custom Elements versione 1. Il team dietro il polyfill di webcomponentjs sta attualmente lavorando per aggiungerlo, ma non è ancora stato rilasciato e in uno stato costruito, il che significa che se esegui l'hashing delle tue risorse per la produzione, non puoi usarle. Da quello che ho capito, dovrebbe uscire all'inizio del prossimo anno.

Anche così, le proprietà personalizzate non sono un buon metodo per condividere gli stili tra i componenti Web. Poiché possono essere utilizzati solo per dichiarare un singolo valore di proprietà, il componente Web dovrebbe comunque incorporare tutti gli stili della guida di stile, sebbene con i loro valori sostituiti con variabili.

Le proprietà personalizzate sono più adatte alle opzioni di tematica, piuttosto che agli stili condivisi. Per questo motivo, le proprietà personalizzate non sono una soluzione praticabile al nostro problema.

@applica regole

Oltre alle proprietà personalizzate, CSS riceve anche le regole @apply . Le regole Apply sono essenzialmente dei mixin per il mondo CSS. Sono dichiarate in modo simile alle proprietà personalizzate, ma possono essere utilizzate per dichiarare gruppi di proprietà anziché solo valori di proprietà. Proprio come le proprietà personalizzate, i loro valori possono essere ereditati e sovrascritti e devono essere dichiarati all'interno di un selettore per funzionare.

 <style> /* Declare rule */ html { --typography: { font: 16px Arial, sans-serif; color: #333333; } } /* Use rule */ input { @apply --typography; } </style>

Il supporto del browser per le regole @apply è praticamente inesistente. Chrome attualmente lo supporta dietro un flag di funzionalità (che non sono riuscito a trovare), ma questo è tutto. Inoltre, non esiste un polyfill funzionante per lo stesso motivo in cui non esiste un polyfill per le proprietà personalizzate. Il team polyfill di webcomponentjs sta anche lavorando per aggiungere le regole @apply , insieme alle proprietà personalizzate, quindi entrambe saranno disponibili una volta rilasciata la nuova versione.

A differenza delle proprietà personalizzate, le regole @apply sono una soluzione molto migliore per condividere gli stili. Poiché possono impostare un gruppo di dichiarazioni di proprietà, è possibile utilizzarle per impostare lo stile predefinito per tutti gli elementi nativi e quindi utilizzarle all'interno del componente Web. Per fare ciò, dovresti creare una regola @apply per ogni elemento nativo.

Tuttavia, per consumare gli stili, dovresti applicarli manualmente a ogni elemento nativo, che duplicherà comunque la dichiarazione di stile in ogni componente web. Anche se è meglio che incorporare tutti gli stili, non è nemmeno molto conveniente perché diventa standard nella parte superiore di ogni componente Web, che devi ricordarti di aggiungere affinché gli stili funzionino correttamente.

 /* styleguide.css */ html { --typography: { color: #333333; font: 16px Arial, sans-serif; } --paragraph: { margin: 0; } --label { display: block; margin-bottom: 5px; } --input-text { background-color: #eaeaea; border: 1px solid grey; border-radius: 4px; box-sizing: border-box; color: inherit; font: inherit; padding: 10px 10px; width: 100%; } --input-submit { font: 16px/1.6 Arial, sans-serif; color: white; background: cornflowerblue; border: 1px solid #1f66e5; border-radius: 4px; padding: 10px 10px; width: 100%; } /* And so on for every native element */ }
 <!-- login-form.html --> <template> <style> :host { @apply --typography; } p { @apply --paragraph; } label { @apply --label; } input-text { @apply --input-text; } .input-submit { @apply --input-submit; } .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- Rest of template DOM --> </template>

A causa della necessità di un ampio standard, non credo che le regole @apply sarebbero una buona soluzione per condividere gli stili tra i componenti Web. Sono un'ottima soluzione per i temi, però.

nel DOM ombra

Secondo la specifica del componente Web, i browser ignorano tutti i <link rel=“stylesheet”> nel DOM ombra, trattandoli proprio come farebbero all'interno di un frammento di documento. Questo ci ha impedito di poter collegare qualsiasi stile condiviso nei nostri componenti Web, il che è stato un peccato, cioè fino a pochi mesi fa, quando il gruppo di lavoro sui componenti Web ha proposto che i <link rel=“stylesheet”> dovrebbero funzionare in l'ombra DOM. Dopo solo una settimana di discussioni, tutti concordarono sul fatto che avrebbero dovuto e pochi giorni dopo lo aggiunsero alle specifiche HTML.

 <template> <link rel="stylesheet" href="styleguide.css"> <style> .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- rest of template DOM --> </template>

Se sembra un po' troppo veloce per il gruppo di lavoro per concordare una specifica, è perché non era una nuova proposta. Il fatto che i tag di link funzionassero nel DOM ombra è stato effettivamente proposto almeno tre anni fa, ma è stato arretrato fino a quando non sono stati in grado di garantire che non fosse un problema per le prestazioni.

Se l'accettazione della proposta non è abbastanza entusiasmante, Chrome 55 (attualmente Chrome Canary) ha aggiunto la funzionalità iniziale di far funzionare i tag di link nel DOM ombra. Sembra persino che questa funzionalità sia arrivata nella versione attuale di Chrome. Anche Safari ha implementato la funzionalità in Safari 18.

Essere in grado di collegare gli stili condivisi è di gran lunga il metodo più conveniente per condividere gli stili tra i componenti Web. Tutto quello che dovresti fare è creare il tag di link e tutti gli elementi nativi avrebbero uno stile di conseguenza, senza richiedere alcun lavoro aggiuntivo.

Naturalmente, il modo in cui i produttori di browser implementano la funzionalità determinerà se questa soluzione è praticabile. Affinché ciò funzioni correttamente, i tag di link dovrebbero essere deduplicati, in modo che più componenti Web che richiedono lo stesso file CSS causino una sola richiesta HTTP. Il CSS dovrebbe anche essere analizzato una sola volta, in modo che ogni istanza del componente Web non debba ricalcolare gli stili condivisi, ma riutilizzi invece gli stili calcolati.

Chrome fa già entrambe le cose. Quindi, se tutti gli altri produttori di browser lo implementano allo stesso modo, i tag di link che lavorano nel DOM ombra risolverebbero sicuramente il problema di come condividere gli stili tra i componenti Web.

Fogli di stile costruibili

Potresti trovare difficile da credere, dal momento che non l'abbiamo ancora nemmeno ottenuto, ma un tag di link che funziona nel DOM ombra non è una soluzione a lungo termine. Invece, è solo una soluzione a breve termine per arrivare alla vera soluzione: fogli di stile costruibili.

I fogli di stile costruibili sono una proposta per consentire la creazione di oggetti StyleSheet in JavaScript tramite una funzione di costruzione. Il foglio di stile costruito potrebbe quindi essere aggiunto al DOM ombra tramite un'API, che consentirebbe al DOM ombra di utilizzare un insieme di stili condivisi.

Sfortunatamente, questo è tutto ciò che ho potuto raccogliere dalla proposta. Ho cercato di trovare maggiori informazioni su cosa fossero i fogli di stile costruibili chiedendo al Web Components Working Group, ma mi hanno reindirizzato alla mailing list del CSS Working Group del W3C, dove ho chiesto di nuovo, ma nessuno ha risposto. Non riuscivo nemmeno a capire come procedeva la proposta, perché non veniva aggiornata da oltre due anni.

Anche così, il Web Components Working Group lo usa come soluzione per condividere gli stili tra i componenti web. Si spera che la proposta venga aggiornata o che il Web Components Working Group rilasci ulteriori informazioni su di essa e sulla sua adozione. Fino ad allora, la soluzione "a lungo termine" sembra non avverrà nel prossimo futuro.

Lezioni imparate

Dopo mesi di ricerca e test, sono abbastanza fiducioso per il futuro. È confortante sapere che dopo anni senza una soluzione per la condivisione degli stili tra i componenti web, finalmente ci sono delle risposte. Quelle risposte potrebbero non essere stabilite per qualche anno in più, ma almeno ci sono.

Se desideri utilizzare una guida di stile condivisa per definire lo stile dei componenti Web oggi, non puoi utilizzare il DOM shadow e creare invece elementi personalizzati oppure puoi utilizzare una libreria di componenti Web che supporta i polyfill per la condivisione degli stili. Entrambe le soluzioni hanno i loro pro e contro, quindi usa quella che funziona meglio per il tuo progetto.

Se decidi di aspettare un po' prima di approfondire i componenti web, tra qualche anno dovremmo avere delle ottime soluzioni per condividere gli stili tra di loro. Quindi, continua a controllare come sta procedendo.

Cose da tenere a mente

Tieni a mente alcune cose se decidi di utilizzare elementi personalizzati o componenti Web oggi.

Ancora più importante, le specifiche del componente Web sono ancora in fase di sviluppo attivo, il che significa che le cose possono e cambieranno. I componenti Web sono ancora molto all'avanguardia, quindi preparati a rimanere all'erta mentre sviluppi con esso.

Se decidi di utilizzare il DOM shadow, sappi che è piuttosto lento e poco performante nei browser polyfilled. È stato per questo motivo che gli sviluppatori di Polymer hanno creato la loro oscura implementazione DOM e l'hanno resa predefinita.

Chrome, Opera e, di recente, Safari sono gli unici browser che supportano la versione shadow DOM 0. Firefox è ancora in fase di sviluppo, anche se lo ha supportato dietro un esperimento dalla versione 29. Microsoft lo sta ancora considerando per Edge e lo ha come priorità assoluta sulla sua road map.

Tuttavia, la versione 0 del DOM ombra è la vecchia specifica. Shadow DOM versione 1 è la nuova e solo Chrome, Safari e Opera lo supportano completamente. Per non parlare del fatto che la versione 0 degli elementi personalizzati ha subito lo stesso aggiornamento e solo Chrome supporta completamente la versione 1 degli elementi personalizzati, mentre l'anteprima tecnica di Safari lo supporta a partire dalla versione 17. La versione 1 degli elementi personalizzati presenta alcune modifiche importanti nel modo in cui vengono scritti i componenti Web, quindi assicurati di comprendere appieno cosa ciò comporta.

Infine, il polyfill webcomponentjs supporta solo l'implementazione della versione 0 del DOM shadow e degli elementi personalizzati. Un ramo della versione 1 di polyfill supporterà la versione 1, ma non è ancora stata rilasciata.