Logo David Burdelak
Webdevelopment

Refaktoryzacja CSS – jak usunąć 80% nieużywanego kodu i przyspieszyć renderowanie (FCP)

Refaktoryzacja CSS i optymalizacja FCP

Puchnące arkusze stylów to cichy zabójca wydajności front-endu. Gdy podczas audytu jednego z moich projektów zobaczyłem, że przeglądarka pobiera setki kilobajtów kodu, z czego większość nie ma żadnego wpływu na wygląd bieżącej strony, uznałem, że czas na radykalne kroki. W tym artykule pokażę Ci, jak krok po kroku przeprowadziłem pełną refaktoryzację architektury stylów, pozbywając się blisko 80% nieużywanego kodu i drastycznie poprawiając wskaźnik First Contentful Paint (FCP).

Zamiast opierać się na teorii, opowiem o konkretnych narzędziach, mechanizmach automatyzacji oraz decyzjach projektowych, które wdrożyłem, aby zapanować nad chaosem w plikach `.css`. Jeśli Twój projekt rozwija się od miesięcy lub lat, ten proces pozwoli Ci odzyskać pełną kontrolę nad kodem.

Dlaczego nieużywany CSS to poważny problem dla FCP?

Zanim przeszedłem do czyszczenia kodu, musiałem precyzyjnie zdiagnozować, dlaczego obecna struktura tak negatywnie wpływa na metryki Core Web Vitals. Przeglądarka traktuje pliki CSS jako zasoby blokujące renderowanie. Oznacza to, że zanim na ekranie użytkownika pojawi się pierwszy piksel tekstu czy grafiki (czyli wspomniany First Contentful Paint), silnik przeglądarki musi pobrać cały plik CSS, przeanalizować działanie strony internetowej w przeglądarce i zbudować drzewo CSSOM (CSS Object Model).

W moim przypadku aplikacja ładowała gigantyczny, monolityczny plik ze stylami dla wszystkich podstron jednocześnie. Efekt? Użytkownik wchodzący na prostą stronę lądowania musiał czekać na przetworzenie reguł obsługujących zaawansowane panele administracyjne, wykresy i ukryte modale. To prosta droga do zniecierpliwienia użytkownika i obniżenia pozycji w Google.

Jak namierzyłem zbędny kod? Moja metodologia

Prace zacząłem od zebrania twardych danych. Nie chciałem usuwać kodu „na oko”, aby przypadkowo nie uszkodzić widoku na innych podstronach. Wykorzystałem do tego trzy sprawdzone kroki:

  • Karta Coverage w Chrome DevTools: To było moje główne narzędzie diagnostyczne. Uruchomiłem nagrywanie pokrycia kodu podczas interakcji ze stroną. Wynik był zatrważający – czerwony pasek wskazał, że aż 82% pobieranego kodu CSS nie zostało w ogóle zainicjowane przez przeglądarkę na kluczowych ścieżkach użytkownika.
  • Audyt Lighthouse: Narzędzie, które na co dzień uzupełniają u mnie zaawansowane narzędzia SEO, jasno wskazało mi rekomendację: „Remove unused CSS”, szacując potencjalne przyspieszenie ładowania strony o ponad 1.2 sekundy w sieci mobilnej 4G.
  • Analiza zależności w repozytorium: Przejrzałem historię commitów i zauważyłem, że w projekcie pozostało mnóstwo stylów po niedokończonych testach A/B oraz starych komponentach, które dawno zostały usunięte z plików HTML/JavaScript.

Moja strategia refaktoryzacji: Od monolitu do modularności

Wiedząc, z czym się mierzysz, zaplanowałem proces zmian tak, aby trwale wyeliminować problem, a nie tylko „pomalować ściany”. Podzieliłem działania na trzy główne etapy, które całkowicie odmieniły architekturę CSS w projekcie.

1. Automatyczne usuwanie martwego kodu za pomocą PurgeCSS

Ręczne przeszukiwanie tysięcy linii kodu byłoby syzyfową pracą. Wdrożyłem do potoku budowania aplikacji (build pipeline) narzędzie PurgeCSS. Zasada jego działania jest prosta: skanuje ono pliki źródłowe (HTML, komponenty React/Vue, szablony) i porównuje je z selektorami w plikach CSS. Jeśli dany selektor nie występuje w strukturze DOM, zostaje bezwzględnie usunięty z finalnego bundla.

Musiałem jednak uważać na klasy generowane dynamicznie przez JavaScript. Aby system ich nie wyciął, skonfigurowałem tzw. safelist (białą listę), na której umieściłem prefiksy klas odpowiedzialnych za stany takie jak .is-active, .has-error czy .fade-in.

2. Wdrożenie techniki Critical CSS

Samo uszczuplenie plików to za mało. Aby FCP wynosiło ułamek sekundy, podzieliłem style na dwa strumienie:

  • Style krytyczne (Critical CSS): Wyodrębniłem reguły odpowiedzialne wyłącznie za sekcję above-the-fold (czyli to, co użytkownik widzi na ekranie od razu po wejściu, bez przewijania – menu, hero section, główny nagłówek). Te style wstrzyknąłem bezpośrednio do sekcji <head> dokumentu HTML w znaczniku <style>. Dzięki temu przeglądarka renderuje bazowy wygląd natychmiast, bez czekania na pliki zewnętrzne.
  • Style niekrytyczne: Pozostałą część stylów (stopka, ukryte elementy, sekcje dolne) przeniosłem do osobnego pliku, który ładuje się asynchronicznie za pomocą prostego triku ze zmianą atrybutu media.

Tak wygląda wdrożona przeze mnie asynchroniczność:

<!-- Style krytyczne są już w head -->
<!-- Style niekrytyczne ładują się w tle -->
<link rel="stylesheet" href="non-critical.css" media="print" onload="this.media='all'">

3. Przejście na architekturę CSS Modules i rezygnacja z BEM

Aby problem globalnego, nadmiarowego CSS nigdy nie powrócił, zdecydowałem się na zmianę podejścia metodologicznego. Porzuciłem klasyczny system oparty na długich nazwach klas (BEM) na rzecz CSS Modules. Dzięki temu każdy komponent ma swój własny, odizolowany plik ze stylami. Styl jest importowany i kompilowany tylko wtedy, gdy dany komponent faktycznie pojawia się na ekranie. Zapobiega to powstawaniu efektu „wspólnego śmietnika”, w którym nikt nie wie, do czego służy dana klasa.

Rezultaty: Liczby, które mówią same za siebie

Efekty refaktoryzacji przerosły moje początkowe założenia. Zmiany nie tylko ułatwiły mi codzienną pracę z kodem, ale przełożyły się bezpośrednio na odczuwalną szybkość działania aplikacji.

Waga głównego pliku CSS spadła z początkowych 240 KB do zaledwie 38 KB (ponad 84% redukcji!). Przełożyło się to na natychmiastowy skok wydajnościowy w testach laboratoryjnych i terenowych:

  • First Contentful Paint (FCP): Skrócenie czasu z 2.4 sekundy do 0.6 sekundy na urządzeniach mobilnych.
  • Lighthouse Performance Score: Wzrost oceny z 61/100 na stabilne 96/100.
  • Total Blocking Time (TBT): Spadek o niemal 400 ms, dzięki mniejszemu obciążeniu procesora podczas parsowania drzewa stylów.

Moja uniwersalna Checklista Refaktoryzacji CSS

Jeśli planujesz podobne porządki w swoim projekcie, przygotowałem dla Ciebie skróconą checklistę kroków, które sam wykonałem. Pozwoli Ci ona przeprowadzić ten proces bezpiecznie i efektywnie.

Faza Diagnostyki i Przygotowania

  • Uruchomienie narzędzia Coverage: Przejdź przez kluczowe podstrony serwisu z włączoną kartą Coverage w Chrome DevTools i zapisz procent nieużywanego kodu.
  • Zabezpieczenie testów regresyjnych: Przed usunięciem jakiejkolwiek linii kodu zrób screenshoty kluczowych widoków (np. za pomocą narzędzi do regresji wizualnej, jak BackstopJS), aby móc porównać stan "przed" i "po".
  • Identyfikacja dynamicznych klas: Zrób listę klas CSS dodawanych przez skrypty JS (np. podczas walidacji formularzy, otwierania menu hamburgera).

Faza Czyszczenia i Automatyzacji

  • Wdrożenie PurgeCSS / UnCSS: Zintegruj narzędzie automatyczne z Webpackiem, Vite lub Gulpem i odpowiednio ustaw reguły wykluczeń (safelist).
  • Wydzielenie Critical CSS: Wyodrębnij style dla obszaru nad krawędzią ekranu (above-the-fold) i umieść je inline w dokumencie HTML.
  • Asynchroniczne ładowanie reszty stylów: Przepnij pozostałe arkusze CSS tak, aby nie blokowały renderowania strony podczas wczytywania drzewa DOM.

Faza Utrzymania Jakości

  • Wdrożenie Stylelinta: Dodaj linter do swojego edytora i procesu CI/CD, aby automatycznie wykrywał duplikaty reguł, puste bloki oraz błędy składniowe.
  • Izolacja stylów: W nowych funkcjonalnościach zacznij stosować CSS Modules, Scoped CSS lub biblioteki CSS-in-JS, aby zablokować powstawanie globalnego długu technologicznego.
  • Weryfikacja wskaźników: Ponownie uruchom testy Lighthouse i porównaj realne wyniki FCP na urządzeniach mobilnych.

Refaktoryzacja CSS bywa odkładana na później, ponieważ rzadko przynosi nowe funkcje widoczne dla biznesu na pierwszy rzut oka. Jednak drastyczne skrócenie czasu FCP i lekkość, z jaką aplikacja działa na słabszych smartfonach, to bezpośredni zysk w postaci wyższej konwersji i lepszych opinii użytkowników. Czysty kod to szybki kod.