Zagnieżdżanie CSS

Jedna z naszych ulubionych funkcji wstępnego przetwarzania kodu CSS jest teraz wbudowana w język: reguły stylów zagnieżdżania.

Adama Argyle'a
Adam Argyle

Przed zagnieżdżeniem trzeba było wyraźnie zadeklarować każdy selektor, oddzielnie od siebie. Prowadzi to do powtarzania, zbiorczego arkusza stylów i rozproszenia 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 nim reguły stylu można grupować.

Po
.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

Wypróbuj tę funkcję w przeglądarce

Zagnieżdżanie pomaga deweloperom, ograniczając potrzebę powtarzania selektorów, a jednocześnie reguły stylu dotyczące wspólnego lokalizowania powiązanych elementów. Może też ułatwiać dopasowanie stylów do kodu HTML, na który kierujesz reklamy. Jeśli komponent .nesting w poprzednim przykładzie został usunięty z projektu, możesz usunąć całą grupę, zamiast wyszukiwać pliki pod kątem powiązanych instancji selektorów.

Funkcja Nest może pomóc w: – organizacji, – zmniejszeniu rozmiaru pliku, – refaktoryzacji.

Zagnieżdżanie jest dostępne od Chrome 112 i można też wypróbować tę wersję w przeglądarce Safari w wersji 162.

Wprowadzenie do zagnieżdżania CSS

W pozostałej części tego posta wykorzystywana jest przykładowa piaskownica do wizualizacji wybranych opcji. W tym stanie domyślnym nic nie jest zaznaczone i wszystko jest widoczne. Wybierając różne kształty i rozmiary, możesz ć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.

Wewnątrz piaskownicy znajdują się okręgi, trójkąty i kwadraty. Niektóre są małe, średnie lub duże. Inne są niebieskie, różowe lub fioletowe. Wszystkie znajdują się w elemencie zawierającym .demo. Poniżej znajduje się podgląd elementów HTML, na które będą kierowane 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 umożliwia definiowanie stylów elementu w kontekście innego selektora.

.parent {
  color: blue;

  .child {
    color: red;
  }
}

W tym przykładzie selektor klasy .child jest umieszczony w selektorze klas .parent. Oznacza to, że zagnieżdżony selektor .child będzie stosowany tylko do elementów podrzędnych wobec elementów z klasą .parent.

Ten przykład można też napisać 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 funkcjonalnie, a przyczyny tych opcji staną się bardziej zrozumiałe w miarę zapoznania się z bardziej zaawansowanymi przykładami w tym artykule.

Wybór kręgów

W pierwszym przykładzie zadaniem jest dodanie stylów, by zanikać i zamazać tylko okręgi w wersji demonstracyjnej.

Bez zagnieżdżania CSS:

.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 .demo z klasą .circle są zamazane i prawie niewidoczne:

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

Zaznaczając dowolne trójkąty i kwadraty

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

Bez zagnieżdżania w CSS są obecnie dostępne 2 sposoby:

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

lub przy użyciu :is()

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

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

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

lub

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

Wynik, wewnątrz elementu .demo pozostaje tylko .circle elementu:

Kolorowa siatka kształtów pozostanie jedynie okręgami, a pozostałe kształty będą prawie niewidoczne.
Wypróbuj wersję demonstracyjną

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

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

Symbol & jest Twoim znajomym, ponieważ pokazuje, jak przyłączyć zagnieżdżone 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 odpowiadają oczekiwanym elementom. Powodem jest to, że bez funkcji & do określenia oczekiwanego wyniku złożonego ze składnika .lg.triangle, .lg.circle rzeczywisty wynik będzie miał postać .lg .triangle, .lg .circle; selektory podrzędne.

Zaznaczam wszystkie kształty oprócz różowych

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

Bez zagnieżdżania CSS:

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

W przypadku zagnieżdżania dostępne są 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 elemencie .demo:

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

Załóżmy, że chcesz kierować reklamy na .demo za pomocą selektora :not(). & jest wymagany w tym celu:

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

Ten skład składa się z .demo i :not() do .demo:not(), w przeciwieństwie do poprzedniego przykładu, który wymagał parametru .demo :not(). To przypomnienie jest bardzo ważne, gdy chcesz zagnieździć interakcję :hover.

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

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

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

Specyfikacja CSS dotycząca zagnieżdżania zawiera więcej przykładów. Jeśli chcesz dowiedzieć się więcej o składni z przykładów, w tym kursie znajdziesz wiele prawidłowych i nieprawidłowych przykładów.

W kilku następnych przykładach omówimy pokrótce funkcję zagnieżdżania CSS, aby pomóc Ci zrozumieć zakres jej funkcji.

Umieszczanie w witrynie @media

Przejście do innego obszaru arkusza stylów w celu znalezienia warunków zapytania o media, które modyfikują selektor i jego style, może być bardzo rozpraszające. Odwracanie uwagi zniknęło z możliwością zagnieżdżenia warunków w kontekście.

Jeśli zapytanie o media zagnieżdżone zmienia style tylko na potrzeby bieżącego kontekstu selektora, można użyć minimalnej składni.

.card {
  font-size: 1rem;

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

Wyraźnego użycia atrybutu & można też używać:

.card {
  font-size: 1rem;

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

Ten przykład przedstawia rozszerzoną składnię z elementem &, a jednocześnie 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 kontynuowane lub dołączone do poprzedniego kontekstu. W razie potrzeby możesz całkowicie zmienić kontekst lub jego kolejność.

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

Symbol & reprezentuje odwołanie do obiektu selektora (nie ciąg znaków) i można go umieścić w dowolnym miejscu w selektorze zagnieżdżonego. Można ją też umieścić kilka razy:

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

Ten przykład może być trochę bezużyteczny, ale w niektórych sytuacjach bardzo przydatna jest możliwość powtórzenia kontekstu selektora.

Przykłady nieprawidłowych zagnieżdżania

Istnieje kilka scenariuszy składni zagnieżdżania, które są nieprawidłowe i mogą Cię zaskoczyć, jeśli zagnieżdżono je w procesorach preprocesorowych.

Zagnieżdżanie i konkatenacja

Wiele konwencji nazewnictwa klas CSS korzysta z możliwości zagnieżdżania selektorów i dołączania ich tak, jakby były ciągami znaków. Nie działa to w przypadku zagnieżdżania CSS, ponieważ selektory nie są ciągami znaków, ale odwołaniami do obiektów.

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

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

Przykład nieuczciwego zagnieżdżania

Zagnieżdżam na listach wyboru i w polu :is()

Rozważ taki zagnieżdżony blok CSS:

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

Jest to pierwszy przykład, który zaczyna się od listy selektorów, a następnie jest dalej zagnieżdżany. Poprzednie przykłady zawierały tylko listę selektora. W tym przykładzie nie ma żadnych nieprawidłowych informacji o zagnieżdżaniu, ale zagnieżdżanie w nich na listach selektorów może być trudne, zwłaszcza te zawierające selektor identyfikatorów.

Aby intencja zagnieżdżania działała, każda lista selektorów, która nie jest najgłębiej zagnieżdżona, zostanie umieszczona przez przeglądarkę przy użyciu elementu :is(). To pakowanie umożliwia grupowanie listy z selektorami we wszystkich kontekstach utworzonych przez Ciebie. Efektem ubocznym tego grupowania, :is(.one, #two), jest przyjmowanie specyficzności najwyższego wyniku w selektorach w nawiasie. Tak zawsze działa funkcja :is(), ale podczas korzystania ze składni zagnieżdżania może to być zaskoczeniem, ponieważ nie odzwierciedla ona dokładnie tego, co została utworzona. Podsumowujemy: zagnieżdżenie za pomocą identyfikatorów i list selektora może prowadzić do bardzo dużej specyficzności selektorów.

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

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

Zachowaj czujność lub naucz linry, aby ostrzegały, gdy są zagnieżdżane w liście selektora, która korzysta z selektora identyfikatora, specyfika zagnieżdżenia w obrębie tej listy będzie wysoka.

Łączenie zagnieżdżania z deklaracjami

Rozważ taki zagnieżdżony blok CSS:

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

Kolor .card elementów to blue.

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

Można obejść ten problem. Poniżej opisujemy 3 style kolorów w polu &, zachowując kolejność kaskadową zgodnie z oczekiwaniami autora. Elementy .card mają kolor czerwony.

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

Właściwie to dobrym praktyką jest pakowanie stylów następujących po zagnieżdżeniu za pomocą właściwości &.

.card {
  color: green;

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

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

Wykrywanie funkcji

Są 2 świetne sposoby wykrywania zagnieżdżania CSS: użyj zagnieżdżenia lub użyj @supports, aby sprawdzić możliwości analizy zagnieżdżania selektorów.

Zrzut ekranu z prezentacją Bramusa Codepen z pytaniem, czy Twoja przeglądarka obsługuje zagnieżdżanie CSS. Pod nim znajduje się zielone pole, które oznacza wsparcie.

Korzystanie z zagnieżdżania:

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

  .no-nesting {
    display: none;
  }
}

Przy użyciu dodatku @supports:

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

Mój kolega Bramus ma świetnego Kodera, który prezentuje tę strategię.

Debugowanie za pomocą Narzędzi deweloperskich w Chrome

Obecna 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 pełnego kontekstu selektora nie jest jeszcze obsługiwane. Mamy projekt i planujemy, żeby to wszystko było przejrzyste i przejrzyste.

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

W Chrome 113 planujemy 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 wprowadzimy więcej cukru składniowego i potencjalnie mniej reguł do zapamiętywania. Analiza zagnieżdżania jest bardzo zapotrzebowana, aby nie była ograniczona ani wymagała trudnych momentów.

Nesting to duże ulepszenie języka CSS. Ma wpływ na niemal każdy aspekt architektoniczny CSS. Aby skutecznie określić wersję 2, musimy dokładnie zbadać i zrozumieć ten duży wpływ.

Na koniec pozwolę sobie obejrzeć prezentację, która łączy funkcje @scope, zagnieżdżania i @layer. Jestem pod ogromnym wrażeniem.

Jasna karta na szarym tle. Karta zawiera tytuł, tekst, kilka przycisków działań i obraz w cyberpunkowym stylu.