Kod kaskadowych arkuszy stylów CSS (ang. Cascading Style Sheets) też można optymalizować. W dużym stopniu decyduje o wydajności i płynności strony internetowej. W tym artykule podsumujemy techniki optymalizowania kaskadowych arkuszy stylów pod kątem wydajności i szybkości stron internetowych.

W artykule o krytycznej ścieżce renderowania i eliminacji blokujących renderowanie zasobów, mieliśmy punkt dotyczący kaskadowych arkuszy stylów. Optymalizując CSS dążymy do tego, aby etap zamiany DOM na CSSOM był wykonywany przy jak najmniejszej ilości operacji. Czasem naprawdę drobne zmiany mogą zrobić wielką różnicę i przyczynić się do eliminacji bardzo szkodliwych opóźnień.

Po co optymalizować CSS? Wydajny kod CSS spowoduje, że strona będzie działać szybko i płynnie a spełnienie zaleceń jakościowych mierzonych za pomocą WebVitals czyli podstawowych wskaźników internetowych rokują lepsze pozycje w wyszukiwarkach i ogólnie pojęte user experience.

Jak działają style CSS?

Wielokrotnie wspominałem, że aby móc coś optymalizować musimy zrozumieć zasadę działania i opracować jakiś sposób mierzenia stopnia osiągnięcia celu. Nie inaczej jest w przypadku kodu CSS.

Optymalizacja może polegać na kombinacji wielu strategii. Może to być

  • umiejętność nadania priorytetu różnym partiom kodu CSS,
  • strategia posługiwania się odpowiednimi selektorami,
  • pisanie „pod urządzenie” np. z wykorzystaniem zapytań medialnych,
  • umiejętność efektywnego przekształcenia zakładanego celu na zwięzły kod CSS,
  • selekcja odpowiednich instrukcji – np. substytutów rokujących szybszy czas wykonywania, które wizualnie wyglądają tak samo.

Przed studiowaniem przykładów zachęcam do przeczytania artykułu o krytycznej ścieżce renderowania. Zrozumienie tego jak przeglądarka zamienia kod HTML i CSS na cały „obrazek” strony, który widzimy w przeglądarce może być bardzo pomocne. Zoptymalizowane i „progresywne” renderowanie ma miejsce wtedy, kiedy najważniejsze elementy pojawiają się stosunkowo szybko. A mniej istotne elementy – np. położone niżej elementy i stylowanie generują się nieco później:

Wizualizacja ładowania się zoptymalizowanej strony

Niezoptymalizowane renderowanie objawia się tym, że strona przez pierwsze sekundy nie jest widoczna w przeglądarce by potem nagle się pojawić w całej okazałości – tego efektu chcemy unikać, bo pozornie wygląda to tak, jakby strona działała wolniej. Innymi słowy, użytkownik nie ma żadnej korzyści z tego, że jednocześnie załaduje się góra i odległy dół strony. Aby artykuł był w pełni zrozumiały, przypomnijmy sobie fachowe nazwy poszczególnych części instrukcji CSS.

.selektor {
właściwość: jakaś wartość;
właściwość2: jakaś wartość;
}

Wcześniej pojawiło się też pojęcie zapytania medialnego – są to instrukcje warunkowe, które decydują o tym czy w konkretnych okolicznościach zastosować daną partię kodu czy nie. np.:

@media print{
body{
background:#fff;
}
}

W powyższym przykładzie instrukcja ma się zastosować w przypadku wydruku strony internetowej.

Techniki optymalizacji kodu CSS

Niektóre, a szczególnie te łatwiejsze techniki były już przytaczane w przypadku innych artykułów. Podsumujmy wszystkie dobre praktyki i poznajmy ich zasadę działania aby mieć szerokie spojrzenie na temat optymalizacji CSS i dokonywać zawsze prawidłowych wyborów.

Usunięcie nadmiarowych instrukcji

Usuwanie nadmiarowych instrukcji jest czymś oczywistym co nie wymaga szczególnego omawiania. Technika ta jest na pierwszym miejscu, ponieważ jest to bolączka wszystkich stron internetowych, które mają problem z wydajnością.

Czym są nadmiarowe instrukcje CSS? Nadmiarowe (redundantne) reguły CSS to nieużywane, lub powtórzone wyrażenia w kodzie które w żaden sposób nie wpływają na układ i malowanie strony.

Skąd się biorą nadmiarowe instrukcje CSS? Ich powstawanie wynika najczęściej z trzech sytuacji:

  • korzystania z frameworków i gotowych stylów CSS,
  • korzystania z gotowych szablonów,
  • złych praktyk, np. dostosowywaniem istniejącego stylu poprzez nadpisywanie instrukcji a nie edycją pierwotnego kodu,
  • złej organizacji pisania kodu – zduplikowane instrukcje pojawiają się po prostu przypadkowo.

Rozważmy taki kod:

h1{
color:red;
}
h1{
color:green;
}

Na jaki kolor zostanie pokolorowany na główek pierwszego stopnia? A no na zielony. Pierwsze trzy linie kodu to instrukcje nadmiarowe. Nie wpływają na wykonywanie się kodu. Zajmują cenny transfer, który jest marnowany przy każdorazowej wizycie i marnują czas jaki przeglądarka przeznacza na interpretację kodu.

Nadmiarową instrukcją jest też jakikolwiek selektor, który nie znajdzie w drzewie DOM odpowiadającemu mu elementu. Jeżeli nie używamy na stronie formularzy takie instrukcje jak:

form input[type=text]{
padding:4px 8px;
}

nie mają jakiegokolwiek sensu. Jeżeli formularz znajduje się na jednej podstronie „kontakt” fragment ten można spokojnie dodać ekskluzywnie na tej podstronie w postaci kodu in-line lub jako osobny plik css z dodatkowymi instrukcjami. Choć wydaje się to oczywiste, i łątwe do zastosowania, jest to jedna z najrzadziej przestrzeganych praktyk w tworzeniu front-endu.

Korzystanie z gotowych szablonów czy frameworków naraża nas na sporą ilość nadmiarowych instrukcji, ponieważ rzadko kiedy wykorzystuje się 100% szablonu. Na szczęście w przeglądarce Chrome istnieje możliwość podglądu nieużywanych instrukcji CSS – dzięki czemu fragmenty takie można łatwo namierzyć i usunąć:

Nieużywane instrukcje CSS w analizie Coverage

Kod zaznaczony na czerwono można spokojnie usunąć, jeżeli przeglądaliśmy stronę w każdym możliwym punkcie granicznym. Czytaj więcej jak włączyć Coverage i odszukać wszystkie nie używane fragmenty CSS.

Umieszczenie krytycznego kodu CSS w HTML

Krytyczny kod CSS to taki, który jest potrzebny do poprawnego pokazania pierwszego ekranu strony internetowej (tzw. fragmentu above the fold). Kod taki najlepiej jest dodać in-line czyli w tagach <style> bezpośrednio do nagłówka strony internetowej:

<style>
   body{
      background:#f8f8f8;
   }
</style>

Istnieją usługi (taki jak np. Critical CSS), które automatyzują wyodrębnianie krytycznego CSS i umieszczanie ich na poszczególnych podstronach. W praktyce narzędzia te mogą pomijać istotne instrukcje, co często wymaga wykonania ręcznych poprawek. Zobacz też:

Istnieje moduł npm i jeżeli korzystasz z Gulpa, Grunta lub webpacka, przejdź na stronę: https://web.dev/extract-critical-css/ po więcej instrukcji.

Opóźnienie ładowania dodatkowych plików CSS

Kiedy już umieścimy krytyczny kod CSS w nagłówku strony, możemy opóźnić ładowanie pozostałych stylów które odnoszą się do elementów położonych niżej. Technika, która często przewija się w instrukcjach i poradnikach to:

 <link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>

Link z odwołaniem do pliku z dodatkowymi regułami CSS staje się arkuszem stylów dopiero po zdarzeniu onload, czyli po załadowaniu strony internetowej.

Separacja stylów według zapytań medialnych

W zapytaniach medialnych możemy przypisywać style CSS do konkretnego rozmiaru okna przeglądarki, typu urządzenia a nawet obrotu ekranu.

Plik z całym kodem CSS:

body{
   font-size: 16px;
}
@media screen and (min-width: 1200px){
   .cta{
      display: block;
   }
}
@media print{
   body{
      background:white;
   }
}

Taki kod można podzielić na pliki, które będą zawierać style tylko dla danego przypadku. Cały blok wewnątrz pierwszego zapytania medialnego można umieścić w osobnym pliku np. style-desktop.css i umieścić na stronie w ten sposób:

<link rel="stylesheet" href="styles-desktop.css" media="(min-width: 1200px)">

Czy to coś da? Oczywiście, że tak. Plik ze stylami dotyczącymi widoku desktop będzie ignorowany na urządzeniach mobilnych i przez to nie będzie powodował blokowania renderowania i nie będzie marnowania transferu.

Wykorzystywanie efektywnych selektorów

Często równoważne selektory charakteryzują się różną wydajnością. Unikalnym elementom na stronie powinno nadawać się unikalne id (a nie klasy) okazuje się, że #selektor jest szybszy od .selektora.

W związku z tym bardzo nieintuicyjna jest kolejna zasada. Okazuje się, że dłużesze i mniej ogólne selektory, np:

div p {
font-size:16px
}

są wolniejsze od prostszych i bardziej ogólnych, np:

h1 {
font-size:32px
}

Pierwszą reguła mówi przeglądarce: jeżeli natrafisz na element h1 ustaw wielkość tekstu na 32px – i na tym koniec. Natomiast druga reguła mówi coś o wiele bardziej skomplikowanego: wybierz wszystkie elementy <p>, a kiedy je znajdziesz przeanalizuj drzewo DOM w górę i sprawdź czy rodzicem tego paragrafu jest element <div>. Jeżeli tak, zastosuj styl a jeżeli nie pomiń….

Pomijając fakt, że elementów p jest zazwyczaj o wiele więcej niż elementów div, to w wyniku potrzeby analizowania zależności pomiędzy elementami cały proces jest o wiele bardziej skomplikowany. Bardziej skomplikowane selektory są wolniejsze bo muszą przeanalizować więcej węzłów w drzewie DOM. Z tego wynika prosta zasada, aby używać prostych i niezagnieżdżonych selektorów.

Unikanie @import

To fakt, że @import spowalnia działanie stylów CSS. Do załadowania krytycznego kodu potrzeba wykonać dodatkowe zapytanie HTTP i stąd biorą się niepotrzebne opóźnienia.

@impot url("main.css");
@import url("footer.css");
@import url("slider.css");

Jak zaradzić w przypadku kiedy w stylu istnieją instrukcje @import? Nic nie stoi na przeszkodzie by takie pliki dodać w odpowiedniej kolejności w kodzie HTML:

<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="main.css">
<link rel="stylesheet" href="footer.css">
<link rel="stylesheet" href="slider.css">

Działa to o wiele lepiej, unikamy w ten sposób łańcucha żądań krytycznych, bo można wykorzystać inne metody optymalizacji (np. opóźnienie ładowania) a w przypadku HTTP/2 pozwala na współbieżne pobieranie plików co jest niemożliwe w przypadku @import.

Podzielenie stylów na fragmenty

Choć powinno unikać się wielokrotnych zapytań HTTP, całkiem racjonalnym posunięciem jest podzielenie plików na mniejsze fragmenty by potrzebne „moduły” ładować tylko tam gdzie trzeba. W przypadku kiedy slajder jest wykorzystywany na stronie głównej plik slider.css ładujemy tylko na niej a te odpowiedzialne za formularze – na stronie kontaktowej.

Przykładowo, na stronie głównej ładujemy ogólny styl strony ale także ten odpowiedzialny za slajder czy animacje:

<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="slider.css">
<link rel="stylesheet" href="animate.css">

Kiedy formularz kontaktowy jest tylko na stronie kontakt, można załadować podstawowy styl i tylko tutaj dodatkowy plik ze stylami formularza kontaktowego:

<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="contact-form.css">

Na pozostałych stronach np. z ofertą czy blogiem, można załadować tylko styl ogólny.

<link rel="stylesheet" href="styles.css">

To spowoduje odciążenie podstron które są najczęściej przeglądane a wykorzystywany w jednym miejscu dodatek nie pogarsza wydajności wszystkich podstron. Tak jak przy wszystkich punktach tym bardziej tutaj istotny jest zdrowy rozsądek aby nie doprowadzić do zniweczenia potencjału pamięci podręcznej. Zbytnie rozdrobnienie szkodzi prędkości i utrudnia rozwijanie strony.

Umiar w stosowaniu kosztownych właściwości

Niektóre instrukcje CSS obciążają silnik przeglądarki bardziej niż inne. Ranking najbardziej obciążających procesor instrukcji to:

  • border-radius,
  • box-shadow,
  • opacity,
  • transform,
  • filter,
  • position:fixed

Na Twojej stronie nadal mogą być te efekty, miej jednak świadomość ich kosztowności podczas tworzenia skomplikowanych efektów.

Skrócone deklaracje CSS

Wiele popularnych i często wykorzystywanych właściwości można skrócić do bardziej zwięzłej postaci przykładowo, zamiast:

.element{
padding-top:8px;
padding-botom:12px;
padding-left:4px;
padding-right:4px;
}

Można poprostu napisać:

.element{
padding: 8px, 4px 12px 4px;
}

Wszystko co dotyczy tła:

.container{ 
   background-color: #aaa;
   background-image: url(images/background.png);
   background-repeat: no-repeat;
   background-position: top right;
}

też można sprytnie zamknąć w jednej linii:

.container{
   background: #aaa url(images/background.png) no-repeat top right;
}

Fajne skróty można stosować w przypadku własności font, border i marginesów:

https://developer.mozilla.org/pl/docs/Web/CSS/Skr%C3%B3cone_deklaracje_CSS

Minifikacja (minimalizacja) CSS

Minifikacja CSS to usunięcie specyficznych znaków w kodzie CSS, które są dodane dla czytelności kodu i nie mają wpływu na interpretację. Chodzi zazwyczaj o 

  • komentarze,
  • znaki nowej linii,
  • spacje
  • i tabulatory.

Teoretycznie, dałoby się odchudzać kod jeszcze o kolejne znaki. Np skracać nazwy klas i identyfiktorów. Choć technika ta jest charakterystyczna dla skryptów JavaScript – w praktyce nie stosuje się tego przy CSS ponieważ logika i semantyczność nazw klas i atrybutów w kodzie HTML są o wiele bardziej istotne.

Minimalizacja kodu to bardzo prosta operacja i można ją z łatwością automatyzować za pomocą własnych narzędzi, skryptów, modułów po stronie serwera np. moduł PageSpeed lub w ostateczności narzędzi online:

Kompresja GZIP zasobów tekstowych

Kompresja GZIP umożliwia skompresować pliki CSS dokładnie tak jak robimy to w przypadku kompresowania archiwów ZIP. Efektywność takiej kompresji jest ogromna i umożliwia zaoszczędzić ok 70% rozmiaru potrzebnego na transfer.

Kompresja dzieje się automatycznie po stronie serwera a przeglądarka po pobraniu plików szybko je dekompresuje. Taka kompresja lekko obciąża serwer i przeglądarkę ale złożoność obliczeniowa pakowania i rozpakowywania jest na tyle mała, że opłaca się te operacje wykonać zaoszczędzając dużo cenniejszy transfer.

Moduł deflate lub GZIP powinien być domyślnie zainstalowany i włączony. Jeżeli nie, można wywołać komendę. Sprawdź czy pliki tekstowe na twojej stronie są kompresowane – to można sprawdzić w narzędziu https://varvy.com/tools/gzip/. Jeżlei nie, zapoznaj się z artykułem jak włączyć kompresję tekstu GZIP / DEFLATE dla zasobów strony internetowej. Pokazuję jak zainstalować i włączyć GZIP lub DEFLATE w różnych przypadkach.

Pamieć podręczna przeglądarki

Kiedy przeglądarka pobierze raz plik CSS, może być on zapisany na dysku twardym aby przy następnej wizycie lub odsłonie kolejnej podstrony tej samej witryny nie musiała tego robić ponownie. Tak jak w przypadku kompresji, ustawienia serwera decydują o tym jak długo przeglądarka ma nawet nie sprawdzać czy plik uległ modyfikacji.

Za pomocą narzędzi GTMetrix i PageSpeed Insights można sprawdzić czy serwer wykorzystuje pamieć podręczną i wspomniane wcześniej kompresję:

Nie zapomnij o szybkim serwerze lub CDN

Szybki serwer zapewnia natychmiastowe wysyłanie pliku po odebraniu żądania klienta o ten plik. Tylko sprawdzony i zoptymalizowany serwer z popularnym oprogramowaniem serwera (Apache lub nginx) pozwoli nam zastosować sprawdzone rozwiązania i uzyskać idealne czasy ładowania się strony. Jeżeli chcesz się dowiedzieć więcej na temat szybkich serwerów czytaj:

Pliki CSS rzadko ulegają modyfikacjom, to też świetnie się nadają do umieszczenia w CDN. CDN radykalnie przyspiesza pobieranie się jakichkolwiek plików, ponieważ jest to infrastruktura, budowana od podstaw, której jedynym celem jest błyskawiczna odpowiedź na żądanie i  dostarczenie plików zapewniając szybki transfer.

Czytaj więcej o CDN, co to jest, na co zwrócić uwagę i czy w Twoim przypadku warto się tym zainteresować.

Mity dotyczące optymalizacji CSS

Istnieje kilka fałszywych zasad optymalizowania, w przypadku których warto wiedzieć z wyprzedzeniem że są własnie takie.

Unikanie stosowania !important – o ile dobrą praktyką jest unikanie instrukcji !important (bo ułatwia rozwijanie kodu) to nie ma negatywnego wpływu na wydajność lub jest on pomijalny. To racja, że wyrażenie !important jest zabronione na stronach AMP.

Podsumowanie

Pliki CSS odgrywają istotną rolę w krytycznej ścieżce renderowania. Jakość kodu CSS determinuje czy strona będzie działać szybko i spełniać najnowsze standardy tworzenia strony w oparciu o podstawowe wskaźniki internetowej WebVitals. Czytaj więcej jak wyeliminować inne zasoby blokujące renderowanie i co to jest krytyczna ścieżka renderowania w osobnym artykule.

Źródła

https://web.dev/extract-critical-css/

Oceń artykuł na temat: Optymalizacja stylów CSS
Średnia : 4.9 , Maksymalnie : 5 , Głosów : 15