Преимущества использования пользовательских свойств в системах проектирования и библиотеках компонентов.
Меня зовут Дэйв, и я старший интерфейсный разработчик в Nordhealth . Я работаю над дизайном и разработкой нашей системы дизайна Nord , которая включает в себя создание веб-компонентов для нашей библиотеки компонентов. Я хотел рассказать, как мы решили проблемы со стилизацией веб-компонентов с помощью пользовательских свойств CSS , а также о некоторых других преимуществах использования пользовательских свойств в системах проектирования и библиотеках компонентов.
Как мы создаем веб-компоненты
Для создания наших веб-компонентов мы используем Lit , библиотеку, которая предоставляет множество шаблонного кода, такого как состояние, стили с заданной областью, шаблоны и многое другое. Lit не только легкий, но и построен на собственных API-интерфейсах JavaScript, а это означает, что мы можем предоставить компактный набор кода, который использует преимущества функций, которые уже есть в браузере.
Но самое привлекательное в веб-компонентах то, что они работают практически с любой существующей платформой JavaScript или даже вообще без нее. Когда на странице есть ссылка на основной пакет JavaScript, использование веб-компонента очень похоже на использование собственного HTML-элемента. Единственным явным признаком того, что это не собственный элемент HTML, является последовательный дефис внутри тегов, который является стандартом, указывающим браузеру, что это веб-компонент.
Инкапсуляция стиля Shadow DOM
Примерно так же, как собственные HTML-элементы имеют Shadow DOM , то же самое можно сказать и о веб-компонентах. Shadow DOM — это скрытое дерево узлов внутри элемента. Лучший способ визуализировать это — открыть веб-инспектор и включить опцию «Показать теневое дерево DOM». Сделав это, попробуйте просмотреть собственный элемент ввода в инспекторе — теперь у вас будет возможность открыть этот ввод и просмотреть все элементы внутри него. Вы даже можете попробовать это с одним из наших веб-компонентов — попробуйте проверить наш пользовательский компонент ввода, чтобы увидеть его теневой DOM.
Одним из преимуществ (или недостатков, в зависимости от вашего мировоззрения) Shadow DOM является инкапсуляция стилей. Если вы пишете CSS внутри своего веб-компонента, эти стили не могут выйти наружу и повлиять на главную страницу или другие элементы; они полностью содержатся внутри компонента. Кроме того, CSS, написанный для главной страницы или родительского веб-компонента, не может проникнуть в ваш веб-компонент.
Такая инкапсуляция стилей является преимуществом нашей библиотеки компонентов. Это дает нам большую гарантию того, что когда кто-то использует один из наших компонентов, он будет выглядеть так, как мы задумали, независимо от стилей, примененных к родительской странице. И чтобы еще больше убедиться, добавляем all: unset;
в корень или «хост» всех наших веб-компонентов.
Однако что, если у кого-то, использующего ваш веб-компонент, есть законная причина изменить определенные стили? Может быть, есть строка текста, которая требует большего контраста из-за контекста или граница должна быть толще? Если никакие стили не могут проникнуть в ваш компонент, как вы можете разблокировать эти параметры стиля?
Вот тут-то и приходят на помощь пользовательские свойства CSS.
Пользовательские свойства CSS
Пользовательские свойства названы очень правильно — это свойства CSS, которым вы можете полностью дать имя и применить любое необходимое значение. Единственное требование — поставить перед ними два дефиса. После того как вы объявили свое пользовательское свойство, его значение можно использовать в CSS с помощью функции var()
.
Когда дело доходит до наследования, все пользовательские свойства наследуются, что соответствует типичному поведению обычных свойств и значений CSS. Любое пользовательское свойство, примененное к родительскому элементу или самому элементу, можно использовать в качестве значения других свойств. Мы активно используем пользовательские свойства для наших токенов дизайна, применяя их к корневому элементу через нашу CSS Framework. Это означает, что все элементы на странице могут использовать эти значения токенов, будь то веб-компонент, вспомогательный класс CSS или разработчик, желающий получить значение из нашего списка токенов.
Эта возможность наследовать пользовательские свойства с использованием функции var()
позволяет нам проникнуть сквозь теневую модель DOM наших веб-компонентов и предоставить разработчикам более детальный контроль над стилизацией наших компонентов.
Пользовательские свойства в компоненте Nord Web
Всякий раз, когда мы разрабатываем компонент для нашей дизайн-системы, мы тщательно подходим к его CSS — нам нравится стремиться к созданию простого, но очень удобного в сопровождении кода. Имеющиеся у нас токены дизайна определяются как пользовательские свойства в нашей основной платформе CSS в корневом элементе.
Эти значения токенов затем используются в наших компонентах. В некоторых случаях мы применяем значение непосредственно к свойству CSS, но в других мы фактически определяем новое контекстное пользовательское свойство и применяем к нему значение.
Мы также абстрагируем некоторые значения, специфичные для компонента, но не в наших токенах, и превратим их в контекстное пользовательское свойство. Пользовательские свойства, контекстно-зависимые от компонента, дают нам два ключевых преимущества. Во-первых, это означает, что мы можем быть более «сухими» с нашим CSS, поскольку это значение можно применять к нескольким свойствам внутри компонента.
А во-вторых, это делает изменения состояния и вариантов компонента действительно понятными — для обновления всех этих свойств необходимо изменить только пользовательское свойство, когда, скажем, вы стилизуете наведение или активное состояние или, в данном случае, вариант.
Но самое важное преимущество заключается в том, что когда мы определяем эти контекстные пользовательские свойства для компонента, мы создаем своего рода собственный CSS API для каждого из наших компонентов, к которому может подключиться пользователь этого компонента.
В предыдущем примере показан один из наших веб-компонентов с контекстным настраиваемым свойством, измененным с помощью селектора. Результатом всего этого подхода является компонент, который обеспечивает пользователю достаточную гибкость стилей, сохраняя при этом большую часть реальных стилей под контролем. Плюс, в качестве бонуса, мы, как разработчики компонентов, имеем возможность перехватывать стили, применяемые пользователем. Если мы хотим настроить или расширить одно из этих свойств, мы можем сделать это без необходимости изменения пользователем какого-либо кода.
Мы считаем этот подход чрезвычайно эффективным не только для нас как создателей компонентов нашей системы проектирования, но и для нашей команды разработчиков, когда они используют эти компоненты в наших продуктах.
Дальнейшее развитие пользовательских свойств
На момент написания мы фактически не раскрываем эти контекстные пользовательские свойства в нашей документации ; однако мы планируем сделать это, чтобы наша более широкая команда разработчиков могла понять и использовать эти свойства. Наши компоненты упаковываются на npm с файлом манифеста , который содержит все, что о них нужно знать. Затем мы используем файл манифеста в качестве данных при развертывании нашего сайта документации, что осуществляется с помощью Eleventy и ее функции глобальных данных . Мы планируем включить эти контекстные пользовательские свойства в этот файл данных манифеста.
Еще одна область, которую мы хотим улучшить, — это то, как эти контекстные пользовательские свойства наследуют значения. В настоящее время, например, если вы хотите настроить цвет двух компонентов-разделителей, вам нужно будет настроить оба этих компонента специально с помощью селекторов или применить пользовательское свойство непосредственно к элементу с атрибутом стиля. Казалось бы, это нормально, но было бы полезнее, если бы разработчик мог определить эти стили в содержащем элементе или даже на корневом уровне.
Причина, по которой вам необходимо установить значение пользовательского свойства непосредственно в компоненте, заключается в том, что мы определяем их для одного и того же элемента через селектор хоста компонента. Токены глобального дизайна, которые мы используем непосредственно в компоненте, проходят напрямую, не затрагиваясь этой проблемой, и даже могут быть перехвачены в родительских элементах. Как мы можем получить лучшее из обоих миров?
Частные и общедоступные пользовательские свойства
Частные пользовательские свойства — это то, что было создано Леа Веру и представляет собой контекстное «частное» пользовательское свойство самого компонента, но установленное в «публичное» пользовательское свойство с резервным вариантом.
Определение наших контекстных пользовательских свойств таким образом означает, что мы по-прежнему можем делать все, что делали раньше, например наследовать значения глобальных токенов и повторно использовать значения во всем коде нашего компонента; но компонент также будет корректно наследовать новые определения этого свойства для себя или любого родительского элемента.
Хотя можно возразить, что этот метод не является по-настоящему «частным», мы все же считаем, что это довольно элегантное решение проблемы, которая нас беспокоила. Когда у нас будет возможность, мы будем решать эту проблему в наших компонентах, чтобы наша команда разработчиков имела больше контроля над использованием компонентов, сохраняя при этом выгоду от имеющихся у нас ограничений.
Надеюсь, эта информация о том, как мы используем веб-компоненты с настраиваемыми свойствами CSS, оказалась для вас полезной. Дайте нам знать, что вы думаете, и если вы решите использовать какой-либо из этих методов в своей работе, вы можете найти меня в Твиттере @DavidDarnes . Вы также можете найти Nordhealth @NordhealthHQ в Твиттере, а также остальных членов моей команды, которые усердно работали над объединением этой системы дизайна и реализацией функций, упомянутых в этой статье: @Viljamis , @WickyNilliams и @eric_habich .
Изображение героя Дэна Кристиана Падурца.