Дизайн системы

Опыт разработки дизайн системы в Figma

Рассказываю, как построить дизайн-систему: структура токенов и адаптивная типографика. Реальный кейс с примерами из Figma.

«Как я строю дизайн-систему в Figma: от примитивов до контекстуальных токенов»

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

С чего всё начиналось

Всё началось с типичной боли: в макетах жили сотни текстовых слоёв с рандомными размерами, отступами и цветами.

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

Так родилась идея построить систему на трёх уровнях: примитивы → семантика → контекст.

Исследование подходов: почему не S, M, L?

Прежде чем прийти к текущей архитектуре, я потратила немало времени на изучение разных подходов к построению дизайн-систем. Я смотрела, как это делают лидеры индустрии: Material Design от Google, Carbon от IBM, Polaris от Shopify, Fluent от Microsoft. Каждый из них предлагает свою философию, и у всех есть чему поучиться.

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

Самый популярный соблазн: t-shirt sizing (XS, S, M, L, XL)
Этот подход кажется очень логичным и «дизайнерским». Вместо сухих цифр — понятные аналогии с размерами одежды. spacing-m, font-size-xl, radius-s. Звучит красиво, правда?

Я долго примеряла этот подход к своей системе. И вот с какими проблемами столкнулась на практике:

1. Потеря семантической нагрузки
Когда я вижу font-size-xl, я понимаю только одно: «этот текст большой». Но почему он большой? Это заголовок самого высокого уровня? Это акцидентный текст в hero-блоке? Или просто крупная подпись? T-shirt sizing говорит о размере, но молчит о смысле. В моей системе мне важно, чтобы дизайнер, беря токен H1, понимал: «это главный заголовок страницы, и он не может использоваться где попало». Цифры (H1, B2) в моем случае несут именно эту смысловую нагрузку — они привязаны к иерархии.

2. Бесконечная шкала и потеря контекста
В какой-то момент у тебя заканчиваются буквы. Сначала идет XS, S, M, L, XL, XXL, XXXL… А что дальше? В сложных интерфейсах, особенно с адаптивностью, требуется гораздо больше градаций, чем может дать алфавит. В моей системе есть H1 (очень крупный), H5 (мелкий заголовок), B4 (мелкий текст), D2 (микро-текст). Попробуй назвать это в T-shirt логике: heading-xxl, heading-xs, body-xs, description-xxs? Начинается путаница уже на уровне названий.

3. «Костыль» в виде плюсов (и где прячется смысл)
В какой-то момент я заметила, что некоторые дизайнеры, работающие с t-shirt системой, начинают добавлять к буквам плюсы: m+, l+, xl–. Это попытка сделать шкалу более гибкой, не ломая её структуру.
Например, появляется отступ, который чуть больше стандартного m, но явно не дотягивает до l. Вместо нового токена дизайнер пишет m+. Формально — расширение градации. Но на практике возникает проблема: сам токен перестаёт что-либо объяснять.

Что значит m+? Это:

  • на 4px больше базового m?
  • на 8px?
  • только для мобилок?
  • только для карточек?

Без пояснения ответа нет. Приходится создавать отдельную инструкцию:
«m+ используется для компенсационных отступов внутри карточек на планшете, значение — 20px».

То есть смысл токена вынесен за пределы Figma — в гугл-док, в Notion, в устные договорённости. Дизайнер видит m+, но чтобы понять, что это и когда применять, должен лезть в инструкцию.

При моём подходе смысл остаётся внутри системы.
Я не использую абстрактные m+. У меня есть, например, card-padding или H1. Да, к ним тоже нужны пояснения. Но эти пояснения работают иначе:

  • H1 — «заголовок самого высокого уровня, только один на страницу». Это условие использования, которое можно написать прямо в описании переменной в Figma.
  • card-padding — «внутренний отступ карточки товара, на каждом брейкпоинте у него свое значение, переключается модом». Тоже можно указать в поле description.
Разница в том, где хранится контекст.
  • В T-shirt с плюсами: токен (m+) — это загадка. Контекст — в отдельном документе.
  • В ролевом подходе: токен (card-padding) сам называет свою роль. Контекст — тут же, в Figma, рядом с переменной.
Пояснения нужны всегда. Но когда токен уже содержит в имени «зачем», а не только «насколько», эти пояснения становятся точечными уточнениями, а не обязательным путеводителем.

Что я выбрала в итоге

После экспериментов я отказалась от t-shirt sizing как от неподходящего инструмента именования токенов и выстроила систему на трёх уровнях, где каждый токен честно говорит о своей природе и месте в иерархии.

  • Примитивы (размеры, отступы) у меня называются просто цифрами: Size-16, Hight-24. Это чистые числа, без оценок «большой» или «маленький».
  • Семантические токены (типографика) называются по ролям: H1, H2, B1, S2. Цифра здесь показывает уровень иерархии, а буква — тип контента (Heading, Body, Subtitle, Description).
  • Контекстуальные токены (компоненты) называют по формуле: {компонент}/{вариант}/{используемый элемент}-{состояние}. Например, button/primary/bg-hover или checkbox/icon/disabled/.
Эта схема оказалась для меня самой прозрачной. Она масштабируется на любые сложные интерфейсы, легко читается и разработчиками, и дизайнерами, и не требует каждый раз расшифровки «а что же значит эта буква».

Уровень 1. Примитивы: числа и цвета как фундамент

Первый уровень — самый скучный, но самый важный. Это кирпичи, из которых всё строится.

В моей системе примитивы живут в отдельной коллекции и выглядят примерно так:
Структура примитивных токенов в Figma
И так же — с размерами, интерлиньяжем, трекингом. Все числовые значения вынесены в переменные, которым можно задать условия использования и видимость:
Структура примитивных токенов в Figma
Зачем это нужно? Теперь, если я захочу изменить оттенок акцентного цвета или базовый размер шрифта, мне достаточно поменять одно значение в одном месте. Магия переменных сделает всё остальное.

Уровень 2. Семантика: говорим на языке смыслов

Примитивы сами по себе ничего не значат. Цвет Naturals/900 может быть где угодно. Чтобы объяснить системе, зачем нужен тот или иной цвет или размер, я создала семантические токены.
Они ссылаются на примитивы, но говорят о роли:
Структура семантических токенов в Figma
То же самое с типографикой. У меня есть четыре семейства стилей:

  • Heading (H1, H2, H3, H4, H5) — заголовки секций
  • Body (B1, B2, B3, B4) — основной текст
  • Subtitle (S1, S2, S3) — текст субтитров
  • Description (D1, D2) — мелкий вспомогательный текст
И для каждого — свои значения на десктопе, планшете и мобилке. Например, H1 на десктопе — 54px, на планшете — 42px, на мобилке — 32px. И всё это через переменные, без дублирования стилей.

Уровень 3. Контекстуальные токены: когда компонент важнее всего

Самый интересный уровень, к которому я пришла не сразу — контекстуальные токены.

Что это такое? Это токены, которые привязаны не к глобальному смыслу («основной текст»), а к конкретному компоненту и его состоянию.
Когда я начала внедрять компоненты (кнопки, чекбоксы, поля ввода), я столкнулась с проблемой: у кнопки в состоянии hover цвет должен быть темнее, чем в default, а в состоянии disabled — совсем серым. Если использовать один и тот же семантический токен для всех состояний — ничего не получится.
Как это выглядит на практике

Вот как у меня организованы цвета для чекбокса:
Структура контекстуальных токенов в Figma
здесь нет общих «цветов текста» или «цветов фона». Есть только цвета для конкретной части компонента в конкретном состоянии.

То же самое для кнопок

В кнопках я пошла ещё дальше — разделила их на три варианта (primary, secondary, tertiary) и для каждого прописала все состояния:
Структура контекстуальных токенов в Figma
Адаптивность через брейкпоинты

Отдельная гордость — адаптивность. У меня есть три набора токенов: для десктопа, планшета и мобилки. И переключаются они через режимы (modes) в Figma Variables.
Например, отступы на десктопе, планшете и мобильной версии:
Структура разделения размеров, отступов по значениям брейкпоинтов
И тот же принцип — для всех отступов, размеров шрифта, интерлиньяжа. Я просто переключаю режим на фрейме, и вся типографика магически подстраивается под нужный брейкпоинт.
Структура значений типографики с разделением по брейкпоинтам
Почему это работает

Такая архитектура даёт несколько ключевых преимуществ:
  1. Единый источник правды. Любое изменение вносится один раз и распространяется автоматически.
  2. Масштабируемость. Когда появляется новый компонент, я не гадаю, какой цвет ему дать — я просто беру готовые контекстуальные токены из аналогичных компонентов.
  3. Понятность для разработки. Разработчик видит в макете не просто «синий», а button/primary/bg-hover. Это ровно то, что он напишет в коде.
  4. Адаптивность «из коробки». Дизайн не ломается при смене брейкпоинта, потому что всё завязано на переменные.

Что дальше

Сейчас я активно внедряю эту систему во все новые макеты и постепенно мигрирую старые. Самое сложное — не техническая часть, а дисциплина: каждый раз, когда хочется «просто поставить цвет» или «сдвинуть на глаз», нужно остановиться и спросить себя: «А есть ли для этого токен? А должен ли он быть?».
Если ответ «нет» — значит, пора создавать новый токен, а не плодить хаос.