Копаем глубже в запросы стиля контейнера

Исходный узел: 1765194

я написал кое-что ранние мысли о запросах в стиле контейнера некоторое время назад. Еще рано. Они уже определены в Спецификация уровня 1 модуля сдерживания CSS (в настоящее время находится в статусе редакционного черновика), но все еще идет пара незавершенных обсуждений.

Основная идея заключается в том, что мы можем определить контейнер, а затем условно применить стили к его потомкам на основе вычисленного стиля.

@container ?  {
  /* conditional styles */
}

Лучший пример, который я видел до сих пор, это удаление курсива из чего-то вроде , качества когда они используются в контексте, где содержимое уже выделено курсивом:

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

Это общая идея. Но если вы этого не знали, Мириам Сюзанн, редактор спецификации, ведет постоянный и тщательный набор личные заметки по запросам в стиле контейнера что общедоступно. На днях он был обновлен, и я провел там некоторое время, пытаясь разобраться в более тонких аспектах стилевых запросов. Это неофициальный материал, но я решил записать некоторые моменты, которые мне особенно запомнились. Кто знает? Может быть, это то, что мы можем в конечном итоге с нетерпением ждать!

Каждый элемент является контейнером стиля

Нам даже не нужно явно назначать container-name or container-type для определения контейнера стиля, потому что по умолчанию все является контейнером стиля.

Итак, вы видите тот пример выше, который удаляет курсив? Обратите внимание, что он не идентифицирует контейнер. Он переходит прямо к запросу, используя style() функция. Итак, какой контейнер запрашивается? Это будет прямой родитель элементов получение примененных стилей. А если не это, то это следующий ближайший относительный контейнер что имеет приоритет.

Мне нравится, что. Это очень CSS-y для запроса, который ищет совпадение, а затем продолжает всплывать, пока не найдет совпадающее условие.

Моему маленькому мозгу было трудно понять, почему мы можем обойтись без неявного контейнера, основанного на стилях, но не так много, когда мы имеем дело с многомерными запросами, такими как size и inline-size. Мириам хорошо объясняет:

Размерные запросы требуют css политика сдерживания от размера, макета и стиля контейнера, чтобы предотвратить циклы макета. Сдерживание — это инвазивная вещь для широкого применения, поэтому было важно, чтобы авторы тщательно контролировали, какие элементы являются (или не являются) размерными контейнерами.

Запросы на основе стилей не имеют такого же ограничения. В CSS уже нет возможности для стилей-потомков влиять на вычисляемые стили предка. Таким образом, не требуется сдерживания, и нет инвазивных или неожиданных побочных эффектов при установлении элемента в качестве контейнер запроса стиля.

(Акцент мой)

Все сводится к последствиям, которых нет, поскольку все является контейнером запроса стиля прямо из коробки.

  • Если контейнер найден: условия разрешаются для этого контейнера.
  • Если совпадают несколько контейнеров: приоритет имеет ближайший относительный контейнер.
  • Если совпадений не найдено: unknown вернулся.

Это то же самое «прощающий» дух, как и остальная часть CSS.

Контейнер может поддерживать как размерные, так и стилевые запросы.

Допустим, мы хотим определить запрос стиля без явного container-name:

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

Это работает, потому что все элементы являются контейнерами стилей, независимо от container-type. Именно это позволяет нам неявно запрашивать стили и полагаться на ближайшее совпадение. И это совершенно нормально, так как, опять же, при установке стилевых контейнеров не возникает неблагоприятных побочных эффектов.

Мы должны использовать явное container-type для многомерных запросов, но не столько для запросов стиля, поскольку каждый элемент является запросом стиля. Это также означает, что этот контейнер является стилем и объемный запрос:

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

Исключение контейнера из запроса

Возможно, мы не хотим, чтобы контейнер участвовал в процессе сопоставления. Вот где можно было бы установить container-type: none на элементе.

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

Контейнеры запросов явного стиля предлагают больше контроля над тем, что запрашивается

Если бы, скажем, мы написали запрос стиля для padding , нет надежного способа определить наиболее подходящий контейнер, независимо от того, работаем ли мы с контейнером с явно заданным именем или с ближайшим прямым родительским контейнером. Это потому что padding не является наследственным имуществом.

Таким образом, в этих случаях мы должны использовать container-name чтобы явно сообщить браузеру, из каких контейнеров они могут извлекать данные. Мы даже можем дать контейнеру несколько явных имен, чтобы он соответствовал большему количеству условий:

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

Ох, и container-name принимает любое количество необязательных и многоразовый имена для контейнера! Это еще больше гибкости, когда речь идет о том, чтобы помочь браузеру сделать выбор при поиске совпадений.

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

Мне как бы интересно, можно ли это также считать «запасным вариантом» в случае, если один контейнер будет передан.

Запросы стилей можно комбинировать

Ассоциация or и and операторы позволяют нам комбинировать wueries, чтобы все было СУХИМ:

@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. */
}

Переключение стилей

Существует небольшое совпадение между запросами в стиле контейнера и проводится работа по определению toggle() функция. Например, мы можем пройтись по двум font-style ценности, скажем italic и normal:

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

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

Прохладный. Но предложение для CSS Toggles предполагает, что toggle() function будет более простым подходом:

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

Но все, что выходит за рамки этого бинарного варианта использования, — это то, где toggle() подходит меньше. Запросы стиля, тем не менее, хороши. Мириам выделяет три случая, когда запросы стиля подходят больше, чем 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;
  }
}

Запросы стилей решают «взлом пользовательского свойства»

Обратите внимание, что запросы стиля являются формальным решением для «Трюк с переключением пользовательских свойств CSS». Там мы устанавливаем пустое пользовательское свойство (--foo: ;) и используйте резервный метод с разделителями-запятыми, чтобы «включать и выключать» свойства, когда затем для пользовательского свойства установлено реальное значение.

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. */
}

Это очень круто, а также много работы, которую запросы к стилю контейнера делают тривиальной.

Запросы стилей и содержимое, созданное с помощью CSS

Для сгенерированного контента, созданного content собственностью ::before и ::after псевдоэлементов, соответствующий контейнер — это элемент, на котором создается содержимое.

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

Запросы стилей и веб-компоненты

Мы можем определить веб-компонент как контейнер и запросить его по стилю. Во-первых, у нас есть компонента:


  
… …

Затем мы используем :host псевдоэлемент в качестве контейнера для установки container-name, чтобы container-type, и некоторые атрибуты высокого уровня на нем:

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

Элементы внутри может запрашивать параметры элемент:

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

Что дальше?

Опять же, все, что я здесь записал, основано на заметках Мириам, и эти заметки не заменяют официальную спецификацию. Но они указывают на то, что обсуждается и что может произойти в будущем. Я ценю, что Мириам связала несколько важных дискуссий, которые все еще ведутся, и мы можем следить за ними, чтобы оставаться в курсе событий:

Отметка времени:

Больше от CSS хитрости