Una guida strategica alle proprietà personalizzate CSS

Pubblicato: 2022-03-10
Riepilogo rapido ↬ Le proprietà dinamiche offrono opportunità per nuove idee creative, ma anche il potenziale per aggiungere complessità ai CSS. Per ottenere il massimo da essi, potremmo aver bisogno di una strategia su come scriviamo e strutturiamo CSS con proprietà personalizzate.

Le proprietà personalizzate CSS (a volte note come "variabili CSS") sono ora supportate in tutti i browser moderni e le persone iniziano a usarle in produzione. Questo è fantastico, ma sono diversi dalle variabili nei preprocessori e ho già visto molti esempi di persone che li usano senza considerare quali vantaggi offrono.

Le proprietà personalizzate hanno un enorme potenziale per cambiare il modo in cui scriviamo e strutturiamo CSS e, in misura minore, il modo in cui utilizziamo JavaScript per interagire con i componenti dell'interfaccia utente. Non mi concentrerò sulla sintassi e su come funzionano (per questo ti consiglio di leggere "È ora di iniziare a utilizzare le proprietà personalizzate"). Invece, voglio dare uno sguardo più approfondito alle strategie per ottenere il massimo dalle proprietà personalizzate CSS.

In che modo sono simili alle variabili nei preprocessori?

Le proprietà personalizzate sono un po' come le variabili nei preprocessori, ma presentano alcune differenze importanti. La prima e più ovvia differenza è la sintassi.

Con SCSS usiamo un simbolo del dollaro per denotare una variabile:

 $smashing-red: #d33a2c;

In Less usiamo un simbolo @ :

 @smashing-red: #d33a2c;

Le proprietà personalizzate seguono convenzioni simili e utilizzano un prefisso -- :

 :root { --smashing-red: #d33a2c; } .smashing-text { color: var(--smashing-red); }

Una differenza importante tra le proprietà personalizzate e le variabili nei preprocessori è che le proprietà personalizzate hanno una sintassi diversa per l'assegnazione di un valore e il recupero di tale valore. Quando si recupera il valore di una proprietà personalizzata si usa la funzione var() .

Altro dopo il salto! Continua a leggere sotto ↓

La prossima differenza più evidente è nel nome. Sono chiamate "proprietà personalizzate" perché in realtà sono proprietà CSS. Nei preprocessori, puoi dichiarare e utilizzare variabili quasi ovunque, inclusi blocchi di dichiarazione esterni, regole multimediali o anche come parte di un selettore.

 $breakpoint: 800px; $smashing-red: #d33a2c; $smashing-things: ".smashing-text, .cats"; @media screen and (min-width: $breakpoint) { #{$smashing-things} { color: $smashing-red; } }

La maggior parte degli esempi precedenti non sarebbe valida utilizzando le proprietà personalizzate.

Le proprietà personalizzate hanno le stesse regole su dove possono essere utilizzate come normali proprietà CSS. È molto meglio pensarli come proprietà dinamiche che variabili. Ciò significa che possono essere utilizzati solo all'interno di un blocco di dichiarazione o, in altre parole, le proprietà personalizzate sono legate a un selettore. Questo può essere il selettore :root o qualsiasi altro selettore valido.

 :root { --smashing-red: #d33a2c; } @media screen and (min-width: 800px) { .smashing-text, .cats { --margin-left: 1em; } }

È possibile recuperare il valore di una proprietà personalizzata ovunque si utilizzi un valore in una dichiarazione di proprietà. Ciò significa che possono essere utilizzati come un singolo valore, come parte di un'istruzione abbreviata o anche all'interno di equazioni calc() .

 .smashing-text, .cats { color: var(--smashing-red); margin: 0 var(--margin-horizontal); padding: calc(var(--margin-horizontal) / 2) }

Tuttavia, non possono essere utilizzati nelle query multimediali o nei selettori inclusi :nth-child() .

Probabilmente c'è molto di più che vuoi sapere sulla sintassi e su come funzionano le proprietà personalizzate, ad esempio come usare i valori di fallback e puoi assegnare variabili ad altre variabili (sì), ma questa introduzione di base dovrebbe essere sufficiente per capire il resto di i concetti in questo articolo. Per ulteriori informazioni sulle specifiche di come funzionano le proprietà personalizzate, puoi leggere "È ora di iniziare a utilizzare le proprietà personalizzate" scritto da Serg Hospodarets.

Dinamico vs. statico

Differenze estetiche a parte, la differenza più significativa tra le variabili nei preprocessori e le proprietà personalizzate è il modo in cui vengono definite. Possiamo fare riferimento alle variabili come con ambito statico o dinamico. Le variabili nei preprocessori sono statiche, mentre le proprietà personalizzate sono dinamiche.

Per quanto riguarda CSS, statico significa che puoi aggiornare il valore di una variabile in diversi punti del processo di compilazione, ma questo non può cambiare il valore del codice che l'ha preceduta.

 $background: blue; .blue { background: $background; } $background: red; .red { background: $background; }

risulta in:

 .blue { background: blue; } .red { background: red; }

Una volta che questo è stato renderizzato in CSS, le variabili sono sparite. Ciò significa che potremmo potenzialmente leggere un file .scss e determinarne l'output senza sapere nulla dell'HTML, del browser o di altri input. Questo non è il caso delle proprietà personalizzate.

I preprocessori hanno una sorta di "ambito del blocco" in cui le variabili possono essere modificate temporaneamente all'interno di un selettore, una funzione o un mixin. Questo cambia il valore di una variabile all'interno del blocco, ma è ancora statico. Questo è legato al blocco, non al selettore. Nell'esempio seguente, la variabile $background viene modificata all'interno del blocco .example . Ritorna al valore iniziale al di fuori del blocco, anche se utilizziamo lo stesso selettore.

 $background: red; .example { $background: blue; background: $background; } .example { background: $background; }

Ciò risulterà in:

 .example { background: blue; } .example { background: red; }

Le proprietà personalizzate funzionano in modo diverso. Per quanto riguarda le proprietà personalizzate, con ambito dinamico significa che sono soggette all'ereditarietà e alla cascata. La proprietà è legata a un selettore e se il valore cambia, ciò influisce su tutti gli elementi DOM corrispondenti proprio come qualsiasi altra proprietà CSS.

Questo è ottimo perché puoi modificare il valore di una proprietà personalizzata all'interno di una query multimediale, con uno pseudo-selettore come hover o anche con JavaScript.

 a { --link-color: black; } a:hover, a:focus { --link-color: tomato; } @media screen and (min-width: 600px) { a { --link-color: blue; } } a { color: var(--link-color); }

Non è necessario modificare la posizione in cui viene utilizzata la proprietà personalizzata: cambiamo il valore della proprietà personalizzata con CSS. Ciò significa che utilizzando la stessa proprietà personalizzata, possiamo avere valori diversi in luoghi o contesti diversi sulla stessa pagina.

Globale vs. Locale

Oltre ad essere statiche o dinamiche, le variabili possono anche essere globali o locali. Se scrivi JavaScript, avrai familiarità con questo. Le variabili possono essere applicate a qualsiasi cosa all'interno di un'applicazione oppure il loro ambito può essere limitato a funzioni o blocchi di codice specifici.

CSS è simile. Abbiamo alcune cose che vengono applicate a livello globale e altre che sono più locali. I colori del marchio, la spaziatura verticale e la tipografia sono tutti esempi di cose che potresti voler applicare a livello globale e coerente nel tuo sito Web o applicazione. Abbiamo anche cose locali. Ad esempio, un componente pulsante potrebbe avere una variante piccola e grande. Non vorrai che le dimensioni di questi pulsanti vengano applicate a tutti gli elementi di input o anche a tutti gli elementi della pagina.

Questo è qualcosa che conosciamo nei CSS. Abbiamo sviluppato sistemi di progettazione, convenzioni di denominazione e librerie JavaScript, il tutto per aiutare a isolare i componenti locali e gli elementi di progettazione globali. Le proprietà personalizzate forniscono nuove opzioni per affrontare questo vecchio problema.

Per impostazione predefinita, le proprietà personalizzate CSS hanno un ambito locale per i selettori specifici a cui le applichiamo. Quindi sono un po' come le variabili locali. Tuttavia, anche le proprietà personalizzate vengono ereditate, quindi in molte situazioni si comportano come variabili globali, specialmente se applicate al selettore :root . Ciò significa che dobbiamo riflettere su come usarli.

Così tanti esempi mostrano proprietà personalizzate applicate all'elemento :root e, sebbene vada bene per una demo, può comportare un ambito globale disordinato e problemi non intenzionali con l'ereditarietà. Fortunatamente, abbiamo già imparato queste lezioni.

Le variabili globali tendono ad essere statiche

Ci sono alcune piccole eccezioni, ma in generale, anche la maggior parte delle cose globali nei CSS sono statiche.

Le variabili globali come i colori del marchio, la tipografia e la spaziatura non tendono a cambiare molto da un componente all'altro. Quando cambiano, tende a essere un rebranding globale o qualche altro cambiamento significativo che accade raramente su un prodotto maturo. Ha ancora senso che queste cose siano variabili, vengono utilizzate in molti luoghi e le variabili aiutano con la coerenza. Ma non ha senso che siano dinamici. Il valore di queste variabili non cambia in alcun modo dinamico.

Per questo motivo, consiglio vivamente di utilizzare i preprocessori per le variabili globali (statiche). Questo non solo garantisce che siano sempre statici, ma li denota visivamente all'interno del codice. Questo può rendere i CSS molto più leggibili e più facili da mantenere.

Le variabili statiche locali sono OK (a volte)

Si potrebbe pensare, data la posizione forte sul fatto che le variabili globali siano statiche, che per riflessione, tutte le variabili locali potrebbero dover essere dinamiche. Sebbene sia vero che le variabili locali tendono ad essere dinamiche, questo non è affatto così forte come la tendenza di una variabile globale ad essere statica.

Le variabili statiche locali sono perfettamente OK in molte situazioni. Uso le variabili dei preprocessori nei file dei componenti principalmente per comodità dello sviluppatore.

Si consideri il classico esempio di un componente pulsante con più variazioni di dimensione.

pulsanti

Il mio scss potrebbe assomigliare a questo:

 $button-sml: 1em; $button-med: 1.5em; $button-lrg: 2em; .btn { // Visual styles } .btn-sml { font-size: $button-sml; } .btn-med { font-size: $button-med; } .btn-lrg { font-size: $button-lrg; }

Ovviamente, questo esempio avrebbe più senso se stessi usando le variabili più volte o derivando valori di margine e riempimento dalle variabili di dimensione. Tuttavia, la possibilità di prototipare rapidamente dimensioni diverse potrebbe essere una ragione sufficiente.

Poiché la maggior parte delle variabili statiche sono globali, mi piace differenziare le variabili statiche che vengono utilizzate solo all'interno di un componente. Per fare ciò, puoi anteporre a queste variabili il nome del componente, oppure puoi usare un altro prefisso come c-variable-name per componente o l-variable-name per locale. Puoi usare qualsiasi prefisso desideri o puoi aggiungere un prefisso alle variabili globali. Qualunque cosa tu scelga, è utile differenziare soprattutto se si converte una base di codice esistente per utilizzare proprietà personalizzate.

Quando utilizzare le proprietà personalizzate

Se va bene usare variabili statiche all'interno dei componenti, quando dovremmo usare le proprietà personalizzate? La conversione delle variabili del preprocessore esistenti in proprietà personalizzate in genere ha poco senso. Dopotutto, il motivo delle proprietà personalizzate è completamente diverso. Le proprietà personalizzate hanno senso quando abbiamo proprietà CSS che cambiano rispetto a una condizione nel DOM, in particolare una condizione dinamica come :focus , :hover , media query o con JavaScript.

Sospetto che useremo sempre una qualche forma di variabili statiche, anche se in futuro potremmo averne bisogno di meno, poiché le proprietà personalizzate offrono nuovi modi per organizzare la logica e il codice. Fino ad allora, penso che nella maggior parte delle situazioni lavoreremo con una combinazione di variabili del preprocessore e proprietà personalizzate.

È utile sapere che possiamo assegnare variabili statiche a proprietà personalizzate. Indipendentemente dal fatto che siano globali o locali, in molte situazioni ha senso convertire variabili statiche in proprietà personalizzate dinamiche localmente.

Nota : sapevi che $var è un valore valido per una proprietà personalizzata? Le versioni recenti di Sass lo riconoscono, e quindi abbiamo bisogno di interpolare variabili assegnate a proprietà personalizzate, come questa: #{$var} . Questo dice a Sass che vuoi emettere il valore della variabile, piuttosto che solo $var nel foglio di stile. Questo è necessario solo per situazioni come le proprietà personalizzate, in cui i nomi delle variabili possono anche essere un CSS valido.

Se prendiamo l'esempio del pulsante sopra e decidiamo che tutti i pulsanti dovrebbero utilizzare la piccola variazione sui dispositivi mobili, indipendentemente dalla classe applicata nell'HTML, questa è ora una situazione più dinamica. Per questo, dovremmo usare proprietà personalizzate.

 $button-sml: 1em; $button-med: 1.5em; $button-lrg: 2em; .btn { --button-size: #{$button-sml}; } @media screen and (min-width: 600px) { .btn-med { --button-size: #{$button-med}; } .btn-lrg { --button-size: #{$button-lrg}; } } .btn { font-size: var(--button-size); }

Qui creo una singola proprietà personalizzata: --button-size . Questa proprietà personalizzata inizialmente ha come ambito tutti gli elementi del pulsante che utilizzano la classe btn . Quindi cambio il valore di --button-size sopra 600px per le classi btn-med e btn-lrg . Infine, applico questa proprietà personalizzata a tutti gli elementi del pulsante in un'unica posizione.

Non essere troppo intelligente

La natura dinamica delle proprietà personalizzate ci consente di creare alcuni componenti intelligenti e complicati.

Con l'introduzione dei preprocessori, molti di noi hanno creato librerie con astrazioni intelligenti utilizzando mixin e funzioni personalizzate. In casi limitati, esempi come questo sono ancora utili oggi, ma per la maggior parte, più a lungo lavoro con i preprocessori, meno funzionalità utilizzo. Oggi utilizzo i preprocessori quasi esclusivamente per le variabili statiche.

Le proprietà personalizzate non saranno (e non dovrebbero) essere immuni da questo tipo di sperimentazione e non vedo l'ora di vedere molti esempi intelligenti. Ma a lungo termine, il codice leggibile e manutenibile vincerà sempre su astrazioni intelligenti (almeno in produzione).

Ho letto di recente un eccellente articolo su questo argomento sul Free Code Camp Medium. È stato scritto da Bill Sourour e si chiama “Don't Do It At Runtime. Fallo in fase di progettazione. Invece di parafrasare le sue argomentazioni, ve lo lascio leggere.

Una differenza fondamentale tra le variabili del preprocessore e le proprietà personalizzate è che le proprietà personalizzate funzionano in fase di esecuzione. Ciò significa che cose che avrebbero potuto essere accettabili al limite, in termini di complessità, con i preprocessori potrebbero non essere una buona idea con le proprietà personalizzate.

Un esempio che mi ha illustrato di recente questo è stato questo:

 :root { --font-scale: 1.2; --font-size-1: calc(var(--font-scale) * var(--font-size-2)); --font-size-2: calc(var(--font-scale) * var(--font-size-3)); --font-size-3: calc(var(--font-scale) * var(--font-size-4)); --font-size-4: 1rem; }

Questo genera una scala modulare. Una scala modulare è una serie di numeri che si relazionano tra loro utilizzando un rapporto. Sono spesso usati nel web design e nello sviluppo per impostare le dimensioni dei caratteri o la spaziatura.

In questo esempio, ogni proprietà personalizzata viene determinata utilizzando calc() , prendendo il valore della precedente proprietà personalizzata e moltiplicandolo per il rapporto. In questo modo, possiamo ottenere il numero successivo nella scala.

Ciò significa che i rapporti vengono calcolati in fase di esecuzione e puoi modificarli aggiornando solo il valore della proprietà --font-scale . Per esempio:

 @media screen and (min-width: 800px) { :root { --font-scale: 1.33; } }

Questo è intelligente, conciso e molto più veloce del calcolare nuovamente tutti i valori se si desidera modificare la scala. È anche qualcosa che non farei nel codice di produzione.

Sebbene l'esempio sopra sia utile per la prototipazione, in produzione, preferirei di gran lunga vedere qualcosa del genere:

 :root { --font-size-1: 1.728rem; --font-size-2: 1.44rem; --font-size-3: 1.2em; --font-size-4: 1em; } @media screen and (min-width: 800px) { :root { --font-size-1: 2.369rem; --font-size-2: 1.777rem; --font-size-3: 1.333rem; --font-size-4: 1rem; } }

Simile all'esempio nell'articolo di Bill, trovo utile vedere quali sono i valori effettivi. Leggiamo il codice molte più volte di quanto lo scriviamo e i valori globali come le scale dei caratteri cambiano raramente durante la produzione.

L'esempio sopra non è ancora perfetto. Viola la regola precedente che i valori globali dovrebbero essere statici . Preferirei di gran lunga utilizzare le variabili del preprocessore e convertirle in proprietà personalizzate dinamiche localmente utilizzando le tecniche dimostrate in precedenza.

È anche importante evitare situazioni in cui si passa dall'utilizzo di una proprietà personalizzata a una diversa proprietà personalizzata. Questo può accadere quando denominiamo proprietà come questa.

Cambia il valore non la variabile

Modificare il valore e non la variabile è una delle strategie più importanti per utilizzare in modo efficace le proprietà personalizzate.

Come regola generale, non dovresti mai cambiare quale proprietà personalizzata viene utilizzata per un singolo scopo. È facile da fare perché questo è esattamente il modo in cui facciamo le cose con i preprocessori, ma ha poco senso con le proprietà personalizzate.

In questo esempio, abbiamo due proprietà personalizzate che vengono utilizzate su un componente di esempio. Passo dall'utilizzo del valore di --font-size-small a --font-size-large a seconda delle dimensioni dello schermo.

 :root { --font-size-small: 1.2em; --font-size-large: 2em; } .example { font-size: var(--font-size-small); } @media screen and (min-width: 800px) { .example { font-size: var(--font-size-large); } }

Un modo migliore per farlo sarebbe definire una singola proprietà personalizzata con ambito al componente. Quindi, utilizzando una query multimediale o qualsiasi altro selettore, modificarne il valore.

 .example { --example-font-size: 1.2em; } @media screen and (min-width: 800px) { .example { --example-font-size: 2em; } }

Infine, in un unico posto, utilizzo il valore di questa proprietà personalizzata:

 .example { font-size: var(--example-font-size); }

In questo esempio e in altri precedenti, le media query sono state utilizzate solo per modificare il valore delle proprietà personalizzate. Potresti anche notare che esiste un solo punto in cui viene utilizzata l'istruzione var() e le normali proprietà CSS vengono aggiornate.

Questa separazione tra dichiarazioni di variabili e dichiarazioni di proprietà è intenzionale. Ci sono molte ragioni per questo, ma i vantaggi sono più evidenti quando si pensa al design reattivo.

Design reattivo con proprietà personalizzate

Una delle difficoltà del design reattivo quando si basa molto sulle query multimediali è che, indipendentemente da come organizzi il tuo CSS, gli stili relativi a un particolare componente vengono frammentati nel foglio di stile.

Può essere molto difficile sapere quali proprietà CSS cambieranno. Tuttavia, le proprietà personalizzate CSS possono aiutarci a organizzare parte della logica relativa al design reattivo e rendere molto più semplice il lavoro con le query multimediali.

Se cambia, è una variabile

Le proprietà che cambiano utilizzando le media query sono intrinsecamente dinamiche e le proprietà personalizzate forniscono i mezzi per esprimere valori dinamici in CSS. Ciò significa che se stai utilizzando una query multimediale per modificare qualsiasi proprietà CSS, devi inserire questo valore in una proprietà personalizzata.

Puoi quindi spostarlo, insieme a tutte le regole multimediali, gli stati al passaggio del mouse o qualsiasi selettore dinamico che definisce come cambia il valore, nella parte superiore del documento.

Separare la logica dal design

Se eseguita correttamente, la separazione tra logica e design significa che le media query vengono utilizzate solo per modificare il valore delle proprietà personalizzate . Significa che tutta la logica relativa al responsive design dovrebbe essere in cima al documento, e ovunque vediamo un'istruzione var() nel nostro CSS, sappiamo immediatamente che questa proprietà cambia. Con i metodi tradizionali di scrittura CSS, non c'era modo di saperlo a colpo d'occhio.

Molti di noi sono diventati molto bravi a leggere e interpretare i CSS a colpo d'occhio mentre tracciavano nella nostra testa quali proprietà sono cambiate in diverse situazioni. Sono stanco di questo e non voglio più farlo! Le proprietà personalizzate ora forniscono un collegamento tra la logica e la sua implementazione, quindi non è necessario tenerne traccia ed è incredibilmente utile!

La piega logica

L'idea di dichiarare variabili all'inizio di un documento o di una funzione non è un'idea nuova. È qualcosa che facciamo nella maggior parte delle lingue e ora è qualcosa che possiamo fare anche nei CSS. Scrivere CSS in questo modo crea una chiara distinzione visiva tra CSS nella parte superiore del documento e sotto. Ho bisogno di un modo per differenziare queste sezioni quando ne parlo e l'idea di una “piega logica” è una metafora che ho iniziato ad usare.
Above the fold contiene tutte le variabili del preprocessore e le proprietà personalizzate. Ciò include tutti i diversi valori che una proprietà personalizzata può avere. Dovrebbe essere facile tracciare come cambia una proprietà personalizzata.

CSS below the fold è semplice, altamente dichiarativo e facile da leggere. Sembra CSS prima delle query multimediali e di altre complessità necessarie dei CSS moderni.

Dai un'occhiata a un esempio molto semplice di un sistema a griglia flexbox a sei colonne:

 .row { --row-display: block; } @media screen and (min-width: 600px) { .row { --row-display: flex; } }

La proprietà personalizzata --row-display è inizialmente impostata su block . Al di sopra di 600px la modalità di visualizzazione è impostata su Flex.

Below the fold potrebbe assomigliare a questo:

 .row { display: var(--row-display); flex-direction: row; flex-wrap: nowrap; } .col-1, .col-2, .col-3, .col-4, .col-5, .col-6 { flex-grow: 0; flex-shrink: 0; } .col-1 { flex-basis: 16.66%; } .col-2 { flex-basis: 33.33%; } .col-3 { flex-basis: 50%; } .col-4 { flex-basis: 66.66%; } .col-5 { flex-basis: 83.33%; } .col-6 { flex-basis: 100%; }

Sappiamo immediatamente che --row-display è un valore che cambia. Inizialmente, sarà block , quindi i valori flex verranno ignorati.

Questo esempio è abbastanza semplice, ma se lo espandiamo per includere una colonna di larghezza flessibile che riempia lo spazio rimanente, è probabile che i valori flex-grow , flex-shrink e flex-basis debbano essere convertiti in proprietà personalizzate. Puoi provare questo o dare un'occhiata a un esempio più dettagliato qui.

Proprietà personalizzate per i temi

Per lo più ho discusso contro l'uso di proprietà personalizzate per variabili dinamiche globali e si spera che l'associazione di proprietà personalizzate al selettore :root sia in molti casi considerata dannosa. Ma ogni regola ha un'eccezione e, per le proprietà personalizzate, è il tema.

L'uso limitato di proprietà personalizzate globali può rendere molto più semplice la creazione di temi.

Il tema si riferisce generalmente al consentire agli utenti di personalizzare l'interfaccia utente in qualche modo. Questo potrebbe essere qualcosa come cambiare i colori su una pagina del profilo. Oppure potrebbe essere qualcosa di più localizzato. Ad esempio, puoi scegliere il colore di una nota nell'applicazione Google Keep.

App Google Keep

La definizione dei temi di solito implica la compilazione di un foglio di stile separato per sovrascrivere un valore predefinito con le preferenze dell'utente o la compilazione di un foglio di stile diverso per ciascun utente. Entrambi possono essere difficili e avere un impatto sulle prestazioni.

Con le proprietà personalizzate, non è necessario compilare un foglio di stile diverso; dobbiamo solo aggiornare il valore delle proprietà in base alle preferenze dell'utente. Poiché sono valori ereditati, se lo facciamo sull'elemento radice possono essere utilizzati ovunque nella nostra applicazione.

Capitalizzare le proprietà dinamiche globali

Le proprietà personalizzate fanno distinzione tra maiuscole e minuscole e poiché la maggior parte delle proprietà personalizzate sarà locale, se si utilizzano proprietà dinamiche globali, può avere senso metterle in maiuscolo.

 :root { --THEME-COLOR: var(--user-theme-color, #d33a2c); }

La capitalizzazione delle variabili spesso significa costanti globali. Per noi, questo significherà che la proprietà è impostata altrove nell'applicazione e che probabilmente non dovremmo modificarla localmente.

Evitare di impostare direttamente le proprietà dinamiche globali

Le proprietà personalizzate accettano un valore di fallback. Può essere utile evitare di sovrascrivere direttamente il valore di una proprietà personalizzata globale e mantenere separati i valori utente. Possiamo usare il valore di fallback per farlo.

L'esempio precedente imposta il valore di --THEME-COLOR sul valore di --user-theme-color se esiste. Se --user-theme-color non è impostato, verrà utilizzato il valore di #d33a2c . In questo modo, non è necessario fornire un fallback ogni volta che utilizziamo --THEME-COLOR .

Nell'esempio seguente potresti aspettarti che lo sfondo sia impostato su green . Tuttavia, il valore di --user-theme-color non è stato impostato sull'elemento radice, quindi il valore di --THEME-COLOR non è cambiato.

 :root { --THEME-COLOR: var(--user-theme-color, #d33a2c); } body { --user-theme-color: green; background: var(--THEME-COLOR); }

L'impostazione indiretta di proprietà dinamiche globali come questa le protegge dalla sovrascrittura locale e garantisce che le impostazioni dell'utente vengano sempre ereditate dall'elemento radice. Questa è una convenzione utile per salvaguardare i valori del tema ed evitare eredità indesiderate.

Se vogliamo esporre proprietà specifiche all'ereditarietà, possiamo sostituire il selettore :root con un selettore * :

 * { --THEME-COLOR: var(--user-theme-color, #d33a2c); } body { --user-theme-color: green; background: var(--THEME-COLOR); }

Ora il valore di --THEME-COLOR viene ricalcolato per ogni elemento e quindi è possibile utilizzare il valore locale di --user-theme-color . In altre parole, il colore di sfondo in questo esempio sarà green .

Puoi vedere alcuni esempi più dettagliati di questo modello nella sezione sulla manipolazione del colore con proprietà personalizzate.

Aggiornamento delle proprietà personalizzate con JavaScript

Se vuoi impostare proprietà personalizzate usando JavaScript c'è un'API abbastanza semplice e si presenta così:

 const elm = document.documentElement; elm.style.setProperty('--USER-THEME-COLOR', 'tomato');

Qui sto impostando il valore di --USER-THEME-COLOR sull'elemento del documento, o in altre parole, l'elemento :root dove verrà ereditato da tutti gli elementi.

Questa non è una nuova API; è lo stesso metodo JavaScript per aggiornare gli stili su un elemento. Questi sono stili in linea, quindi avranno una specificità maggiore rispetto ai normali CSS.

Ciò significa che è facile applicare personalizzazioni locali:

 .note { --note-color: #eaeaea; } .note { background: var(--note-color); }

Qui ho impostato un valore predefinito per --note-color e lo scopo sul componente .note . Mantengo la dichiarazione della variabile separata dalla dichiarazione della proprietà, anche in questo semplice esempio.

 const elm = document.querySelector('#note-uid'); elm.style.setProperty('--note-color', 'yellow');

Quindi mirino a un'istanza specifica di un elemento .note e modifico il valore della proprietà personalizzata --note-color solo per quell'elemento. Questo ora avrà una specificità maggiore rispetto al valore predefinito.

Puoi vedere come funziona con questo esempio usando React. Queste preferenze dell'utente potrebbero essere salvate in una memoria locale o, magari nel caso di un'applicazione più grande, in un database.

Manipolazione del colore con proprietà personalizzate

Oltre ai valori esadecimali e ai colori con nome, CSS ha funzioni di colori come rgb() e hsl() . Questi ci consentono di specificare i singoli componenti di un colore come la tonalità o la luminosità. Le proprietà personalizzate possono essere utilizzate insieme alle funzioni colore.

 :root { --hue: 25; } body { background: hsl(var(--hue), 80%, 50%); }

Questo è utile, ma alcune delle funzionalità più utilizzate dei preprocessori sono funzioni di colore avanzate che ci consentono di manipolare il colore utilizzando funzioni come schiarire, scurire o desaturare:

 darken($base-color, 10%); lighten($base-color, 10%); desaturate($base-color, 20%);

Sarebbe utile avere alcune di queste funzionalità nei browser. Stanno arrivando, ma fino a quando non avremo funzioni di modifica del colore native in CSS, le proprietà personalizzate potrebbero colmare parte di questa lacuna.

Abbiamo visto che le proprietà personalizzate possono essere utilizzate all'interno di funzioni di colore esistenti come rgb() e hsl() ma possono essere utilizzate anche in calc() . Ciò significa che possiamo convertire un numero reale in una percentuale moltiplicandolo, ad esempio calc(50 * 1%) = 50% .

 :root { --lightness: 50; } body { background: hsl(25, 80%, calc(var(--lightness) * 1%)); }

Il motivo per cui vogliamo memorizzare il valore della luminosità come numero reale è che possiamo manipolarlo con calc prima di convertirlo in percentuale. Ad esempio, se voglio scurire un colore del 20% , posso moltiplicare la sua luminosità per 0.8 . Possiamo renderlo un po' più facile da leggere separando il calcolo della luminosità in una proprietà personalizzata con ambito locale:

 :root { --lightness: 50; } body { --lightness: calc(var(--lightness * 0.8)); background: hsl(25, 80%, calc(var(--lightness) * 1%)); }

Potremmo anche astrarre più calcoli e creare qualcosa come funzioni di modifica del colore in CSS usando proprietà personalizzate. Questo esempio è probabilmente troppo complesso per la maggior parte dei casi pratici di temi, ma dimostra tutta la potenza delle proprietà personalizzate dinamiche.

Semplifica il tema

Uno dei vantaggi dell'utilizzo di proprietà personalizzate è la possibilità di semplificare i temi. Non è necessario che l'applicazione sia a conoscenza di come vengono utilizzate le proprietà personalizzate. Al contrario, utilizziamo JavaScript o il codice lato server per impostare il valore delle proprietà personalizzate. Il modo in cui questi valori vengono utilizzati è determinato dai fogli di stile.

Ciò significa ancora una volta che siamo in grado di separare la logica dal design. Se hai un team di progettazione tecnica, gli autori possono aggiornare i fogli di stile e decidere come applicare proprietà personalizzate senza modificare una singola riga di codice JavaScript o back-end.

Le proprietà personalizzate consentono anche di spostare parte della complessità dei temi nel CSS e questa complessità può avere un impatto negativo sulla manutenibilità del tuo CSS, quindi ricorda di mantenerlo semplice ove possibile.

Utilizzo delle proprietà personalizzate oggi

Anche se stai supportando IE10 e 11, puoi iniziare a utilizzare le proprietà personalizzate oggi stesso. La maggior parte degli esempi in questo articolo hanno a che fare con il modo in cui scriviamo e strutturiamo i CSS. I vantaggi sono significativi in ​​termini di manutenibilità, tuttavia, la maggior parte degli esempi riduce solo ciò che potrebbe essere fatto altrimenti con codice più complesso.

Uso uno strumento chiamato postcss-css-variables per convertire la maggior parte delle funzionalità delle proprietà personalizzate in una rappresentazione statica dello stesso codice. Altri strumenti simili ignorano le proprietà personalizzate all'interno di query multimediali o selettori complessi, trattando le proprietà personalizzate in modo molto simile alle variabili del preprocessore.

Ciò che questi strumenti non possono fare è emulare le funzionalità di runtime delle proprietà personalizzate. Ciò significa che nessuna funzionalità dinamica come temi o modifica delle proprietà con JavaScript. Questo potrebbe andare bene in molte situazioni. A seconda della situazione, la personalizzazione dell'interfaccia utente potrebbe essere considerata un miglioramento progressivo e il tema predefinito potrebbe essere perfettamente accettabile per i browser meno recenti.

Caricamento del foglio di stile corretto

Ci sono molti modi in cui puoi usare postCSS. Uso un processo gulp per compilare fogli di stile separati per browser più recenti e meno recenti. Una versione semplificata del mio compito gulp è simile a questa:

 import gulp from "gulp"; import sass from "gulp-sass"; import postcss from "gulp-postcss"; import rename from "gulp-rename"; import cssvariables from "postcss-css-variables"; import autoprefixer from "autoprefixer"; import cssnano from "cssnano"; gulp.task("css-no-vars", () => gulp .src("./src/css/*.scss") .pipe(sass().on("error", sass.logError)) .pipe(postcss([cssvariables(), cssnano()])) .pipe(rename({ extname: ".no-vars.css" })) .pipe(gulp.dest("./dist/css")) ); gulp.task("css", () => gulp .src("./src/css/*.scss") .pipe(sass().on("error", sass.logError)) .pipe(postcss([cssnano()])) .pipe(rename({ extname: ".css" })) .pipe(gulp.dest("./dist/css")) );

Ciò si traduce in due file CSS: uno normale con proprietà personalizzate ( styles.css ) e uno per i browser meno recenti ( styles.no-vars.css ). Voglio che IE10 e 11 siano serviti styles.no-vars.css e altri browser per ottenere il normale file CSS.

Normalmente, consiglierei l'utilizzo di query di funzionalità, ma IE11 non supporta le query di funzionalità e abbiamo utilizzato proprietà personalizzate in modo così esteso che in questo caso ha senso servire un foglio di stile diverso.

Servire in modo intelligente un foglio di stile diverso ed evitare un flash di contenuto senza stile non è un compito semplice. Se non hai bisogno delle funzionalità dinamiche delle proprietà personalizzate, potresti considerare di servire tutti styles.no-vars.css e di utilizzare le proprietà personalizzate semplicemente come strumento di sviluppo.

Se vuoi sfruttare appieno tutte le funzionalità dinamiche delle proprietà personalizzate, ti suggerisco di utilizzare una tecnica CSS critica. Seguendo queste tecniche, il foglio di stile principale viene caricato in modo asincrono mentre il CSS critico viene renderizzato in linea. L'intestazione della tua pagina potrebbe assomigliare a questa:

 <head> <style> /* inlined critical CSS */ </style> <script> loadCSS('non-critical.css'); </script> </head>

Possiamo estenderlo per caricare styles.css o styles.no-vars.css a seconda che il browser supporti le proprietà personalizzate. Possiamo rilevare un supporto come questo:

 if ( window.CSS && CSS.supports('color', 'var(--test)') ) { loadCSS('styles.css'); } else { loadCSS('styles.no-vars.css'); }

Conclusione

Se hai lottato per organizzare i CSS in modo efficiente, hai difficoltà con i componenti reattivi, desideri implementare temi lato client o semplicemente vuoi iniziare con il piede giusto con proprietà personalizzate, questa guida dovrebbe dirti tutto ciò che devi sapere.

Si tratta di comprendere la differenza tra variabili dinamiche e statiche nei CSS, nonché alcune semplici regole:

  1. Separare la logica dal design;
  2. Se una proprietà CSS cambia, considera l'utilizzo di una proprietà personalizzata;
  3. Modificare il valore delle proprietà personalizzate, non quale proprietà personalizzata viene utilizzata;
  4. Le variabili globali sono generalmente statiche.

If you follow these conventions, you will find that working with custom properties is a whole lot easier than you think. This might even change how you approach CSS in general.

Ulteriori letture

  • “It's Time To Start Using Custom Properties,” Serg Hospodarets
    A general introduction to the syntax and the features of custom properties.
  • “Pragmatic, Practical, And Progressive Theming With Custom Properties,” Harry Roberts
    More useful information on theming.
  • Custom Properties Collection, Mike Riethmuller on CodePen
    A number of different examples you can experiment with.