Zagłębianie się w zapytania w stylu kontenera

Węzeł źródłowy: 1765194

Napisałem trochę wczesne przemyślenia na temat zapytań w stylu kontenera jakiś czas temu. To wciąż wczesne dni. Są już zdefiniowane w Specyfikacja CSS Containment Module Level 1 (obecnie w wersji roboczej redaktora), ale nadal toczy się kilka ważnych dyskusji.

Podstawową ideą jest to, że możemy zdefiniować kontener, a następnie warunkowo zastosować style do jego elementów podrzędnych na podstawie jego obliczonego stylu.

@container ?  {
  /* conditional styles */
}

Najlepszym przykładem, jaki do tej pory widziałem, jest usunięcie kursywy z czegoś takiego , , gdy są używane w kontekście, w którym treść jest już pisana kursywą:

em, i, q {
  font-style: italic; /* default UA behavior */
}

/* When the container's font-style is italic, remove italics from these elements. */
@container style(font-style: italic) {
  em, i, q {
    font-style: normal;
  }
}

Taka jest ogólna koncepcja. Ale jeśli tego nie wiedziałeś, Miriam Suzanne, która jest redaktorem specyfikacji, prowadzi ciągły i dokładny zestaw osobiste notatki dotyczące zapytań w stylu kontenera to jest publicznie dostępne. Został zaktualizowany któregoś dnia i spędziłem tam trochę czasu, próbując ogarnąć bardziej szczegółowe aspekty zapytań o styl. To nieoficjalne informacje, ale pomyślałem, że zanotuję kilka rzeczy, które zwróciły moją uwagę. Kto wie? Może to jest coś, na co w końcu możemy się doczekać!

Każdy element jest kontenerem stylu

Nie musimy nawet jawnie przypisywać a container-name or container-type zdefiniować kontener stylów, ponieważ wszystko jest domyślnie kontenerem stylów.

Widzisz ten przykład powyżej, który usuwa kursywę? Zauważ, że nie identyfikuje kontenera. Przeskakuje bezpośrednio do zapytania za pomocą style() funkcjonować. Więc o który kontener pytasz? To będzie bezpośredni rodzic elementów otrzymania zastosowanych stylów. A jeśli nie to, to tak następny najbliższy względny kontener to ma pierwszeństwo.

Lubię to. To bardzo CSS-y, aby zapytanie wyszukało dopasowanie, a następnie kontynuowało bąbelkowanie, aż znajdzie pasujący warunek.

Mojemu małemu mózgowi trudno było zrozumieć, dlaczego możemy uciec z niejawnym kontenerem opartym na stylach, ale nie tak bardzo, gdy mamy do czynienia z zapytaniami wymiarowymi, takimi jak size i inline-size. Miriam ładnie to wyjaśnia:

Zapytania wymiarowe wymagają css powstrzymywanie od rozmiaru, układu i stylu kontenera, aby zapobiec powstawaniu pętli układu. Przechowywanie jest inwazyjną rzeczą, którą można zastosować w szerokim zakresie, dlatego ważne było, aby autorzy mieli dokładną kontrolę nad tym, które elementy są (lub nie) rozmiarami pojemników.

Zapytania oparte na stylach nie mają tego samego ograniczenia. W CSS nie ma już możliwości, aby style potomne miały wpływ na obliczone style przodka. Nie jest więc wymagane żadne zabezpieczenie i nie ma inwazyjnych ani nieoczekiwanych skutków ubocznych ustanowienia elementu jako elementu stylowy kontener zapytań.

(podkreślenie moje)

Wszystko sprowadza się do konsekwencji — z których nie ma żadnych, jeśli chodzi o stylowy kontener zapytań zaraz po wyjęciu z pudełka.

  • Jeśli kontener zostanie znaleziony: warunki dotyczące tego kontenera są rozpatrywane.
  • Jeśli pasuje wiele kontenerów: najbliższy względny kontener ma pierwszeństwo.
  • Jeśli nie znaleziono żadnych dopasowań: unknown zwrócony.

To samo „przebaczający” duch, jak reszta CSS.

Kontener może obsługiwać zarówno zapytania dotyczące wymiarów, jak i stylów

Powiedzmy, że chcemy zdefiniować zapytanie stylu bez jawnego container-name:

@container style(font-style: italic) {
  em {
    font-style: normal;
  }
}

To działa, ponieważ wszystkie elementy są kontenerami stylu, nieważne container-type. To właśnie pozwala nam niejawnie wyszukiwać style i polegać na najbliższym dopasowaniu. I to jest całkowicie w porządku, ponieważ ponownie nie ma żadnych niepożądanych skutków ubocznych podczas tworzenia kontenerów stylu.

Musimy użyć jawnego container-type dla zapytań wymiarowych, ale nie tak bardzo dla zapytań o styl, ponieważ każdy element jest zapytaniem o styl. Oznacza to również, że ten pojemnik jest jednocześnie stylem i zapytanie wymiarowe:

.card-container {
  container: card / inline-size; /* implictly a style query container as well */
}

Wykluczanie kontenera z zapytania

Być może nie chcemy, aby kontener uczestniczył w procesie dopasowywania. Tam być może uda się ustawić container-type: none na elemencie.

.some-element {
  container-type: none;
}

Kontenery zapytań w stylu jawnym zapewniają większą kontrolę nad tym, co jest przedmiotem zapytania

Gdybyśmy, powiedzmy, mieli napisać zapytanie o styl padding , nie ma niezawodnego sposobu określenia najlepiej dopasowanego kontenera, niezależnie od tego, czy pracujemy z jawnie nazwanym kontenerem, czy z najbliższym bezpośrednim rodzicem. To jest ponieważ padding nie jest własnością dziedziczną.

Więc w takich przypadkach powinniśmy używać container-name aby wyraźnie poinformować przeglądarkę, z których kontenerów może pobierać. Możemy nawet nadać kontenerowi wiele wyraźnych nazw, aby dopasować go do większej liczby warunków:

.card {
  container-name: card layout theme;
}

Aha, i container-name akceptuje dowolną liczbę opcjonalnych i wielokrotnego użytku nazwy dla kontenera! To jeszcze większa elastyczność, jeśli chodzi o pomoc przeglądarce w dokonaniu wyboru podczas wyszukiwania dopasowań.

.theme {
  container-name: theme;
}
.grid {
  container-name: layout;
}
.card {
  container-name: card layout theme;
}

Zastanawiam się, czy można to również uznać za „powrót” w przypadku pominięcia jednego kontenera.

Zapytania stylów można łączyć

Połączenia or i and operatorzy pozwalają nam łączyć wuerie, aby wszystko było SUCHE:

@container bubble style(--arrow-position: start start) or style(--arrow-position: end start) {
  .bubble::after {
    border-block-end-color: inherit;
    inset-block-end: 100%;
  }
}

/* is the same as... */
@container bubble style(--arrow-position: start start) {
  /* etc. */
}
@container bubble style(--arrow-position: end start) {
  /* etc. */
}

Przełączanie stylów

Istnieje niewielkie nakładanie się zapytań w stylu kontenera i trwają prace nad zdefiniowaniem a toggle() funkcjonować. Na przykład możemy przejść przez dwa font-style wartości, powiedzmy italic i normal:

em, i, q {
  font-style: italic;
}

@container style(font-style: italic) {
  em, i, q {
    font-style: normal;
  }
}

Chłodny. Ale propozycja CSS Toggles sugeruje, że toggle() funkcja byłaby prostszym podejściem:

em, i, q {
  font-style: toggle(italic, normal);
}

Ale wszystko poza tego rodzaju binarnym przypadkiem użycia jest gdzie toggle() jest mniej odpowiedni. Zapytania o styl są jednak dobre. Miriam identyfikuje trzy przypadki, w których zapytania stylu są bardziej odpowiednie niż a toggle():

/* When font-style is italic, apply background color. */
/* Toggles can only handle one property at a time. */
@container style(font-style: italic) {
  em, i, q {
    background: lightpink;
  }
}

/* When font-style is italic and --color-mode equals light */
/* Toggles can only evaluate one condition at a time */
@container style((font-style: italic) and (--color-mode: light)) {
  em, i, q {
    background: lightpink;
  }
}

/* Apply the same query condition to multiple properties */
/* Toggles have to set each one individually as separate toggles */
@container style(font-style: italic) {
  em, i, q {
    /* clipped gradient text */
    background: var(--feature-gradient);
    background-clip: text;
    box-decoration-break: clone;
    color: transparent;
    text-shadow: none;
  }
}

Zapytania dotyczące stylów rozwiązują „Custom Property Toggle Hack”

Zauważ, że zapytania stylów są formalnym rozwiązaniem dla „Sztuczka przełączania właściwości niestandardowych CSS”. Tam ustawiamy pustą właściwość niestandardową (--foo: ;) i użyć metody zastępczej oddzielonej przecinkami, aby „włączać” i wyłączać właściwości, gdy właściwość niestandardowa jest ustawiona na wartość rzeczywistą.

button {
  --is-raised: ; /* off by default */
  
  border: 1px solid var(--is-raised, rgb(0 0 0 / 0.1));
  box-shadow: var(
    --is-raised,
    0 1px hsl(0 0% 100% / 0.8) inset,
    0 0.1em 0.1em -0.1em rgb(0 0 0 / 0.2)
  );
  text-shadow: var(--is-raised, 0 -1px 1px rgb(0 0 0 / 0.3));
}

button:active {
  box-shadow: var(--is-raised, 0 1px 0.2em black inset);
}

#foo {
  --is-raised: initial; /* turned on, all fallbacks take effect. */
}

To super fajne, a także dużo pracy, dzięki której zapytania dotyczące kontenerów stylu są trywialne.

Zapytania stylu i treść generowana przez CSS

W przypadku treści generowanych przez content własność ::before i ::after pseudoelementy, pasującym kontenerem jest element, na podstawie którego generowana jest treść.

.bubble {
  --arrow-position: end end;
  container: bubble;
  border: medium solid green;
  position: relative;
}

.bubble::after {
  content: "";
  border: 1em solid transparent;
  position: absolute;
}

@container bubble style(--arrow-position: end end) {
  .bubble::after {
    border-block-start-color: inherit;
    inset-block-start: 100%;
    inset-inline-end: 1em;
  }
}

Zapytania stylu i komponenty sieciowe

Możemy zdefiniować komponent sieciowy jako kontener i przeszukiwać go według stylu. Po pierwsze mamy tzw komponentu:


  
… …

Następnie używamy tzw :host pseudoelement jako kontener do ustawienia a container-name, A container-typei niektóre atrybuty wysokiego poziomu:

:host {
  container: media-host / inline-size;
  --media-location: before;
  --media-style: square;
  --theme: light;
}

Elementy wewnątrz może zapytać o parametry element:

@container media-host style(--media-style: round) {
  [part='img'] {
    border-radius: 100%;
  }
}

Co dalej?

Ponownie, wszystkie rzeczy, które tu zanotowałem, są oparte na notatkach Miriam i te notatki nie zastępują oficjalnej specyfikacji. Są jednak wskazówką tego, o czym się dyskutuje i gdzie sprawy mogą wylądować w przyszłości. Doceniam, że Miriam połączyła kilka znakomitych dyskusji, które wciąż toczą się, a które możemy śledzić, aby być na bieżąco:

Znak czasu:

Więcej z Sztuczki CSS