Определенные автором имена 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 Любая незарегистрированная декларация пользовательских свойств | var() |
Просмотр переходов | view-transition-name view-transition-class | ::view-transition-* псевдоэлементы |
Расположение якоря | anchor-name | position-anchor |
Анимация, управляемая прокруткой | view-timeline-name scroll-timeline-name | animation-timeline |
Список стилей | @counter-style | list-style |
Счетчики | counter-reset counter-set counter-increment | |
Контейнерные запросы | container-name | @container |
Страница | page | @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
позволяет двум областям дерева стилизовать один и тот же элемент, указан следующий каскадный порядок:
- Сначала проверьте стиль внутри теневого контекста. Это стиль детали «по умолчанию».
- Затем примените внешний стиль, как определено в
::part
. Это «индивидуальный» стиль детали. - Затем примените любой внутренний стиль, определенный вместе с
!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 результат оказывается непоследовательным и ошибочным. Ни одна из рассмотренных здесь функций не работает одинаково в разных браузерах и не соответствует спецификациям. Хорошей новостью является то, что дельта, обеспечивающая единообразие опыта, — это ограниченный список ошибок и проблем со спецификациями. Давайте это исправим! Тем временем, мы надеемся, что этот обзор может помочь, если вы боретесь с несоответствиями, описанными в этой статье.