Определенные автором имена CSS и теневой DOM: в спецификации и на практике

Определенные автором имена CSS и теневой DOM должны работать вместе. Однако браузеры несовместимы со спецификацией, иногда друг с другом, и каждое имя CSS несовместимо по-своему.

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

Что такое имена CSS, определенные автором?

Определенные автором имена CSS — это относительно старый синтаксический механизм CSS, первоначально представленный для правила @keyframes , которое определяет <keyframe-name> либо как пользовательский идентификатор, либо как строку. Цель этой концепции — объявить что-то в одной части таблицы стилей и ссылаться на это в другой части.

/* "fade-in" is a CSS name, representing a set of keyframes */
@keyframes fade-in {
  from { opacity: 0 };
  to { opacity: 1 }
}

.card {
  /* "fade-in" is a reference to the above keyframes */
  animation-name: fade-in;
}

Другие функции CSS, использующие имена CSS, — это шрифты, объявления свойств, запросы контейнеров, а в последнее время — переходы представлений, позиционирование привязки и анимация, управляемая прокруткой. В следующей неполной таблице приведены имена, состояние которых Chrome проверяет.

Особенность Объявление имени Ссылка на имя
Ключевые кадры @keyframes animation-name
Шрифты @font-face { }
@font-palette-values
font-family
font-palette
Декларации о собственности @property Любое пользовательское свойство
Посмотреть переход view-transition-name
view-transition-class
::view-transition-group()
Расположение якоря anchor-name position-anchor
Анимация, управляемая прокруткой animation-timeline view-timeline-name
scroll-timeline-name
Стиль счетчика @counter-style
Counter-reset
counter-set
counter-increment
list-style
Контейнерные запросы container-name @container
CSS-переменная --something var(--something)
Страница @page

Как видно из таблицы, имя CSS обычно имеет соответствующую ссылку CSS. Например, animation-name — это ссылка на имя @keyframes . Имена CSS отличаются от имен, определенных в DOM, таких как имена атрибутов и тегов, поскольку они объявляются, а затем на них ссылаются в контексте таблиц стилей.

Как имена связаны с теневым DOM

В то время как имена CSS созданы для создания связей между различными частями документа или таблицы стилей, Shadow DOM создан для обратного. Он инкапсулирует отношения, поэтому они не передаются через веб-компоненты, которые должны иметь собственное пространство имен.

Объединив имена CSS и теневой DOM, процесс создания веб-компонентов должен быть достаточно выразительным, чтобы быть гибким, но достаточно ограниченным, чтобы быть стабильным.

Это хорошо в теории. На практике браузеры несовместимы в том, как имена CSS взаимодействуют с теневым DOM, как между функциями в одном браузере, между браузерами, так и между функциями и спецификациями.

Как имена и теневой DOM должны работать вместе

Чтобы понять проблему, стоит понять, как эти части CSS теоретически должны работать вместе.

Общее правило

Общее правило поведения CSS-имен в теневых деревьях определено в спецификации CSS Scoping Level 1 . Подводя итог: имя CSS является глобальным внутри области, в которой оно определено, то есть к нему можно получить доступ из теневых деревьев-потомков, но не из родственных или родительских теневых деревьев. Обратите внимание, что это отличается от имен на веб-платформе, таких как идентификаторы элементов, которые инкапсулированы в той же области дерева.

Исключение из правила: @property

В отличие от других имен CSS, свойства CSS не инкапсулируются теневым DOM. Скорее, они являются общим средством передачи параметров между разными теневыми деревьями. Это делает дескриптор @property особенным: он должен вести себя как объявление глобального типа документа, определяющее, как действует конкретное именованное свойство. Поскольку свойства должны совпадать во всех теневых деревьях, несоответствие объявлений свойств может привести к неожиданным результатам, поэтому объявления @property указываются так, чтобы их сглаживали и разрешали в соответствии с порядком документа.

Как правило должно работать с ::part

Теневые части предоставляют элемент внутри теневого дерева его родительскому дереву. Таким образом, родительское дерево сможет получить доступ к этому элементу, а также стилизовать его с помощью элемента ::part .

Поскольку ::part позволяет двум областям дерева стилизовать один и тот же элемент, указан следующий каскадный порядок:

  1. Сначала проверьте стиль внутри теневого контекста. Это стиль детали «по умолчанию».
  2. Затем примените внешний стиль, как определено в ::part . Это «индивидуальный» стиль детали.
  3. Затем примените любой внутренний стиль, определенный вместе с !important . Это позволяет пользовательскому элементу объявить, что определенное свойство определенной части не может быть изменено с помощью ::part .

Это означает, что имена из теневого DOM не могут ссылаться на ::part , поскольку ::part является стилем области хоста, а не стилем теневой области. Например:

// inside the shadow DOM:
@keyframes fade-in {
  from { opacity: 0}
}

// This shouldn't work!
// The host style shouldn't know the name "fade-in"
::part(slider) {
  animation-name: fade-in;  
}

Как правило должно работать со встроенными стилями

В отличие от ::part , встроенные стили с атрибутом style или стили, которые программно устанавливают стиль с помощью скрипта, ограничены областью действия элемента. Это связано с тем, что для применения стиля к элементу вам необходим доступ к дескриптору элемента и, следовательно, к самому теневому корню.

Как имена CSS и теневой DOM работают вместе в реальности

Хотя предыдущие правила четко определены и последовательны, текущие реализации не всегда это отражают. На практике @property работает не так, как указано в спецификации, и единообразно во всех браузерах, а в большинстве других функций есть открытые ошибки (некоторые из них еще не выпущены, поэтому есть время их исправить).

Чтобы протестировать и продемонстрировать, как эти функции работают на практике, мы создали следующую страницу: https://css-names-in-the-shadow.glitch.me/ . На этой странице есть несколько iframe, каждый из которых ориентирован на одну из функций и тестирует шесть сценариев:

  • Внешняя ссылка на внешнее имя : теневой DOM не используется, это должно работать.
  • Внешняя ссылка на внутреннее имя : это не должно работать, так как это будет означать, что имя, определенное в теневом контексте, просочилось.
  • Внутренняя ссылка на внешнее имя : это должно работать, поскольку имена в области дерева наследуются теневыми корнями.
  • Внутренняя ссылка на внутреннее имя : это должно работать, поскольку оба имени ссылки находятся в одной области.
  • Ссылка ::part на внешнее имя : это должно работать, поскольку и ::part , и имя объявлены в одной области.
  • ::part ссылка на внутреннее имя : это не должно работать, поскольку внешняя область не должна получать информацию об именах, объявленных внутри теневого DOM.

@keyframes

Как определено в спецификации, вы должны иметь возможность ссылаться на имена ключевых кадров из теневого корня, если at-правило @keyframes находится в области предка. На практике ни один браузер не реализует такое поведение, и на определения ключевых кадров можно ссылаться только в той области, в которой они определены. См. выпуск 10540 .

@property

Как определено в спецификации, любое объявление @property будет сведено к области документа. Однако сегодня во всех браузерах вы можете объявлять @property только в области документа, а объявления @property в теневых корнях игнорируются.
См. выпуск 10541 .

Специфические ошибки браузера

Другие функции не демонстрируют единообразного поведения в разных браузерах:

  • @font-face в Safari сводится к корневой области.
  • Chromium не позволяет наследовать правила @anchor-name в теневом корне
  • @scroll-timeline-name и @view-timeline-name неправильно определены в ::part (также в Chromium).
  • Ни один браузер не позволяет объявлять @font-palette-values в теневых корнях.
  • view-transition-class может быть определен внутри теневого корня (сам переход находится вне теневого корня).
  • Firefox позволяет ::part получать доступ к внутренним теневым именам (запросы контейнера, ключевые кадры).
  • Firefox и Safari не учитывают @counter-style в теневом корне.

Обратите внимание, что counter-reset , counter-set , counter-increment имеют немного разные правила, поскольку они являются неявными именами, а объявление свойств CSS имеет установленный и хорошо проверенный набор правил.

Заключение

Плохая новость заключается в том, что при проверке снимка текущего состояния взаимодействия с учетом имен CSS и теневого DOM результат оказывается непоследовательным и ошибочным. Ни одна из рассмотренных здесь функций не работает одинаково в разных браузерах и не соответствует спецификациям. Хорошей новостью является то, что дельта, обеспечивающая единообразие опыта, — это ограниченный список ошибок и проблем со спецификациями. Давайте это исправим! Тем временем, мы надеемся, что этот обзор может помочь, если вы боретесь с несоответствиями, описанными в этой статье.