Zagnieżdżanie CSS

Jedna z naszych ulubionych funkcji preprocesora CSS jest teraz wbudowana w język: zagnieżdżanie reguł stylów.

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 z nimi reguły stylu 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ą 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łaściwości CSS

W dalszej części tego artykułu do wizualizacji wyborów służy ta demonstracyjna piaskownica. 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.

Wybieranie kręgów

W tym pierwszym przykładzie zadaniem jest dodanie stylów, aby rozmyć i rozjaśnić tylko koła w ramach tego demo.

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

Wybieranie dowolnych 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:

Kolorowa siatka kształtów zawiera tylko koła, a pozostałe kształty są prawie niewidoczne.
Wypróbuj demo

Wybieranie dużych trójkątów i okręgów

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 obecnie:

.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:

Kolorowa siatka zawiera tylko małe i średnie kształty.
Wypróbuj demo
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);
    }
  }
}

Chociaż jest to prawidłowy sposób zagnieżdżania, wyniki nie będą odpowiadać elementom, których oczekujesz. Dzieje się tak, ponieważ bez &, który określa pożądany wynik złożenia .lg.triangle, .lg.circle, rzeczywisty wynik to .lg .triangle, .lg .circle; selektory potomków.

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

To zadanie wymaga użycia 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, aby:

.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 tej pory były kontynuacją lub dodaniem do poprzedniego kontekstu. 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.

Przykłady nieprawidłowego zagnieżdżenia

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

Zagnieżdżanie i konkatenacja

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żenia 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 za pomocą identyfikatorów i list 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.

Mieszanie 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.

Można to obejść. Poniższy kod owija 3 style kolorów w element &, który zachowuje kolejność kaskadową zgodnie z zamiarem autora. Elementy .card będą miały kolor czerwony.

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

Warto otaczać dowolne style, które następują po zagnieżdżeniu, elementem &.

.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 funkcja parsowania selektora obsługuje zagnieżdżenie.

Zrzut ekranu z demonstracją Bramusa w Codepen, w której pytamy, 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 kodpen, który pokazuje tę strategię.

Debugowanie za pomocą Narzędzi deweloperskich w Chrome

Obecna obsługa zagnieżdżania w DevTools 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ż zaprojektowane rozwiązania, które 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 potencjalnie 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.

Zespolone elementy 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.