CSS Nesting natywnie w przeglądarce 👨👩👧👦
Dlaczego coś znanego developerom w zasadzie od lat, może być zwiastunem przełomowych zmian w technologiach webowych?
Wstęp ⚙️
W dniu 29.07.2023 Firefox w wersji 117 wprowadził wparcie dla natywnego zagdnieżdżania selektorów w CSS, wypełniając tym brakującą lukę obok Chrome i Safari. Czy zatem możemy już zacząć mówić o początku końca ery preprocesorów?
Z powodu ograniczeń w języku CSS powstały tzn. preprocesory, które pozwalają na pisanie kodu w składni przypominającym język CSS (wprowadzając dodatkowe możliwości), a następnie jego kompilację go do zwykłego CSS-a, który jest interpretowany przez przeglądarkę, w przeciwieństwie do kodu źródłowego pisanego w składni danego preprocesora.
Zagnieżdzanie selektorów w preprocesorach dostępne było już od dawna, podobnie jak w technologiach typu CSS-in-JS, jednak po raz pierwszy staje się to dostępne w CSS-ie natywnie, co za tym idzie - bezpośrednio w przeglądarkach.
Składnia zagnieżdżania selektorów 📝
Składnia zagnieżdżania w CSS jest analogiczna jak w preprocesorach takich jak Scss. Zamiast używać zapisu:
Możemy użyć składni:
Oczywiście elementy możemy zagnieżdżać wielokrotnie. Przkład:
Również bezproblemowo działa zagnieżdżanie media queries czy container queries. Przykład:
Wsparcie przeglądarek 🌐
Wsparcie dla CSS nesting można dynamicznie sprawdzać za pomocą reguły @supports
w CSS lub za pomocą querySelector
w JS-ie, opierając się na dostępności selectora &
:
Za pomocą komponentu poniżej, możesz sprawdzić czy używana przez Ciebie przeglądarka wspiera już zagnieżdżanie selektorów:
❌ Twoje przeglądarka nie wspiera jeszcze tej funkcjonalności!
ℹ️ Sprawdź wsparcie: caniuse.com/css-nesting
Różnice względem dziedziczenia w preprocesorach 🤔
Jest kilka różnić, które warto mieć na uwadze. Pierwsza istotna różnica polega na tym, że jeśli selector nie zaczyna się od jednego z tych znaków: & @ : . > ~ + # [ *
(np. article
, p
img
czy każdy inny tzw. Type selector) to selektor ten należy poprzedzić znakiem &
:
Wynika to z faktu, że mocno obciążyło by to przeglądarkę, gdyby za każdym razem musiała rozwiązywać problem polegający na tym, aby rozróżnić, czy ma do czynienia z selektorem czy z właściwością CSS. Przykład:
Jednak istnieje spore prawdopodonieństwo, że w kolejnych wersjach ograniczenie to zostanie wyeliminowane.
Druga istotna różnica to brak możliwości stosowania konkatenacji z selektorem rodzica, który jest np. bardzo często wykorzystywany na potrzeby metodologii BEM. Przykład:
Kod Scss jest kompilowany i w trakcie kompilacji zapis &__link
zamieniany jest na .navigation__link
. W natwynym CSS-ie nie ma takiej możliwości, więc musimy użyć pełnej nazwy selektora rodzica.
Warto również wiedzieć, że specyficzność selektorów może się różnić przy takim samym zapisie w przypadku Scss oraz CSS. Przykład:
Dlaczego tak się dzieje? Po wyjaśnienie specyficzności pseudoselektora :is()
odsyłam do MDN docs.
Wskazówki dotyczące zagnieżdżania selektorów 💡
Zbyt głębokie selektory
Warto uważać na zbyt złożone selektory, szczególnie łatwo się na tym złapać podczas wykorzystywania zagnieżdżania selektorów. Wpływa to po pierwsze na czytelność kodu, ale co ważniejsze negatywnie odbija się również na wydajności parsowania stylów przez przeglądarkę podczas fazy Style computation w procesie renderowania widoku. Warto ograniczać się do 3, 4 poziomów (zakładając wyjątki w razie potrzeby), jednak w pierwszej kolejności warto rozważyć dodanie abstrakcji w postaci klasy, w celu uproszczenia kodu.
Chaotyczna kolejność
O ile nie nadpisujemy wartości, to kolejność selektorów nie ma większego znaczenia, jednak warto przyjąć i starać się przestrzegać wybranej przez siebie konwencji. Ma to znaczenie zwłaszcza w większych projektach, które posiadają wiele plików ze stylami, nad którymi pracuje wielu programistów. Proponowana prze ze mnie kolejność:
- Właściwości dla obecnego selektora
- Zagnieżdżone elementy
- Zagnieżdżone media queries w kolejności mobile first
Czy technologie webowe zataczają koło? 🔄
10 lat temu podstawowy wachlarz technologii webowych (HTML, CSS i JS) i ich ówczesne możliwości były zbyt prymitywne jak na wyzwania, które stały przed aplikacjami webowymi. Z tego powodu powstało wiele technologii, które:
- Rozszerzały możliwości tych języków, nadbudowując je (np. Sass, TypeScript).
- Pozwałały skompilować całość do języka zrozumiałego przez przeglądarkę (np. Webpack, Babel).
Ogromne możliwości, które otworzyły się przed developerami sprawiły, że łatwo było się tymi możliwościami zachłysnąć. Dobór zbyt wielu technologi, skomplikowany proces kompilacji oparty na wielu narzędziach i konfiguracjach, które były trudne do zrozumienia, niejednokrotnie sprawiały, że utrzymywanie takich aplikacji stawało się bardzo problematyczne, a próg wejścia dla nowych osób był bardzo wysoki.
Jednak od tamtego czasu języki natywnie dostępne w przeglądarce zaliczyły spory progres. Natywnie dostępne zagnieżdżanie selektorów w czystym CSS jest jednym z wielu przykładów, obok CSS Custom Properties albo ESModules czy WebComponents dla JS-a. Do tego w ostatnim czasie na popularności zyskują też technologie, które niejako próbują łączyć i wykorzystać korzyści obu tych światów (Tailwind CSS czy Vite).
Tworzenie stylów dla większych aplikacji bez Scss albo CSS-in-JS, podobnie jak organizowanie logiki kodu bez TypeScript-a czy Webpacka, na chwilę obecną wydaje się być wręcz niemożliwe. Mam jednak wrażenie, że odległość pomiędzy szeroko rozumianymi technologiami opartymi na kompilacji a technologiami natywnymi z każdym rokiem się zmniejsza. Czy zatoczymy koło i w przyszłości będziemy znów pisać aplikacje webowe bez użycia preprocesorów i kompilatorów? Czas pokaże.