Zagnieżdżanie CSS

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

Adam Argyle
Adam Argyle

Przed zagnieżdżeniem każdy selektor musiał zostać jawnie zadeklarowany, niezależnie od siebie. Powoduje to powtórzenia, masowe arkusze stylów i rozproszenie podczas tworzenia.

Przed
.nesting {
  color: hotpink;
}

.nesting > .is {
  color: rebeccapurple;
}

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

Po zagnieżdżeniu można kontynuować selektory, a powiązane z nimi reguły stylu można zgrupować.

Po
.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

Spróbuj to zrobić w przeglądarce

Zagnieżdżanie pomaga programistom, bo ogranicza konieczność powtarzania selektorów, a jednocześnie w jednym lokowaniu reguł stylów powiązanych z powiązanymi elementami. Pozwala to również dopasować style do docelowego kodu HTML. Jeśli z projektu został usunięty komponent .nesting z poprzedniego przykładu, możesz usunąć całą grupę, zamiast wyszukiwać powiązane instancje selektora.

Nesting może pomóc w: – organizacji, – zmniejszaniu rozmiaru plików, – refaktoryzacji.

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

Pierwsze kroki z zagnieżdżaniem w CSS

W pozostałej części tego posta będziesz używać tej demonstracyjnej piaskownicy,która pomoże Ci zwizualizować wybrane opcje. W takim stanie domyślnym nic nie jest zaznaczone i wszystko jest widoczne. Wybierając różne kształty i rozmiary, możesz poćwiczyć składnię i zobaczyć, jak działa.

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

W piaskownicy znajdują się okręgi, trójkąty i kwadraty. Niektóre są małe, średnie lub duże. Inne są w kolorze niebieskim, różowym lub fioletowym. Wszystkie one znajdują się w elemencie zawierającym .demo. Poniżej znajduje się podgląd elementów HTML, na które będziesz kierować reklamy.

<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 pozwala definiować style elementu w kontekście innego selektora.

.parent {
  color: blue;

  .child {
    color: red;
  }
}

W tym przykładzie selektor klasy .child jest umieszczony w selektorze klasy .parent. Oznacza to, że zagnieżdżony selektor .child będzie mieć zastosowanie tylko do elementów, które są elementami 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;
  }
}

Te 2 przykłady są równoważne pod względem praktycznym, a dostępne opcje staną się bardziej szczegółowe w miarę analizowania bardziej zaawansowanych przykładów w tym artykule.

Zaznaczanie kręgów

W tym przykładzie chodzi o dodanie stylów do zanikania i zamazywania tylko kręgów w wersji demonstracyjnej.

Bez zagnieżdżania obecnie CSS:

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

W przypadku zagnieżdżania można to zrobić na 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);
  }
}

W efekcie wszystkie elementy wewnątrz .demo z klasą .circle są rozmyte i prawie niewidoczne:

Kolorowa siatka kształtów nie ma już okręgów,
    są bardzo niewyraźne w tle.
Wypróbuj wersję demonstracyjną

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

To zadanie wymaga wybrania wielu zagnieżdżonych elementów, tzw. selektora grupy.

Obecnie bez zagnieżdżania, CSS pozwala na 2 sposoby:

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

lub za pomocą funkcji :is()

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

W przypadku zagnieżdżania możesz to zrobić na 2 sposoby:

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

lub

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

Wynik – wewnątrz .demo pozostaje tylko tyle elementów: .circle:

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

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, by można było je wybrać.

Bez zagnieżdżania obecnie 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 możesz to zrobić na 2 sposoby:

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

lub

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

W efekcie wszystkie duże trójkąty i okręgi są ukryte w obiekcie .demo:

W 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 tu Twoim znajomym, ponieważ pokazuje wprost, jak łączyć zagnieżdżone 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 pasują do elementów, których możesz się spodziewać. 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.

Zaznaczam wszystkie kształty z wyjątkiem różowych.

To zadanie wymaga pseudoklasy negacji funkcjonalnej, gdzie elementy nie mogą mieć określonego selektora.

Bez zagnieżdżania obecnie CSS:

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

W przypadku zagnieżdżania możesz to zrobić na 2 sposoby:

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

lub

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

W efekcie wszystkie kształty, które nie są różowe, są ukryte w obiekcie .demo:

Kolorowa siatka jest teraz monochromatyczna i zawiera tylko różowe kształty.
Wypróbuj wersję demonstracyjną
Precyzja i elastyczność dzięki &

Załóżmy, że chcesz kierować reklamy na wybrany kraj lub grupę odbiorców .demo za pomocą selektora :not(). & jest wymagany do tych celów:

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

Spowoduje to złożenie ciągu .demo i :not() na .demo:not() w odróżnieniu od poprzedniego przykładu, który wymagał .demo :not(). To przypomnienie jest bardzo ważne, gdy chcesz umieścić interakcję :hover.

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

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

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

Specyfikacja CSS na potrzeby zagnieżdżania zawiera więcej przykładów. Jeśli chcesz dowiedzieć się więcej o składni za pomocą przykładów, zapoznaj się z wieloma przykładami prawidłowych i nieprawidłowych.

W kilku następnych przykładach przedstawimy pokrótce funkcję zagnieżdżania CSS, która pomoże Ci poznać szeroki zakres możliwości, jakie zapewnia.

Umieszczanie @media

Przejście do innego obszaru arkusza stylów w celu znalezienia warunków zapytania o mediach, które modyfikuje selektor i jego style, może być bardzo rozpraszające. To wszystko eliminuje możliwość zagnieżdżenia warunków bezpośrednio w kontekście.

Dla wygody składni, jeśli zagnieżdżone zapytanie o media modyfikuje tylko style bieżącego kontekstu selektora, można użyć składni minimalnej.

.card {
  font-size: 1rem;

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

Bezpośrednie użycie atrybutu & można też zastosować:

.card {
  font-size: 1rem;

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

Ten przykład pokazuje rozwiniętą składnię za pomocą &, a jednocześnie kieruje reklamy na karty .large, aby zademonstrować działanie dodatkowych funkcji zagnieżdżania.

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

Zagnieżdżanie 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 & reprezentuje odwołanie do obiektu selektora (a nie ciągu znaków) i można go umieścić w dowolnym miejscu w zagnieżdżonym selektorze. Można go nawet umieścić wiele razy:

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

Choć ten przykład wydaje się bezużyteczny, z pewnością przydaje się możliwość powtórzenia kontekstu selektora.

Nieprawidłowe przykłady zagnieżdżania

Jest kilka scenariuszy zagnieżdżania, które są nieprawidłowe i mogą Cię zaskoczyć, jeśli zagnieżdżasz dane w preprocesorach.

Zagnieżdżanie i łączenie

Wiele konwencji nazewnictwa klas CSS liczy na to, że zagnieżdżenie łączy lub dołącza selektory tak, jakby były to ciągi tekstowe. Ta metoda nie działa w przypadku zagnieżdżania CSS, ponieważ selektory nie są ciągami, a są odwołaniami do obiektów.

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

Dokładniejsze informacje znajdziesz w specyfikacji.

Przykład nieuczciwego zagnieżdżania

Zagnieżdżanie w listach selektora i polu :is()

Rozważ taki zagnieżdżony blok CSS:

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

To pierwszy przykład, który zaczyna się od listy selektorów, a następnie jest nadal zagnieżdżony. Poprzednie przykłady zakończyły się tylko listą selektora. W tym przykładzie zagnieżdżania nie ma nic nieprawidłowego, ale zawiera on potencjalnie trudny szczegół implementacji, jeśli chodzi o zagnieżdżanie list wewnątrz selektorów, zwłaszcza te, które zawierają selektor identyfikatorów.

Aby zagnieżdżenie działało, każda lista selektorów, która nie jest najbardziej zagnieżdżona, zostanie opakowana przez przeglądarkę ciągiem :is(). To opakowanie zachowuje grupowanie listy selektora w dowolnym kontekście tworzenia treści. Efektem ubocznym tej grupy (:is(.one, #two)) jest to, że przyjmuje specyficzność najwyższego wyniku w selektorach w nawiasie. W ten sposób :is() zawsze działa, ale użycie składni zagnieżdżania może powodować niespodziankę, ponieważ nie jest to dokładnie to, co zostało opracowane. Podsumowano sztuczkę – zagnieżdżenie z identyfikatorami i listami selektorów może prowadzić do selektorów o bardzo dużej szczegółowości.

Aby wyraźnie podsumować ten trudny przykład, poprzedni blok zagnieżdżenia zostanie zastosowany do dokumentu w ten sposób:

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

Uważaj na zagnieżdżenie w obrębie listy selektora korzystającego z selektora identyfikatora lub naucz je ostrzegać, ponieważ tego typu zagnieżdżenie w obrębie tej listy selektora będzie bardzo dokładne.

Łączenie zagnieżdżania i deklaracji

Rozważ taki zagnieżdżony blok CSS:

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

Kolor elementów .card będzie miał wartość blue.

Wszelkie deklaracje stylów wymieszanych są przenoszone na górę, tak jakby zostały utworzone przed zagnieżdżeniem. Więcej informacji znajdziesz w specyfikacji.

Jest kilka sposobów na obejście tego problemu. Poniżej opisujemy 3 style kolorów w polu &, które zachowują kolejność kaskadową zgodną z oczekiwaniami 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 świetne sposoby wykrywania zagnieżdżenia CSS: użyj zagnieżdżenia lub użyj @supports do sprawdzenia możliwości analizy selektora zagnieżdżenia.

Zrzut ekranu pokazujący wersję demonstracyjną Codepen firmy Bramus z pytaniem, czy Twoja przeglądarka obsługuje zagnieżdżanie CSS. Pod nim znajduje się zielone pole sygnalizujące wsparcie.

Korzystanie z 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ą reprezentowane w panelu Style zgodnie z oczekiwaniami, ale śledzenie zagnieżdżenia i jego pełnego kontekstu selektora nie jest jeszcze obsługiwane. Mamy plany i plany, żeby wszystko było przejrzyste i zrozumiałe.

Zrzut ekranu przedstawiający zagnieżdżoną składnię 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ść

Zagnieżdżanie CSS jest dostępne tylko w wersji 1. W wersji 2 dodaliśmy więcej cukru składowego i potencjalnie mniej reguł do zapamiętania. Istnieje duże zapotrzebowanie na możliwości analizowania zagnieżdżenia w taki sposób, aby nie przeprowadzać żadnych ograniczeń i nie występować w trudnych momentach.

Nesting to duże ulepszenie języka CSS. Wywiera wpływ na niemal każdy aspekt architektoniczny CSS. Zanim będzie można opracować wersję 2, trzeba będzie dokładnie zrozumieć i poznać ten ogromny wpływ.

Na koniec oto wersja demonstracyjna, która wykorzystuje @scope, zagnieżdżanie i @layer. To takie ekscytujące!

Jasna karta na szarym tle. Karta ma tytuł i tekst, kilka przycisków polecenia oraz obraz w cyberpunkowym stylu.