Поглиблення запитів у стилі контейнерів

Вихідний вузол: 1765194

Я написав деякі перші думки щодо запитів стилю контейнера трохи назад. Ще рано. Вони вже визначені в Специфікація CSS Containment Module Level 1 (наразі в статусі чернетки редактора), але все ще триває кілька незавершених дискусій.

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

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

Це загальна ідея. Але якщо ви цього не знали, Міріам Сюзанна, яка є редактором специфікації, веде постійний і ретельний набір особисті примітки щодо запитів стилю контейнера що є загальнодоступним. Днями його оновили, і я провів там деякий час, намагаючись обернути голову навколо більш тонких аспектів запитів стилю. Це неофіційно, але я подумав, що занотую деякі речі, які мене вразили. Хто знає? Можливо, це те, на що ми можемо з нетерпінням чекати!

Кожен елемент є контейнером стилю

Нам навіть не потрібно явно призначати a 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. */
}

Перемикання стилів

Існує невелике збіг між запитами стилю контейнера та проводиться робота для визначення a 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() функція була б простішим підходом:

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

Але все, що виходить за межі цього виду бінарного використання, є де toggle() є менш придатним. Однак запити щодо стилю готові. Міріам визначає три випадки, коли стильові запити є більш придатними, ніж 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;
  }
}

Запити стилів вирішують проблему «Custom Property Toggle Hack»

Зауважте, що стильові запити є формальним рішенням для «Трюк перемикання настроюваних властивостей 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 псевдоелемент як контейнер для встановлення a 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-хитрощі