Zagnieżdżanie CSS

Jedną z naszych ulubionych funkcji wstępnego przetwarzania danych CSS jest teraz wbudowana w język: zagnieżdżanie reguł stylu.

Adam Argyle
Adam Argyle

Przed zagnieżchnięciem każdy selektor musiał być zadeklarowany osobno. Prowadzi to do powtarzania się elementów, nadmiaru plików czcionki i rozproszonego procesu tworzenia.

Przed
.nesting {
  color: hotpink;
}

.nesting > .is {
  color: rebeccapurple;
}

.nesting > .is > .awesome {
  color: deeppink;
}

Po zagnieżdżeniu selektory można kontynuować, a powiązane reguły stylów można grupować.

Po
.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

Wypróbuj to w przeglądarce

Zagnieżdżanie ułatwia deweloperom pracę, ponieważ zmniejsza potrzebę powtarzania selektorów, a także współlokalizuje reguły stylów dla powiązanych elementów. Może też pomóc w dopasowaniu stylów do kodu HTML, na który są one kierowane. Jeśli element .nesting został usunięty z projektu, możesz usunąć całą grupę, zamiast szukać w plikach powiązanych selektorów.

Umieszczanie w grupach może być przydatne w tych sytuacjach:

Zagnieżdżanie jest dostępne w wersji Chrome 112, a także w wersji technicznej Safari 162.

Wprowadzenie do zagnieżdżania w CSS

W dalszej części tego artykułu, aby ułatwić Ci wizualizację wyborów, użyjemy następującego środowiska demonstracyjnego. W tym domyślnym stanie nic nie jest wybrane, a wszystko jest widoczne. Wybierając różne kształty i rozmiary, możesz ćwiczyć składnię i sprawdzać, jak działa.

Kolorowa siatka małych i dużych kół, trójkątów i kwadratów.

W piaskownicy znajdują się koła, trójkąty i kwadraty. Niektóre są małe, średnie lub duże. Inne są niebieskie, różowe lub fioletowe. Wszystkie znajdują się wewnątrz elementu .demo. Poniżej znajduje się podgląd elementów HTML, które będą podlegać kierowaniu.

<div class="demo">
  <div class="sm triangle pink"></div>
  <div class="sm triangle blue"></div>
  <div class="square blue"></div>
  <div class="sm square pink"></div>
  <div class="sm square blue"></div>
  <div class="circle pink"></div>
  …
</div>

Przykłady zagnieżdżania

Zagnieżdżanie CSS umożliwia definiowanie stylów dla elementu w kontekście innego selektora.

.parent {
  color: blue;

  .child {
    color: red;
  }
}

W tym przykładzie selektor klasy .child jest zagnieżdżony w selektorze klasy .parent. Oznacza to, że zanurzony selektor .child będzie miał zastosowanie tylko do elementów, które są podrzędnymi elementów z klasą .parent.

Ten przykład można też zapisać za pomocą symbolu &, aby wyraźnie wskazać, gdzie należy umieścić klasę nadrzędną.

.parent {
  color: blue;

  & .child {
    color: red;
  }
}

Oba te przykłady są funkcjonalnie równoważne, a przyczyna, dla której masz te opcje, stanie się jaśniejsza, gdy omówimy bardziej zaawansowane przykłady w tym artykule.

Zaznaczanie kręgów

W tym pierwszym przykładzie zadaniem jest dodanie stylów, aby rozmyć i przyciemnić tylko koła w ramach tej prezentacji.

Bez zagnieżdżania, CSS obecnie:

.demo .circle {
  opacity: .25;
  filter: blur(25px);
}

W przypadku zagnieżdżania dostępne są 2 sposoby:

/* & is explicitly placed in front of .circle */
.demo {
  & .circle {
    opacity: .25;
    filter: blur(25px);
  }
}

lub

/* & + " " space is added for you */
.demo {
  .circle {
    opacity: .25;
    filter: blur(25px);
  }
}

Wynik: wszystkie elementy wewnątrz elementu .demo z klasą .circle są rozmyte i niemal niewidoczne:

Kolorowa siatka kształtów nie zawiera już okręgów,
    są one bardzo słabo widoczne w tle.
Wypróbuj demo

Zaznaczanie trójkątów i kwadratów

To zadanie wymaga wybrania wielu elementów zagnieżdżonych, zwanych też selektorem grupy.

Bez zagnieżdżania, czyli w obecnej wersji usługi porównywania cen, są 2 sposoby:

.demo .triangle,
.demo .square {
  opacity: .25;
  filter: blur(25px);
}

lub :is()

/* grouped with :is() */
.demo :is(.triangle, .square) {
  opacity: .25;
  filter: blur(25px);
}

W przypadku zagnieżdżania dostępne są 2 prawidłowe sposoby:

.demo {
  & .triangle,
  & .square {
    opacity: .25;
    filter: blur(25px);
  }
}

lub

.demo {
  .triangle, .square {
    opacity: .25;
    filter: blur(25px);
  }
}

Wynik: w sekcji .demo pozostają tylko elementy .circle:

Na kolorowej siatce kształtów pozostały tylko okręgi,
    wszystkie pozostałe kształty są niemal niewidoczne.
Wypróbuj demo

Wybieranie dużych trójkątów i kół

To zadanie wymaga selektora złożonego, w którym elementy muszą mieć obie klasy, aby mogły zostać wybrane.

Bez zagnieżdżania CSS:

.demo .lg.triangle,
.demo .lg.square {
  opacity: .25;
  filter: blur(25px);
}

lub

.demo .lg:is(.triangle, .circle) {
  opacity: .25;
  filter: blur(25px);
}

W przypadku zagnieżdżania dostępne są 2 prawidłowe sposoby:

.demo {
  .lg.triangle,
  .lg.circle {
    opacity: .25;
    filter: blur(25px);
  }
}

lub

.demo {
  .lg {
    &.triangle,
    &.circle {
      opacity: .25;
      filter: blur(25px);
    }
  }
}

Wynik: wszystkie duże trójkąty i koła są ukryte w .demo:

Na kolorowej siatce widoczne są tylko małe i średnie kształty.
Wypróbuj wersję demonstracyjną
Wskazówka dotycząca selektorów złożonych i zagnieżdżania

Symbol & jest Twoim sprzymierzeńcem, ponieważ pokazuje, jak dołączać wbudowane selektory. Na przykład:

.demo {
  .lg {
    .triangle,
    .circle {
      opacity: .25;
      filter: blur(25px);
    }
  }
}

Jest to prawidłowy sposób zagnieżdżania, ale wyniki nie będą odpowiadać elementom, których oczekujesz. Powodem jest to, że jeśli za pomocą funkcji & nie określisz oczekiwanego wyniku połączenia .lg.triangle, .lg.circle, rzeczywisty wynik będzie miał postać .lg .triangle, .lg .circle; selektory podrzędne.

Zaznaczanie wszystkich kształtów oprócz różowych

To zadanie wymaga pseudoklasy funkcjonalnej negacji, w której elementy nie mogą mieć określonego selektora.

Bez zagnieżdżania, CSS obecnie:

.demo :not(.pink) {
  opacity: .25;
  filter: blur(25px);
}

W przypadku zagnieżdżania dostępne są 2 prawidłowe sposoby:

.demo {
  :not(.pink) {
    opacity: .25;
    filter: blur(25px);
  }
}

lub

.demo {
  & :not(.pink) {
    opacity: .25;
    filter: blur(25px);
  }
}

Wynik: wszystkie kształty, które nie są różowe, są ukryte wewnątrz .demo:

Kolorowa siatka jest teraz monochromatyczna i wyświetla tylko różowe kształty.
Wypróbuj demo
Dokładność i elastyczność dzięki &

Załóżmy, że chcesz kierować reklamy na użytkowników z dodatkiem .demo za pomocą selektora :not(). & jest wymagany do tych celów:

.demo {
  &:not() {
    ...
  }
}

W ten sposób wartości .demo:not() są łączone w wartość .demo:not(), w przeciwieństwie do poprzedniego przykładu, w którym potrzebna była wartość .demo :not(). To przypomnienie jest bardzo ważne, gdy chcesz zagnieżdżać interakcję :hover.

.demo {
  &:hover {
    /* .demo:hover */
  }

  :hover {
    /* .demo :hover */
  }
}

Więcej przykładów zagnieżdżania

Więcej przykładów znajdziesz w specyfikacji CSS dotyczącej zagnieżdżania. Jeśli chcesz dowiedzieć się więcej o składni, zapoznaj się z przykładami, które obejmują wiele prawidłowych i nieprawidłowych przykładów.

W kilku kolejnych przykładach krótko omówimy funkcję zagnieżdżania CSS, aby pomóc Ci zrozumieć zakres jej możliwości.

Umieszczanie @media

Przechodzenie do innego obszaru arkusza stylów w celu znalezienia warunków zapytania o multimedia, które modyfikują selektor i jego style, może być bardzo uciążliwe. Ta uciążliwość zniknie dzięki możliwości zagnieżdżania warunków bezpośrednio w kontekście.

Dla ułatwienia, jeśli zagnieżbione zapytanie o multimedia modyfikuje tylko style dla bieżącego kontekstu selektora, można użyć minimalnej składni.

.card {
  font-size: 1rem;

  @media (width >= 1024px) {
    font-size: 1.25rem;
  }
}

Możesz też użyć opcji &:

.card {
  font-size: 1rem;

  @media (width >= 1024px) {
    &.large {
      font-size: 1.25rem;
    }
  }
}

Ten przykład pokazuje rozszerzoną składnię z użyciem &, a także kierowanie na karty .large, aby zademonstrować, że dodatkowe funkcje zagnieżdżania nadal działają.

Dowiedz się więcej o zagnieżdżaniu reguł @.

Umieszczanie w dowolnym miejscu

Wszystkie przykłady do tego momentu są kontynuowane lub dołączone w poprzednim kontekście. W razie potrzeby możesz całkowicie zmienić kontekst lub zmienić jego kolejność.

.card {
  .featured & {
    /* .featured .card */
  }
}

Symbol & to odwołanie do obiektu selektora (a nie ciągu znaków). Można go umieścić w dowolnym miejscu w selektorze zagnieżdżonym. Może on być nawet umieszczony kilka razy:

.card {
  .featured & & & {
    /* .featured .card .card .card */
  }
}

Chociaż ten przykład może wydawać się bezużyteczny, w pewnych sytuacjach możliwość powtórzenia kontekstu selektora może być przydatna.

Nieprawidłowe przykłady zagnieżdżania

Jeśli używasz zagnieżdżania w preprocesorach, musisz pamiętać o kilku nieprawidłowych scenariuszach składni zagnieżdżania, które mogą Cię zaskoczyć.

Zagnieżdżanie i łączenie

Wiele konwencji nazewnictwa klas CSS zakłada, że zagnieżdżanie umożliwia konkatenację lub dołączanie selektorów tak, jakby były one ciągami znaków. Nie działa to w przypadku zagnieżdżania CSS, ponieważ selektory nie są ciągami znaków, tylko odwołaniami do obiektów.

.card {
  &--header {
    /* is not equal to ".card--header" */
  }
}

Bardziej szczegółowe wyjaśnienie znajdziesz w specyfikacji.

Przykład skomplikowanego zagnieżdżania

Umieszczanie w listach selektorów i w elementach :is()

Weź pod uwagę ten zagnieżdżony blok kodu CSS:

.one, #two {
  .three {
    /* some styles */
  }
}

Ten pierwszy przykład zaczyna się od listy selektorów, a potem przechodzi do dalszego zagnieżdżania. W poprzednich przykładach lista selektorów była ostatnim elementem. W tym przykładzie zagnieżdżania nie ma nic nieprawidłowego, ale w przypadku zagnieżdżania w listach selektorów, zwłaszcza tych, które zawierają selektor identyfikatora, mogą wystąpić potencjalnie trudne do wdrożenia szczegóły.

Aby zamierzony efekt zagnieżdżania był widoczny, przeglądarka otacza każdą listę selektorów, która nie jest najbardziej wewnętrznym zagnieżdżeniem, elementem :is(). Dzięki temu grupowanie listy selektorów jest zachowane w ramach dowolnych kontekstów autorskich. Skutkiem ubocznym tego grupowania (:is(.one, #two)) jest to, że przyjmuje ono specyficzność najwyższego wyniku w selektorach w nawiasach. Właśnie tak :is() zawsze działa, ale może to być zaskakujące, gdy używasz nawiasów klamrowych, ponieważ nie jest to dokładnie to, co zostało napisane. Podsumowując: zagnieżdżanie z identyfikatorami i listami selektorów może prowadzić do bardzo specyficznych selektorów.

Aby wyraźnie pokazać ten trudny przykład, zastosujemy poprzedni blok zagnieżdżenia w dokumentie w ten sposób:

:is(.one, #two) .three {
  /* some styles */
}

Zwróć uwagę na to, czy Twoje narzędzia do sprawdzania kodu ostrzegają, gdy w liście selektorów, która używa selektora identyfikatora, występuje zagnieżdżenie. Specyficzność wszystkich zagnieżdżeń w tej liście selektorów będzie wysoka.

Łączenie zagnieżdżania i deklaracji

Weź pod uwagę ten zagnieżdżony blok kodu CSS:

.card {
  color: green;
  & { color: blue; }
  color: red;
}

Kolor elementów .card będzie blue.

Wszystkie deklaracje stylów wymieszane są przenoszone na górę, tak jakby zostały zapisane przed zagnieżchnięciem. Więcej informacji znajdziesz w specyfikacji.

Jest kilka sposobów na obejście tego problemu. Poniższy kod owija 3 style kolorów w element &, który zachowuje kolejność kaskadową zgodnie z zamiarem autora. Kolor elementów .card będzie czerwony.

.card {
  color: green;
  & { color: blue; }
  & { color: red; }
}

Warto też pakować wszystkie style, które są zagnieżdżone, za pomocą atrybutu &.

.card {
  color: green;

  @media (prefers-color-scheme: dark) {
    color: lightgreen;
  }

  & {
    aspect-ratio: 4/3;
  }
}

Wykrywanie cech

Istnieją 2 skuteczne sposoby wykrywania zagnieżdżania w CSS: można użyć zagnieżdżania lub użyć @supports, aby sprawdzić, czy selektor obsługuje zagnieżdżanie.

Zrzut ekranu pokazujący wersję demonstracyjną Codepen firmy Bramus z pytaniem, czy Twoja przeglądarka obsługuje zagnieżdżanie CSS. Pod tym pytaniem znajduje się zielone pole, które sygnalizuje, że można uzyskać pomoc.

Używanie zagnieżdżania:

html {
  .has-nesting {
    display: block;
  }

  .no-nesting {
    display: none;
  }
}

Używasz @supports:

@supports (selector(&)) {
  /* nesting parsing available */
}

Mój współpracownik Bramus ma świetny kod Codepen, który przedstawia tę strategię.

Debugowanie za pomocą Narzędzi deweloperskich w Chrome

Obecnie obsługa zagnieżdżania w Narzędziach deweloperskich jest minimalna. Obecnie style są wyświetlane w panelu Style zgodnie z oczekiwaniami, ale śledzenie zagnieżdżania i pełnego kontekstu selektora nie jest jeszcze obsługiwane. Mamy już gotowe projekty i plany, które mają zapewnić przejrzystość i jasność.

Zrzut ekranu przedstawiający składnię zagnieżdżania w Narzędziach deweloperskich w Chrome.

W Chrome 113 planujemy wprowadzić dodatkową obsługę zagnieżdżania CSS. Więcej informacji już wkrótce.

Przyszłość

Zespolone reguły CSS są dostępne tylko w wersji 1. Wersja 2 wprowadzi więcej składni składnikowych i mniej reguł do zapamiętania. Istnieje duże zapotrzebowanie na to, aby parsowanie zagnieżdżania nie było ograniczone ani nie miało trudnych momentów.

Nesting to duże ulepszenie języka CSS. Ma to wpływ na proces tworzenia w przypadku niemal każdego aspektu architektonicznego usługi porównywania cen. Zanim można będzie określić wersję 2, należy dokładnie zbadać ten duży wpływ i go zrozumieć.

Na koniec przykład demonstracyjny, w którym użyto funkcji @scope, zagnieżdżania i funkcji @layer. To bardzo ekscytujące!

Jasna karta na szarym tle. Karta zawiera tytuł i tekst, kilka przycisków działania oraz obraz w stylu cyberpunk.