От автора: для сотни инженеров, разработчиков и дизайнеров, работающих над Yelp, обеспечение визуальной согласованности — это сложная задача. Мы перенесли веб-компоненты из Yelp Cheetah в React, чтобы повысить производительность дизайнеров и разработчиков, обеспечивая при этом визуальную согласованность в веб-приложении. И создали Lemon Reset — пакет, содержащий согласованные, кросс-браузерные теги React DOM, оснащенные модулями CSS.
Поскольку компоненты системы проектирования являются строительными блоками front-end, мы должны были перенести их в React, прежде чем разработчики смогут перенести свои функции. Мы внесли много проектных решений в отношении этих новых компонентов, в том числе о том, и о том, как решить проблему с помощью взаимодействия React CSS.
В традиционном веб-стеке, где CSS и файлы разметки независимы, мы применяли стили с использованием селекторов, которые нацелены на элементы и классы DOM. Эти селекторы имеют бесконечное наследование благодаря каскадной части каскадных таблиц стилей — возможно, лучшей и худшей части CSS. Это означает, что мы должны вручную использовать имена классов, используя соглашение об именах BEM, чтобы избежать столкновений именования и войн специфичности. Полагаясь на человеческие соглашения типа ручного пространства имен, область видимости не будет расширяться по мере роста организации и кода.
По мере того как мы добавляли всё новые и новые функции, разработчикам стало сложнее отслеживать все таблицы стилей, которые им нужно было импортировать для своих страниц. Когда мы пишем разметку для button, мы предполагаем, что button.css также будет загружаться всякий раз, когда мы используем компонент Button. Поскольку наши компоненты не могли объявить зависимости от CSS, разработчикам было необходимо убедиться, что стили, которые им нужны для их компонентов, были включены в пакет CSS как шаблоны.
Файл шаблона:
1 2 3 |
<link rel="stylesheet" type="text/css" href="web-pkg.scss"> <button class="btn">Wow!</button> |
web-pkg.scss
1 2 |
import 'yelp_styleguide/assets/scss/lib/buttons'; import 'cool_feature'; |
yelp_styleguide/assets/scss/lib/_buttons.scss
1 2 3 4 5 |
@import 'colors'; .btn { background: $yelpy-red; } |
_cool_feature.scss
1 2 3 |
.my-feature { ... } |
Чтобы решить некоторые из этих проблем в нашей постоянно растущей кодовой базе front-end, мы создали систему проектирования с компонентами многократного использования . Вместо того, чтобы писать каждый компонент с нуля, разработчики могут создавать пользовательские интерфейсы, просто склеивая готовые компоненты, позволяя нам быстрее
итерировать и создавать визуально единообразный продукт.
Но единообразие, которое мы хотели, было неосуществимо из-за каскадного характера CSS. Наши согласованные имена классов позволили разработчикам отклониться от нашей системы проектирования, создав новые, более конкретные правила CSS, что усложняет для нас постоянный визуальный язык.
1 2 3 4 5 6 |
.btn { color: white; } .btn--primary { background: red; } |
1 |
<button class="btn btn--primary">Wow!</button> |
Это означает, что наши стандартные компоненты не всегда могут быть такими стандартными в использовании, поскольку такие вещи, как правила цвета, могут быть легко переопределены одной строкой исключений CSS.
1 2 3 |
.btn.btn--primary { background: blue; } |
С сотнями разработчиков многих функциональных команд наша команда разработчиков front-end не может просматривать каждый фрагмент кода, который касается файла SCSS. После всех усилий по стандартизации компонентов одна строка CSS все же пробралась в кодовую базу и превратила красные кнопки в синие!
На помощь придёт React
В мире React мы можем объединить JSX вместе с требуемым стилем. Наши компоненты React декларируют зависимости от CSS, которые им нужны, а это значит, что мы можем статически извлекать все стили, необходимые на странице, чтобы нашим разработчикам больше не приходилось вручную следить за тем, чтобы CSS компоненты были включены!
Button.js
1 2 3 |
import styles from'./Button.scss'; <Button className={styles.button}/> |
Button.scss
1 2 3 |
.button { background: red; } |
К тому же, вместо того, чтобы полагаться на человеческие соглашения об именах, мы можем использовать модули CSS для автоматического определения имен классов. Это означает, что нам больше не нужно беспокоиться об именовании и стилизации конфликтов!
Звучит слишком хорошо, чтобы быть правдой. В чём же подвох?
Если всё в CSS-модулях становится пространством имен, как мы разделяем основные стили, которые мы хотим включить во все страницы наших компонентов? В мире разметки и CSS мы предоставили Meyer Reset — глобальную таблицу стилей со стилями, которые нацелены на элементы DOM, такие как h1 для красных заголовков Yelpy, которые мы знаем и любим; однако любой селектор CSS, ориентированный на элемент DOM, а не на класс, не может быть охвачен, а область видимости – весь модуль CSS!
1 2 3 4 |
h1 { font-size: $h1-font-size; color: $yelpy-red; } |
Рассмотрим несколько вариантов.
Мы могли бы просто сделать то, к чему привыкли, и включить пакет со стилями, которые хотели бы получить в глобальном масштабе, например, Meyer Reset, стили печати и scss-сеть. Мы могли бы объявить зависимость для всех наших компонентов в пакете глобального стиля, позволить Webpack решать, какие стили нам нужны, и выгружать эти стили как глобальные на странице, но побочные эффекты кажутся страшнымми, когда мы можем явно выражать зависимости от наших CSS.
Глобальные стили также будут загрязнять всю страницу и сделать невозможным использование кнопки Yelp в изоляции, не затрагивая другие стили на странице. Yelp Wifi и Yelp Reservations являются отдельными продуктами из приложения Yelp для потребителей, поэтому для них важно иметь возможность заимствовать компоненты по своему усмотрению, а не быть вынужденными принять всю систему проектирования.
Итак, что же делать основной веб-команде?
Решение
Нам понадобилось решение, удовлетворяющее двум ключевым требованиям:
Компоненты должны быть изолированы друг от друга
Компоненты должны быть достаточно обобщенными, чтобы разработчики могли использовать их по всему сайту, но достаточно последовательными, чтобы соответствовать нашей системе проектирования
Решением, которое мы приняли, было создание компонентов для всего и стилизация компонентов в двух слоях:
Основные компоненты для тегов HTML, которые будут обрабатывать стили базовой строки для сброса браузера.
Компоненты системы проектирования, которые абстрагируют от лежащих в основе элементов DOM и раскрывают заранее определенный разработчиками набор стилей.
Слой 1: Lemon Reset
Первый слой компонентов должен содержать базовый стиль, который требуется каждому компоненту: таблица стилей сброса для согласованности браузера. Мы не обнаружили ни одной существующей библиотеки с открытым исходным кодом, которая предоставляла базовые компоненты React с популярными стилями сброса CSS стилей Eric Meyer, стили Meyer Reset уже встроены для использования с модулями CSS, поэтому мы создали свои собственные.
Первым слоем компонентов стал Lemon Reset, теперь откроем предоставление для вашего удобства!
Для каждого элемента в Meyer Reset мы создали компонент React:
1 2 3 4 5 6 7 8 9 10 11 |
export const LemonReset = ({ tag: Tag, children, className, tagRef, ...otherProps }: Props) => ( <Tag className={classNames(styles\[`lemon--${Tag}`\], className)} ref={tagRef} {...otherProps}> {children} </Tag> ); export const H1 = ({ children, className, ...otherProps }: TagProps) => ( <LemonReset tag="h1" className={className} {...otherProps}> {children} </LemonReset> ); |
Компонент «LemonReset.H1» имеет стиль сброса CSS для h1 из Meyer Reset, и всё. Второй слой компонентов абстракции может использовать эти компоненты сброса, передавая все необходимые реквизиты вплоть до основного элемента HTML.
Слой 2: компоненты системы проектирования
Компоненты системы проектирования, которые абстрагируют от лежащих в основе элементов DOM и раскрывают заранее определенный разработчиками набор стилей.
Вдохновленные React Primitives, мы создали эти компоненты абстракции, инкапсулирующие Lemon Reset для использования нашими разработчиками, например:
<Text>
<Heading>
<Container>
<Button>
И 65 других компонентов
Это позволяет нам сдерживать шаблоны, которые появляются в пользовательском интерфейсе. Разработчики должны выбирать значения из системы проектирования, которые мы предоставляем для реквизита, такого как font size, color, margin, padding, и background-color. Эти стандарты помогают нам создавать визуальную согласованность между решениями Yelp и поверх существующими решениями для потребностей проектирования.
Используя CSS-модули и предоставляя компоненты абстракции со встроенными стилями из нашей системы проектирования для разработчиков, мы можем увеличить производительность дизайнеров и разработчиков и обеспечить визуальную согласованность в веб-приложении.
Автор: Theresa Ma
Источник: //engineeringblog.yelp.com/
Редакция: Команда webformyself.