Как стать автором
Обновить
0
Badoo
Big Dating

Дизайн-токены способны на большее: создаем единый источник информации о компонентах UI

Время на прочтение 14 мин
Количество просмотров 18K
Автор оригинала: Cristiano Rastelli

Термином «дизайн-система» в IT давно никого не удивишь. Компании систематизируют дизайн продуктов, придумывая свои или используя чужие инструменты для управления стилями, паттернами и компонентами. 


Badoo не является исключением: с помощью нашей дизайн-системы Cosmos мы поддерживаем общие принципы дизайна для четырёх приложений, работающих на четырёх платформах. 


image


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


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


Несмотря на потенциал дизайн-токенов, во многих компаниях их структура остаётся довольно простой, что сильно ограничивает возможности их применения. 


Я хочу поделиться адаптированным переводом статьи моего коллеги Кристиано Растелли (Cristiano Rastelli), который несколько лет развивает дизайн-систему Cosmos. На примере своего опыта он показывает, как работать с токенами более эффективно: добавлять метаданные и использовать их для описания свойств компонентов. 


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


В последние месяцы я не мог не заметить, как много людей (и компаний) подразумевают под дизайн-токенами только цвет, типографику и интервалы, а в некоторых случаях — тайминг, размеры и возвышение (elevation).


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


Дизайн-токены для цветов, типографики и интервалов


Как я уже говорил, многие считают дизайн-токены «набором ключевых свойств дизайна приложения или сайта». Это простое определение часто вводит в заблуждение и ограничивает возможности применения токенов.


Например, взгляните на эту песочницу: Design System Playground. Всё, что создатели предлагают менять в ней, — это цвета и типографика. 


Попробуйте этот инструмент для управления дизайн-токенами — и вы увидите, что он тоже основан на идее «ключевых» значений свойств дизайна:



Скриншот интерфейса, который используется для добавления нового дизайн-токена на сайте designtokens.dev


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


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


Хочу уточнить: в этом нет ничего плохого. Но, как я сказал выше, я считаю, что дизайн-токены способны на большее.


По моему опыту, огромный потенциал и сила дизайн-токенов проявляются в полной мере тогда, когда они:


  1. используются для выражения свойств и значений компонентов;
  2. аннотируются с помощью метаинформации.

Дизайн-токены для компонентов


Нет причин не использовать дизайн-токены для описания свойств компонентов.
С технической точки зрения дизайн-токены являются упорядоченными списками пар «ключ — значение», которые описывают дизайнерские решения. То, какими правилами и ограничениями мы будем руководствоваться при описании через них интерфейса, — лишь вопрос удобства (и контекста). 


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


Более того, основные цвета и шрифты меняются нечасто (например, при ребрендинге), а вот компоненты, по моему опыту, развиваются непрерывно. Например, в нашу дизайн-систему в последние месяцы было добавлено много новых компонентов. Некоторые из имеющихся были усовершенствованы, а какие-то — даже отрефакторены. Несколько компонентов были расширены с целью использования их в других контекстах или визуальных состояниях. И лишь пара цветов и один шрифт были изменены.


Как мы в Badoo используем дизайн-токены


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



Пример набора дизайн-токенов для компонента (Lifestyle Badge)


Вот так, к примеру, выглядит JSON-файл для компонента Lifestyle Badge (для управления токенами мы используем Style Dictionary):


{
   "lifestylebadge": {
       "height": {
           "value": "34",
           "type": "size"
       },
       "border_radius": {
           "value": "17",
           "type": "size"
       },
       "padding": {
           "start": {
               "value": "{spacing.md.value}",
               "type": "size"
           },
           "end": {
               "value": "{spacing.md.value}",
               "type": "size"
           }
       },
       "icon_size": {
           "value": "{icon.size.md.value}",
           "type": "size"
       },
       "spacing_icon_text": {
           "value": "{spacing.xsm.value}",
           "type": "size"
       },
       "base": {
           "text_color": {
               "value": "{color.gray_dark.value}",
               "type": "color"
           },
           "background_color": {
               "value": "{color.gray_light.value}",
               "type": "color"
           }
       },
       "selected": {
           "text_color": {
               "value": "{color.white.value}",
               "type": "color"
           },
           "background_color": {
               "value": "{color.primary.value}",
               "type": "color"
           }
       }
   }
}

Эти значения преобразуются в разные форматы и распределяются по различным платформам (мобильный веб, iOS, Android) и продуктам (сейчас мы поддерживаем четыре основных бренда и несколько немарочных (white label brands)).


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



Так выглядят токены Lifestyle Badge под Android в нашей дизайн-системе


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


Со временем разработчики начинают требовать наличия конкретных токенов при добавлении компонентов в систему. В этом случае отпадает необходимость открывать Sketch-файл (или страницу в Sympli) и выяснять размеры, цвета и спецификации компонентов. Вместо этого им достаточно обновить в своих кодовых базах версию пакета дизайн-токенов и запустить скрипт синхронизации/обновления, после чего они автоматически получат все спецификации, представленные в виде легкочитаемых имён переменных. 


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



Пример набора дизайн-токенов для другого компонента (Action Sheet). С их помощью можно выразить даже выравнивание по вертикальной оси (gravity)!


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


  • В сложных дизайн-системах наподобие нашей это упрощает создание многопродуктовых компонентов: это те же самые компоненты с тем же кодом, которые по-разному выглядят и работают в разных продуктах/брендах. Таким образом, снижается стоимость масштабирования — например, при добавлении новых приложений в портфолио.
  • Когда дизайнер решает обновить визуальный стиль компонента (например, фоновый цвет) или его макет/геометрию (например, горизонтальный отступ), то для этого требуется лишь обновить несколько значений в одном JSON-файле и изменить в кодовой базе номер версии импортируемого токена. Больше ничего: стоимость изменений чрезвычайно низкая!

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


Метаинформация дизайн-токенов


Оба основных инструмента управления дизайн-токенами (Theo и Style Dictionary) поддерживают добавление метаданных к паре «ключ — значение». Их можно использовать для добавления комментариев и аннотаций, однако по-настоящему сила метаданных раскрывается тогда, когда их используют для создания дополнительного смыслового слоя.


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


Как мы используем метаданные


Первой метаинформацией, которую мы добавили после внедрения дизайн-токенов в нашу дизайн-систему, стала «документация/комментарий» («documentation/comment»). С её помощью мы ввели в токен дополнительные данные, которые будут отображаться на сайте дизайн-системы. Мы применили пространство имён documentation, а не просто свойство comment, на тот случай, если нам понадобится добавить больше информации: к примеру, если дизайн-токен устареет и нужно будет описать его замену.


Вот пример добавления комментария в дизайн-токен:


{
   "actionsheet": {
       …
       "item": {
           "height": {
               "value": "48",
               "type": "size",
               "documentation": {
                   "comment": "Notice: this is the 'internal' size, the border will be added as extra"
               }
           },
           …
       }
   }
}

А так выглядит документация этого токена:



Сразу после этого мы добавили второй вид метаинформации — «тип» ("type"). Это скорее семантическое понятие, а не программистское. 


Вот примеры типов, которые мы используем:


{
   "color": {
       "primary": {
           "value": "{color.palette.purple_grape.value}",
           "type": "color"
       }
   },
   "icon": {
       "size": {
           "xsm": {
               "value": "10",
               "type": "size"
           }
       }
   },
   "tooltip": {
       "shadow": {
           "opacity": {
               "value": "0.08",
               "type": "opacity"
           }
       },
       "animation": {
           "timing_bounce": {
               "value": "0.9",
               "type": "time",
               "unit": "s"
           }
       }
   },
   "chat": {
       "bubble": {
           "relative_width": {
               "value": "0.8",
               "type": "ratio"
           }
       }
   },
   "actionsheet": {
       "gravity": {
           "value": "center",
           "type": "string"
       }
   }
}

Метаданные «тип» используются в скриптах постобработки для генерирования конкретных результатов для разных платформ. Вот фрагмент кода шаблона, который используется для генерирования XML-файла под Android:



В зависимости от «типа» токена мы генерируем значения с форматированием, необходимым для Android


А это отформатированный под наши требования результат:



Сгенерированный под Android XML-файл с соответствующим форматом/типом, который зависит от метазначения "type" в токене


Позднее мы добавили третий вид метаданных: «группа» ("group"). Это важнейший вид метаинформации, который используется по многим причинам.


Фильтрация


Для некоторых продуктов и задач нам нужно создавать подмножества токенов (например, только цвета). Для этого мы добавили в словарь стилей конкретный фильтр:



Скрипт для фильтрации «правильных» цветов


Вы могли заметить, что здесь мы используем два условия: одно для выбора токенов по типу «цвет» ("color"), второе — для отсеивания любых «псевдонимов» ("alias"). Это сделано потому, что мы используем специальные вспомогательные промежуточные цвета (вроде color-palette-purple-grave) в качестве указателей на их шестнадцатеричные значения, но поскольку они не нужны нам в виде сгенерированных токенов, мы отмечаем их флагом isAlias.


Затем этот фильтр используется для генерирования файлов только с цветами:



Мы фильтруем дизайн-токены в зависимости от метаданных "type", чтобы сгенерировать файлы только с цветами


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


Группировка


Ещё один способ использования метаданных — группировка дизайн-токенов по определённым правилам.


Например, с помощью атрибута «группа» ("group") можно создавать списки токенов, относящихся к одной группе, а затем использовать эти списки (или ассоциативные массивы) в кодовой базе.
Сгруппируем дизайн-токены по группам в зависимости от значений цветов и размеров иконок:


// color/xxx.json
{
    "color": {
        "primary": {
            "value": "{color.palette.purple_grape.value}",
            "type": "color",
            "group": "brand"
        },
        …
        "generic_red": {
            "value": "{color.palette.pink_salmon.value}",
            "type": "color",
            "group": "generic"
        },
        …
        "gray_dark": {
            "value": "#767676",
            "type": "color",
            "group": "mono"
        },
        "feature": {
            "verification": {
                "value": "{color.palette.blue_neon.value}",
                "type": "color",
                "group": "features"
            },
            …
        },
        "provider": {
            facebook": {
                "value": "#4867aa",
                "type": "color",
                "group": "providers"
            },
            …
        },
        "others": {
            error": {
                "value": "{color.generic_red.value}",
                "type": "color",
                "group": "others"
            },
            …
        }
    }
}

// icon.json
{
    "icon": {
        "size": {
            "xsm": {
                "value": "10",
                "type": "size",
                "group": "size"
            },
            "sm": {
                "value": "16",
                "type": "size",
                "group": "size"
            },
            "md": {
                "value": "22",
                "type": "size",
                "group": "size"
            },
            "lg": {
                "value": "30",
                "type": "size",
                "group": "size"
            },
            "xlg": {
                "value": "36",
                "type": "size",
                "group": "size"
            },
            "xxlg": {
                "value": "46",
                "type": "size",
                "group": "size"
            }
        },
        "jumbo-size": {
            "sm": {
                "value": "{brick.size.sm.value}",
                "type": "size",
                "group": "size"
            },
            "md": {
                "value": "{brick.size.md.value}",
                "type": "size",
                "group": "size"
            },
            "lg": {
                "value": "{brick.size.lg.value}",
                "type": "size",
                "group": "size"
            }
        }
    }
}

// spacing.json
{
    "spacing": {
        "xsm": {
            "value": "4",
            "type": "size",
            "group": "size"
        },
        "sm": {
            "value": "8",
            "type": "size",
            "group": "size"
        },
        "md": {
            "value": "12",
            "type": "size",
            "group": "size"
        },
        "lg": {
            "value": "16",
            "type": "size",
            "group": "size"
        },
        "xlg": {
            "value": "24",
            "type": "size",
            "group": "size"
        },
        "xxlg": {
            "value": "32",
            "type": "size",
            "group": "size"
        },
        "gap": {
            "value": "{spacing.lg.value}",
            "type": "size",
            "group": "size"
        }
    }
}

Можно сгенерировать ассоциативные массивы в Sass (они же Sass maps, или карты):


$tokens-color-map: (
    providers: (
        provider-facebook: $token-color-provider-facebook,
        …
    ),
    mono: (
        black: $token-color-black,
        gray-dark: $token-color-gray-dark,
        gray: $token-color-gray,
        gray-light: $token-color-gray-light,
        white: $token-color-white
    ),
    others: (
        error: $token-color-error,
        …
    ),
    brand: (
        primary: $token-color-primary,
        …
    ),
    generic: (
        generic-red: $token-color-generic-red,
        …
    ),
    features: (
        …
        feature-verification: $token-color-feature-verification
    ),
);
$tokens-icon-map: (
    size: (
        size-xsm: $token-icon-size-xsm,
        size-sm: $token-icon-size-sm,
        size-md: $token-icon-size-md,
        size-lg: $token-icon-size-lg,
        size-xlg: $token-icon-size-xlg,
        size-xxlg: $token-icon-size-xxlg,
        jumbo-size-sm: $token-icon-jumbo-size-sm,
        jumbo-size-md: $token-icon-jumbo-size-md,
        jumbo-size-lg: $token-icon-jumbo-size-lg
    ),
);
$tokens-spacing-map: (
    size: (
        xsm: $token-spacing-xsm,
        sm: $token-spacing-sm,
        md: $token-spacing-md,
        lg: $token-spacing-lg,
        xlg: $token-spacing-xlg,
        xxlg: $token-spacing-xxlg,
        gap: $token-spacing-gap
    ),
);
...

Теперь эти карты позволят нам объявлять подобные конструкции:




Использование в нашей кодовой базе Sass-карт, сгенерированных дизайн-токенами


Таким образом, мы не просто абстрагируемся от объявления списков классов/свойств в зависимости от цвета, размера и прочих параметров — для нас гораздо важнее то, что у разных продуктов могут быть разные списки цветов, интервалов, размеров иконок и т. д., и при этом один и тот же Sass-код теперь работает для любых списков цветов, интервалов, размеров и прочего.


Эти Sass-файлы являются универсальными и могут использоваться для компонентов разных продуктов и на разных платформах.


При добавлении или удалении цвета список обновляется автоматически. Не нужно менять код на уровне компонента или приложения. Всё управляется с помощью объявлений дизайн-токенов.
Это работает не только для CSS/Sass. Те же преимущества списков токенов, относящихся к одной группе, можно получить и в JavaScript:



На сайте дизайн-системы мы демонстрируем все возможные цвета компонентов, используя списки токенов, сгенерированные через ассоциированные с ними метаданные "group"


Это приводит нас ещё к одному способу применения — CSS-в-JS. В этом случае список возможных цветов (отступов, размеров) для компонентов становится просто циклом/картой списка объектов «ключ — значение», сгенерированным инструментом для работы с дизайн-токенами.
В рамках этого же подхода мы недавно использовали свойство "group" для генерирования TypeScript-определений. С помощью специального шаблона в словаре стилей мы можем генерировать такие файлы:


declare namespace Tokens {
    namespace Color {
        enum Providers {
            PROVIDER_FACEBOOK = 'provider-facebook',
            …
        }
        enum Mono {
            BLACK = 'black',
            GRAY_DARK = 'gray-dark',
            GRAY = 'gray',
            GRAY_LIGHT = 'gray-light',
            WHITE = 'white'
        }
        enum Others {
            ERROR = 'error',
            …
        }
        enum Brand {
            PRIMARY = 'primary',
            …
        }
        enum Generic {
            GENERIC_RED = 'generic-red',
            …
        }
        enum Features {
            …
            FEATURE_VERIFICATION = 'feature-verification'
        }
        type Color = Providers | Mono | Others | Brand | Generic | Features;
    }
    namespace Icon {
        enum Size {
            XSM = 'xsm',
            SM = 'sm',
            MD = 'md',
            LG = 'lg',
            XLG = 'xlg',
            XXLG = 'xxlg',
            JUMBO_SM = 'jumbo-sm',
            JUMBO_MD = 'jumbo-md',
            JUMBO_LG = 'jumbo-lg'
        }
    }
    namespace Spacing {
        enum Size {
            XSM = 'xsm',
            SM = 'sm',
            MD = 'md',
            LG = 'lg',
            XLG = 'xlg',
            XXLG = 'xxlg',
            GAP = 'gap'
        }
    }
}
export default Tokens;

Эти TypeScript-определения и перечисления позднее могут напрямую использоваться клиентскими кодовыми базами для обеспечения строгой типобезопасности (и автоматически заполняться в IDE).
В качестве ещё одного возможного использования метаданных для добавления в дизайн-токены дополнительного семантического значения, в частности взаимосвязей, можно упомянуть объявление текстовых стилей. Все мы знаем, что для создания минимально жизнеспособного текстового стиля нужно задать не меньше четырёх параметров: font-family, font-size, line-height и font-weight. 


Как определить и явно объявить взаимосвязи между отдельными дизайн-токенами, чтобы создать «текстовый стиль H1»? Можно отразить это в наименованиях, чтобы все свойства одного стиля имели одинаковый префикс или суффикс. Тогда почему бы не использовать метаданные, ассоциированные с токенами, для их идентификации и фильтрации/группировки в ходе обработки? 


Нам это реализовывать не нужно, но если бы потребовалось, то я однозначно воспользовался бы атрибутом метаданных, ассоциированным с разными значениями стилей.


Заключение


Основные выводы:


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

Например, с помощью метаданных можно:


  • добавлять комментарии к дизайн-токенам;
  • использовать фильтры и генерировать селективные результаты;
  • генерировать на основе токенов динамические списки для Sass, JavaScript, TypeScript;
  • благодаря этим динамическим спискам использовать одни и те же компоненты (с одинаковым кодом) в разных продуктах и на разных платформах.

Я показал, как мы реализуем этот подход в нашей дизайн-системе. Но мы используем дизайн-токены для удовлетворения наших потребностей. Уверен, что у вас есть (или вы найдёте) множество других примеров того, как можно использовать дизайн-токены, помимо цветов, типографики и интервалов.


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


А потом обязательно расскажите об этом сообществу!


P. S. Больше о том, что такое дизайн-токены, можно узнать из онлайн-курса и выступления Джины Энн, публикации Дэнни Бэнкса, репозитория Стюарта Робсона и статьи Робина Рендла. Есть ещё репозиторий рабочей группы W3C, в который можно внести свой вклад. Чтобы получить более полное представление о том, как в Badoo используются дизайн-токены, почитайте эту статью: How to manage your Design Tokens with Style Dictionary.

Теги:
Хабы:
+19
Комментарии 10
Комментарии Комментарии 10

Публикации

Информация

Сайт
badoo.com
Дата регистрации
Дата основания
2006
Численность
501–1 000 человек
Местоположение
Россия
Представитель
Yuliya Telezhkina

Истории