Przewodnik strategiczny dotyczący niestandardowych właściwości CSS
Opublikowany: 2022-03-10Niestandardowe właściwości CSS (czasami znane jako „zmienne CSS”) są teraz obsługiwane we wszystkich nowoczesnych przeglądarkach, a ludzie zaczynają używać ich w środowisku produkcyjnym. To świetnie, ale różnią się one od zmiennych w preprocesorach, a widziałem już wiele przykładów osób, które ich używają, nie zastanawiając się, jakie korzyści oferują.
Właściwości niestandardowe mają ogromny potencjał, aby zmienić sposób pisania i struktury CSS oraz, w mniejszym stopniu, sposób, w jaki używamy JavaScript do interakcji z komponentami UI. Nie zamierzam skupiać się na składni i sposobie ich działania (polecam przeczytanie „Czas zacząć korzystać z właściwości niestandardowych”). Zamiast tego, chcę przyjrzeć się bliżej strategiom maksymalnego wykorzystania niestandardowych właściwości CSS.
Jak są podobne do zmiennych w preprocesorach?
Właściwości niestandardowe przypominają trochę zmienne w preprocesorach, ale mają pewne istotne różnice. Pierwszą i najbardziej oczywistą różnicą jest składnia.
W SCSS
używamy symbolu dolara do oznaczenia zmiennej:
$smashing-red: #d33a2c;
W Less używamy symbolu @
:
@smashing-red: #d33a2c;
Właściwości niestandardowe są zgodne z podobnymi konwencjami i używają przedrostka --
:
:root { --smashing-red: #d33a2c; } .smashing-text { color: var(--smashing-red); }
Jedną z ważnych różnic między niestandardowymi właściwościami a zmiennymi w preprocesorach jest to, że niestandardowe właściwości mają inną składnię przypisywania wartości i pobierania tej wartości. Podczas pobierania wartości właściwości niestandardowej używamy funkcji var()
.
Kolejna najbardziej oczywista różnica dotyczy nazwy. Nazywa się je „właściwościami niestandardowymi”, ponieważ tak naprawdę są właściwościami CSS. W preprocesorach można deklarować i używać zmiennych prawie wszędzie, w tym poza blokami deklaracji, w regułach mediów, a nawet jako część selektora.
$breakpoint: 800px; $smashing-red: #d33a2c; $smashing-things: ".smashing-text, .cats"; @media screen and (min-width: $breakpoint) { #{$smashing-things} { color: $smashing-red; } }
Większość powyższych przykładów byłaby nieprawidłowa przy użyciu właściwości niestandardowych.
Właściwości niestandardowe mają te same zasady dotyczące miejsca, w którym mogą być używane, jak normalne właściwości CSS. O wiele lepiej jest myśleć o nich jako o właściwościach dynamicznych niż o zmiennych. Oznacza to, że można ich używać tylko w bloku deklaracji, czyli innymi słowy, niestandardowe właściwości są powiązane z selektorem. Może to być selektor :root
lub dowolny inny poprawny selektor.
:root { --smashing-red: #d33a2c; } @media screen and (min-width: 800px) { .smashing-text, .cats { --margin-left: 1em; } }
Możesz pobrać wartość właściwości niestandardowej w dowolnym miejscu, w którym w przeciwnym razie użyłbyś wartości w deklaracji właściwości. Oznacza to, że mogą być używane jako pojedyncza wartość, jako część skróconej instrukcji lub nawet w równaniach calc()
.
.smashing-text, .cats { color: var(--smashing-red); margin: 0 var(--margin-horizontal); padding: calc(var(--margin-horizontal) / 2) }
Nie można ich jednak używać w zapytaniach o media ani w selektorach, w tym :nth-child()
.
Prawdopodobnie chcesz wiedzieć o wiele więcej o składni i działaniu właściwości niestandardowych, na przykład o tym, jak używać wartości zastępczych i czy możesz przypisać zmienne do innych zmiennych (tak), ale to podstawowe wprowadzenie powinno wystarczyć, aby zrozumieć resztę koncepcje zawarte w tym artykule. Więcej informacji na temat specyfiki działania właściwości niestandardowych można znaleźć w artykule „Czas zacząć korzystać z właściwości niestandardowych”, napisanym przez Serga Hospodaretsa.
Dynamiczny a statyczny
Pomijając różnice kosmetyczne, najważniejszą różnicą między zmiennymi w preprocesorach a właściwościami niestandardowymi jest ich zakres. Możemy odnosić się do zmiennych jako o zakresie statycznym lub dynamicznym. Zmienne w preprocesorach są statyczne, podczas gdy właściwości niestandardowe są dynamiczne.
W przypadku CSS, static oznacza, że możesz aktualizować wartość zmiennej w różnych punktach procesu kompilacji, ale nie może to zmienić wartości kodu, który pojawił się przed nią.
$background: blue; .blue { background: $background; } $background: red; .red { background: $background; }
prowadzi do:
.blue { background: blue; } .red { background: red; }
Po wyrenderowaniu do CSS zmienne znikną. Oznacza to, że potencjalnie moglibyśmy odczytać plik .scss
i określić jego dane wyjściowe bez znajomości kodu HTML, przeglądarki lub innych danych wejściowych. Nie dotyczy to właściwości niestandardowych.
Preprocesory mają rodzaj „zakresu bloków”, w którym zmienne można tymczasowo zmieniać w selektorze, funkcji lub mixinie. Zmienia to wartość zmiennej wewnątrz bloku, ale nadal jest statyczna. Jest to związane z blokiem, a nie z selektorem. W poniższym przykładzie zmienna $background
jest zmieniana wewnątrz bloku .example
. Zmienia się z powrotem do wartości początkowej poza blokiem, nawet jeśli używamy tego samego selektora.
$background: red; .example { $background: blue; background: $background; } .example { background: $background; }
Spowoduje to:
.example { background: blue; } .example { background: red; }
Właściwości niestandardowe działają inaczej. W przypadku właściwości niestandardowych zakres dynamiczny oznacza, że podlegają one dziedziczeniu i kaskadzie. Właściwość jest powiązana z selektorem i jeśli wartość się zmieni, ma to wpływ na wszystkie pasujące elementy DOM, tak jak każda inna właściwość CSS.
Jest to świetne, ponieważ możesz zmienić wartość właściwości niestandardowej w zapytaniu o media za pomocą pseudoselektora, takiego jak hover, a nawet 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); }
Nie musimy zmieniać miejsca użycia niestandardowej właściwości — zmieniamy wartość niestandardowej właściwości za pomocą CSS. Oznacza to, że używając tej samej właściwości niestandardowej, możemy mieć różne wartości w różnych miejscach lub kontekście na tej samej stronie.
Globalne a lokalne
Oprócz tego, że są statyczne lub dynamiczne, zmienne mogą być również globalne lub lokalne. Jeśli piszesz JavaScript, będziesz z tym zaznajomiony. Zmienne mogą być stosowane do wszystkiego w aplikacji lub ich zakres może być ograniczony do określonych funkcji lub bloków kodu.
CSS jest podobny. Mamy kilka rzeczy, które są stosowane globalnie i inne, które są bardziej lokalne. Kolory marki, odstępy w pionie i typografia to przykłady rzeczy, które możesz chcieć zastosować globalnie i spójnie w swojej witrynie lub aplikacji. Mamy też lokalne rzeczy. Na przykład składnik przycisku może mieć mały i duży wariant. Nie chciałbyś, aby rozmiary z tych przycisków były stosowane do wszystkich elementów wejściowych, a nawet każdego elementu na stronie.
To jest coś, co znamy w CSS. Opracowaliśmy systemy projektowe, konwencje nazewnictwa i biblioteki JavaScript, a wszystko po to, aby pomóc w izolowaniu lokalnych komponentów i globalnych elementów projektowych. Właściwości niestandardowe zapewniają nowe opcje rozwiązywania tego starego problemu.
Niestandardowe właściwości CSS są domyślnie ograniczone lokalnie do określonych selektorów, do których je stosujemy. Są więc trochę jak zmienne lokalne. Jednak właściwości niestandardowe są również dziedziczone, więc w wielu sytuacjach zachowują się jak zmienne globalne — zwłaszcza po zastosowaniu do selektora :root
. Oznacza to, że musimy być ostrożni, jak z nich korzystać.
Tak wiele przykładów pokazuje, że niestandardowe właściwości są stosowane do elementu :root
i chociaż jest to w porządku w przypadku wersji demonstracyjnej, może skutkować bałaganem w zakresie globalnym i niezamierzonymi problemami z dziedziczeniem. Na szczęście już nauczyliśmy się tych lekcji.
Zmienne globalne mają tendencję do bycia statycznymi
Jest kilka małych wyjątków, ale ogólnie rzecz biorąc, większość globalnych rzeczy w CSS jest również statyczna.
Zmienne globalne, takie jak kolory marki, typografia i odstępy, nie zmieniają się zbytnio w zależności od komponentu. Kiedy się zmieniają, zwykle jest to globalny rebranding lub inna znacząca zmiana, która rzadko zdarza się w dojrzałym produkcie. Nadal ma sens, aby te rzeczy były zmiennymi, są używane w wielu miejscach, a zmienne pomagają zachować spójność. Ale nie ma sensu, żeby były dynamiczne. Wartość tych zmiennych nie zmienia się w sposób dynamiczny.
Z tego powodu zdecydowanie zalecam używanie preprocesorów dla zmiennych globalnych (statycznych). Zapewnia to nie tylko, że są one zawsze statyczne, ale także wizualnie oznacza je w kodzie. Dzięki temu CSS będzie o wiele bardziej czytelny i łatwiejszy w utrzymaniu.
Lokalne zmienne statyczne są w porządku (czasami)
Można by pomyśleć, że biorąc pod uwagę silne stanowisko, że zmienne globalne są statyczne, wszystkie zmienne lokalne mogą wymagać odbicia dynamicznego. Chociaż prawdą jest, że zmienne lokalne mają tendencję do bycia dynamicznymi, nie jest to tak silne, jak tendencja do statycznych zmiennych globalnych.
Zmienne lokalnie statyczne są w wielu sytuacjach całkowicie OK. Używam zmiennych preprocesorów w plikach składowych głównie dla wygody programisty.
Rozważ klasyczny przykład komponentu przycisku z różnymi rozmiarami.
Mój scss
może wyglądać mniej więcej tak:
$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; }
Oczywiście ten przykład miałby więcej sensu, gdybym używał zmiennych wiele razy lub wyprowadzał wartości marginesów i dopełnienia ze zmiennych rozmiaru. Jednak wystarczającym powodem może być możliwość szybkiego prototypowania różnych rozmiarów.
Ponieważ większość zmiennych statycznych ma charakter globalny, lubię rozróżniać zmienne statyczne, które są używane tylko wewnątrz komponentu. Aby to zrobić, możesz poprzedzić te zmienne nazwą komponentu lub możesz użyć innego przedrostka, takiego jak c-variable-name
dla komponentu lub l-variable-name
dla lokalnego. Możesz użyć dowolnego prefiksu lub możesz poprzedzić zmienne globalne. Cokolwiek wybierzesz, warto rozróżnić, zwłaszcza w przypadku konwertowania istniejącej bazy kodu na korzystanie z właściwości niestandardowych.
Kiedy używać właściwości niestandardowych
Jeśli używanie zmiennych statycznych w komponentach jest w porządku, kiedy powinniśmy używać właściwości niestandardowych? Konwertowanie istniejących zmiennych preprocesora na właściwości niestandardowe zwykle nie ma większego sensu. W końcu powód dla właściwości niestandardowych jest zupełnie inny. Własności niestandardowe mają sens, gdy mamy właściwości CSS, które zmieniają się w zależności od warunku w DOM — zwłaszcza warunku dynamicznego, takiego jak :focus
, :hover
, media query lub JavaScript.
Podejrzewam, że zawsze będziemy używać jakiejś formy zmiennych statycznych, chociaż w przyszłości będziemy potrzebować ich mniej, ponieważ niestandardowe właściwości oferują nowe sposoby organizowania logiki i kodu. Do tego czasu myślę, że w większości sytuacji będziemy pracować z kombinacją zmiennych preprocesora i właściwości niestandardowych.
Warto wiedzieć, że możemy przypisać zmienne statyczne do właściwości niestandardowych. Niezależnie od tego, czy są one globalne, czy lokalne, w wielu sytuacjach sensowne jest przekonwertowanie zmiennych statycznych na lokalnie dynamiczne właściwości niestandardowe.
Uwaga : Czy wiesz, że $var
jest prawidłową wartością właściwości niestandardowej? Najnowsze wersje Sassa to rozpoznają i dlatego musimy interpolować zmienne przypisane do właściwości niestandardowych, na przykład: #{$var}
. To mówi Sassowi, że chcesz wypisać wartość zmiennej, a nie tylko $var
w arkuszu stylów. Jest to potrzebne tylko w sytuacjach, takich jak właściwości niestandardowe, w których nazwy zmiennych mogą być również prawidłowym kodem CSS.
Jeśli weźmiemy powyższy przykład przycisku i zdecydujemy, że wszystkie przyciski powinny używać małej odmiany na urządzeniach mobilnych, niezależnie od klasy zastosowanej w HTML, jest to teraz bardziej dynamiczna sytuacja. W tym celu powinniśmy użyć właściwości niestandardowych.
$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); }
Tutaj tworzę jedną niestandardową właściwość: --button-size
. Ta właściwość niestandardowa jest początkowo objęta zakresem wszystkich elementów przycisku przy użyciu klasy btn
. Następnie zmieniam wartość --button-size
powyżej 600px dla klas btn-med
i btn-lrg
. Na koniec stosuję tę niestandardową właściwość do wszystkich elementów przycisku w jednym miejscu.
Nie bądź zbyt sprytny
Dynamiczny charakter właściwości niestandardowych pozwala nam tworzyć sprytne i skomplikowane komponenty.
Wraz z wprowadzeniem preprocesorów wielu z nas stworzyło biblioteki ze sprytnymi abstrakcjami przy użyciu domieszek i funkcji niestandardowych. W nielicznych przypadkach przykłady takie jak ten są nadal przydatne, ale w większości przypadków im dłużej pracuję z preprocesorami, tym mniej funkcji używam. Obecnie używam preprocesorów prawie wyłącznie dla zmiennych statycznych.
Właściwości niestandardowe nie będą (i nie powinny) być odporne na tego typu eksperymenty i nie mogę się doczekać wielu sprytnych przykładów. Ale na dłuższą metę czytelny i łatwy w utrzymaniu kod zawsze zwycięży sprytne abstrakcje (przynajmniej w produkcji).
Przeczytałem niedawno świetny artykuł na ten temat na Free Code Camp Medium. Został napisany przez Billa Souroura i nosi tytuł „Nie rób tego w czasie wykonywania. Zrób to w czasie projektowania”. Zamiast parafrazować jego argumenty, pozwolę ci go przeczytać.
Jedną z kluczowych różnic między zmiennymi preprocesora a właściwościami niestandardowymi jest to, że właściwości niestandardowe działają w czasie wykonywania. Oznacza to, że rzeczy, które mogłyby być na granicy dopuszczalne, pod względem złożoności, z preprocesorami, mogą nie być dobrym pomysłem z niestandardowymi właściwościami.
Jednym z przykładów, który ostatnio mi to zilustrował, był ten:
: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; }
To generuje skalę modułową. Skala modułowa to szereg liczb, które są ze sobą powiązane za pomocą współczynnika. Są często używane w projektowaniu i programowaniu stron internetowych do ustawiania rozmiarów czcionek lub odstępów.
W tym przykładzie każda właściwość niestandardowa jest określana za pomocą funkcji calc()
, biorąc wartość poprzedniej właściwości niestandardowej i mnożąc ją przez współczynnik. W ten sposób możemy uzyskać kolejną liczbę w skali.
Oznacza to, że współczynniki są obliczane w czasie wykonywania i można je zmienić, aktualizując tylko wartość właściwości --font-scale
. Na przykład:
@media screen and (min-width: 800px) { :root { --font-scale: 1.33; } }
Jest to sprytne, zwięzłe i znacznie szybsze niż ponowne obliczanie wszystkich wartości, jeśli chcesz zmienić skalę. To także coś, czego nie zrobiłbym w kodzie produkcyjnym.
Chociaż powyższy przykład jest przydatny do prototypowania, w produkcji zdecydowanie wolałbym zobaczyć coś takiego:
: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; } }
Podobnie jak w przykładzie z artykułu Billa, uważam, że pomocne jest sprawdzenie, jakie są rzeczywiste wartości. Kod czytamy znacznie więcej razy niż go piszemy, a wartości globalne, takie jak skale czcionek, zmieniają się w produkcji rzadko.
Powyższy przykład nadal nie jest doskonały. Narusza to wcześniejszą zasadę, że wartości globalne powinny być statyczne . Zdecydowanie wolałbym używać zmiennych preprocesora i konwertować je na lokalnie dynamiczne właściwości niestandardowe przy użyciu technik przedstawionych wcześniej.
Ważne jest również, aby uniknąć sytuacji, w których przechodzimy z jednej właściwości niestandardowej do innej. Może się to zdarzyć, gdy nazwiemy właściwości w ten sposób.
Zmień wartość, a nie zmienną
Zmiana wartości, a nie zmiennej jest jedną z najważniejszych strategii efektywnego korzystania z właściwości niestandardowych.
Zasadniczo nigdy nie należy zmieniać, która właściwość niestandardowa jest używana w jednym celu. Jest to łatwe, ponieważ dokładnie tak robimy rzeczy z preprocesorami, ale nie ma to większego sensu w przypadku właściwości niestandardowych.
W tym przykładzie mamy dwie niestandardowe właściwości, które są używane w przykładowym komponencie. Przełączam się z używania wartości --font-size-small
na --font-size-large
w zależności od rozmiaru ekranu.
: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); } }
Lepszym sposobem na to byłoby zdefiniowanie pojedynczej właściwości niestandardowej, której zakres obejmuje komponent. Następnie za pomocą zapytania o media lub dowolnego innego selektora zmień jego wartość.
.example { --example-font-size: 1.2em; } @media screen and (min-width: 800px) { .example { --example-font-size: 2em; } }
Wreszcie w jednym miejscu używam wartości tej niestandardowej właściwości:
.example { font-size: var(--example-font-size); }
W tym i wcześniejszym przykładzie zapytania o media były używane tylko do zmiany wartości właściwości niestandardowych. Możesz również zauważyć, że jest tylko jedno miejsce, w którym używana jest instrukcja var()
, a zwykłe właściwości CSS są aktualizowane.
Ten rozdział między deklaracjami zmiennych a deklaracjami własności jest celowy. Powodów jest wiele, ale korzyści są najbardziej oczywiste, gdy myślimy o responsywnym projektowaniu.
Responsywny projekt z niestandardowymi właściwościami
Jedną z trudności związanych z projektowaniem responsywnym, które w dużym stopniu opiera się na zapytaniach o media, jest to, że niezależnie od tego, jak organizujesz CSS, style odnoszące się do konkretnego komponentu ulegają fragmentacji w całym arkuszu stylów.
Ustalenie, jakie właściwości CSS ulegną zmianie, może być bardzo trudne. Mimo to niestandardowe właściwości CSS mogą pomóc nam uporządkować część logiki związanej z projektowaniem responsywnym i znacznie ułatwić pracę z zapytaniami o media.
Jeśli się zmieni, to jest zmienna
Właściwości, które zmieniają się za pomocą zapytań o media, są z natury dynamiczne, a właściwości niestandardowe zapewniają środki do wyrażania wartości dynamicznych w CSS. Oznacza to, że jeśli używasz zapytania o media do zmiany dowolnej właściwości CSS, powinieneś umieścić tę wartość we właściwości niestandardowej.
Następnie możesz przenieść to, wraz ze wszystkimi regułami multimediów, stanami najechania kursorem lub dowolnymi selektorami dynamicznymi, które definiują sposób zmiany wartości, na górę dokumentu.
Oddziel logikę od projektu
Prawidłowo wykonane rozdzielenie logiki i projektu oznacza, że zapytania o media są używane tylko do zmiany wartości właściwości niestandardowych . Oznacza to, że cała logika związana z projektowaniem responsywnym powinna znajdować się na górze dokumentu, a gdziekolwiek widzimy instrukcję var()
w naszym CSS, od razu wiemy, że ta właściwość się zmienia. Przy tradycyjnych metodach pisania CSS nie można było się o tym przekonać na pierwszy rzut oka.
Wielu z nas bardzo dobrze czytało i interpretowało CSS na pierwszy rzut oka, śledząc w głowie, które właściwości zmieniły się w różnych sytuacjach. Jestem tym zmęczony i nie chcę już tego robić! Właściwości niestandardowe zapewniają teraz łącze między logiką a jej implementacją, więc nie musimy tego śledzić, a to jest niezwykle przydatne!
Złożenie logiki
Pomysł deklarowania zmiennych na górze dokumentu lub funkcji nie jest nowym pomysłem. Jest to coś, co robimy w większości języków, a teraz możemy to zrobić również w CSS. Pisanie CSS w ten sposób tworzy wyraźną wizualną różnicę między CSS u góry dokumentu i poniżej. Potrzebuję sposobu na rozróżnienie tych sekcji, kiedy o nich mówię, a pomysł „logicznego złożenia” jest metaforą, której zacząłem używać.
W części strony widocznej na ekranie znajdują się wszystkie zmienne preprocesora i właściwości niestandardowe. Obejmuje to wszystkie różne wartości, jakie może mieć właściwość niestandardowa. Prześledzenie, jak zmienia się właściwość niestandardowa, powinno być łatwe.
CSS poniżej strony po przewinięciu jest prosty, wysoce deklaratywny i łatwy do odczytania. Czuje się jak CSS przed zapytaniami o media i innymi niezbędnymi zawiłościami współczesnego CSS.
Spójrz na naprawdę prosty przykład sześciokolumnowego systemu siatki flexbox:
.row { --row-display: block; } @media screen and (min-width: 600px) { .row { --row-display: flex; } }
Właściwość niestandardowa --row-display
jest początkowo ustawiona na block
. Powyżej 600 pikseli tryb wyświetlania jest ustawiony na flex.
Poniżej zakładka może wyglądać tak:
.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%; }
Od razu wiemy, że --row-display
to wartość, która się zmienia. Początkowo będzie to block
, więc wartości flex zostaną zignorowane.
Ten przykład jest dość prosty, ale jeśli rozszerzymy go, aby zawierał kolumnę o elastycznej szerokości, która wypełnia pozostałą przestrzeń, prawdopodobnie wartości flex-grow
, flex-shrink
i flex-basis
musiałyby zostać przekonwertowane na niestandardowe właściwości. Możesz spróbować tego lub spojrzeć na bardziej szczegółowy przykład tutaj.
Niestandardowe właściwości motywów
Przeważnie sprzeciwiałem się używaniu niestandardowych właściwości dla globalnych zmiennych dynamicznych i mam nadzieję, że sugerowałem, że dołączanie niestandardowych właściwości do selektora :root
jest w wielu przypadkach uważane za szkodliwe. Ale każda reguła ma wyjątek, a dla właściwości niestandardowych jest to motyw.
Ograniczone użycie globalnych właściwości niestandardowych może znacznie ułatwić tworzenie kompozycji.
Tematyka ogólnie odnosi się do umożliwienia użytkownikom dostosowania interfejsu użytkownika w pewien sposób. Może to być coś w rodzaju zmiany kolorów na stronie profilu. Lub może to być coś bardziej zlokalizowanego. Na przykład możesz wybrać kolor notatki w aplikacji Google Keep.
Tworzenie motywów zazwyczaj obejmuje kompilację oddzielnego arkusza stylów, aby nadpisać domyślną wartość preferencjami użytkownika lub kompilację innego arkusza stylów dla każdego użytkownika. Obie te rzeczy mogą być trudne i mieć wpływ na wydajność.
Dzięki właściwościom niestandardowym nie musimy kompilować innego arkusza stylów; wystarczy zaktualizować wartość właściwości zgodnie z preferencjami użytkownika. Ponieważ są to wartości dziedziczone, jeśli zrobimy to na elemencie głównym, można ich użyć w dowolnym miejscu naszej aplikacji.
Wykorzystaj globalne właściwości dynamiczne
Właściwości niestandardowe rozróżniają wielkość liter, a ponieważ większość właściwości niestandardowych będzie lokalna, jeśli używasz globalnych właściwości dynamicznych, sensowne może być ich pisanie wielkimi literami.
:root { --THEME-COLOR: var(--user-theme-color, #d33a2c); }
Wielkie litery zmiennych często oznaczają stałe globalne. Dla nas będzie to oznaczać, że właściwość jest ustawiona w innym miejscu aplikacji i prawdopodobnie nie powinniśmy jej zmieniać lokalnie.
Unikaj bezpośredniego ustawiania globalnych właściwości dynamicznych
Właściwości niestandardowe akceptują wartość zastępczą. Przydatne może być uniknięcie bezpośredniego nadpisywania wartości globalnych właściwości niestandardowych i oddzielenie wartości użytkownika. W tym celu możemy użyć wartości zastępczej.
Powyższy przykład ustawia wartość --THEME-COLOR
na wartość --user-theme-color
, jeśli istnieje. Jeśli --user-theme-color
nie jest ustawiona, zostanie użyta wartość #d33a2c
. W ten sposób nie musimy podawać opcji zastępczej za każdym razem, gdy używamy --THEME-COLOR
.
W poniższym przykładzie można by się spodziewać, że tło zostanie ustawione na green
. Jednak wartość --user-theme-color
nie została ustawiona w elemencie głównym, więc wartość --THEME-COLOR
nie uległa zmianie.
:root { --THEME-COLOR: var(--user-theme-color, #d33a2c); } body { --user-theme-color: green; background: var(--THEME-COLOR); }
Pośrednie ustawienie globalnych właściwości dynamicznych w ten sposób chroni je przed lokalnym nadpisaniem i zapewnia, że ustawienia użytkownika są zawsze dziedziczone z elementu głównego. Jest to przydatna konwencja, która chroni Twoje wartości motywu i pozwala uniknąć niezamierzonego dziedziczenia.
Jeśli chcemy udostępnić określone właściwości dziedziczeniu, możemy zastąpić selektor :root
selektorem *
:
* { --THEME-COLOR: var(--user-theme-color, #d33a2c); } body { --user-theme-color: green; background: var(--THEME-COLOR); }
Teraz wartość --THEME-COLOR
jest przeliczana dla każdego elementu i dlatego można użyć lokalnej wartości --user-theme-color
. Innymi słowy, kolorem tła w tym przykładzie będzie green
.
Możesz zobaczyć bardziej szczegółowe przykłady tego wzoru w sekcji Manipulowanie kolorem za pomocą właściwości niestandardowych.
Aktualizowanie niestandardowych właściwości za pomocą JavaScript
Jeśli chcesz ustawić niestandardowe właściwości za pomocą JavaScript, istnieje dość prosty interfejs API i wygląda to tak:
const elm = document.documentElement; elm.style.setProperty('--USER-THEME-COLOR', 'tomato');
Tutaj ustawiam wartość --USER-THEME-COLOR
na elemencie dokumentu, czyli innymi słowy na elemencie :root
, gdzie będzie on dziedziczony przez wszystkie elementy.
To nie jest nowy interfejs API; jest to ta sama metoda JavaScript do aktualizowania stylów w elemencie. Są to style inline, więc będą miały większą szczegółowość niż zwykły CSS.
Oznacza to, że łatwo jest zastosować lokalne dostosowania:
.note { --note-color: #eaeaea; } .note { background: var(--note-color); }
Tutaj ustawiłem domyślną wartość dla --note-color
i zawęziłem ją do komponentu .note
. Trzymam deklarację zmiennej oddzielnie od deklaracji właściwości, nawet w tym prostym przykładzie.
const elm = document.querySelector('#note-uid'); elm.style.setProperty('--note-color', 'yellow');
Następnie wybieram konkretne wystąpienie elementu .note
i zmieniam wartość właściwości niestandardowej --note-color
tylko dla tego elementu. Będzie to teraz miało wyższą specyficzność niż wartość domyślna.
Możesz zobaczyć, jak to działa na tym przykładzie, używając Reacta. Te preferencje użytkownika mogą być zapisane w pamięci lokalnej lub, być może w przypadku większej aplikacji, w bazie danych.
Manipulowanie kolorem za pomocą dostosowanych właściwości
Oprócz wartości szesnastkowych i nazwanych kolorów CSS zawiera funkcje kolorów, takie jak rgb()
i hsl()
. Pozwalają nam one określić poszczególne składowe koloru, takie jak odcień czy jasność. Właściwości niestandardowe mogą być używane w połączeniu z funkcjami koloru.
:root { --hue: 25; } body { background: hsl(var(--hue), 80%, 50%); }
Jest to przydatne, ale niektóre z najczęściej używanych funkcji preprocesorów to zaawansowane funkcje kolorów, które pozwalają nam manipulować kolorami za pomocą funkcji takich jak rozjaśnianie, przyciemnianie lub zmniejszanie nasycenia:
darken($base-color, 10%); lighten($base-color, 10%); desaturate($base-color, 20%);
Przydałoby się mieć niektóre z tych funkcji w przeglądarkach. Nadchodzą, ale dopóki nie będziemy mieli natywnych funkcji modyfikacji kolorów w CSS, niestandardowe właściwości mogą wypełnić część tej luki.
Widzieliśmy, że właściwości niestandardowe mogą być używane w istniejących funkcjach kolorów, takich jak rgb()
i hsl()
, ale można ich również używać w calc()
. Oznacza to, że możemy przeliczyć liczbę rzeczywistą na procent, mnożąc ją, np. calc(50 * 1%)
= 50%
.
:root { --lightness: 50; } body { background: hsl(25, 80%, calc(var(--lightness) * 1%)); }
Powodem, dla którego chcemy przechowywać wartość jasności jako liczbę rzeczywistą, jest to, abyśmy mogli manipulować nią za pomocą calc
przed przekształceniem jej na wartość procentową. Na przykład, jeśli chcę przyciemnić kolor o 20%
, mogę pomnożyć jego jasność przez 0.8
. Możemy to nieco ułatwić, dzieląc obliczenia jasności na niestandardową właściwość o zasięgu lokalnym:
:root { --lightness: 50; } body { --lightness: calc(var(--lightness * 0.8)); background: hsl(25, 80%, calc(var(--lightness) * 1%)); }
Moglibyśmy nawet wyabstrahować więcej obliczeń i stworzyć coś w rodzaju funkcji modyfikacji kolorów w CSS za pomocą niestandardowych właściwości. Ten przykład jest prawdopodobnie zbyt złożony dla większości praktycznych przypadków tworzenia motywów, ale pokazuje pełną moc dynamicznych właściwości niestandardowych.
Uprość motywy
Jedną z zalet korzystania z właściwości niestandardowych jest możliwość uproszczenia motywów. Aplikacja nie musi wiedzieć, w jaki sposób używane są właściwości niestandardowe. Zamiast tego używamy JavaScript lub kodu po stronie serwera, aby ustawić wartość właściwości niestandardowych. Sposób użycia tych wartości jest określany przez arkusze stylów.
Oznacza to po raz kolejny, że jesteśmy w stanie oddzielić logikę od projektowania. Jeśli masz zespół ds. projektowania technicznego, autorzy mogą aktualizować arkusze stylów i decydować, jak zastosować niestandardowe właściwości bez zmiany ani jednej linii kodu JavaScript lub kodu zaplecza.
Właściwości niestandardowe pozwalają również przenieść część złożoności motywów do CSS, a ta złożoność może mieć negatywny wpływ na łatwość utrzymania Twojego CSS, więc pamiętaj, aby zachować prostotę tam, gdzie to możliwe.
Korzystanie z niestandardowych właściwości dzisiaj
Nawet jeśli obsługujesz IE10 i 11, możesz już dziś zacząć korzystać z właściwości niestandardowych. Większość przykładów w tym artykule dotyczy tego, jak piszemy i tworzymy CSS. Korzyści są znaczące pod względem łatwości utrzymania, jednak większość przykładów ogranicza tylko to, co można by zrobić w bardziej złożonym kodzie.
Używam narzędzia o nazwie postcss-css-variables, aby przekonwertować większość cech niestandardowych właściwości na statyczną reprezentację tego samego kodu. Inne podobne narzędzia ignorują niestandardowe właściwości w zapytaniach o media lub złożonych selektorach, traktując niestandardowe właściwości podobnie jak zmienne preprocesora.
To, czego te narzędzia nie mogą zrobić, to emulować funkcje środowiska wykonawczego właściwości niestandardowych. Oznacza to brak dynamicznych funkcji, takich jak tworzenie motywów lub zmiana właściwości za pomocą JavaScript. W wielu sytuacjach może to być w porządku. W zależności od sytuacji dostosowywanie interfejsu użytkownika można uznać za stopniowe ulepszenie, a domyślny motyw może być całkowicie akceptowalny dla starszych przeglądarek.
Ładowanie odpowiedniego arkusza stylów
Istnieje wiele sposobów korzystania z postCSS. Używam procesu gulp
do kompilowania oddzielnych arkuszy stylów dla nowszych i starszych przeglądarek. Uproszczona wersja mojego zadania gulp
wygląda tak:
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")) );
Powoduje to powstanie dwóch plików CSS: zwykłego z niestandardowymi właściwościami ( styles.css
) i jednego dla starszych przeglądarek ( styles.no-vars.css
). Chcę, aby IE10 i 11 były obsługiwane styles.no-vars.css
i inne przeglądarki, aby uzyskać zwykły plik CSS.
Zwykle zalecałbym używanie zapytań o funkcje, ale IE11 nie obsługuje zapytań o funkcje, a my używaliśmy niestandardowych właściwości tak intensywnie, że w tym przypadku sensowne jest udostępnianie innego arkusza stylów.
Inteligentne udostępnianie innego arkusza stylów i unikanie błysku treści bez stylu nie jest prostym zadaniem. Jeśli nie potrzebujesz dynamicznych funkcji właściwości niestandardowych, możesz rozważyć udostępnianie wszystkich stylów przeglądarki styles.no-vars.css
i używanie właściwości niestandardowych po prostu jako narzędzia programistycznego.
Jeśli chcesz w pełni wykorzystać wszystkie dynamiczne funkcje właściwości niestandardowych, sugeruję użycie krytycznej techniki CSS. Zgodnie z tymi technikami główny arkusz stylów jest ładowany asynchronicznie, podczas gdy krytyczny CSS jest renderowany w linii. Nagłówek Twojej strony może wyglądać mniej więcej tak:
<head> <style> /* inlined critical CSS */ </style> <script> loadCSS('non-critical.css'); </script> </head>
Możemy to rozszerzyć, aby załadować style.css lub styles.css
styles.no-vars.css
w zależności od tego, czy przeglądarka obsługuje niestandardowe właściwości. Możemy wykryć wsparcie w ten sposób:
if ( window.CSS && CSS.supports('color', 'var(--test)') ) { loadCSS('styles.css'); } else { loadCSS('styles.no-vars.css'); }
Wniosek
Jeśli zmagasz się z efektywną organizacją CSS, masz trudności z responsywnymi komponentami, chcesz zaimplementować motywy po stronie klienta lub po prostu chcesz zacząć dobrze z niestandardowymi właściwościami, ten przewodnik powinien zawierać wszystko, co musisz wiedzieć.
Sprowadza się do zrozumienia różnicy między zmiennymi dynamicznymi i statycznymi w CSS, a także kilku prostych zasad:
- Oddziel logikę od projektu;
- Jeśli zmieni się właściwość CSS, rozważ użycie właściwości niestandardowej;
- Zmień wartość właściwości niestandardowych, a nie tę, która jest używana;
- Zmienne globalne są zwykle statyczne.
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.
Dalsza lektura
- “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.