Стилизация веб-компонентов с помощью общей таблицы стилей

Опубликовано: 2022-03-10
Краткий обзор ↬ Веб-компоненты — это удивительная новая функция Интернета, позволяющая разработчикам определять свои собственные пользовательские элементы HTML. В сочетании с руководством по стилю веб-компоненты могут создавать компонентный API, который позволяет разработчикам перестать копировать и вставлять фрагменты кода и вместо этого просто использовать элемент DOM. Используя теневой DOM, мы можем инкапсулировать веб-компонент и не беспокоиться о войнах специфичности с любой другой таблицей стилей на странице. Однако в настоящее время веб-компоненты и руководства по стилю, похоже, противоречат друг другу.

Веб-компоненты — это удивительная новая функция Интернета, позволяющая разработчикам определять свои собственные пользовательские элементы HTML. В сочетании с руководством по стилю веб-компоненты могут создавать компонентный API, который позволяет разработчикам перестать копировать и вставлять фрагменты кода и вместо этого просто использовать элемент DOM. Используя теневой DOM, мы можем инкапсулировать веб-компонент и не беспокоиться о войнах специфичности с любой другой таблицей стилей на странице.

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

Дальнейшее чтение на SmashingMag:

  • Применение лучших практик в компонентных системах
  • Как использовать препроцессор LESS CSS для более умных таблиц стилей
  • Подробное знакомство с Adobe Edge Reflow

Итак, как они могут сосуществовать, когда глобальные руководства по стилю продолжают обеспечивать согласованность и стили даже для веб-компонентов с теневым DOM? К счастью, есть решения, которые работают сегодня, и еще больше решений, которые позволят глобальным руководствам по стилю задавать стили для веб-компонентов. (В оставшейся части этой статьи я буду использовать термин «веб-компоненты» для обозначения пользовательских элементов с теневым DOM.)

Еще после прыжка! Продолжить чтение ниже ↓

Какой должна быть стилизация глобального руководства по стилю в веб-компоненте?

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

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

Ниже приведен простой веб-компонент формы входа, который инкапсулирует все свои стили.

 <template> <style> :host { color: #333333; font: 16px Arial, sans-serif; } p { margin: 0; } p + p { margin-top: 20px; } a { color: #1f66e5; } label { display: block; margin-bottom: 5px; } input[type="text"], input[type="password"] { background-color: #eaeaea; border: 1px solid grey; border-radius: 4px; box-sizing: border-box; color: inherit; font: inherit; padding: 10px 10px; width: 100%; } input[type="submit"] { font: 16px/1.6 Arial, sans-serif; color: white; background: cornflowerblue; border: 1px solid #1f66e5; border-radius: 4px; padding: 10px 10px; width: 100%; } .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <div class="container"> <form action="#"> <p> <label for="username">User Name</label> <input type="text" name="username"> </p> <p> <label for="password">Password</label> <input type="password" name="password"> </p> <p> <input type="submit" value="Login"> </p> <p class="footnote">Not registered? <a href="#">Create an account</a></p> </form> </div> </template> <script> const doc = (document._currentScript || document.currentScript).ownerDocument; const template = doc.querySelector('#login-form-template'); customElements.define('login-form', class extends HTMLElement { constructor() { super(); const root = this.attachShadow({mode: 'closed'}); const temp = document.importNode(template.content, true); root.appendChild(temp); } }); </script>

Примечание. Примеры кода написаны в спецификации версии 1 для веб-компонентов.

Форма входа с логином и паролем.
Простой веб-компонент формы входа в систему

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

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

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

В предыдущем примере веб-компонент формы входа объявлял стили только для двух своих уникальных классов: .container и .footnote . Остальные стили будут принадлежать общей таблице стилей и будут стилизовать абзацы, теги привязки, поля ввода и так далее.

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

Как раньше выполнялась стилизация теневого DOM с помощью внешних таблиц стилей

Первоначальная спецификация для веб-компонентов (известная как версия 0) позволяла любой внешней таблице стилей проникать в теневую модель DOM с помощью селекторов ::shadow или /deep/ CSS. Использование ::shadow и /deep/ позволило вам ввести руководство по стилю в теневой DOM и настроить общие стили, независимо от того, хотел этого веб-компонент или нет.

 /* Style all p tags inside a web components shadow DOM */ login-form::shadow p { color: red; }

С появлением новейшей версии спецификации веб-компонентов (известной как версия 1) авторы убрали возможность внешних таблиц стилей проникать в теневую модель DOM и не предоставили альтернативы. Вместо этого философия изменилась с использования драконов для стилизации веб-компонентов на использование мостов. Другими словами, авторы веб-компонентов должны отвечать за то, какие правила внешнего стиля разрешены для стилизации их компонентов, а не вынуждены разрешать их.

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

Что вы можете сделать сегодня

Сегодня вы можете использовать три метода, которые позволят веб-компоненту совместно использовать стили: @import , пользовательские элементы и библиотека веб-компонентов.

Использование @import

Сегодня единственный нативный способ добавить таблицу стилей в веб-компонент — это использовать @import . Хотя это работает, это анти-шаблон. Однако для веб-компонентов это еще большая проблема с производительностью.

 <template> <style> @import "styleguide.css" </style> <style> .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- rest of template DOM --> </template>

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

Примечание. Из-за различий в том, как работает полифилл импорта HTML, по сравнению с импортом собственного HTML, WebPagetest.org можно использовать для получения надежных результатов только в браузерах, которые изначально поддерживают импорт HTML (например, Chrome).

Гистограмма производительности собственного веб-компонента.
Результаты трех тестов производительности показывают, что @import заставляет браузер работать на полсекунды медленнее, чем при простом внедрении стилей непосредственно в веб-компонент.

В конце концов, @import по-прежнему является анти-шаблоном и может быть проблемой производительности в веб-компонентах. Так что это не отличное решение.

Не используйте теневой DOM

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

Не используя теневую модель DOM, вы будете создавать собственные элементы вместо веб-компонентов (см. отступление ниже), единственное отличие состоит в отсутствии теневой модели DOM и области видимости. Ваш элемент будет подчиняться стилям страницы, но мы уже должны иметь дело с этим сегодня, так что мы ничего не знаем, как с этим справиться. Пользовательские элементы полностью поддерживаются полифиллом webcomponentjs, который отлично поддерживается браузерами.

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

Если вы решите создать пользовательские элементы, помните о некоторых различиях между пользовательскими элементами и веб-компонентами.

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

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

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

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

Наконец, элемент <slot> не работает вне теневого DOM. Это означает, что вам придется использовать другой метод, чтобы разработчики могли вставлять свой контент в ваш пользовательский элемент. Обычно это влечет за собой просто манипулирование DOM самостоятельно, чтобы вставить их содержимое туда, куда вы хотите.

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

 <!-- login-form.html --> <template> <style> login-form .container { max-width: 300px; padding: 50px; border: 1px solid grey; } login-form .footnote { text-align: center; } </style> <!-- Rest of template DOM --> </template> <script> (function() { const doc = (document._currentScript || document.currentScript).ownerDocument; const template = doc.querySelector('#login-form-template'); customElements.define('login-form', class extends HTMLElement { constructor() { super(); } // Without the shadow DOM, we have to manipulate the custom element // after it has been inserted in the DOM. connectedCallback() { const temp = document.importNode(template.content, true); this.appendChild(temp); } }); })(); </script>
 <!-- index.html --> <link rel="stylesheet" href="styleguide.css"> <link rel="import" href="login-form.html"> <login-form></login-form>

С точки зрения производительности пользовательские элементы почти так же быстры, как неиспользуемые веб-компоненты (т. е. связывание общей таблицы стилей в head и использование только собственных элементов DOM). Из всех методов, которые вы можете использовать сегодня, это, безусловно, самый быстрый.

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

В стороне: пользовательский элемент по-прежнему является веб-компонентом во всех смыслах и целях. Термин «веб-компоненты» используется для описания четырех отдельных технологий: пользовательских элементов, тегов шаблонов, импорта HTML и теневого DOM.

К сожалению, этот термин использовался для описания всего, что использует любую комбинацию четырех технологий. Это привело к путанице в отношении того, что люди имеют в виду, когда говорят «веб-компонент». Как обнаружил Роб Додсон, я счел полезным использовать разные термины, говоря о пользовательских элементах с теневым DOM и без него.

Большинство разработчиков, с которыми я разговаривал, склонны ассоциировать термин «веб-компонент» с пользовательским элементом, использующим теневую модель DOM. Итак, для целей этой статьи я создал искусственное различие между веб-компонентом и пользовательским элементом.

Использование библиотеки веб-компонентов

Еще одно решение, которое вы можете использовать сегодня, — это библиотека веб-компонентов, такая как Polymer, SkateJS или X-Tag. Эти библиотеки помогают заполнить пробелы современной поддержки, а также могут упростить код, необходимый для создания веб-компонента. Они также обычно предоставляют дополнительные функции, упрощающие написание веб-компонентов.

Например, Polymer позволяет создать простой веб-компонент всего за несколько строк JavaScript. Дополнительным преимуществом является то, что Polymer предоставляет решение для использования теневой модели DOM и общей таблицы стилей. Это означает, что сегодня вы можете создавать веб-компоненты с общими стилями.

Для этого создайте то, что они называют модулем стиля, который содержит все общие стили. Это может быть либо <style> со встроенными общими стилями, либо <link rel=“import”> , указывающий на общую таблицу стилей. В любом случае включите стили в свой веб-компонент с помощью <style include> , а затем Polymer проанализирует стили и добавит их как встроенный <style> в ваш веб-компонент.

 <!-- shared-styles.html --> <dom-module> <!-- Link to a shared style sheet --> <!-- <link rel="import" href="styleguide.css"> --> <!-- Inline the shared styles --> <template> <style> :host { color: #333333; font: 16px Arial, sans-serif; } /* Rest of shared CSS */ </style> </template> </dom-module>
 <!-- login-form.html --> <link rel="import" href="../polymer/polymer.html"> <link rel="import" href="../shared-styles/shared-styles.html"> <dom-module> <template> <!-- Include the shared styles --> <style include="shared-styles"></style> <style> .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- Rest of template DOM --> </template> <script> Polymer({ is: 'login-form' }); </script> </dom-module>

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

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

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

Гистограмма производительности веб-компонента Polymer.

Результаты тестов производительности показывают, что при использовании Polymer веб-компоненты будут отображаться на полсекунды медленнее, чем нативные веб-компоненты.

Обещание будущего

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

Пользовательские свойства

Пользовательские свойства (или переменные CSS, как их еще называют) — это способ установки и использования переменных в CSS. Это понятие не является новым для препроцессоров CSS, но как нативная функция CSS пользовательские свойства на самом деле более эффективны, чем переменная препроцессора.

Чтобы объявить пользовательское свойство, используйте обозначение пользовательского свойства –my-variable: value и получите доступ к переменной, используя property: var(–my-variable) . Пользовательское свойство каскадируется, как и любое другое правило CSS, поэтому его значение наследуется от его родителя и может быть переопределено. Единственное предостережение относительно пользовательских свойств заключается в том, что они должны быть объявлены внутри селектора и не могут быть объявлены сами по себе, в отличие от переменной препроцессора.

 <style> /* Declare the custom property */ html { --main-bg-color: red; } /* Use the custom property */ input { background: var(--main-bg-color); } </style>

Одна вещь, которая делает пользовательские свойства такими мощными, — это их способность проникать в теневой DOM. Это не та же идея, что и у селекторов /deep/ и ::shadow , потому что они не проникают в веб-компонент. Вместо этого автор веб-компонента должен использовать пользовательское свойство в своем CSS, чтобы его можно было применить. Это означает, что автор веб-компонента может создать собственный API свойств, который потребители веб-компонента могут использовать для применения своих собственных стилей.

 <template> <style> /* Declare the custom property API */ :host { --main-bg-color: brown; } .one { color: var(--main-bg-color); } </style> <div class="one">Hello World</div> </template> <script> /* Code to set up my-element web component */ </script> <my-element></my-element> <style> /* Override the custom variable with own value */ my-element { --main-bg-color: red; } </style>

Браузерная поддержка пользовательских свойств на удивление хороша. Единственная причина, по которой это решение нельзя использовать сегодня, заключается в том, что нет работающего полифилла без Custom Elements версии 1. Команда, стоящая за полифиллом webcomponentjs, в настоящее время работает над его добавлением, но он еще не выпущен и находится в состоянии сборки, это означает, что если вы хешируете свои активы для производства, вы не можете их использовать. Насколько я понимаю, он должен выйти где-то в начале следующего года.

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

Пользовательские свойства больше подходят для параметров тем, а не для общих стилей. Из-за этого пользовательские свойства не являются жизнеспособным решением нашей проблемы.

/* Используйте пользовательское свойство */ input { background: var(–main-bg-color); } </стиль>

Одна вещь, которая делает пользовательские свойства такими мощными, — это их способность проникать в теневой DOM. Это не та же идея, что и у селекторов /deep/ и ::shadow , потому что они не проникают в веб-компонент. Вместо этого автор веб-компонента должен использовать пользовательское свойство в своем CSS, чтобы его можно было применить. Это означает, что автор веб-компонента может создать собственный API свойств, который потребители веб-компонента могут использовать для применения своих собственных стилей.

 <template> <style> /* Declare the custom property API */ :host { --main-bg-color: brown; } .one { color: var(--main-bg-color); } </style> <div class="one">Hello World</div> </template> <script> /* Code to set up my-element web component */ </script> <my-element></my-element> <style> /* Override the custom variable with own value */ my-element { --main-bg-color: red; } </style>

Браузерная поддержка пользовательских свойств на удивление хороша. Единственная причина, по которой это решение нельзя использовать сегодня, заключается в том, что нет работающего полифилла без Custom Elements версии 1. Команда, стоящая за полифиллом webcomponentjs, в настоящее время работает над его добавлением, но он еще не выпущен и находится в состоянии сборки, это означает, что если вы хешируете свои активы для производства, вы не можете их использовать. Насколько я понимаю, он должен выйти где-то в начале следующего года.

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

Пользовательские свойства больше подходят для параметров тем, а не для общих стилей. Из-за этого пользовательские свойства не являются жизнеспособным решением нашей проблемы.

@применить правила

Помимо настраиваемых свойств, CSS также получает правила @apply . Правила применения — это, по сути, примеси для мира CSS. Они объявляются аналогично пользовательским свойствам, но могут использоваться для объявления групп свойств, а не только значений свойств. Так же, как и пользовательские свойства, их значения могут наследоваться и переопределяться, и для того, чтобы они работали, они должны быть объявлены внутри селектора.

 <style> /* Declare rule */ html { --typography: { font: 16px Arial, sans-serif; color: #333333; } } /* Use rule */ input { @apply --typography; } </style>

Браузерная поддержка правил @apply практически отсутствует. В настоящее время Chrome поддерживает его с помощью флага функции (которого я не смог найти), но это все. Также нет рабочего полифилла по той же причине, что и полифилла для пользовательских свойств. Команда webcomponentjs polyfill также работает над добавлением правил @apply вместе с настраиваемыми свойствами, поэтому оба будут доступны после выпуска новой версии.

В отличие от пользовательских свойств правила @apply — гораздо лучшее решение для совместного использования стилей. Поскольку они могут настроить группу объявлений свойств, вы можете использовать их для настройки стиля по умолчанию для всех собственных элементов, а затем использовать их внутри веб-компонента. Для этого вам нужно будет создать правило @apply для каждого нативного элемента.

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

 /* styleguide.css */ html { --typography: { color: #333333; font: 16px Arial, sans-serif; } --paragraph: { margin: 0; } --label { display: block; margin-bottom: 5px; } --input-text { background-color: #eaeaea; border: 1px solid grey; border-radius: 4px; box-sizing: border-box; color: inherit; font: inherit; padding: 10px 10px; width: 100%; } --input-submit { font: 16px/1.6 Arial, sans-serif; color: white; background: cornflowerblue; border: 1px solid #1f66e5; border-radius: 4px; padding: 10px 10px; width: 100%; } /* And so on for every native element */ }
 <!-- login-form.html --> <template> <style> :host { @apply --typography; } p { @apply --paragraph; } label { @apply --label; } input-text { @apply --input-text; } .input-submit { @apply --input-submit; } .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- Rest of template DOM --> </template>

Из-за необходимости обширного шаблона я не думаю, что правила @apply будут хорошим решением для совместного использования стилей между веб-компонентами. Тем не менее, они являются отличным решением для тематики.

в теневом доме

Согласно спецификации веб-компонента, браузеры игнорируют любые теги <link rel=“stylesheet”> в теневой модели DOM, обрабатывая их точно так же, как если бы они находились внутри фрагмента документа. Из-за этого мы не могли ссылаться на какие-либо общие стили в наших веб-компонентах, что было неудачно — то есть до тех пор, пока несколько месяцев назад рабочая группа веб-компонентов не предложила, чтобы теги <link rel=“stylesheet”> работали в тень ДОМ. После всего лишь недели обсуждения все согласились, что должны, и через несколько дней добавили его в спецификацию HTML.

 <template> <link rel="stylesheet" href="styleguide.css"> <style> .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- rest of template DOM --> </template>

Если это звучит слишком быстро, чтобы рабочая группа могла согласовать спецификацию, то это потому, что это не было новым предложением. Заставить теги link работать в теневой модели DOM было фактически предложено по крайней мере три года назад, но это было отложено до тех пор, пока они не смогли убедиться, что это не является проблемой для производительности.

Если принятие предложения недостаточно увлекательно, в Chrome 55 (в настоящее время Chrome Canary) добавлена ​​начальная функциональность, позволяющая тегам link работать в теневой модели DOM. Кажется даже, что эта функция появилась в текущей версии Chrome. Даже Safari реализовал эту функцию в Safari 18.

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

Конечно, то, как производители браузеров реализуют эту функцию, будет определять жизнеспособность этого решения. Чтобы это работало правильно, теги link должны быть дедуплицированы, чтобы несколько веб-компонентов, запрашивающих один и тот же файл CSS, вызывали только один HTTP-запрос. CSS также нужно будет анализировать только один раз, чтобы каждый экземпляр веб-компонента не должен был повторно вычислять общие стили, а вместо этого повторно использовал вычисленные стили.

Chrome уже делает и то, и другое. Итак, если все другие производители браузеров реализуют это так же, то теги link , работающие в теневой модели DOM, определенно решат проблему совместного использования стилей между веб-компонентами.

Конструктивные таблицы стилей

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

Конструктивные таблицы стилей — это предложение, позволяющее создавать объекты StyleSheet в JavaScript с помощью функции конструктора. Затем созданную таблицу стилей можно добавить в теневую DOM через API, что позволит теневой DOM использовать набор общих стилей.

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

Несмотря на это, рабочая группа веб-компонентов использует его как решение для совместного использования стилей между веб-компонентами. Будем надеяться, что либо предложение будет обновлено, либо рабочая группа по веб-компонентам опубликует дополнительную информацию о нем и его принятии. До тех пор кажется, что «долгосрочное» решение не появится в обозримом будущем.

Уроки выучены

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

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

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

О чем следует помнить

Имейте в виду несколько вещей, если вы решите использовать пользовательские элементы или веб-компоненты сегодня.

Самое главное, что спецификация веб-компонента все еще активно развивается, а это значит, что все может и будет меняться. Веб-компоненты по-прежнему находятся на переднем крае, поэтому будьте готовы оставаться в напряжении, пока вы разрабатываете их.

Если вы решите использовать теневой DOM, знайте, что он довольно медленный и неэффективный в полифилл-браузерах. Именно по этой причине разработчики Polymer создали свою теневую реализацию DOM и сделали ее своей по умолчанию.

Chrome, Opera и, в последнее время, Safari — единственные браузеры, поддерживающие теневую модель DOM версии 0. Firefox все еще находится в разработке, хотя начиная с версии 29 поддерживает ее в рамках эксперимента. высокий приоритет на его дорожной карте.

Однако теневая версия DOM 0 — это старая спецификация. Версия Shadow DOM 1 является новой, и только Chrome, Safari и Opera полностью ее поддерживают. Не говоря уже о том, что версия 0 пользовательских элементов прошла такое же обновление, и только Chrome полностью поддерживает версию 1 пользовательских элементов, в то время как техническая предварительная версия Safari поддерживает ее начиная с версии 17. Версия 1 пользовательских элементов имеет некоторые серьезные изменения в написании веб-компонентов, поэтому убедитесь, что полностью понимаете, что это влечет за собой.

Наконец, полифилл webcomponentjs поддерживает только версию 0 реализации теневого DOM и пользовательских элементов. Ветка полифилла версии 1 будет поддерживать версию 1, но она еще не выпущена.