Главная » Хабрахабр » [Перевод] Sketch + Node.js: генерируем иконки для множества платформ и брендов. Часть 2

[Перевод] Sketch + Node.js: генерируем иконки для множества платформ и брендов. Часть 2

Это вторая часть статьи о создании инструмента, способного экспортировать все помещённые в Sketch-файл иконки: в разных форматах, для разных платформ, с возможностью A/B-тестирования каждой из иконок.

Первую часть вы можете прочесть по ссылке.

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

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

Скрипт сборки

Написанный на Node.js скрипт сборки достаточно прямолинеен в своей работе: импортировав зависимости, объявив список Sketch-файлов для обработки (он представляет собой список брендов, каждому из которых сопутствует список относящихся к нему файлов) и убедившись в том, что на клиенте установлен Sketch, скрипт по очереди обрабатывает бренды, проделывая с каждым из них серию действий.

  1. Берёт соответствующие брендам дизайн-токены (нам нужны значения цветов).
  2. Клонирует ассоциированные с брендом Sketch-файлы, разархивирует их, извлекая внутренние JSON-файлы, и обрабатывает некоторые из их внутренних значений (об этом чуть позже).
  3. Считывает из этих JSON-файлов необходимые метаданные (document.json, meta.json и pages/pageUniqueID.json). Нас интересуют списки общих стилей и содержащихся в файлах ресурсов/иконок.
  4. Проведя ещё несколько манипуляций с JSON-файлами, воссоздаёт архив и при помощи Sketch-файлов (клонированных и обновлённых) экспортирует и создаёт финальные выходные файлы для трёх платформ (iOS, Android, Mobile Web).

Соответствующие части скрипта сборки вы найдёте здесь:

// ... modules imports here
const SKETCH_FILES = { badoo: ['icons_common'], blendr: ['icons_common', 'icons_blendr'], fiesta: ['icons_common', 'icons_fiesta'], hotornot: ['icons_common', 'icons_hotornot'],
};
const SKETCH_FOLDER_PATH = path.resolve(__dirname, '../src/');
const SKETCH_TEMP_PATH = path.resolve(SKETCH_FOLDER_PATH, 'tmp');
const DESTINATION_PATH = path.resolve(__dirname, '../dist');
console.log('Build started...');
if (sketchtool.check()) `); build();
} else { console.info('You need Sketch installed to run this script'); process.exit(1);
}
// ----------------------------------------
function build() { // be sure to start with a blank slate del.sync([SKETCH_TEMP_PATH, DESTINATION_PATH]); // process all the brands declared in the list of Sketch files Object.keys(SKETCH_FILES).forEach(async (brand) => { // get the design tokens for the brand const brandTokens = getDesignTokens(brand); // prepare the Sketch files (unzipped) and get a list of them const sketchUnzipFolders = await prepareSketchFiles({ brand, sketchFileNames: SKETCH_FILES[brand], sketchFolder: SKETCH_FOLDER_PATH, sketchTempFolder: SKETCH_TEMP_PATH }); // get the Sketch metadata const sketchMetadata = getSketchMetadata(sketchUnzipFolders); const sketchDataSharedStyles = sketchMetadata.sharedStyles; const sketchDataAssets = sketchMetadata.assetsMetadata; generateAssetsPDF({ platform: 'ios', brand, brandTokens, sketchDataSharedStyles, sketchDataAssets }); generateAssetsSVGDynamicMobileWeb({ platform: 'mw', brand, brandTokens, sketchDataSharedStyles, sketchDataAssets }); generateAssetsVectorDrawableDynamicAndroid({ platform: 'android', brand, brandTokens, sketchDataSharedStyles, sketchDataAssets }); });
}

На самом деле, код конвейера значительно сложнее. Причина этой сложности скрывается в функциях prepareSketchFiles, getSketchMetadata и generateAssets[format][platform]. Ниже я попробую описать их более подробно.

Подготовка Sketch-файлов

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

Файлы, ассоциированные с конкретным брендом (например, в случае с Blendr это файлы icons_common.sketch и icons_blendr.sketch) клонируются во временную папку (точнее в подпапку, названную по имени обрабатываемого бренда) и разархивируются.

К названию ресурсов, подлежащих A/B-тестированию, добавляется префикс — таким образом, при экспорте они будут сохранены в подпапке с заранее указанным названием (соответствующим уникальному имени эксперимента). Затем обрабатываются JSON-файлы. Понять, подлежит ли ресурс A/B-тестированию, можно по названию страницы, на которой он хранится: если подлежит, в названии будет содержаться префикс "XP_".

В примере выше экспортируемые ресурсы будут сохраняться в подпапке «this__is_an_experiment» с названием файла вида «icon-name[variant-name].ext».

Считывание метаданных Sketch

Второй важный этап — извлечение всех необходимых метаданных из Sketch-файлов, а точнее из внутренних JSON-файлов. Как мы увидели выше, это два основных файла (document.json и meta.json) и файлы страниц (pages/pageUniqueId.json).

Файл document.json используется для получения списка общих стилей, который появится под свойством объекта layerStyles:

{ "_class": "document", "do_objectID": "45D2DA82-B3F4-49D1-A886-9530678D71DC", "colorSpace": 1, ... "layerStyles": { "_class": "sharedStyleContainer", "objects": [ { "_class": "sharedStyle", "do_objectID": "9BC39AAD-CDE6-4698-8EA5-689C3C942DB4", "name": "features/feature-like", "value": { "_class": "style", "fills": [ { "_class": "fill", "isEnabled": true, "color": { "_class": "color", "alpha": 1, "blue": 0.10588235408067703, "green": 0.4000000059604645, "red": 1 }, "fillType": 0, "noiseIndex": 0, "noiseIntensity": 0, "patternFillType": 1, "patternTileScale": 1 } ], "blur": {...}, "startMarkerType": 0, "endMarkerType": 0, "miterLimit": 10, "windingRule": 1 } }, ...

Мы храним основную информацию о каждом стиле в объекте формата «ключ-значение». Он будет использован позднее, когда нам понадобится извлечь название стиля на основании уникального ID (свойство do_objectID в Sketch):

const parsedSharedStyles = {};
parsedDocument.layerStyles.objects.forEach((object) => { parsedSharedStyles[object.do_objectID] = { name: object.name, isFill: _.get(object, 'value.fills[0].color') !== undefined, isBorder: _.get(object, 'value.borders[0].color') !== undefined, };
});

Теперь мы переходим к файлу meta.json и получаем список страниц. Нас интересуют их unique-id и name:

{ "commit": "623a23f2c4848acdbb1a38c2689e571eb73eb823", "pagesAndArtboards": { "EE6BE8D9-9FAD-4976-B0D8-AB33D2B5DBB7": { "name": "Icons", "artboards": { "3275987C-CE1B-4369-B789-06366EDA4C98": { "name": "badge-feature-like" }, "C6992142-8439-45E7-A346-FC35FA01440F": { "name": "badge-feature-crush" }, ... "7F58A1C4-D624-40E3-A8C6-6AF15FD0C32D": { "name": "tabbar-livestream" } ... } }, "ACF82F4E-4B92-4BE1-A31C-DDEB2E54D761": { "name": "XP_this__is_an_experiment", "artboards": { "31A812E8-D960-499F-A10F-C2006DDAEB65": { "name": "this__is_an_experiment/tabbar-livestream[variant1]" }, "20F03053-ED77-486B-9770-32E6BA73A0B8": { "name": "this__is_an_experiment/tabbar-livestream[variant2]" }, "801E65A4-3CC6-411B-B097-B1DBD33EC6CC": { "name": "this__is_an_experiment/tabbar-livestream[control]" } } },

Затем мы считываем соответствующие каждой странице JSON-файлы в папке pages (повторю, что названия файлов — вида [pageUniqueId].json) и изучаем хранящиеся на этой странице ресурсы (они выглядят как слои). Таким образом, мы получаем имя, ширину/высоту каждой иконки, метаданные Sketch по иконке данного слоя, а если мы имеем дело со страницей эксперимента, то ещё и названия A/B-теста и варианта данной иконки.

Если вам интересно, что внутри, советую создать новый пустой Sketch-файл, добавить в него какой-нибудь контент и сохранить; затем переименовать его расширение в ZIP, разархивировать его и изучить один из файлов в папке pages. Примечание: объект page.json имеет очень сложное устройство, поэтому я не буду на нём останавливаться.

Он нам понадобится, чтобы определить, какие варианты иконки использованы в каком эксперименте, — названия вариантов иконки привязаны к «базовому» объекту. В процессе обработки артбордов мы также создадим список экспериментов (и соответствующих ресурсов).

Для каждого обрабатываемого Sketch-файла, относящегося к бренду, мы создаём объект assetsMetadata, который выглядит следующим образом:

{ "navigation-bar-edit": { "do_objectID": "86321895-37CE-4B3B-9AA6-6838BEDB0977", ...sketch_artboard_properties, "name": "navigation-bar-edit", "assetname": "navigation-bar-edit", "source": "icons_common", "width": 48, "height": 48 "layers": [ { "do_objectID": "A15FA03C-DEA6-4732-9F85-CA0412A57DF4", "name": "Path", ...sketch_layer_properties, "sharedStyleID": "6A3C0FEE-C8A3-4629-AC48-4FC6005796F5", "style": { ... "fills": [ { "_class": "fill", "isEnabled": true, "color": { "_class": "color", "alpha": 1, "blue": 0.8784313725490196, "green": 0.8784313725490196, "red": 0.8784313725490196 }, } ], "miterLimit": 10, "startMarkerType": 0, "windingRule": 1 }, }, ], ... }, "experiment-name/navigation-bar-edit[variant]": { "do_objectID": "00C0A829-D8ED-4E62-8346-E7EFBC04A7C7", ...sketch_artboard_properties, "name": "experiment-name/navigation-bar-edit[variant]", "assetname": "navigation-bar-edit", "source": "icons_common", "width": 48, "height": 48 ...

Как видите, в порядке эксперимента одной иконке (в данном случае navigation-bar-edit) может соответствовать множество ресурсов. При этом одна и та же иконка может появляться под тем же названием в другом ассоциируемом с брендом Sketch-файле. Это очень полезно: мы пользуемся этой хитростью, чтобы скомпилировать общий набор иконок, а затем определить конкретные варианты в соответствии с брендом. Именно поэтому мы объявили ассоциируемые с определённым брендом Sketch-файлы как массив:

const SKETCH_FILES = { badoo: ['icons_common'], blendr: ['icons_common', 'icons_blendr'], fiesta: ['icons_common', 'icons_fiesta'], hotornot: ['icons_common', 'icons_hotornot'],
};

В данном случае порядок имеет принципиальное значение. По сути, в вызываемой скриптом функции getSketchMetadata мы не возвращаем объекты assetsMetadata по одному на файл в виде списка. Вместо этого мы осуществляем глубокое слияние объектов — объединяем их и возвращаем единый объект assetsMetadata.

Однако логика не настолько проста, как кажется. В общем, это не что иное, как «логическое» слияние Sketch-файлов и их ресурсов в едином файле. Вот схема, которую мы создали в попытке разобраться, что происходит, когда иконки с одинаковыми названиями (и, возможно, подлежащие A/B-тестированию) в разных файлах ассоциируются с одним и тем же брендом:

Создание готовых файлов в разных форматах для разных платформ

Заключительный этап нашего процесса — непосредственно создание файлов иконок в разных форматах для разных платформ (PDF для iOS, SVG/JSX для Web и VectorDrawable для Android).

Именно здесь процесс начинает дробиться и меняться в зависимости от платформы. Как можно понять из количества передаваемых функциям generateAssets[format][platform] параметров, эта часть конвейера самая сложная. Ниже вы увидите логический ход скрипта целиком и то, как относящаяся к генерированию ресурсов часть разделяется на три похожих, но отличающихся друг от друга процесса:

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

Для генерирования файлов для Android необходимо выполнить дополнительное действие (о нём чуть позже): мы меняем свойство fill-rule каждого слоя с even-odd на non-zero (этим управляет свойство JSON-объекта windingRule, в котором 1 означает «чёт/нечет», а 0 — «не равно нулю»).

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

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

sketchtool.run(`export slices ${cloneSketchFile} --formats=svg --scales=1 --output=${destinationFolder} --overwriting`);

Как можно догадаться, эта команда экспортирует ресурсы в папку назначения в конкретном формате, опционально применяя масштабирование (мы пока сохраняем первоначальный масштаб). Ключевой здесь является опция -overwriting: подобно тому, как мы проводим глубокое слияние объектов assetsMetadata (соответствующее «логическому» Sketch-файлов), при экспорте мы сливаем множество файлов в один каталог (относящийся к бренду/платформе). Это означает, что, если ресурс — идентифицируемый по названию слоя — уже существовал в предыдущем Sketch-файле, он будет переписан при следующем экспорте. Опять же, это не более чем обычная операция слияния.

Такое происходит, когда иконка в файле подвергается A/B-тестированию, но в последующем файле перезаписывается. Впрочем, в этом примере некоторые ресурсы могут оказаться «призраками». Такие файлы будут удалены позже, перед завершением процесса. Тогда файлы вариантов экспортируются в папку назначения, имеют соответствующую ресурсу ссылку в объекте assetsMetadata (со своими ключом и свойствами), но не ассоциируются ни с одним базовым ресурсом (из-за глубокого слияния объектов assetsMetadata).

Как уже было сказано, для разных платформ требуются разные итоговые форматы. iOS подходят PDF-файлы, и мы можем напрямую экспортировать их с помощью команды SketchTool. Для Mobile Web необходимы JSX-файлы, а для Android — VectorDrawable. По этой причине мы экспортируем ресурсы в формате SVG во временную папку и уже после этого подвергаем обработке.

PDF-файлы для iOS

Как ни странно, PDF — единственный (?) формат, который поддерживается Xcode и OS/iOS для импорта и визуализации векторных ресурсов (вот короткое объяснение данного выбора Apple).

Поскольку мы можем напрямую экспортировать в PDF через SketchTool, никаких дополнительных действий не потребуется: просто сохраните файлы непосредственно в папке назначения, и всё.

Файлы в формате React/JSX для Web

В случае с Web мы используем SVGR— библиотеку Node, позволяющую конвертировать SVG в компоненты React. Однако мы хотим делать кое-что покруче: «динамически раскрашивать» иконку во время исполнения (цвета при этом берутся из токенов). Для этого перед конвертированием мы меняем значения fill для векторов, к которым прежде применялся общий стиль, на значения из токенов, соответствующие этому стилю.

Так что если экспортированный из Sketch файл badge-feature-like.svg выглядит так:

<?xml version="1.0" encoding="UTF-8"?>
<svg width="128px" height="128px" viewBox="0 0 128 128" version="1.1" xmlns="<a href="http://www.w3.org/2000/svg">http://www.w3.org/2000/svg</a>" xmlns:xlink="<a href="http://www.w3.org/1999/xlink">http://www.w3.org/1999/xlink</a>"> <!-- Generator: sketchtool 52.2 (67145) -<a href="http://www.bohemiancoding.com/sketch"> http://www.bohemiancoding.com/sketch</a> --> <title>badge-feature-like</title> <desc>Created with sketchtool.</desc> <g id="Icons" fill="none" fill-rule="evenodd"> <g id="badge-feature-like"> <circle id="circle" fill="#E71032" cx="64" cy="64" r="64"> <path id="Shape" fill="#FFFFFF" d="M80.4061668,..."></path> </g> </g>
</svg>

то итоговый ресурс/иконка badge-feature-like.js будет выглядеть так:

/* This file is generated automatically - DO NOT EDIT */
/* eslint-disable max-lines,max-len,camelcase */
const React = require('react');
module.exports = function badge_feature_like({ tokens }) { return ( <svg data-origin="pipeline" viewBox="0 0 128 128"> <g fill="none" fillRule="evenodd"> <circle fill={tokens.TOKEN_COLOR_FEATURE_LIKED_YOU} cx={64} cy={64} r={64} /> <path fill="#FFF" d="M80.4061668,..." /> </g> </svg> );
};

Как видите, мы заменили статическое значение цвета fill динамическим, берущим значения из токенов (их можно сделать доступными для компонента React <Icon/> через Context API, но это отдельная история).

Эта замена возможна благодаря метаданным Sketch для ресурсов объекта assetsMetadata: рекурсивно пройдясь по слоям, вы можете создать селектор DOM (для примера выше это #Icons #badge-feature-like #circle) и использовать его для поиска узла в древе SVG и замены значения его атрибута fill (для этого нам нужна библиотека cheerio).

Файлы VectorDrawable для Android

Android поддерживает векторную графику с помощью кастомного векторного формата VectorDrawable. Обычно конвертирование из SVG в VectorDrawable производится прямо в Android Studio. Однако нам хотелось полностью автоматизировать процесс, поэтому мы искали способ конвертирования при помощи кода.

Она не только активно поддерживается (во всяком случае активнее, чем все остальные), но и более функциональна, чем остальные. Изучив различные инструменты и библиотеки, мы остановились на svg2vectordrawable.

Одна из вытекающих из этого проблем — то, что в более старых версиях (до 24) не поддерживается значение even-odd атрибута fill-rule. Реалии таковы, что VectorDrawable и SVG не одинаковы в своей функциональности: некоторые функции SVG (к примеру, радиальные градиенты и комплексное выделение) не поддерживаются VectorDrawable, а другие начали поддерживаться совсем недавно (начиная с версии Android API 24). Вот почему на одном из более ранних этапов мы привели fill каждого вектора в Sketch-файлах к значению non-zero. Однако нам в Badoo необходима поддержка версии Android 5 и выше.

В принципе, дизайнеры могут выполнить это действие вручную:

Поэтому мы решили добавить в процесс для Android дополнительный этап, на котором все векторы в JSON автоматически конвертируются в non-zero. Но об этом легко забыть и допустить ошибку. Это делается для того, чтобы при экспорте иконок в SVG они уже были в необходимом формате, а каждый создаваемый объект VectorDrawable поддерживался устройствами на Android 5.

Готовый файл badge-feature-like.xml при этом выглядит так:

<!-- This file is generated automatically - DO NOT EDIT -->
<vector xmlns:android="<a href="http://schemas.android.com/apk/res/android">http://schemas.android.com/apk/res/android</a>" android:width="128dp" android:height="128dp" android:viewportWidth="128" android:viewportHeight="128"> <path android:fillColor="?color_feature_liked_you" android:pathData="M64 1a63 63 0 1 0 0 126A63 63 0 1 0 64 1z" /> <path android:fillColor="#FFFFFF" android:pathData="M80.406 ..." />
</vector>

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

Так что нам пришлось придумать новый формат для названий иконок: в случае с подлежащими тестированию ресурсами они выглядят примерно так: ic_icon-name__experiment-name__variant-name. Стоит отметить, что Android Studio отличается строгими требованиями к организации ресурсов: никаких вложенных папок и больших букв в названиях!

Словарь JSON как библиотека ресурсов

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

После извлечения плоского списка иконок из объекта assetsMetadata мы проходимся по нему, проверяя каждую из них:

  • обычный ли это ресурс (например, tabbar-livestream); если да, то просто оставляем его;
  • если это вариант для A/B-теста (например, experiment/tabbar-livestream[variant]), мы ассоциируем его название, путь, имена A/B-теста и варианта со свойством abtests
    базового ресурса (в нашем случае это tabbar-livestream), после чего удаляем запись о варианте из списка/объекта (имеет значение лишь «базовый» элемент);
  • если это «призрак», то удаляем файл и убираем запись из списка/объекта.

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

Вот, например, файл assets.json, сгенерированный для приложения Blendr под Mobile Web: Словарь сохраняется в формате JSON в папке назначения для бренда и платформы.

{ "platform": "mw", "brand": "blendr", "assets": { "badge-feature-like": { "assetname": "badge-feature-like", "path": "assets/badge-feature-like.jsx", "width": 64, "height": 64, "source": "icons_common" }, "navigation-bar-edit": { "assetname": "navigation-bar-edit", "path": "assets/navigation-bar-edit.jsx", "width": 48, "height": 48, "source": "icons_common" }, "tabbar-livestream": { "assetname": "tabbar-livestream", "path": "assets/tabbar-livestream.jsx", "width": 128, "height": 128, "source": "icons_blendr", "abtest": { "this__is_an_experiment": { "control": "assets/this__is_an_experiment/tabbar-livestream__control.jsx", "variant1": "assets/this__is_an_experiment/tabbar-livestream__variant1.jsx", "variant2": "assets/this__is_an_experiment/tabbar-livestream__variant2.jsx" }, "a_second-experiment": { "control": "assets/a_second-experiment/tabbar-livestream__control.jsx", "variantA": "assets/a_second-experiment/tabbar-livestream__variantA.jsx" } } }, ... }
}

Теперь остаётся лишь упаковать все папки assets в ZIP-архивы для более удобного скачивания.

Итог

Описанный в статье процесс — от клонирования и манипуляций со Sketch-файлами до экспорта и конвертирования ресурсов в поддерживаемые платформами форматы и сохранения собранной метаинформации в библиотеке ресурсов — повторяется с каждым объявленным в скрипте сборки брендом.

Вот скриншот, демонстрирующий внешний вид папок src и dist после завершения процесса:

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

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

Заключение (и усвоенные уроки)

Я всегда любил Sketch. Программа долгие годы была инструментом «по умолчанию» для разработчиков и дизайнеров. Поэтому мне было очень любопытно изучить средства интеграции вроде html-sketchapp и другие подобные инструменты, которые мы могли бы использовать в рабочем процессе и своих конвейерах.

Я, как и многие другие, всегда стремился к такому (идеальному) процессу:

Поэтому я стал присматриваться к другим сервисам, таким как Figma с его открытыми API и Framer X с удобной интеграцией с React, поскольку не чувствовал у Sketch движения к интеграции с кодом (каким бы он ни был). Однако должен признаться, что я начал сомневаться в том, что Sketch — подходящий инструмент, особенно с учётом дизайн-системы.

Не полностью, но во многом. Так вот, этот проект меня переубедил.

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

Создав Node-приложения на Electron или Carlo, мы можем облегчить для дизайнеров процесс выполнения множества рутинных действий. Тот факт, что Sketch-файлами можно управлять подобным образом, открыл мне дорогу ко множеству будущих разработок и улучшений: от плагинов для проверки наименования, стилизации и структуры слоёв для иконок до интеграции с нашей Wiki и документацией нашей дизайн-системы (обоюдной).

Если иконки нет, то её не существует в кодовой базе (вернее не должно существовать; но теперь мы знаем, что такие случаи — исключение). Одним из неожиданных (по крайней мере, для меня) бонусов стало то, что Sketch-файлы с иконками Cosmos стали «источником истины» — нечто похожее произошло и с дизайн-системой Cosmos. Теперь это кажется очевидным, но так было не всегда — опять же, по крайней мере, для меня.

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

Имейте в виду, что он может не отвечать требованиям вашей компании. И последнее: описанный мной конвейер был построен для наших нужд, а потому он адаптирован под наши реалии.

Может быть, другим способом, с другими подходами и другими файловыми форматами, с меньшей сложностью (например, вам может не понадобиться поддержка нескольких брендов и A/B-тестирования), но теперь вы можете автоматизировать процесс доставки иконок с кастомным скриптом Node.js и Sketch. Главное, чем я хотел поделиться, — что всё возможно.

Это весело и довольно просто. Ищите свой способ!

Благодарности

Этот огромный проект был разработан совместно с Нихилом Вермой (Mobile Web), создавшим первую версию скрипта сборки, Артёмом Рудым (Android) и Игорем Савельевым (iOS), которые разработали скрипты для импорта и приёма ресурсов для своих платформ.

Работать с вами над проектом и претворять его в жизнь было настоящим удовольствием. Спасибо, ребята!


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

[Из песочницы] ВИЧ – методы лечения от первых лекарств до сегодняшнего дня

Прежде, чем приступить к изложению материала, хотелось бы сказать несколько слов о себе: участник сообществ по борьбе с отрицанием ВИЧ („ВИЧ/СПИД диссидентством“): в 2016-2018 годах „ВИЧ/СПИД диссиденты и их дети“, с 2018 года – „ВИЧ/СПИД отрицание и альтернативная медицина“. Это ...

Изюминки прошедшей Moscow Python Conf++ 2019: трансформация в площадку для общения

Самыми горячими темами Moscow Python Conf++ оказались асинхронная разработка, а также сопоставление Python, его лучших практик и инструментария с аналогами из других языков, и его место в ландшафте современной разработки. Плюс мы пригласили выступить Бенджамина Петерсона, одного из разработчиков CPython, ...