Un ghid de strategie pentru proprietăți personalizate CSS

Publicat: 2022-03-10
Rezumat rapid ↬ Proprietățile dinamice oferă oportunități pentru noi idei creative, dar și potențialul de a adăuga complexitate CSS. Pentru a profita la maximum de ele, s-ar putea să avem nevoie de o strategie pentru modul în care scriem și structuram CSS cu proprietăți personalizate.

Proprietățile personalizate CSS (uneori cunoscute ca „variabile CSS”) sunt acum acceptate în toate browserele moderne, iar oamenii încep să le folosească în producție. Acest lucru este grozav, dar sunt diferite de variabilele din preprocesoare și am văzut deja multe exemple de oameni care le folosesc fără a lua în considerare avantajele pe care le oferă.

Proprietățile personalizate au un potențial imens de a schimba modul în care scriem și structuram CSS și, într-o măsură mai mică, modul în care folosim JavaScript pentru a interacționa cu componentele UI. Nu mă voi concentra pe sintaxă și modul în care funcționează (pentru asta vă recomand să citiți „Este timpul să începeți să utilizați proprietăți personalizate”). În schimb, vreau să arunc o privire mai profundă asupra strategiilor pentru a profita la maximum de CSS Custom Properties.

Cum sunt similare cu variabilele din preprocesoare?

Proprietățile personalizate sunt puțin ca variabilele din preprocesoare, dar au unele diferențe importante. Prima și cea mai evidentă diferență este sintaxa.

Cu SCSS folosim un simbol dolar pentru a indica o variabilă:

 $smashing-red: #d33a2c;

În Less folosim un simbol @ :

 @smashing-red: #d33a2c;

Proprietățile personalizate urmează convenții similare și folosesc un prefix -- :

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

O diferență importantă între proprietățile personalizate și variabilele din preprocesoare este că proprietățile personalizate au o sintaxă diferită pentru atribuirea unei valori și recuperarea acelei valori. Când regăsim valoarea unei proprietăți personalizate, folosim funcția var() .

Mai multe după săritură! Continuați să citiți mai jos ↓

Următoarea diferență cea mai evidentă este în nume. Ele sunt numite „proprietăți personalizate” deoarece sunt într-adevăr proprietăți CSS. În preprocesoare, puteți declara și utiliza variabile aproape oriunde, inclusiv în afara blocurilor de declarare, în regulile media sau chiar ca parte a unui selector.

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

Majoritatea exemplelor de mai sus ar fi invalide folosind proprietăți personalizate.

Proprietățile personalizate au aceleași reguli despre unde pot fi utilizate ca proprietăți CSS normale. Este mult mai bine să ne gândim la ele ca proprietăți dinamice decât variabile. Aceasta înseamnă că pot fi utilizate numai în interiorul unui bloc de declarații sau, cu alte cuvinte, proprietățile personalizate sunt legate de un selector. Acesta poate fi selectorul :root sau orice alt selector valid.

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

Puteți prelua valoarea unei proprietăți personalizate oriunde ați utiliza altfel o valoare într-o declarație de proprietate. Aceasta înseamnă că pot fi folosite ca o singură valoare, ca parte a unei instrucțiuni scurte sau chiar în interiorul ecuațiilor calc() .

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

Cu toate acestea, ele nu pot fi utilizate în interogări media sau selectoare, inclusiv :nth-child() .

Probabil că sunt multe despre care doriți să știți despre sintaxă și despre cum funcționează proprietățile personalizate, cum ar fi cum să utilizați valorile de rezervă și puteți atribui variabile altor variabile (da), dar această introducere de bază ar trebui să fie suficientă pentru a înțelege restul conceptele din acest articol. Pentru mai multe informații despre modul în care funcționează proprietățile personalizate, puteți citi „Este timpul să începeți să utilizați proprietățile personalizate” scris de Serg Hospodarets.

Dinamic vs. Static

Lăsând la o parte diferențele cosmetice, cea mai semnificativă diferență între variabilele din preprocesoare și proprietățile personalizate este modul în care sunt definite. Ne putem referi la variabile fie static, fie dinamic. Variabilele din preprocesoare sunt statice, în timp ce proprietățile personalizate sunt dinamice.

În ceea ce privește CSS, static înseamnă că puteți actualiza valoarea unei variabile în diferite puncte ale procesului de compilare, dar acest lucru nu poate schimba valoarea codului care a apărut înaintea acesteia.

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

rezultă în:

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

Odată ce aceasta este redată în CSS, variabilele dispar. Aceasta înseamnă că am putea citi un fișier .scss și putem determina rezultatul acestuia fără a ști nimic despre HTML, browser sau alte intrări. Acesta nu este cazul proprietăților personalizate.

Preprocesoarele au un fel de „sfera de aplicare a blocurilor” în care variabilele pot fi modificate temporar în interiorul unui selector, funcție sau mixing. Aceasta modifică valoarea unei variabile din interiorul blocului, dar este încă static. Aceasta este legată de bloc, nu de selector. În exemplul de mai jos, variabila $background este modificată în interiorul blocului .example . Se schimbă înapoi la valoarea inițială în afara blocului, chiar dacă folosim același selector.

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

Aceasta va avea ca rezultat:

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

Proprietățile personalizate funcționează diferit. În ceea ce privește proprietățile personalizate, domeniul dinamic înseamnă că sunt supuse moștenirii și cascadei. Proprietatea este legată de un selector și, dacă valoarea se schimbă, aceasta afectează toate elementele DOM care se potrivesc la fel ca orice altă proprietate CSS.

Acest lucru este grozav deoarece puteți modifica valoarea unei proprietăți personalizate în interiorul unei interogări media, cu un pseudoselector, cum ar fi hover, sau chiar cu 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); }

Nu trebuie să schimbăm locul în care este utilizată proprietatea personalizată - modificăm valoarea proprietății personalizate cu CSS. Aceasta înseamnă că folosind aceeași proprietate personalizată, putem avea valori diferite în locuri sau context diferite pe aceeași pagină.

Global vs. Local

Pe lângă faptul că sunt statice sau dinamice, variabilele pot fi și globale sau locale. Dacă scrieți JavaScript, veți fi familiarizat cu acest lucru. Variabilele pot fi fie aplicate la tot ceea ce este în interiorul unei aplicații, fie domeniul lor poate fi limitat la anumite funcții sau blocuri de cod.

CSS este similar. Avem unele lucruri care sunt aplicate la nivel global și unele lucruri care sunt mai locale. Culorile mărcii, spațiile verticale și tipografia sunt exemple de lucruri pe care ați putea dori să le aplicați la nivel global și în mod consecvent pe site-ul sau aplicația dvs. Avem și lucruri locale. De exemplu, o componentă de buton poate avea o variantă mică și mare. Nu ați dori ca dimensiunile acestor butoane să fie aplicate tuturor elementelor de intrare sau chiar fiecărui element din pagină.

Acesta este ceva cu care suntem familiarizați în CSS. Am dezvoltat sisteme de proiectare, convenții de denumire și biblioteci JavaScript, toate pentru a ajuta la izolarea componentelor locale și a elementelor globale de design. Proprietățile personalizate oferă noi opțiuni pentru a rezolva această problemă veche.

Proprietățile personalizate CSS sunt, în mod implicit, aplicate la nivel local la selectorii specifici cărora le aplicăm. Deci sunt cam ca variabilele locale. Cu toate acestea, proprietățile personalizate sunt, de asemenea, moștenite, astfel încât în ​​multe situații se comportă ca variabile globale - mai ales atunci când sunt aplicate selectorului :root . Aceasta înseamnă că trebuie să fim atenți la modul în care le folosim.

Atât de multe exemple arată proprietăți personalizate care sunt aplicate elementului :root și, deși, acest lucru este în regulă pentru o demonstrație, poate duce la un domeniu global dezordonat și probleme neintenționate cu moștenirea. Din fericire, am învățat deja aceste lecții.

Variabilele globale tind să fie statice

Există câteva mici excepții, dar, în general, majoritatea lucrurilor globale din CSS sunt, de asemenea, statice.

Variabilele globale precum culorile mărcii, tipografia și spațierea nu tind să se schimbe prea mult de la o componentă la alta. Când se schimbă, aceasta tinde să fie o rebranding globală sau o altă schimbare semnificativă care se întâmplă rar la un produs matur. Încă are sens ca aceste lucruri să fie variabile, sunt folosite în multe locuri, iar variabilele ajută la coerență. Dar nu are sens ca ei să fie dinamici. Valoarea acestor variabile nu se modifică în niciun mod dinamic.

Din acest motiv, recomand cu tărie utilizarea preprocesoarelor pentru variabilele globale (statice). Acest lucru nu numai că asigură că acestea sunt întotdeauna statice, dar le denotă vizual în cod. Acest lucru poate face CSS mult mai lizibil și mai ușor de întreținut.

Variabilele statice locale sunt în regulă (uneori)

S-ar putea să credeți, având în vedere poziția puternică asupra variabilelor globale care sunt statice, că prin reflecție, toate variabilele locale ar putea trebui să fie dinamice. Deși este adevărat că variabilele locale tind să fie dinamice, aceasta nu este nici pe departe la fel de puternică ca tendința ca o variabilă globală să fie statică.

Variabilele statice local sunt perfect OK în multe situații. Folosesc variabilele preprocesoarelor în fișierele componente mai ales ca comoditate pentru dezvoltatori.

Luați în considerare exemplul clasic al unei componente de buton cu mai multe variații de dimensiune.

butoane

scss -ul meu ar putea arăta cam așa:

 $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; }

Evident, acest exemplu ar avea mai mult sens dacă aș folosi variabilele de mai multe ori sau aș obține valori de marjă și de umplutură din variabilele de dimensiune. Cu toate acestea, capacitatea de a prototip rapid diferite dimensiuni ar putea fi un motiv suficient.

Deoarece majoritatea variabilelor statice sunt globale, îmi place să diferențiez variabilele statice care sunt utilizate numai în interiorul unei componente. Pentru a face acest lucru, puteți prefix aceste variabile cu numele componentei sau puteți utiliza un alt prefix, cum ar fi c-variable-name pentru componentă sau l-variable-name pentru local. Puteți folosi orice prefix doriți sau puteți prefix variabilele globale. Indiferent de ce alegeți, este util să faceți diferențieri, mai ales dacă convertiți o bază de cod existentă pentru a utiliza proprietăți personalizate.

Când să utilizați proprietăți personalizate

Dacă este în regulă să folosim variabile statice în interiorul componentelor, când ar trebui să folosim proprietăți personalizate? Convertirea variabilelor de preprocesor existente în proprietăți personalizate are de obicei puțin sens. La urma urmei, motivul proprietăților personalizate este complet diferit. Proprietățile personalizate au sens atunci când avem proprietăți CSS care se modifică în raport cu o condiție din DOM - în special o condiție dinamică, cum ar fi :focus , :hover , interogări media sau cu JavaScript.

Bănuiesc că vom folosi întotdeauna o anumită formă de variabile statice, deși s-ar putea să avem nevoie de mai puține în viitor, deoarece proprietățile personalizate oferă noi modalități de organizare a logicii și a codului. Până atunci, cred că în majoritatea situațiilor vom lucra cu o combinație de variabile de preprocesor și proprietăți personalizate.

Este util să știm că putem atribui variabile statice proprietăților personalizate. Indiferent dacă sunt globale sau locale, este logic în multe situații să convertiți variabile statice în proprietăți personalizate dinamice local.

Notă : Știați că $var este o valoare validă pentru o proprietate personalizată? Versiunile recente ale Sass recunosc acest lucru și, prin urmare, trebuie să interpolăm variabilele atribuite proprietăților personalizate, cum ar fi: #{$var} . Acest lucru îi spune lui Sass că doriți să scoateți valoarea variabilei, mai degrabă decât doar $var în foaia de stil. Acest lucru este necesar doar pentru situații precum proprietăți personalizate, în care numele variabilelor pot fi, de asemenea, un CSS valid.

Dacă luăm exemplul de buton de mai sus și decidem că toate butoanele ar trebui să folosească variația mică pe dispozitivele mobile, indiferent de clasa aplicată în HTML, aceasta este acum o situație mai dinamică. Pentru aceasta, ar trebui să folosim proprietăți personalizate.

 $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); }

Aici creez o singură proprietate personalizată: --button-size . Această proprietate personalizată este aplicată inițial la toate elementele butoanelor folosind clasa btn . Apoi modific valoarea --button-size peste 600px pentru clasele btn-med și btn-lrg . În cele din urmă, aplic această proprietate personalizată tuturor elementelor butoanelor într-un singur loc.

Nu fi prea inteligent

Natura dinamică a proprietăților personalizate ne permite să creăm niște componente inteligente și complicate.

Odată cu introducerea preprocesoarelor, mulți dintre noi au creat biblioteci cu abstracții inteligente folosind mixuri și funcții personalizate. În cazuri limitate, exemple ca acesta sunt încă utile astăzi, dar în cea mai mare parte, cu cât lucrez mai mult cu preprocesoare, cu atât folosesc mai puține funcții. Astăzi, folosesc preprocesoare aproape exclusiv pentru variabile statice.

Proprietățile personalizate nu vor (și nu ar trebui) să fie imune la acest tip de experimentare și aștept cu nerăbdare să văd multe exemple inteligente. Dar, pe termen lung, codul care poate fi citit și care poate fi întreținut va câștiga întotdeauna abstracțiile inteligente (cel puțin în producție).

Am citit recent un articol excelent pe acest subiect pe Free Code Camp Medium. A fost scris de Bill Sourour și se numește „Don’t Do It At Runtime. Fă-o la momentul proiectării.” În loc să-i parafrazez argumentele, vă las să-l citiți.

O diferență cheie între variabilele de preprocesor și proprietățile personalizate este că proprietățile personalizate funcționează în timpul execuției. Aceasta înseamnă că lucrurile care ar fi putut fi acceptabile la limită, în termeni de complexitate, cu preprocesoare ar putea să nu fie o idee bună cu proprietăți personalizate.

Un exemplu care mi-a ilustrat acest lucru recent a fost acesta:

 :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; }

Aceasta generează o scară modulară. O scară modulară este o serie de numere care se raportează între ele folosind un raport. Ele sunt adesea folosite în design și dezvoltare web pentru a seta dimensiunile fonturilor sau spațierea.

În acest exemplu, fiecare proprietate personalizată este determinată folosind calc() , luând valoarea proprietății personalizate anterioare și înmulțind aceasta cu raportul. Făcând acest lucru, putem obține următorul număr din scară.

Aceasta înseamnă că rapoartele sunt calculate în timpul execuției și le puteți modifica prin actualizarea numai a valorii proprietății --font-scale . De exemplu:

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

Acest lucru este inteligent, concis și mult mai rapid decât să calculați din nou toate valorile dacă doriți să schimbați scara. De asemenea, este ceva ce nu aș face în codul de producție.

Deși exemplul de mai sus este util pentru prototipare, în producție, aș prefera mult să văd ceva de genul acesta:

 :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; } }

Similar cu exemplul din articolul lui Bill, mi se pare util să văd care sunt valorile reale. Citim cod de mai multe ori decât îl scriem, iar valorile globale, cum ar fi scalele fonturilor, se modifică rar în producție.

Exemplul de mai sus nu este încă perfect. Încalcă regula de mai devreme conform căreia valorile globale ar trebui să fie statice . Aș prefera mult să folosesc variabile de preprocesor și să le convertesc în proprietăți personalizate dinamice local folosind tehnicile demonstrate mai devreme.

De asemenea, este important să evităm situațiile în care trecem de la utilizarea unei proprietăți personalizate la o proprietate personalizată diferită. Acest lucru se poate întâmpla atunci când numim proprietăți ca aceasta.

Schimbați valoarea, nu variabila

Modificarea valorii și nu a variabilei este una dintre cele mai importante strategii pentru utilizarea eficientă a proprietăților personalizate.

Ca regulă generală, nu trebuie să schimbați niciodată ce proprietate personalizată este utilizată pentru un singur scop. Este ușor de făcut, deoarece exact așa facem lucrurile cu preprocesoarele, dar nu are sens cu proprietăți personalizate.

În acest exemplu, avem două proprietăți personalizate care sunt utilizate pe o componentă exemplu. Trec de la utilizarea valorii --font-size-small la --font-size-large , în funcție de dimensiunea ecranului.

 :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); } }

O modalitate mai bună de a face acest lucru ar fi definirea unei singure proprietăți personalizate în domeniul componentului. Apoi, folosind o interogare media sau orice alt selector, modificați valoarea acestuia.

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

În cele din urmă, într-un singur loc, folosesc valoarea acestei proprietăți personalizate:

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

În acest exemplu și în altele înainte, interogările media au fost folosite doar pentru a modifica valoarea proprietăților personalizate. De asemenea, puteți observa că există un singur loc în care este utilizată instrucțiunea var() și proprietățile CSS obișnuite sunt actualizate.

Această separare între declarațiile de variabile și declarațiile de proprietate este intenționată. Există multe motive pentru acest lucru, dar beneficiile sunt cele mai evidente atunci când ne gândim la designul responsive.

Design receptiv cu proprietăți personalizate

Una dintre dificultățile cu designul responsive atunci când se bazează în mare măsură pe interogări media este că, indiferent de modul în care vă organizați CSS, stilurile legate de o anumită componentă devin fragmentate în foaia de stil.

Poate fi foarte dificil să știi ce proprietăți CSS se vor schimba. Cu toate acestea, proprietățile personalizate CSS ne pot ajuta să organizăm o parte din logica legată de designul receptiv și să facem mult mai ușor lucrul cu interogări media.

Dacă se schimbă, este o variabilă

Proprietățile care se modifică folosind interogări media sunt în mod inerent dinamice, iar proprietățile personalizate oferă mijloacele de exprimare a valorilor dinamice în CSS. Aceasta înseamnă că, dacă utilizați o interogare media pentru a modifica orice proprietate CSS, ar trebui să plasați această valoare într-o proprietate personalizată.

Puteți muta apoi acest lucru, împreună cu toate regulile media, stările de trecere cu mouse-ul sau orice selectoare dinamice care definesc modul în care se schimbă valoarea, în partea de sus a documentului.

Separați logica de proiectare

Când este făcută corect, separarea logicii și a designului înseamnă că interogările media sunt folosite doar pentru a modifica valoarea proprietăților personalizate . Înseamnă că toată logica legată de design responsive ar trebui să fie în partea de sus a documentului și oriunde vedem o instrucțiune var() în CSS-ul nostru, știm imediat că această proprietate se schimbă. Cu metodele tradiționale de scriere CSS, nu exista nicio modalitate de a ști acest lucru dintr-o privire.

Mulți dintre noi s-au priceput foarte bine la citirea și interpretarea CSS dintr-o privire, în timp ce urmărim în capul nostru care proprietăți s-au schimbat în diferite situații. M-am săturat de asta și nu mai vreau să fac asta! Proprietățile personalizate oferă acum o legătură între logică și implementarea acesteia, așa că nu trebuie să urmărim acest lucru și este incredibil de util!

The Logic Fold

Ideea de a declara variabile în partea de sus a unui document sau a unei funcții nu este o idee nouă. Este ceva ce facem în majoritatea limbilor și acum este ceva ce putem face și în CSS. Scrierea CSS în acest fel creează o distincție vizuală clară între CSS în partea de sus a documentului și dedesubt. Am nevoie de o modalitate de a diferenția aceste secțiuni atunci când vorbesc despre ele, iar ideea de „pliu logic” este o metaforă pe care am început să o folosesc.
Deasupra pliului conține toate variabilele preprocesorului și proprietățile personalizate. Aceasta include toate valorile diferite pe care le poate avea o proprietate personalizată. Ar trebui să fie ușor de urmărit cum se modifică o proprietate personalizată.

CSS de sub fold este simplu și extrem de declarativ și ușor de citit. Se simte ca CSS înaintea interogărilor media și a altor complexități necesare ale CSS-ului modern.

Aruncă o privire la un exemplu foarte simplu de sistem de grilă flexbox cu șase coloane:

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

Proprietatea personalizată --row-display este setată inițial să block . Peste 600px, modul de afișare este setat la flex.

Sub pliul ar putea arăta astfel:

 .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%; }

Știm imediat --row-display este o valoare care se schimbă. Inițial, va fi block , deci valorile flex vor fi ignorate.

Acest exemplu este destul de simplu, dar dacă l-am extins pentru a include o coloană de lățime flexibilă care umple spațiul rămas, este probabil ca valorile flex-grow , flex-shrink și flex-basis să fie convertite în proprietăți personalizate. Puteți încerca acest lucru sau puteți arunca o privire la un exemplu mai detaliat aici.

Proprietăți personalizate pentru tematică

În cea mai mare parte, am argumentat împotriva utilizării proprietăților personalizate pentru variabilele dinamice globale și sperăm că am sugerat că atașarea proprietăților personalizate la selectorul :root este în multe cazuri considerată dăunătoare. Dar fiecare regulă are o excepție, iar pentru proprietățile personalizate, este tematică.

Utilizarea limitată a proprietăților personalizate globale poate face mult mai ușoară tematica.

Tematica se referă, în general, la a permite utilizatorilor să personalizeze interfața de utilizare într-un fel. Acest lucru ar putea fi ceva asemănător cu schimbarea culorilor pe o pagină de profil. Sau poate fi ceva mai localizat. De exemplu, puteți alege culoarea unei note în aplicația Google Keep.

Aplicația Google Keep

Tematica implică de obicei compilarea unei foi de stil separate pentru a înlocui o valoare implicită cu preferințele utilizatorului sau compilarea unei foi de stil diferite pentru fiecare utilizator. Ambele pot fi dificile și au un impact asupra performanței.

Cu proprietăți personalizate, nu trebuie să compilam o altă foaie de stil; trebuie doar să actualizăm valoarea proprietăților în funcție de preferințele utilizatorului. Deoarece sunt valori moștenite, dacă facem acest lucru pe elementul rădăcină, acestea pot fi folosite oriunde în aplicația noastră.

Valorificați proprietățile dinamice globale

Proprietățile personalizate țin cont de majuscule și minuscule și, deoarece majoritatea proprietăților personalizate vor fi locale, dacă utilizați proprietăți dinamice globale, poate avea sens să le scrieți cu majuscule.

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

Valorificarea variabilelor înseamnă adesea constante globale. Pentru noi, acest lucru va semnifica faptul că proprietatea este setată în altă parte în aplicație și că probabil nu ar trebui să o schimbăm local.

Evitați setarea directă a proprietăților dinamice globale

Proprietățile personalizate acceptă o valoare alternativă. Poate fi util să evitați suprascrierea directă a valorii proprietăților personalizate globale și să păstrați separat valorile utilizatorului. Putem folosi valoarea de rezervă pentru a face acest lucru.

Exemplul de mai sus setează valoarea --THEME-COLOR la ​​valoarea --user-theme-color dacă există. Dacă --user-theme-color nu este setată, se va folosi valoarea #d33a2c . În acest fel, nu trebuie să oferim o rezervă de fiecare dată când folosim --THEME-COLOR .

Vă puteți aștepta în exemplul de mai jos ca fundalul să fie setat la green . Cu toate acestea, valoarea --user-theme-color nu a fost setată pe elementul rădăcină, așa că valoarea --THEME-COLOR nu s-a schimbat.

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

Setarea indirectă a proprietăților dinamice globale le protejează împotriva suprascrierii la nivel local și asigură că setările utilizatorului sunt întotdeauna moștenite de la elementul rădăcină. Aceasta este o convenție utilă pentru a vă proteja valorile temei și pentru a evita moștenirea neintenționată.

Dacă vrem să expunem proprietăți specifice moștenirii, putem înlocui selectorul :root cu un selector * :

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

Acum valoarea lui --THEME-COLOR este recalculată pentru fiecare element și, prin urmare, valoarea locală a lui --user-theme-color poate fi utilizată. Cu alte cuvinte, culoarea de fundal din acest exemplu va fi green .

Puteți vedea câteva exemple mai detaliate ale acestui model în secțiunea Manipularea culorii cu proprietăți personalizate.

Actualizarea proprietăților personalizate cu JavaScript

Dacă doriți să setați proprietăți personalizate folosind JavaScript, există un API destul de simplu și arată astfel:

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

Aici setez valoarea --USER-THEME-COLOR pe elementul document, sau cu alte cuvinte, elementul :root unde va fi moștenit de toate elementele.

Acesta nu este un nou API; este aceeași metodă JavaScript pentru actualizarea stilurilor pe un element. Acestea sunt stiluri inline, așa că vor avea o specificitate mai mare decât CSS obișnuit.

Aceasta înseamnă că este ușor să aplicați personalizări locale:

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

Aici am setat o valoare implicită pentru --note-color și am aplicat aceasta la componenta .note . Păstrez declarația variabilei separată de declarația proprietății, chiar și în acest exemplu simplu.

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

Apoi țintesc o anumită instanță a unui element .note și modific valoarea proprietății personalizate --note-color numai pentru acel element. Aceasta va avea acum o specificitate mai mare decât valoarea implicită.

Puteți vedea cum funcționează acest lucru cu acest exemplu folosind React. Aceste preferințe ale utilizatorului ar putea fi salvate în stocarea locală sau, poate, în cazul unei aplicații mai mari, într-o bază de date.

Manipularea culorii cu proprietăți personalizate

Pe lângă valorile hexadecimale și culorile numite, CSS are funcții de culori precum rgb() și hsl() . Acestea ne permit să specificăm componentele individuale ale unei culori, cum ar fi nuanța sau luminozitatea. Proprietățile personalizate pot fi utilizate împreună cu funcțiile de culoare.

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

Acest lucru este util, dar unele dintre cele mai utilizate caracteristici ale preprocesoarelor sunt funcțiile avansate de culoare care ne permit să manipulăm culoarea folosind funcții precum luminarea, închiderea sau desaturarea:

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

Ar fi util să existe unele dintre aceste caracteristici în browsere. Ele vin, dar până când vom avea funcții native de modificare a culorii în CSS, proprietățile personalizate ar putea umple o parte din acest gol.

Am văzut că proprietățile personalizate pot fi utilizate în interiorul funcțiilor de culoare existente, cum ar fi rgb() și hsl() , dar pot fi folosite și în calc() . Aceasta înseamnă că putem converti un număr real într-un procent prin înmulțirea lui, de exemplu calc(50 * 1%) = 50% .

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

Motivul pentru care dorim să stocăm valoarea luminozității ca număr real este ca să o putem manipula cu calc înainte de a o converti într-un procent. De exemplu, dacă vreau să întunec o culoare cu 20% , îi pot înmulți luminozitatea cu 0.8 . Putem face acest lucru puțin mai ușor de citit, separând calculul luminozității într-o proprietate personalizată locală:

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

Am putea chiar să abstragă mai multe calcule și să creăm ceva de genul funcțiilor de modificare a culorii în CSS folosind proprietăți personalizate. Acest exemplu este probabil prea complex pentru majoritatea cazurilor practice de tematică, dar demonstrează întreaga putere a proprietăților personalizate dinamice.

Simplificați tematica

Unul dintre avantajele utilizării proprietăților personalizate este capacitatea de a simplifica tematica. Aplicația nu trebuie să fie conștientă de modul în care sunt utilizate proprietățile personalizate. În schimb, folosim JavaScript sau codul serverului pentru a seta valoarea proprietăților personalizate. Modul în care sunt utilizate aceste valori este determinat de foile de stil.

Aceasta înseamnă încă o dată că suntem capabili să separăm logica de design. Dacă aveți o echipă de proiectare tehnică, autorii pot actualiza foile de stil și pot decide cum să aplice proprietăți personalizate fără a modifica o singură linie de cod JavaScript sau backend.

Proprietățile personalizate permit, de asemenea, să mutați o parte din complexitatea tematicii în CSS, iar această complexitate poate avea un impact negativ asupra mentenanței CSS-ului dvs., așa că nu uitați să o păstrați simplu ori de câte ori este posibil.

Utilizarea proprietăților personalizate astăzi

Chiar dacă acceptați IE10 și 11, puteți începe să utilizați proprietăți personalizate astăzi. Majoritatea exemplelor din acest articol au de-a face cu modul în care scriem și structuram CSS. Beneficiile sunt semnificative în ceea ce privește mentenabilitatea, cu toate acestea, majoritatea exemplelor reduc doar ceea ce s-ar putea face altfel cu un cod mai complex.

Folosesc un instrument numit postcss-css-variables pentru a converti majoritatea caracteristicilor proprietăților personalizate într-o reprezentare statică a aceluiași cod. Alte instrumente similare ignoră proprietățile personalizate din interogările media sau selectoarele complexe, tratând proprietățile personalizate la fel ca variabilele de preprocesor.

Ceea ce nu pot face aceste instrumente este să emuleze caracteristicile de rulare ale proprietăților personalizate. Aceasta înseamnă că nu există caracteristici dinamice, cum ar fi tematica sau modificarea proprietăților cu JavaScript. Acest lucru ar putea fi OK în multe situații. În funcție de situație, personalizarea UI poate fi considerată o îmbunătățire progresivă, iar tema implicită ar putea fi perfect acceptabilă pentru browserele mai vechi.

Se încarcă foaia de stil corectă

Există multe moduri în care puteți utiliza postCSS. Folosesc un proces gulp pentru a compila foi de stil separate pentru browsere mai noi și mai vechi. O versiune simplificată a sarcinii mele gulp arată astfel:

 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")) );

Rezultă două fișiere CSS: unul obișnuit cu proprietăți personalizate ( styles.css ) și unul pentru browsere mai vechi ( styles.no-vars.css ). Vreau ca IE10 și 11 să fie servite styles.no-vars.css și alte browsere pentru a obține fișierul CSS obișnuit.

În mod normal, aș susține utilizarea interogărilor de caracteristici, dar IE11 nu acceptă interogări de caracteristici și am folosit proprietăți personalizate atât de larg încât să servească o altă foaie de stil are sens în acest caz.

Servirea inteligentă a unei foi de stil diferite și evitarea unei fulgerări de conținut fără stil nu este o sarcină simplă. Dacă nu aveți nevoie de caracteristicile dinamice ale proprietăților personalizate, puteți lua în considerare servirea tuturor browser-ului styles.no-vars.css și utilizarea proprietăților personalizate pur și simplu ca instrument de dezvoltare.

Dacă doriți să profitați din plin de toate caracteristicile dinamice ale proprietăților personalizate, vă sugerez să utilizați o tehnică CSS critică. Urmând aceste tehnici, foaia de stil principală este încărcată asincron în timp ce CSS-ul critic este redat inline. Antetul paginii dvs. ar putea arăta cam așa:

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

Putem extinde aceasta pentru a încărca fie styles.css , fie styles.no-vars.css în funcție de dacă browserul acceptă proprietăți personalizate. Putem detecta suport astfel:

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

Concluzie

Dacă te-ai chinuit să organizezi eficient CSS, ai dificultăți cu componentele receptive, vrei să implementezi tematica la nivelul clientului sau doar vrei să începi cu piciorul bun cu proprietăți personalizate, acest ghid ar trebui să îți spună tot ce trebuie să știi.

Se rezumă la înțelegerea diferenței dintre variabilele dinamice și statice în CSS, precum și câteva reguli simple:

  1. Separați logica de proiectare;
  2. Dacă o proprietate CSS se modifică, luați în considerare utilizarea unei proprietăți personalizate;
  3. Modificați valoarea proprietăților personalizate, nu care proprietate personalizată este utilizată;
  4. Variabilele globale sunt de obicei statice.

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.

Lectură suplimentară

  • “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.