https://habrahabr.ru/company/alfa/blog/340522/- Open source
- JavaScript
- HTML
- CSS
- Блог компании «Альфа-Банк»
Дизайн — это фашизм. Фашизму нужна питательная среда. Он начинает раскрываться в полной мере только на крупных масштабах. Идеальная среда для фашизма — это большая компания с огромным количеством продуктов. Например, Google или… Альфа-Банк. Фашизм априори не гибок…
Все кнопочки на всех продуктах компании должны носить одинаковые рубашки, только одного номенклатурного цвета #F02823. Любая ссылка также имеет свою униформу: цвет #0A1E32, нижнее подчеркивание на расстоянии 2px. Если мы нажмем на ссылку, она должна незамедлительно выполнить команду — перенести нас на другой раздел приложения. За неподчинение — изгнание из дизайна системы Альфа-Банка в Зеленый Банк или расстрел. И неизвестно, что бы в этом случае выбрала ссылка.
Но это фашизм во имя Любви. Мы любим своего пользователя: мы хотим, чтобы на каждом нашем продукте пользователь получал одинаковый опыт. Чтобы ему было легко и просто. Чтобы Банк быстро и эффективно решал его задачи.
Дизайн — это фашизм во имя Любви
Это фашизм ради драйва и скорости. Мы хотим разрабатывать наши приложения быстро, чтобы разработчики не изобретали каждый раз велосипед для новых приложений и могли шарить лучшие UI/UX-практики между командами.
Любой фашизм предполагает идеологию. Любой фашизм предполагает централизованное принятие решений. Для любого фашизма компании необходимо завести Самый Главный Комитет по Цензуре и Унификации или СГКпЦиУ.
Но, подождите, теперь Альфа-Банк — это бирюзовая компания, в движок которой зашит манифест Agile и Scrum. Это означает, что мы осознанно приняли стратегию, что все решения «зашиты» в команды, а не в комитеты по типу СГКпЦиУ…
Как сохранить консистентность дизайна и не потерять гибкость разработки?
Наша библиотека компонентов
ARUI Feather базируется на двух хорошо знакомых решениях из мира фронтенда:
БЭМ-методологии и
React.
Здесь не будет рассказа про выбор инструментов: мне больше хочется рассказать про принципы и практики масштабирования дизайн-систем, которые мы выработали в процессе создания
ARUI Feather.
Подробнее о том, почему у нас именно
БЭМ-методология +
React, можно узнать из
этого видео с Яндекс.Деньги FrontendMix 2017.
В основе инженерных решений ARUI Feather лежит философия
KISS / YAGNI / DRY
KISS означает, что мы изначально для себя решили избегать сложных решений. Перед нами стояла задача сделать код дизайн-системы, в котором сможет разобраться самостоятельно любая команда.
ARUI Feather — это АК-47 мира дизайн-систем. Даже лежа по уши в песке в окопах под Багдадом, вы можете самостоятельно разобрать и собрать ее, не обращаясь в сервис-центр ВМС США.
Также, следуя
YAGNI, мы делаем только необходимое, и последний тренд — мы чаще удаляем компоненты, чем создаем новые, потому что на самом деле многие вещи лежат за зоной ответственности дизайн-системы.
ARUI Feather — это АК-47 мира дизайн-систем. Даже лежа по уши в песке в окопах под Багдадом, вы можете самостоятельно разобрать и собрать ее, не обращаясь в сервис-центр ВМС США
По этой причине мы используем
БЭМ-методологию не в полной реализации, исключая из нее
миксы и
уровни переопределения. Оба эти подхода про “смешивание”, что при масштабировании включает на проектах “безумный миксер”, делая код тяжелым для отладки.
Технически мы поддерживаем дизайн-систему через наше собственное Open Source решение —
cn-decorator, которое позволяет использовать
БЭМ-методологии и
React вместе.
С какими проблемами мы столкнулись при масштабировании дизайн-системы?
В Альфа-Банке уже более 30 команд, которые разрабатывают своей фронтенд независимо, используя
ARUI Feather и
cn-decorator.
У нас нет отдельной выделенной команды, которая сконцентрирована на разработке UI/UX-библиотеки. Разработка ведется по принципам, сложившимся в Open Source: есть мейнтейнеры библиотеки компонентов, есть контрибьюторы и есть, конечно, пользователи. И все эти люди так или иначе участники разных команд. Мы осознанно пошли на этот шаг, чтобы избежать появления в компании узкого звена в виде команды разработки библиотеки, которой другие команды делают заказ и ожидают, когда им помогут.
Также это помогает решать задачи, которые действительно важны для создания продукта, а не задачи из вакуума, которые часто любят себе выдумывать сервисные команды.
Далее я расскажу про топ вопросов от команд, которые поступают мейнтейнерам, и как мы их решаем…
А как вообще мне компонент написать-то?
Так выглядит самый простой компонент, написанный с использованием
cn-decorator.
import cn from 'cn-decorator';
@cn('button')
class Button extends React.Component {
render(cn) {
return <button className={ cn() } />;
}
}
Достаточно просто использовать декоратор
@cn и передать название блока, в данном примере
‘button’. Теперь метод
render получит свой экземпляр
cn, который может быть использован для генерации имен классов. И наш финальный БЭМ-блок в HTML будет выглядеть приблизительно так:
<button class="button"></button>
Но ведь в
БЭМ-методологии есть еще элементы, модификаторы,
миксы и
уровни переопределения… Пример чуть сложнее:
import cn from 'cn-decorator';
@cn('button')
class Button extends React.Component {
render(cn) {
return (
<button className={ cn({ disabled: true }) }>
<span className={ cn('text') }>Text</span>
</button>
);
}
}
В результате у нас получается следующая верстка:
<button class="button button_disabled">
<span class="button__text">Text</span>
</button>
На примере наглядно показано, как
cn-decorator умеет обращаться с модификаторами и элементами. Остается добавить немного CSS, и компонент готов!
Мы тут подумали: если поменять цвет рамочек вот у этой кнопки, то конверсия повысится на 200%! Нам что, кнопку с нуля делать?
Альфа-Банк — это на 100% продуктовая компания. Наши команды на регулярной основе проводят десятки экспериментов. Иногда даже небольшое изменение цвета рамочки может привести к изменению конверсии.
Если бы у нас был комитет СГКпЦиУ, то нам бы пришлось вынести решение об таком незначительном эксперименте на его ближайшее собрание, дождаться вердикта и, спустя долгие полгода, все-таки повысить конверсию. Технически мы бы использовали WebComponents и запретили бы любое вмешательство в верстку и API компонента.
Но жизнь богаче, и каждая из команд имеет полное право на проведение экспериментов с дизайном. Для этого в
cn-decorator встроен механизм
className proxy…
import Button from 'arui-feather/button';
class App extends React.Component {
render() {
return <Button className="my-class" />;
}
}
В результате мы получаем следующую верстку:
<button class="button my-class"></button>
Теперь мы можем просто на проекте в селекторе
.my-class перекрыть пару свойств нашей кнопки…
Мы еще подумали и, кажется, знаем, как повысить конверсию на 500%! Но, нам нужна кнопка… Нет, она должна нажиматься как старая, но выглядит-то она совсем по-другому… Нам опять с нуля делать?
И такое случается. Согласитесь, обидно писать компонент, логика работы которого тебя полностью устраивает, но выглядит он совершенно по-другому. Чуть выше я говорил о том, что мы не любим все паттерны смешивания, а предпочитаем паттерны на основе композиции. Поэтому у нас есть механизм
перегрузки имени блока, который позволяет разобрать компонент на составные части: стили и логику.
На этой картинке две кнопки. Они выглядят совершенно по-разному, тем не менее шарят между собой все поведение. Мы добиваемся этого тем, что умеем
перегружать базовое имя блока.
Достаточно просто передекорировать компонент и написать для него новые стили:
import cn from 'arui-feather/cn';
import Button from 'arui-feather/button';
import './tag-button.css';
@cn('tag-button')
class TagButton extends Button {};
Результирующая верстка
TagButton:
<button class="tag-button tag-button_disabled">
<span class="tag-button__text">Text</span>
</button>
Такой несложный паттерн позволяет нам шарить логику компонента между разными представлениями.
А у нас тут дизайнер нарисовал компонент, который выглядит как Link, но работает как Select… А у вас такого нет! А это повысит конверсию на 1000%!
Это были простые примеры, но часто наши компоненты составные (помните, мы любим композицию). Например, таким составным компонентов является
Select: он состоит из двух компонентов
Button и
Popup.
Приблизительно так выглядит код
Select:
import cn from 'arui-feather/cn';
import Button from 'arui-feather/button';
import Popup from 'arui-feather/popup';
@cn('select')
class Select extends React.Component {
render(cn) {
return (
<div className={ cn() }>
<Button />
<Popup />
</div>
);
}
}
Но иногда командам нужно поменять составной компонент. Например, команде нужно, чтобы
Popup выпадал из ссылки, а не из кнопки. Приблизительно так:
Но у нас модульная система на ES6 modules. Единственная возможность заменить составной компонент — это сделать патч на уровне сборки. Здесь на помощь снова приходит
cn-decorator и его фича
Dependency Injection Components. Давайте передадим наши составные компоненты через
cn:
import cn from 'arui-feather/cn';
import Button from 'arui-feather/button';
import Popup from 'arui-feather/popup';
@cn('select', Button, Popup)
class Select extends React.Component {
render(cn, Button, Popup) {
return (
<div className={ cn() }>
<Button />
<Popup />
</div>
);
}
}
Теперь мы можем сделать собственный
Select, заменив в нем
Button на наш собственный.
import cn from 'arui-feather/cn';
import Select from 'arui-feather/select';
import Popup from 'arui-feather/popup';
import MyLinkButton from './my-link-button';
@cn('my-link-select', MyLinkButton, Popup)
class MyLinkSelect extends Select {};
Ура! Теперь мы можем менять любой составной компонент композиции!
Теперь вы видели все
Дизайн-система в большой компании — это не про технологии: это не про холивар Angular vs БЭМ vs React. Дизайн-система — это поиск компромиссов между консистентностью и возможностью проводить быстрые эксперименты. Дизайн-система — это работа с комьюнити и работа с бизнес-требованиями одновременно. Это b2b- и b2c-решение: на одной чаше весов бизнес, который хочет быстро, дешево и качественно, и с другой стороны разработчики, которые хотят гибко, расширяемо, но предсказуемо и надежно.
Хочется завершить эту статью одним очень точным законом, который лучше всего объясняет архитектуру дизайн-систем (да и в принципе любую архитектуру):
«Организации, проектирующие системы (здесь имеется в виду более широкое толкование, включающее не только информационные системы), неизбежно производят конструкцию, чья структура является копией структуры взаимодействия внутри самой организации»
—Закон Конвея
Наши Open Source-решения:ARUI Feather — Библиотека UI-компонентов Альфа Банка
cn-decorator — Лучший способ использовать
БЭМ-методологию с
ReactНаши вакансии во фронтенд-разработке и дизайне:Дизайнер интерфейсов / Дизайнер цифровых продуктовФронтенд-разработкаДизайн