Zagnieżdżanie CSS

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

Przed zagnieżdżeniem każdy selektor musiał być zadeklarowany osobno. Prowadzi to do powtórzeń, zwiększenia rozmiaru arkusza stylów 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 stylów można grupować.

Po
.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

Wypróbuj w przeglądarce.

Zagnieżdżanie pomaga deweloperom, ponieważ zmniejsza potrzebę powtarzania selektorów, a także umożliwia umieszczanie reguł stylów dla powiązanych elementów w tym samym miejscu. Może też pomóc w dopasowaniu stylów do kodu HTML, do którego są kierowane. Jeśli komponent .nesting w poprzednim przykładzie został usunięty z projektu, możesz usunąć całą grupę zamiast szukać w plikach powiązanych instancji selektora.

Zagnieżdżanie może pomóc w: - organizacji, - zmniejszeniu rozmiaru pliku, - refaktoryzacji.

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

Pierwsze kroki z zagnieżdżaniem CSS

W dalszej części tego posta używamy piaskownicy demonstracyjnej,aby pomóc Ci w wizualizacji wyborów. 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ć ją w działaniu.

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ę w elemencie zawierającym .demo. Poniżej znajduje się podgląd elementów HTML, do których będziesz kierować style.

<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 .child klasy jest zagnieżdżony w selektorze klasy .parent. Oznacza to, że zagnieżdżony selektor .child będzie stosowany 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 powinna znajdować się klasa nadrzędna.

.parent {
  color: blue;

  & .child {
    color: red;
  }
}

Te 2 przykłady są funkcjonalnie równoważne, a powód, dla którego masz opcje, stanie się jaśniejszy, gdy w tym artykule omówimy bardziej zaawansowane przykłady.

Wybieranie kół

W tym pierwszym przykładzie zadaniem jest dodanie stylów, które będą przyciemniać i rozmywać tylko koła w wersji demonstracyjnej.

Bez zagnieżdżania, CSS obecnie:

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

Z zagnieżdżaniem, są 2 prawidłowe 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 w .demo z klasą .circle są rozmyte i prawie niewidoczne:

Kolorowa siatka kształtów nie zawiera już kół, są one bardzo słabo widoczne w tle.
Wypróbuj wersję demonstracyjną

Wybieranie dowolnych trójkątów i kwadratów

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

Bez zagnieżdżania, CSS obecnie, są 2 sposoby:

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

lub za pomocą :is()

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

Z zagnieżdżaniem, są 2 prawidłowe sposoby:

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

lub

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

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

Na kolorowej siatce kształtów pozostają tylko koła, a wszystkie inne kształty są prawie niewidoczne.
Wypróbuj wersję demonstracyjną

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

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);
}

Z zagnieżdżaniem, 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 wersję demonstracyjną
Wskazówka dotycząca selektorów złożonych i zagnieżdżania

Symbol & jest tu przydatny, ponieważ wyraźnie pokazuje, jak łączyć zagnieżdżone selektory. Przyjrzyj się temu przykładowi:

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

Jest to prawidłowy sposób zagnieżdżania, ale wyniki nie będą zgodne z oczekiwanymi elementami. Powodem jest to, że bez & określającego pożądany wynik połączenia .lg.triangle, .lg.circle rzeczywisty wynik to .lg .triangle, .lg .circle; selektory potomków.

Wybieranie wszystkich kształtów z wyjątkiem różowych

To zadanie wymaga funkcjonalnej pseudoklasy 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);
}

Z zagnieżdżaniem, 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 w .demo:

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

Załóżmy, że chcesz kierować style do .demo za pomocą selektora :not(). W tym celu wymagany jest symbol &:

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

Łączy on .demo i :not() w .demo:not(), w przeciwieństwie do poprzedniego przykładu, który wymagał .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 na podstawie przykładów, znajdziesz tam wiele prawidłowych i nieprawidłowych przykładów.

W kolejnych przykładach krótko przedstawimy funkcję zagnieżdżania CSS, aby pomóc Ci zrozumieć zakres możliwości, jakie ona wprowadza.

Zagnieżdżanie @media

Przechodzenie do innej części arkusza stylów w celu znalezienia warunków zapytania o media, które modyfikują selektor i jego style, może być bardzo rozpraszające. Dzięki możliwości zagnieżdżania warunków bezpośrednio w kontekście to rozproszenie znika.

Aby ułatwić składnię, jeśli zagnieżdżone zapytanie o media modyfikuje tylko style 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ć wyraźnie symbolu &:

.card {
  font-size: 1rem;

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

Ten przykład pokazuje rozszerzoną składnię z &, a także kierowanie stylów do kart .large, aby zademonstrować, że dodatkowe funkcje zagnieżdżania nadal działają.

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

Zagnieżdżanie w dowolnym miejscu

Wszystkie dotychczasowe przykłady kontynuowały lub dodawały do poprzedniego kontekstu. W razie potrzeby możesz całkowicie zmienić lub zmienić kolejność kontekstu.

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

Symbol & reprezentuje odniesienie 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ć kilka razy:

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

Ten przykład jest nieco bezużyteczny, ale z pewnością istnieją sytuacje, w których możliwość powtórzenia kontekstu selektora jest przydatna.

Nieprawidłowe przykłady zagnieżdżania

Istnieje kilka nieprawidłowych scenariuszy składni zagnieżdżania, które mogą Cię zaskoczyć, jeśli używasz zagnieżdżania w preprocesorach.

Zagnieżdżanie i łączenie

Wiele konwencji nazewnictwa klas CSS opiera się na zagnieżdżaniu, które umożliwia łączenie lub dołączanie selektorów 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 odniesieniami do obiektów.

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

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

Trudny przykład zagnieżdżania

Zagnieżdżanie na listach selektorów i w :is()

Rozważmy ten blok CSS z zagnieżdżaniem:

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

To pierwszy przykład, który zaczyna się od listy selektorów, a następnie jest zagnieżdżany. Poprzednie przykłady kończyły się tylko listą selektorów. W tym przykładzie zagnieżdżania nie ma nic nieprawidłowego, ale jest potencjalnie trudny szczegół implementacji dotyczący zagnieżdżania na listach selektorów, zwłaszcza tych, które zawierają selektor identyfikatora.

Aby zagnieżdżanie działało zgodnie z zamierzeniami, każda lista selektorów, która nie jest najbardziej wewnętrznym zagnieżdżeniem, zostanie opakowana przez przeglądarkę za pomocą :is(). To opakowanie zachowuje grupowanie listy selektorów w dowolnych kontekstach utworzonych przez autora. Efektem ubocznym tego grupowania, :is(.one, #two), jest to, że przyjmuje ono specyficzność najwyższego wyniku w selektorach w nawiasach. Tak zawsze :is() działa, ale może to być zaskoczeniem podczas używania składni zagnieżdżania, 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 selektorów o bardzo wysokiej specyficzności.

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

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

Zwróć uwagę lub naucz swoje narzędzia do sprawdzania kodu, aby ostrzegały, gdy zagnieżdżasz kod na liście selektorów, która używa selektora identyfikatora. Specyficzność całego zagnieżdżania na tej liście selektorów będzie wysoka.

Łączenie zagnieżdżania i deklaracji

Rozważmy ten blok CSS z zagnieżdżaniem:

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

Kolor elementów .card będzie blue.

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

Istnieją sposoby na obejście tego problemu. Poniższy kod opakowuje 3 style kolorów w &, co zachowuje kolejność kaskadową zgodnie z zamierzeniami autora. Kolor elementów .card będzie czerwony.

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

W rzeczywistości dobrym rozwiązaniem jest opakowanie wszystkich stylów, które następują po zagnieżdżaniu, za pomocą &.

.card {
  color: green;

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

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

Wykrywanie funkcji

Istnieją 2 świetne sposoby wykrywania funkcji zagnieżdżania CSS: użyj zagnieżdżania lub użyj @supports, aby sprawdzić możliwość analizowania selektora zagnieżdżania.

Zrzut ekranu z demonstracji Bramusa w Codepen, w której pojawia się pytanie, czy przeglądarka obsługuje zagnieżdżanie CSS. Pod tym pytaniem znajduje się zielone pole, które oznacza, że jest ono obsługiwane.

Używanie zagnieżdżania:

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

  .no-nesting {
    display: none;
  }
}

Używanie @supports:

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

Mój kolega Bramus ma świetny Codepen, który pokazuje 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ą wyświetlane w panelu Style zgodnie z oczekiwaniami, ale śledzenie zagnieżdżania i jego pełnego kontekstu selektora nie jest jeszcze obsługiwane. Mamy projekt i plany, aby to zmienić.

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. Wersja 2 wprowadzi więcej składniowych ułatwień i potencjalnie mniej reguł do zapamiętania. Istnieje duże zapotrzebowanie na to, aby analizowanie zagnieżdżania nie było ograniczone ani nie sprawiało problemów.

Zagnieżdżanie to duże ulepszenie języka CSS. Ma wpływ na tworzenie kodu w niemal każdym aspekcie architektury CSS. Ten duży wpływ należy dokładnie zbadać i zrozumieć, zanim będzie można skutecznie określić wersję 2.

Na koniec przykład który łączy @scope, zagnieżdżanie i @layer. To bardzo ekscytujące!

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