Дизайн
Ekaterina Godunova
3179

От UI-кит до дизайн-системы

Опыт онлайн-кинотеатра ivi.

В закладки

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

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

Материалы ivi

Меж тем компания год от года удваивала штат — нужно было масштабировать отдел дизайна и оптимизировать процессы создания и передачи макетов в разработку.

Умножаем всё это на «зоопарк» платформ, которые нужно поддерживать, и получаем подобие вавилонского столпотворения, которое просто не способно «нормально делать» и приносить доход.

Развитие платформ часто шло параллельно, и одна и та же функциональность могла выходить на разных платформах с лагом в несколько месяцев.

Отдельные наборы макетов для каждой платформы

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

Это нужно для того, чтобы развитие функциональности стало возможным сразу на всех наших платформах одновременно: веб, iOS, Android, Smart TV, tvOS, Android TV, Windows 10, Xbox One, PS4, Roku — не прорабатывая при этом каждую из них в отдельности. И это у нас получилось!

Дизайн → данные

Когда принципиальные договорённости отделов продукта и разработки были достигнуты, мы сели за подбор технологического стека и проработку деталей всего процесса — от макета до релиза. Чтобы полностью автоматизировать процесс передачи дизайна в разработку, исследовали вариант с парсером параметров компонентов прямо из Sketch-файлов с макетами.

Оказалось, что находить нужные нам куски кода и извлекать нужные нам параметры — затея сложная и опасная. Во-первых, дизайнерам придётся быть крайне аккуратными в именовании всех слоёв исходника, во-вторых, это работает только для самых простых компонентов, а в-третьих, зависимость от чужой технологии и структуры кода исходного Sketch-макета ставит под угрозу будущее всего проекта.

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

Дело оставалось за малым: где и как хранить данные, как передавать в разработку и как интерпретировать в разработке на всех поддерживаемых нами платформах. Вечер переставал быть томным…

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

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

Стилям любого из уровней, кроме пиктограмм, присваиваем несемантические имена, например, названия городов, имена нимф, покемонов, марки автомобилей... Тут условие одно: список не должен исчерпаться раньше, чем закончатся стили — шоу маст гоу он! Семантикой же не стоит увлекаться, чтобы не пришлось добавлять среднюю кнопку между «small» и «medium», например.

Визуальный язык

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

Ранее мы уже успели «обкатать» большинство элементов дизайна в приложении под Windows 10, которое на тот момент было для нас новой платформой, то есть требовалась отрисовка и разработка «с нуля».

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

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

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

Теперь нужно привести к одному размеру макета все большие экраны и вписать их в общую сетку. Apple TV и Roku разрабатываются в размер 1920 x 1080, Android TV — 960 x 540, Smart TV в зависимости от вендора бывают такими же, а бывают 1280 x 720. Когда приложение рендерится и отображается на экранах Full HD, 960 умножается на 2, 1280 на 1,33, а 1920 выводится как есть.

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

Единый UI для всех платформ

Теперь для отрисовки новой фичи нам не нужно рисовать макеты под каждую из платформ, плюс варианты адаптивности для каждой из них. Достаточно показать один макет и его адаптивность для всех платформ и устройств любой ширины: телефоны — 320–599, всё остальное — 600–1280.

Данные → разработка

Конечно, как бы нам ни хотелось прийти к абсолютно унифицированному дизайну, каждая платформа имеет свои уникальные особенности. Несмотря на то, что и веб, и Smart TV используют стек ReactJS + TypeScript, приложение для Smart TV запускается на устаревших WebKit- и Presto-клиентах, и потому не может использовать общие стили с вебом.

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

Поэтому для нас не подходит распространённая схема — поставлять готовые CSS-стили или React-компоненты. Мы решили передавать данные в формате JSON, описывая значения в абстрактном декларативном виде.

Так свойство rounding: 8 приложение Windows 10 преобразует в CornerRadius="8", веб — в border-radius: 8px, Android — в android:radius="8dp", iOS — в self.layer.cornerRadius = 8.0.

Cвойство offsetTop: 12 один и тот же веб-клиент в разных случаях может интерпретировать как top, margin-top, padding-top или transform.

Декларативность описания также предполагает, что если платформа технически не может использовать какое-либо свойство или его значение, она может его проигнорировать. С точки зрения терминологии — мы сделали некое подобие языка эсперанто: что-то взяли из Android, что-то из SVG, что-то из CSS.

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

Например, состояние «в фокусе» для Smart TV диктует изменение позиции текста под постером, значит — для этой платформы данный компонент в значении свойства «отступ» будет содержать необходимые ей 8 поинтов отступа.

Хоть это и усложняет инфраструктуру дизайн-системы, зато даёт дополнительную степень свободы, оставляя нам возможность самим управлять визуальной «непохожестью» платформ, а не быть заложниками нами же созданной архитектуры.

Пиктограммы

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

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

Предпросмотр

Поверх JSON’а с данными мы написали инструмент для предпросмотра компонентов — JS-приложение, на лету пропускающее JSON-данные через свои генераторы разметки и стилей, и отображающее в браузере различные вариации каждого из компонентов. Предпросмотр является точно таким же клиентом, как и платформенные приложения, и работает с теми же данными.

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

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

Документация

На основе тех данных, которые в виде JSON поставляются платформам, автоматически формируется документация по компонентам. Описывается перечень свойств и возможные типы значений в каждом из них.

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

Депрекатор

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

Клиентская разработка

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

Для верстки экранов iOS-приложения мы используем два базовых механизма, которые предоставляет iviUIKit: cвободная компоновка элементов и компоновка коллекций элементов. Мы используем VIPER, и всё взаимодействие с iviUIKit сосредоточено во View, а большая часть взаимодействия с Apple UIKit сосредоточена в iviUIKit.

Размеры и расположение элементов задаются в терминах колонок и синтаксических конструкций, работающих поверх нативных констрейнтов iOS SDK, делающих их более прикладными. Особенно это упростило нам жизнь при работе с UICollectionView.

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

Для генерации стилей в проекте Android мы используем Gradle, превращая данные дизайн-системы в стили формата XML. При этом у нас есть несколько генераторов различного уровня:

  • Базовые. Парсят данные примитивов для генераторов более высокого уровня.
  • Ресурсные. Скачивают картинки, иконки и прочую графику.
  • Компонентные. Пишутся для каждого компонента, где описано какие свойства и как перевести в стили.

Релизы приложений

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

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

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

Итоги

Скоро год как дизайн-система стала частью инфраструктуры, обслуживающей развитие онлайн-кинотеатра ivi, уже можно делать кое-какие выводы:

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

Предпросмотр компонентов дизайн-системы ivi — design.ivi.ru.

Материал опубликован пользователем.
Нажмите кнопку «Написать», чтобы поделиться мнением или рассказать о своём проекте.

Написать
{ "author_name": "Ekaterina Godunova", "author_type": "self", "tags": [], "comments": 13, "likes": 14, "favorites": 73, "is_advertisement": false, "subsite_label": "design", "id": 72354, "is_wide": true, "is_ugc": true, "date": "Thu, 20 Jun 2019 13:14:16 +0300", "is_special": false }
0
{ "id": 72354, "author_id": 59671, "diff_limit": 1000, "urls": {"diff":"\/comments\/72354\/get","add":"\/comments\/72354\/add","edit":"\/comments\/edit","remove":"\/admin\/comments\/remove","pin":"\/admin\/comments\/pin","get4edit":"\/comments\/get4edit","complain":"\/comments\/complain","load_more":"\/comments\/loading\/72354"}, "attach_limit": 2, "max_comment_text_length": 5000, "subsite_id": 199114, "last_count_and_date": null }
13 комментариев
Популярные
По порядку
Написать комментарий...
3

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

2. Разработчик использует отдельный фреймворк для построения UI. Интерфейс фреймворка довольно удобен и прост в рамках принятой парадигмы. Вхождение новых разработчиков как правило происходит довольно быстро. Со стороны UI клиентский разработчик за пределами фреймворка только собирает и располагает на экране компоненты дизайн-системы

3. Ситуация с отладкой, по крайней мере, не стала хуже, так как конкретные имплементации дизайн-системы нативны для каждой платформы.

Из дополнительных ускорений могу отметить существенное сокращение времени на разработку и поддержку нескольких приложений в рамках одной кодовой базы. Например, для команды iOS, помимо основного, это приложение под apple tv и приложение ivi для детей. Но это тема для отдельной статьи =) Над масштабированием дизайн-системы мы продолжаем активно работать.

Ответить
1

Круто, спасибо!

Ответить
1

Интересная статья, спасибо! Расскажите, как дизайн система повлияла на скорость создания новых релизов ваших приложений? Сократилось ли время на прототипирование, разработку, отладку?

Ответить
0

промахнулся и ответил не в тред. коммент ниже)

Ответить
1

Как вы унифицируете дизайн между iOs и Smart tv? Первое — маленький экран, управление пальцем, расстояние от глаз до устройства на уровне вытянутой руки, второй — огромный экран, управление пультом, расстояние на уровне пяти метров. Как дизайнеры учитывают эту разницу для разных платформ, рисуя только один макет?

Ответить
0

Разница в размерах, как вы заметили, условно кратна разнице расстояний взаимодействия. Поэтому мы взяли макет планшета и растянули его на весь экран телевизора. Всё просто. С навигацией сложнее, она на этих устройствах разная.

Ответить
1

Есть косяк в интерфейсе Apple TV 4K куда прислать скрин?

Ответить
0

ikhromov[at]ivi.ru
Это был первый релиз, многое ещё не сделано

Ответить
0

Отправил скрин.

Ответить
0

Спасибо, получил, починим!

Ответить
0

Принципиально не пользуюсь последними версиями с новым дизайном на смартфоне и планшете. Да они красивые и функциональные, но мне не нужен портал Иви на своём устройстве.

Ответить
0

Похоже вы изобрели Design tokens. Так вышло или ориентировались на похожие подходы, например https://www.lightningdesignsystem.com/design-tokens/ ? или https://medium.com/eightshapes-llc/tokens-in-design-systems-25dd82d58421

Ответить
0

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

Ответить
{ "page_type": "article" }

Прямой эфир

[ { "id": 1, "label": "100%×150_Branding_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox_method": "createAdaptive", "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfl" } } }, { "id": 2, "label": "1200х400", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfn" } } }, { "id": 3, "label": "240х200 _ТГБ_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fizc" } } }, { "id": 4, "label": "Article Branding", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "p1": "cfovx", "p2": "glug" } } }, { "id": 5, "label": "300x500_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfk" } } }, { "id": 6, "label": "1180х250_Interpool_баннер над комментариями_Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "bugf", "p2": "ffyh" } } }, { "id": 7, "label": "Article Footer 100%_desktop_mobile", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjxb" } } }, { "id": 8, "label": "Fullscreen Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjoh" } } }, { "id": 9, "label": "Fullscreen Mobile", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjog" } } }, { "id": 10, "disable": true, "label": "Native Partner Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyb" } } }, { "id": 11, "disable": true, "label": "Native Partner Mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyc" } } }, { "id": 12, "label": "Кнопка в шапке", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "p1": "bscsh", "p2": "fdhx" } } }, { "id": 13, "label": "DM InPage Video PartnerCode", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox_method": "createAdaptive", "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "bugf", "p2": "flvn" } } }, { "id": 14, "label": "Yandex context video banner", "provider": "yandex", "yandex": { "block_id": "VI-223676-0", "render_to": "inpage_VI-223676-0-1104503429", "adfox_url": "//ads.adfox.ru/228129/getCode?pp=h&ps=bugf&p2=fpjw&puid1=&puid2=&puid3=&puid4=&puid8=&puid9=&puid10=&puid21=&puid22=&puid31=&puid32=&puid33=&fmt=1&dl={REFERER}&pr=" } }, { "id": 15, "label": "Баннер в ленте на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byudx", "p2": "ftjf" } } }, { "id": 16, "label": "Кнопка в шапке мобайл", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byzqf", "p2": "ftwx" } } }, { "id": 17, "label": "Stratum Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fzvb" } } }, { "id": 18, "label": "Stratum Mobile", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fzvc" } } }, { "id": 19, "disable": true, "label": "Тизер на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "p1": "cbltd", "p2": "gazs" } } } ] { "page_type": "default" }