Jak skonfigurować schematy kolorów aplikacji z niestandardowymi właściwościami CSS
Opublikowany: 2022-03-10Zmienne to podstawowe narzędzie, które pomaga uporządkować kolory w projekcie. Przez długi czas inżynierowie front-end używali zmiennych preprocesorów do konfigurowania kolorów w projekcie. Jednak obecnie wielu programistów preferuje nowoczesny, natywny mechanizm organizowania zmiennych kolorów: niestandardowe właściwości CSS. Najważniejszą ich przewagą nad zmiennymi preprocesorowymi jest to, że działają w czasie rzeczywistym, a nie na etapie kompilacji projektu oraz posiadają wsparcie dla modelu kaskadowego, który pozwala na dziedziczenie i redefiniowanie wartości w locie.
Kiedy próbujesz zorganizować schemat kolorów aplikacji, zawsze możesz umieścić wszystkie niestandardowe właściwości, które odnoszą się do koloru w sekcji głównej, nazwać je i używać we wszystkich wymaganych miejscach.
To opcja, ale czy pomaga rozwiązać problemy z motywami aplikacji, białymi etykietami, odświeżeniem marki lub organizowaniem trybu jasnego lub ciemnego? Co zrobić, jeśli musisz dostosować schemat kolorów, aby zwiększyć kontrast? Przy obecnym podejściu będziesz musiał zaktualizować każdą wartość w swoich zmiennych.
W tym artykule chcę zasugerować bardziej elastyczne i odporne podejście do dzielenia zmiennych kolorów za pomocą właściwości niestandardowych, które mogą rozwiązać wiele z tych problemów.
Konfiguracja palety kolorów
Kolorystyka każdej strony internetowej zaczyna się od ustawienia schematu kolorów. Taki schemat oparty jest na kole kolorów. Zwykle tylko kilka podstawowych kolorów stanowi podstawę palety, reszta to kolory pochodne — tony i półcienie. Najczęściej paleta jest statyczna i nie zmienia się podczas działania aplikacji webowej.
Zgodnie z teorią kolorów istnieje tylko kilka opcji schematów kolorów:
- Schemat monochromatyczny (jeden kolor podstawowy)
- Schemat uzupełniający (dwa kolory podstawowe)
- Schemat triady (trzy kolory podstawowe)
- Schemat tetradyczny (cztery podstawowe kolory)
- Sąsiadujący wzór (dwa lub trzy kolory podstawowe)
Dla mojego przykładu wygeneruję schemat kolorów triady za pomocą usługi Paletton:
Mam teraz trzy główne kolory. Na ich podstawie obliczę tony i tony pośrednie (format HSL w połączeniu z funkcją calc
jest do tego bardzo przydatnym narzędziem). Zmieniając wartość jasności mogę wygenerować kilka dodatkowych kolorów dla palety.
Teraz, jeśli paleta zostanie zmodyfikowana, konieczna będzie zmiana tylko wartości kolorów podstawowych. Reszta zostanie przeliczona automatycznie.
Jeśli wolisz formaty HEX lub RGB, to nie ma znaczenia; paletę można utworzyć na etapie kompilacji projektu z odpowiednimi funkcjami preprocesora (np. z SCSS i funkcją color-adjust
). Jak wspomniałem wcześniej, ta warstwa jest w większości statyczna; niezwykle rzadko można zmienić paletę w uruchomionej aplikacji. Dlatego możemy to obliczyć za pomocą preprocesorów.
Uwaga : Polecam również generowanie zarówno literału HEX, jak i RGB dla każdego koloru. Umożliwi to w przyszłości zabawę z kanałem alfa.
Poziom palety to jedyny poziom, na którym kolor jest zakodowany bezpośrednio w nazwach zmiennych, tzn. możemy jednoznacznie zidentyfikować kolor poprzez odczytanie nazwy.
Zdefiniuj motyw lub funkcjonalne kolory
Gdy paleta jest już gotowa, kolejnym krokiem jest poziom funkcjonalnych kolorów . Na tym poziomie wartość koloru nie jest tak ważna, jak jego przeznaczenie, funkcja, jaką pełni i co dokładnie koloruje. Na przykład kolor podstawowy lub marki aplikacji, kolor obramowania, kolor tekstu na ciemnym tle, kolor tekstu na jasnym tle, kolor tła przycisku, kolor linku, kolor linku do najechania kursorem, kolor tekstu podpowiedzi itd. .
Są to niezwykle powszechne rzeczy w prawie każdej witrynie lub aplikacji. Można powiedzieć, że takie kolory odpowiadają za określony motyw kolorystyczny aplikacji. Również wartości takich zmiennych są pobierane stricte z palety. W ten sposób możemy łatwo zmieniać motywy aplikacji, po prostu operując różnymi paletami kolorów.
Poniżej stworzyłem trzy typowe kontrolki interfejsu użytkownika: przycisk, link i pole wejściowe. Są one kolorowane za pomocą zmiennych funkcjonalnych, które zawierają wartości z palety, którą wcześniej wygenerowałem powyżej. Główną zmienną funkcjonalną odpowiedzialną za motyw aplikacji (marka warunkowa) jest zmienna koloru podstawowego.
Za pomocą trzech przycisków u góry możesz przełączać motywy (zmienić kolor marki dla elementów sterujących). Zmiana następuje przy użyciu odpowiedniego API CSSOM (setProperty).
Takie podejście jest wygodne nie tylko w przypadku tematów, ale także konfiguracji poszczególnych stron internetowych. Na przykład w witrynie zubry.by użyłem wspólnego arkusza stylów i zmiennej funkcjonalnej --page-color
, aby pokolorować logo, nagłówki, kontrolki i zaznaczenie tekstu na wszystkich stronach. I we własnych stylach każdej strony, właśnie przedefiniowałem tę zmienną, aby ustawić jej indywidualny, podstawowy kolor.
Użyj kolorów komponentów
Duże projekty internetowe zawsze zawierają dekompozycję; dzielimy wszystko na małe elementy i wykorzystujemy je ponownie w wielu miejscach. Każdy komponent zwykle ma swój własny styl, co oznacza, że nie ma znaczenia, czego użyliśmy do dekompozycji modułów BEM lub CSS, ani innego podejścia; ważne jest, aby każdy taki fragment kodu mógł być nazwany zakresem lokalnym i ponownie użyty.
Ogólnie rzecz biorąc, widzę sens w stosowaniu zmiennych kolorów na poziomie komponentów w dwóch przypadkach.
Pierwszym z nich jest sytuacja, w której komponenty, które zgodnie ze wskazówkami stylistycznymi aplikacji są powtarzane z różnymi ustawieniami, np. przyciski odpowiadające różnym potrzebom, np. przycisk główny (marka), przycisk pomocniczy, przycisk trzeciorzędny i tak dalej.
Druga to sytuacja, gdy komponenty, które mają kilka stanów o różnych kolorach, np. najechanie przyciskiem, stan aktywny i stan skupienia; stany normalne i nieprawidłowe dla pola wejściowego lub pola wyboru i tak dalej.
Rzadszym przypadkiem, w którym zmienne składowe mogą się przydać, jest funkcjonalność „białej etykiety”. „Biała etykieta” to funkcja usługi, która pozwala użytkownikowi dostosować lub oznakować część interfejsu użytkownika, aby poprawić wrażenia z interakcji z klientami. Na przykład dokumenty elektroniczne, które użytkownik udostępnia swoim klientom za pośrednictwem usługi lub szablonów wiadomości e-mail. W takim przypadku zmienne na poziomie komponentów pomogą skonfigurować niektóre komponenty oddzielnie od reszty motywu kolorystycznego aplikacji.
W poniższym przykładzie dodałem teraz kontrolki do dostosowywania kolorów głównego przycisku (marki). Używając zmiennych kolorów na poziomie komponentu, możemy skonfigurować kontrolki interfejsu użytkownika niezależnie od siebie.
Jak określić, jaki poziom ma zmienna?
Natknąłem się na pytanie, jak zrozumieć, co można umieścić w korzeniu (poziom tematyczny lub funkcjonalny), a co zostawić na poziomie komponentu. To doskonałe pytanie, na które trudno odpowiedzieć, nie widząc sytuacji, z którą pracujesz.
Niestety to samo podejście co w programowaniu nie działa z kolorami i stylami, jeśli widzimy trzy identyczne fragmenty kodu, to musimy go zrefaktoryzować.
Kolor może się powtarzać z komponentu na komponent, ale to nie znaczy, że jest to reguła. Między takimi składnikami nie może być żadnego związku. Na przykład obramowanie pola wejściowego i tło głównego przycisku. Tak, w powyższym przykładzie tak jest, ale sprawdźmy następujący przykład:
Kolor ciemnoszary jest powtarzany — jest to obramowanie pola wejściowego, kolor wypełnienia ikony zamykania i tło przycisku pomocniczego. Ale te elementy nie są ze sobą w żaden sposób połączone. Jeśli zmieni się kolor obramowania pola wejściowego, nie zmienimy tła drugiego przycisku. W takim przypadku musimy zachować tutaj tylko zmienną z palety.
A co z zielonym? Możemy go jednoznacznie zdefiniować jako kolor podstawowy lub markowy, najprawdopodobniej jeśli zmieni się kolor głównego przycisku, to zmieni się też kolor linku i nagłówka pierwszego poziomu.
A co z czerwienią? Nieprawidłowy stan pól wejściowych, komunikaty o błędach i przyciski destrukcyjne będą miały ten sam kolor na całym poziomie aplikacji. To jest wzór. Teraz mogę zdefiniować kilka wspólnych zmiennych funkcjonalnych w sekcji głównej:
Jeśli chodzi o poziom kolorów komponentów, możemy łatwo zidentyfikować komponenty, które można dostosować za pomocą niestandardowych właściwości.
Przycisk jest powtarzany z różnymi ustawieniami, zmienia się kolor tła i tekst dla różnych przypadków użycia — podstawowy, drugorzędny, trzeciorzędny, destrukcyjny lub negatywny.
Pole wejściowe ma dwa stany — nieprawidłowy i normalny, gdzie kolory tła i obramowania różnią się. Wstawmy więc te ustawienia do zmiennych kolorów na poziomie odpowiednich komponentów.
W przypadku pozostałych komponentów nie jest konieczne definiowanie lokalnych zmiennych kolorów, będzie to zbędne.
Musisz zagłębić się w język wzorców swojego projektu, który prawdopodobnie jest rozwijany przez zespół projektowy i UX. Inżynierowie muszą w pełni zrozumieć całą koncepcję języka wizualnego, dopiero wtedy możemy określić, co jest wspólne i powinno żyć na poziomie funkcjonalnym, a co powinno pozostać w lokalnym zasięgu widzialności.
Ale wszystko nie jest takie skomplikowane, są rzeczy oczywiste. Ogólne tło strony, tło i kolor głównego tekstu, w większości przypadków określa motyw Twojej aplikacji. Niezwykle wygodnie jest zbierać takie rzeczy, które odpowiadają za konfigurację konkretnego trybu (np. tryb ciemny lub jasny).
Dlaczego nie umieścić wszystkiego w sekcji głównej?
Miałem takie doświadczenie. W projekcie Lition zespół i ja stanęliśmy przed faktem, że potrzebujemy obsługi IE11 dla aplikacji internetowej, ale nie dla strony internetowej i lądowań. Pomiędzy projektami użyto wspólnego UI Kit i zdecydowaliśmy się umieścić wszystkie zmienne w katalogu głównym, co pozwoli nam je przedefiniować na dowolnym poziomie.
A także przy takim podejściu do aplikacji internetowej i przypadku IE11, po prostu przekazaliśmy kod przez następującą wtyczkę postprocesora i przekształciliśmy te zmienne w literały dla wszystkich komponentów interfejsu użytkownika w projekcie. Ta sztuczka jest możliwa tylko wtedy, gdy wszystkie zmienne zostały zdefiniowane w sekcji głównej, ponieważ postprocesor nie może zrozumieć specyfiki modelu kaskadowego.
Teraz rozumiem, że to nie była właściwa droga. Po pierwsze, jeśli umieścisz kolory komponentów w sekcji głównej, złamiesz zasadę separacji obaw. W rezultacie możesz skończyć z nadmiarowym CSS w arkuszu stylów. Na przykład masz folder komponentów, w którym każdy komponent ma swój własny styl. Masz również wspólny arkusz stylów, w którym opisujesz zmienne kolorów w sekcji głównej. Decydujesz się usunąć komponent przycisku; w takim przypadku należy pamiętać o usunięciu zmiennych powiązanych z przyciskiem z pliku wspólnych stylów.
Po drugie, nie jest to najlepsze rozwiązanie pod względem wydajności. Tak, zmiana koloru powoduje tylko proces odmalowania, a nie reflow/layoutu, to samo w sobie nie jest zbyt kosztowne, ale gdy dokonasz jakichś zmian na najwyższym poziomie, zużyjesz więcej zasobów na sprawdzenie całego drzewa niż w przypadku tych zmiany zachodzą na małym obszarze lokalnym. Aby uzyskać więcej informacji, polecam zapoznać się z benchmarkiem wydajności zmiennych CSS od Lisi Linhart.
Na moim obecnym projekcie Tispr, zespół i ja używamy split i nie zrzucamy wszystkiego w korzeniu, na wysokim poziomie tylko paleta i funkcjonalne kolory. Nie boimy się też IE11, ponieważ ten problem rozwiązuje odpowiedni polyfill. Wystarczy zainstalować moduł npm ie11-custom-properties i zaimportować bibliotekę do paczki JS aplikacji:
// Use ES6 syntax import "ie11-custom-properties"; // or CommonJS require('ie11-custom-properties');
Lub dodaj moduł za pomocą tagu skryptu:
<script async src="./node_modules/ie11-custom-properties/ie11CustomProperties.js">
Możesz także dodać bibliotekę bez npm przez CDN. Praca tego wypełniacza opiera się na fakcie, że IE11 ma minimalne wsparcie dla właściwości niestandardowych, gdzie właściwości można definiować i odczytywać na podstawie kaskady. Nie jest to możliwe w przypadku właściwości zaczynających się od podwójnych myślników, ale prawdopodobnie z pojedynczym myślnikiem (mechanizm podobny do przedrostków dostawcy). Więcej na ten temat można przeczytać w dokumentacji repozytorium, a także zapoznać się z pewnymi ograniczeniami. Inne przeglądarki zignorują to wypełnienie.
Poniżej znajduje się paleta aplikacji internetowej Tispr oraz elementy sterujące funkcjonalnością „białej etykiety” dla dokumentów elektronicznych (takich jak umowy użytkownika, faktury lub oferty).
Dlaczego nie przechowywać zmiennych kolorów po stronie JavaScript?
Kolejne rozsądne pytanie: dlaczego nie przechowywać zmiennych palety i funkcji w kodzie JavaScript? Można to również dynamicznie zmieniać, a później ponownie definiować kolory za pomocą stylów wbudowanych. Może to być opcja, ale najprawdopodobniej takie podejście byłoby mniej optymalne, ponieważ musisz mieć dostęp do niektórych elementów i zmieniać ich właściwości kolorystyczne. Za pomocą zmiennych CSS zmienisz tylko jedną właściwość, tj. wartość zmiennej.
W JavaScript nie ma natywnych funkcji ani API do pracy z kolorami. W module 5 CSS Color będzie wiele możliwości tworzenia kolorów pochodnych lub ich obliczania. Z perspektywy przyszłości niestandardowe właściwości CSS są bogatsze i bardziej elastyczne niż zmienne JS. Ponadto przy zmiennych JS nie będzie możliwości korzystania z dziedziczenia w kaskadzie i to jest główna wada.
Wniosek
Podział kolorów na trzy poziomy (paleta, funkcjonalność i komponent) może pomóc Ci lepiej dostosować się do zmian i nowych wymagań podczas pracy nad projektem. Uważam, że niestandardowe właściwości CSS są właściwym narzędziem do organizowania podziału kolorów — nie ma znaczenia, czego używasz do stylizacji: czysty CSS, preprocesory czy podejście CSS-w-JS.
Doszedłem do tego podejścia z własnego doświadczenia, ale nie jestem sam. Sara Soueidan opisała w swoim artykule podobne podejście, w którym dzieli zmienne na poziomy globalne i składowe.
Sugeruję również zapoznanie się z artykułem Lea Verou, w którym opisuje możliwe przypadki zastosowania zmiennych CSS (nie tylko pod względem koloru).