Наследуемые свойства в SDUI платформе ВкусВилл. Часть 1: Контекст
Концепция была разработана в рамках работы над дизайн-платформой для SDUI приложения ВкусВилл , и основной задачей было сократить количество параметров и возможных состояний виджетов, уменьшив таким образом дерево объектов, но при этом сохранить вариативность и управляемость. В дальнейшем, под платформой понимается решение, которое обеспечивает реализацию всех SDUI функций, т.е. технологии, а под дизайн-системой - решение по структурированию, иерархизации и оформлению пользовательских виджетов (пресетов).
О том, как применить подобный подход к цветовой семантике без SDUI на примере текущего приложения “ВкусВилл”, я писал в своем телеграм-канале.
Зачем нужны наследуемые свойства
Чтобы ответить на этот вопрос, нужно сначала разобраться, что такое SDUI. SDUI (Server Driven User Interface) - это концепция пользовательского интерфейса, управляемого сервером. Основная часть этой концепции относится не столько к дизайну, сколько к работе с данными и обработке действий пользователя. В традиционных приложениях вся обработка происходит на стороне клиента, включая большую часть бизнес-логики.
В SDUI-приложении клиент настолько “тупой”, что виджетам остается только отправлять события на сервер (например, “На меня нажали”) и ждать от него инструкций о том, как должен измениться интерфейс после этого действия. Вся бизнес-логика в этом случае выносится на серверную сторону.
А клиент умеет только работать с контрактом. Контракт - это чаще всего JSON-дерево объектов и их параметров. Такой подход позволяет вносить изменения на клиенте без выпуска новых версий приложения в магазины приложений. Чем более вариативен отрисовщик и чем более управляем интерфейс, тем больше видов изменений можно внести без выпуска новой версии.
Если каждый раз при изменении передавать все параметры измененных виджетов, используя классические настройки, потребуется передавать большое количество данных дерева объектов и их параметров для обновления интерфейса. Именно для оптимизации этого процесса, ускорения реакции интерфейса на изменения и была разработана концепция наследуемых свойств.
Контракт
Контракт - это JSON, который передается с серверной части приложения на клиентскую и содержит параметры виджетов, которые на клиентской стороне преобразуются в элементы интерфейса.
Возьмем простой контракт из консервативного SDUI-приложения: кнопка с иконкой будет выглядеть следующим образом:
В целом, все достаточно компактно и понятно. Но что, если мы захотим изменить кнопку, изменить ее внешний вид и содержание? Нам придется охватить все параметры и, конечно же, выпустить новую версию приложения в магазинах. Мы же хотим избежать этого как можно дольше. Если мы возьмем, к примеру, JSON-макет простой страницы в Figma, а файлы Figma, как ни странно, в формате JSON, там будет более 800 тысяч строк 🤯.
Так насколько же наследуемые стили позволяют сократить размер контракта? Тот же экран, но уже оптимизированный под нашу платформу в формате JSON имеет всего около 1200 строк. Примерно так выглядит кнопка с иконкой и текстом в этом формате:
Значительно больше строк, чем в первом примере, но все еще читаемо, и внутри может быть все, что угодно. Мы не ограничены заранее определенными параметрами виджета кнопки. Таким образом нам удалось достичь вариативности макета как в Figma и в то же время существенно оптимизировать данные дерева объектов и их параметры.
Контекст (микротема)
Обычная структура семантического токена цвета в теме примерно следующая:
light-theme. background-brand-accent
Иногда к этому добавляется еще уровень токенов компонентов. Все имя токена полностью “лежит” в каком-либо параметре, например “button-background”. Цвет текста отдельно в свойствах “icon-color” и “label-color”, что-то вроде light-theme.text-color-on-accent.
В результате получаем зеленую кнопку.
Но контекст добавляет к этой картине новое измерение
За цвет в такой системе отвечают два свойства: одно для токена цвета и одно для его контекста. Значение контекста указывается в свойстве “context”, а в самом токене указывается только цвет. Например, в свойстве background указывается значение “primary”, а в свойствах text-color и icon-color указывается значение “on-primary”. По умолчанию значение свойства context равно “inherit”. Это означает, что виджет наследует значение контекста от родительского виджета. Если мы хотим, чтобы кнопка была зеленой, мы должны установить значение контекста на “green-accent”.
Контексты и цветовые токены в библиотеке Figma представлены в виде стилей и визуализируются в виде модулей:
Таким образом, правильное управление наследованием свойств позволяет легко управлять внешним видом компонента в коде, используя всего один параметр.
Как видно из приведенного выше примера, кнопка "В корзину" не меняет свой цвет, потому что у нее задан определенный контекст “green-accent”, а не “inherit”, и поэтому она не наследует контекст от своего родительского виджета.
Контексты в фигме
В коде с такими сложными механиками более менее все понятно, но как это реализовать в Figma? К сожалению, идеального решения не существует, даже с использованием новых variables. Сейчас у нас цвета на стилях, и даже если перейти на переменные, то без тарифа Enterprise картина кардинально не изменится. Теоретически, можно было бы попытаться создать mode для каждого контекста и разные коллекции для разных тем. Возможно, это сработало бы, но это очень затратно. Кроме того, в нашей платформе виджеты автоматически выгружаются на фронтенд, а это значит, что параметр также необходимо передавать.
Получается что контекст надо выставлять дважды: один раз для автоматики, второй раз для отображения в фигме руками через замену стилей. Это не так страшно, как кажется, через поиск это делается довольно быстро. Однако, даже у этой проблемы есть решение. Сейчас мы работаем над собственным редактором для экранов платформы. Но о нём и о других наследуемых свойствах в следующих статьях и в моём телеграм канале: