{
    "version": "https:\/\/jsonfeed.org\/version\/1",
    "title": "Блог Михаила Озорнина: заметки с тегом фронтенд",
    "_rss_description": "Главная · Блог · Работы ·",
    "_rss_language": "ru",
    "_itunes_email": "",
    "_itunes_categories_xml": "",
    "_itunes_image": "",
    "_itunes_explicit": "",
    "home_page_url": "https:\/\/mikeozornin.ru\/blog\/tags\/frontend\/",
    "feed_url": "https:\/\/mikeozornin.ru\/blog\/tags\/frontend\/json\/",
    "icon": "https:\/\/mikeozornin.ru\/blog\/user\/userpic@2x.jpg?1614204384",
    "author": {
        "name": "Михаил Озорнин",
        "url": "https:\/\/mikeozornin.ru\/blog\/",
        "avatar": "https:\/\/mikeozornin.ru\/blog\/user\/userpic@2x.jpg?1614204384"
    },
    "items": [
        {
            "id": "164",
            "url": "https:\/\/mikeozornin.ru\/blog\/all\/what-is-a-good-handoff\/",
            "title": "Как выглядит хороший макет",
            "content_html": "<p class=\"lead\">В продолжении тредов Романа Шамина про дружбу дизайнеров и фронтендеров решил вытащить в блог одну из статей наших гайдов — про то, что такое «хорошо переданный макет». Все вытащенные из гайдов статьи доступны по тегу <a href=\"http:\/\/mikeozornin.ru\/blog\/tags\/guidelines\/\">гайдлайны.<\/a><\/p>\n<p>Треды Романа Шамина:<br \/>\n<a href=\"https:\/\/teletype.in\/@romanshamin\/what-design-want-from-frontend\">teletype.in\/@romanshamin\/what-design-want-from-frontend<\/a><br \/>\n<a href=\"https:\/\/teletype.in\/@romanshamin\/what-frontend-want-from-design\">teletype.in\/@romanshamin\/what-frontend-want-from-design<\/a><\/p>\n<p>Дальше привожу статью из наших командных договоренностей.<\/p>\n<h2>О чем эта статья<\/h2>\n<p>Если макет сделан не очень удобно для разработчиков, это плохо: разработчику будет сложно понять как сделать правильно, он потратит больше времени и где-то ошибется. Дизайнеру придется писать больше замечаний, люди будут менее довольны друг другом.<\/p>\n<p>Эта статья описывает как хорошо передать макет в разработку.<\/p>\n<h2>Хранение и состав макетов<\/h2>\n<h3>Список самих экранов<\/h3>\n<p><b>Уровень «Минимально достаточный»<\/b><br \/>\nВсем членам команды продукта понятно где и как искать макеты:<\/p>\n<ul>\n<li>где найти макеты по нужной фиче,<\/li>\n<li>где найти старый макет,<\/li>\n<li>как понять актуальная версия или нет.<\/li>\n<\/ul>\n<p>Договоренности команды о процессе выкладывания макетов записаны.<\/p>\n<p>При обновлении макетов уведомляется проектная команда (аналитик, фронтенд, тех. писатель, QA).<\/p>\n<h3>Состав экранов<\/h3>\n<p><b>Уровень «Минимально достаточный»<\/b><br \/>\nДля фичи понятен набор экранов, на которые вносятся изменения: какие экраны новые, какие экраны дорабатываются.<\/p>\n<p>Если экранов несколько, то понятны переходы между экранами.<\/p>\n<p><b>Уровень «Хорошо»<\/b><br \/>\nДоступна актуальная схема экранов продукта (карта сайта).<\/p>\n<p>Если в фиче несколько экранов, то для них сделан кликабельный прототип с переходами между экранами.<\/p>\n<h2>Экраны<\/h2>\n<h3>Структура экранов<\/h3>\n<p><b>Уровень «Минимально достаточный»<\/b><br \/>\nПонятна структура экрана: из каких блоков состоит экран, какие между ними соотношения по размерам и отступам.<\/p>\n<p>Как ведут себя блоки экрана при прокрутке и ресайзе.<\/p>\n<p><b>Уровень «Хорошо»<\/b><br \/>\nУ продукта есть понятная сетка, описаны типовые лейауты страниц: список, дашборд, форма редактирования, страница просмотра и т. д.<\/p>\n<h3>Элементы на странице<\/h3>\n<p><b>Уровень «Минимально достаточный»<\/b><br \/>\nПо каждому визуальному элементу на макете понятно, что это такое: текст, компонент, иконка, паттерн.<\/p>\n<p>По каждому компоненту понятно, что это за компонент, какой его режим используется, понятно, если в коде нет этого компонента или функции существующего компонента.<\/p>\n<p>Понятны размеры элемента, его взаимоотношения с соседними элементами.<\/p>\n<p>Понятно как элемент тянется, что будет, если в элементе будет меньше или больше контента.<\/p>\n<p><b>Уровень «Хорошо»<\/b><br \/>\nВсе элементы описаны, разработчик знает где искать документацию на каждый используемый тип элемента: текст, компонент, иконку, паттерн.<\/p>\n<p>Размеры элементов и отступы между элементами логичны, ожидаемы и понятны.<\/p>\n<p>Примеры:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-1@2x.png\" width=\"600\" height=\"88\" alt=\"\" \/>\n<div class=\"e2-text-caption\"><b>Плохо:<\/b> Случайные размеры блоков, фронтендерам сложно понять отступы<\/div>\n<\/div>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-2@2x.png\" width=\"595\" height=\"330\" alt=\"\" \/>\n<div class=\"e2-text-caption\"><b>Плохо:<\/b> Случайные размеры блоков, фронтендерам сложно понять отступ<\/div>\n<\/div>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-3@2x.png\" width=\"600\" height=\"88\" alt=\"\" \/>\n<div class=\"e2-text-caption\"><b>Хорошо:<\/b> Размеры блоков понятны<\/div>\n<\/div>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-4@2x.png\" width=\"600\" height=\"96\" alt=\"\" \/>\n<div class=\"e2-text-caption\"><b>Хорошо:<\/b> Размеры блоков понятны, блоки расположены предсказуемо по сетке<\/div>\n<\/div>\n<h3>Кегли<\/h3>\n<p><b>Уровень «Минимально достаточный»<\/b><br \/>\nДля каждого текста на странице понятен стиль или mixin, который нужно взять, эти стили и mixin’ы однозначно определяются из самого макета.<\/p>\n<p>Эти mixin’ы не противоречат компонентам, паттернам или аналогичным элементам других экранов продуктов.<\/p>\n<p><b>Уровень «Хорошо»<\/b><br \/>\nЕсть актуальная таблица миксинов, описаны общие принципы их использования.<\/p>\n<p>Примеры:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-5@2x.png\" width=\"453\" height=\"168\" alt=\"\" \/>\n<div class=\"e2-text-caption\"><b>Плохо:<\/b> Кегль не замапплен, неясно какой типографический миксин взять<\/div>\n<\/div>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-6@2x.png\" width=\"453\" height=\"206\" alt=\"\" \/>\n<div class=\"e2-text-caption\"><b>Хорошо:<\/b> Кегль и цвет замапплен, понятно что указать<\/div>\n<\/div>\n<h3>Цвета<\/h3>\n<p>Речь идет про цвет любого интерфейсного элемента: текста, иконки, рамки, фона, блока, линии.<\/p>\n<p><b>Уровень «Минимально достаточный»<\/b><br \/>\nНе используются цвета не из палитры проекта.<\/p>\n<p>Переменные цветов подписаны, разработчику легко понять, какую переменную использовать (копировать hex-код не норм).<\/p>\n<p><b>Уровень «Хорошо»<\/b><br \/>\nСоставлена таблица цветовых токенов: default text, disabled text, error icon для всех используемых в проекте тем.<\/p>\n<p>В макетах прилинкованы цвета из этой таблицы (не error-500, а validation-text-color).<\/p>\n<p>Примеры:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-7@2x.png\" width=\"453\" height=\"168\" alt=\"\" \/>\n<div class=\"e2-text-caption\"><b>Плохо:<\/b> Цвета не замаплены, неясно какой цвет выбирать<\/div>\n<\/div>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-8@2x.png\" width=\"453\" height=\"206\" alt=\"\" \/>\n<div class=\"e2-text-caption\"><b>Хорошо:<\/b> Цвета замаплены, понятно что указать<\/div>\n<\/div>\n<h3>Отступы<\/h3>\n<p><b>Уровень «Минимально достаточный»<\/b><br \/>\nПонятны сами отступы между всеми блоками на странице и их внутренняя логика.<\/p>\n<p>Отступы на макете не противоречат компонентам (например, в компоненте «кнопка» отступы 6 16, а на макете 6 12).<\/p>\n<p><b>Уровень «Хорошо»<\/b><br \/>\nОписана интерфейсная микросетка и описание модулей.<\/p>\n<p>Логика отступов описана, есть типовые отступы и их завязка на сетку.<\/p>\n<h3>Пиктограммы<\/h3>\n<p><b>Уровень «Минимально достаточный»<\/b><br \/>\nРазработчик знает, как вообще подключаются пиктограммы в проект.<\/p>\n<p>Понятно, что это за конкретная пиктограмма, как ее подключить и вставить.<\/p>\n<p><b>Уровень «Хорошо»<\/b><br \/>\nЕсть общий список пиктограмм, их коды, параметры вставки в код продукта и документацию.<\/p>\n<p><aside class=\"aside-margin-right\">Чтобы не мучиться со сборкой пакетов иконок, <a href=\"http:\/\/mikeozornin.ru\/blog\/tags\/icon-font\/\">почитайте как собирать их автоматически<\/a><\/aside><\/p>\n<p>Иконки версионируются, к продукту или в документацию можно подключить нужную версию пакета иконок.<\/p>\n<p>Примеры:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-9@2x.png\" width=\"453\" height=\"310\" alt=\"\" \/>\n<div class=\"e2-text-caption\"><b>Плохо:<\/b> Иконка не замаплена, нужно искать её в наборе самостоятельно<\/div>\n<\/div>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-10@2x.png\" width=\"453\" height=\"267\" alt=\"\" \/>\n<div class=\"e2-text-caption\"><b>Хорошо:<\/b> Указано какая иконка и из какого набора. Примечание: mc — префикс одного из наших наборов иконок.<\/div>\n<\/div>\n<h2>Текст<\/h2>\n<p>См. также <i>Правильный процесс вычитки<\/i> (ссылки нет, статья не вытащена наружу).<\/p>\n<p><b>Уровень «Минимально достаточный»<\/b><br \/>\nРыба в макете правильная с точки зрения состояний, смысла и точная с точки зрения чисел и значений.<\/p>\n<p>Нет никаких Значение-1, Значение-2, Значение-3 и Лорем ипсумов.<\/p>\n<p>Из текста понятно, о чем он: это важно, чтобы техписатели могли его понять и улучшить.<\/p>\n<p><b>Уровень «Хорошо»<\/b><br \/>\nТекст в состоянии production-не стыдно, техписатели только полируют его.<\/p>\n<h2>Состояния<\/h2>\n<h3>Другие состояния<\/h3>\n<p><b>Уровень «Минимально достаточный»<\/b><br \/>\nПонятно, как сделать все остальные состояния: переключенные вкладки, переключатели, чекбоксы и радиокнопки (если они влияют на компоновку интерфейса).<\/p>\n<p>Отрисованы или описаны (если этого достаточно) все варианты дропдаунов, селектов и других выпадаек.<\/p>\n<p>Кроме самих этих состояний описаны переходы между ними.<\/p>\n<p><b>Уровень «Хорошо»<\/b><br \/>\nЕсли блоки меняются значительно, то лучше не пытаться описать отдельные выпадайки, а нарисовать блоки целиком, чтобы было меньше путаницы<\/p>\n<p>Пример:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-11@2x.png\" width=\"750\" height=\"1220\" alt=\"\" \/>\n<div class=\"e2-text-caption\">Хорошо: Нарисованы альтернативные состояний экрана<\/div>\n<\/div>\n<h3>Загрузка<\/h3>\n<p><b>Уровень «Минимально достаточный»<\/b><br \/>\nПонятно как загружаются элементы экрана, в какой последовательности, как отображается процесс загрузки.<\/p>\n<p>Как должен вести себя продукт, если загрузка медленная, элементов много, или загрузка не удалась.<\/p>\n<h3>Пустое состояние<\/h3>\n<p><b>Уровень «Минимально достаточный»<\/b><br \/>\nПонятно, как выглядит пустое состояние всех блоков и элементов, когда в них нет данных.<\/p>\n<p><b>Уровень «Хорошо»<\/b><br \/>\nПустые состояния систематизированы, используются типовые решения.<\/p>\n<p>Пример:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-12@2x.png\" width=\"398.5\" height=\"1000\" alt=\"\" \/>\n<\/div>\n<h3>Валидация<\/h3>\n<p>См. <a href=\"https:\/\/mosaic.ptsecurity.com\/components\/validation\/overview\">Валидация данных<\/a><\/p>\n<p><b>Уровень «Минимально достаточный»<\/b><br \/>\nПонятно, когда и как срабатывает валидация.<\/p>\n<p>Как и когда показывается.<\/p>\n<p>Все сообщения об ошибках проходят через дизайнера, для каждой из возможных ошибок дизайнер придумывает способ предотвращения ошибки или способ отображения, если её нельзя предотвратить<\/p>\n<p><b>Уровень «Хорошо»<\/b><br \/>\nВалидация соответствует общему гайдлайну.<\/p>\n<p>Валидация способствует не попаданию с ситуацию срабатывания валидации.<\/p>\n<h3>Много данных<\/h3>\n<p><b>Уровень «Минимально достаточный»<\/b><br \/>\nПонятно как работает экран, когда во всех потенциальных местах (текст, списки, таблицы) будет много данных.<\/p>\n<p>Все вытащенные из гайдов статьи доступны по тегу <a href=\"http:\/\/mikeozornin.ru\/blog\/tags\/guidelines\/\">гайдлайны.<\/a><\/p>\n<h2>См. также<\/h2>\n<p>Гайд Контура: <a href=\"https:\/\/guides.kontur.ru\/principles\/layouts\/\">guides.kontur.ru\/principles\/layouts\/<\/a><br \/>\nЧек-лист <a href=\"https:\/\/twitter.com\/Prit4er1\">Prit4er1<\/a>: <a href=\"https:\/\/www.notion.so\/5c03c7554ff542da9c77a6f420935282\">notion.so\/5c03c7554ff542da9c77a6f420935282<\/a><\/p>\n",
            "date_published": "2021-05-11T10:19:02+03:00",
            "date_modified": "2024-01-07T14:57:25+03:00",
            "image": "https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-1@2x.png",
            "_date_published_rfc2822": "Tue, 11 May 2021 10:19:02 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "164",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [],
                "og_images": [
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-1@2x.png",
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-2@2x.png",
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-3@2x.png",
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-4@2x.png",
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-5@2x.png",
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-6@2x.png",
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-7@2x.png",
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-8@2x.png",
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-9@2x.png",
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-10@2x.png",
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-11@2x.png",
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/what-is-a-good-handoff-12@2x.png"
                ]
            }
        },
        {
            "id": "100",
            "url": "https:\/\/mikeozornin.ru\/blog\/all\/web-dark-mode\/",
            "title": "Как включить темную тему на сайте",
            "content_html": "<p class=\"lead\">Темная тема — модно, все включают её себе в макоси (на следующий день, конечно, выключают обратно). Часто выключают потому, что сама тема темная, а весь контент вокруг (письма, сайты) — светлые. Светлый контент бьет по глазам своей яркостью.<\/p>\n<p>Некоторые сайты включают темную тему исходя из времени суток. Например, Авиасейлз:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/mikeozornin.ru\/blog\/pictures\/dark-mode-aviasales@2x.png\" width=\"846\" height=\"80\" alt=\"\" \/>\n<\/div>\n<h2>Скоро наступит будущее (UPD. Наступило в macOS 10.14.4)<\/h2>\n<p>В самой новой версии Сафари (<a href=\"https:\/\/developer.apple.com\/safari\/technology-preview\/\">Safari Technologies Preview<\/a>) уже появился детект темной темы внутри браузера. В macOS 10.14.4 эта поддержка появился в обычном сафари.<\/p>\n<p>Из браузера можно узнать установлена ли темная тема у посетителя и сделать немного магии. Светлая:<\/p>\n<div class=\"e2-text-picture\">\n<a href=\"http:\/\/mikeozornin.ru\" class=\"e2-text-picture-link\">\n<img src=\"https:\/\/mikeozornin.ru\/blog\/pictures\/mikeozornin.ru-light-mode@2x.png\" width=\"1137\" height=\"687\" alt=\"\" \/>\n<\/a><\/div>\n<p>Темная:<\/p>\n<div class=\"e2-text-picture\">\n<a href=\"http:\/\/mikeozornin.ru\" class=\"e2-text-picture-link\">\n<img src=\"https:\/\/mikeozornin.ru\/blog\/pictures\/mikeozornin.ru-dark-mode@2x.png\" width=\"1137\" height=\"687\" alt=\"\" \/>\n<\/a><\/div>\n<p>Тем, у кого есть новый Сафари 68+ можно попробовать здесь:<\/p>\n<p class=\"loud\"><a href=\"http:\/\/mikeozornin.ru\">mikeozornin.ru<\/a> или <a href=\"http:\/\/mikeozornin.ru\/blog\">mikeozornin.ru\/blog<\/a><\/p>\n<h2>Как включить детект<\/h2>\n<p>Чтобы включить темную тему используется медиа-запрос prefers-color-scheme:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">@media (prefers-color-scheme: dark) {\r\n    \/\/какие-то отличия темной темы, от светлой\r\n}<\/code><\/pre><p>Чтобы отлаживать, не включая-выключая системную тему, в Сафари (в той самой Technologies Preview) есть кнопка включения темной темы локально в браузере:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/mikeozornin.ru\/blog\/pictures\/safari-preview@2x.png\" width=\"1000\" height=\"53\" alt=\"\" \/>\n<\/div>\n<p>Если кому-то интересно как сделана темная тема, то кроме css-файла на сайте есть <a href=\"https:\/\/github.com\/mikeozornin\/mikeozornin.ru\/blob\/master\/src\/static\/css\/main-page.less\">less-файл на гитхабе<\/a>. Но осторожно, там очень много быдлокода и костылей.<\/p>\n",
            "date_published": "2018-12-13T10:49:51+03:00",
            "date_modified": "2019-04-06T13:20:02+03:00",
            "image": "https:\/\/mikeozornin.ru\/blog\/pictures\/dark-mode-cover.png",
            "_date_published_rfc2822": "Thu, 13 Dec 2018 10:49:51 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "100",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": [
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/dark-mode-cover.png",
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/dark-mode-aviasales@2x.png",
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/mikeozornin.ru-light-mode@2x.png",
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/mikeozornin.ru-dark-mode@2x.png",
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/safari-preview@2x.png"
                ]
            }
        },
        {
            "id": "79",
            "url": "https:\/\/mikeozornin.ru\/blog\/all\/how-to-build-icon-font-from-sketch-2\/",
            "title": "Как собрать шрифт с иконками — 2",
            "content_html": "<p class=\"lead\">С момента <a href=\"http:\/\/mikeozornin.ru\/blog\/all\/how-to-build-icon-font-from-sketch\/\">первого поста<\/a> я доработал схему сборки, расскажу что и зачем там сделано. Сначала будет вступление-рассказ о чем все это, если вы хотите нового, прокрутите до заголовка <a href=\"http:\/\/mikeozornin.ru\/blog\/all\/how-to-build-icon-font-from-sketch-2#new\" class=\"nu\">«<u>Новое в этой версии<\/u>»<\/a>.<\/p>\n<h2>Что вообще происходит<\/h2>\n<p>У дизайнера есть несколько разных способов передать иконки разработчику:<\/p>\n<ul>\n<li>отдельными файлами или спрайтом в ПНГ,<\/li>\n<li>отдельными файлами или спрайтом в СВГ,<\/li>\n<li>иконочным шрифтом.<\/li>\n<\/ul>\n<p>Разработчики фронтенда все чаще привыкли использовать иконки в виде шрифта. Этим же способом распространяются популярные иконочные наборы (например, Font Awesome). У нас в компании разработчики тоже просят «дай шрифт». Мы некоторое время отлаживали схему сборки шрифта: как из файла Скетча автоматически получить файл, пригодный для фронтенда, не замучив дизайнера.<\/p>\n<p>Рассказ может быть полезен разработчикам фронтенда и дизайнерам интерфейсов. В меньшей степени он будет полезен бекендным разработчикам интерфейсов (классический ASP.NET MVC или что-то подобное): схема будет та же, но не будет готовых файлов конфигураций и скриптов. Если кто-то расскажет как прикрутить к этому NuGet, напишите, я добавлю.<\/p>\n<h2>Зачем это делать, есть же фонтелло<\/h2>\n<p>Есть много готовых сервисов, которые собирают шрифт по загруженным СВГ-файлам, например fontello. Мы не стали использовать ни один из них, потому что с ними могут быть сложности:<\/p>\n<p><b>Дизайнер может случайно сломать шрифт<\/b>. Если забыть и не экспортировать иконку, которую уже давал, то следующая версия шрифта будет без него и в неизвестном месте сломается интерфейс. Ситуацию усугубляет факт, что дизайнеров у каждого продукта несколько, а общий набор иконок пополняют 5-6 человек.<\/p>\n<p>Хорошее решение — простое, в нем минимум ручных действий.<\/p>\n<p><b>Нескольким дизайнерам работать непросто<\/b>. Если несколько дизайнеров поддерживают один шрифт, то возникает много вопросов синхронизации: где хранить исходники, файлы СВГ и файлы шрифта, кто собирает и кому передает, как не забыть иконку.<\/p>\n<p>Хорошее решение позволяет добавлять иконки скольким угодно дизайнерам так, что они не испортят чужую работу.<\/p>\n<p><b>Cложно интегрировать в общий процесс сборки продукта<\/b>. Отдельно стоящий сервис тяжело встроить в общий процесс разработки и сборки, а у кого-то есть еще и процесс CI. Придется вручную собирать сервисом файл, куда-то его загружать и как-то версионировать.<\/p>\n<p>Хорошее решение встраивается в процесс разработки.<\/p>\n<p><b>Не всех устраивает внешний сервис<\/b>. Многие компании не верят во внешние сервисы: они могут изменить набор функций, упасть во время подготовки релиза, стать платными или закрыться. В конце концов, их могут хакнуть. Мы — ИБ-компания, и каждый раз раздражать профессионально деформированных безопасников и разработчиков наличием внешнего сервиса не хочется.<\/p>\n<p>Хорошее решение работает внутри компании.<\/p>\n<p><b>Формируется не всё, что надо<\/b>. Некоторые сервисы выдают шрифт, а иконки кодируют номерами символов. К сожалению, на эти номера полагаться нельзя. Если убрать иконку или поменять порядок, то в следующий раз сервис может выдать совсем другие коды и все иконки непредсказуемо поменяются.<\/p>\n<p>Если не формировать вместе со шрифтом ЛЕСС-файл, то разработчикам придется в каждом использовании иконки указывать размер кегля, они могут забыть или ошибиться.<\/p>\n<p>Хорошее решение дает разработчикам все что нужно. Иконка кодируется понятным названием, коды символов и размер подставляются автоматически.<\/p>\n<h2>Новое в этой версии <span id=\"new\">&nbsp<\/span><\/h2>\n<p>Раньше я описывал схему сборки шрифта, которая позволяет автоматически собрать шрифтовой файл с иконками из исходника Скетча, сгенерировать для него демо-страницу и ЛЕСС-файл, а также опубликовать пакет в НПМ-репозиторий.<\/p>\n<p>В новой версии мы научились ещё два важных пункта: поставлять иконки в виде скетч-библиотеки и ТТФ-файла. Я пройдусь по каждому изменению, а в конце приложу итоговое решение.<\/p>\n<h3>Поставка иконок в виде библиотеки Скетча<\/h3>\n<p><aside class=\"aside-margin-right\"><span class=\"related-title\"><a href=\"https:\/\/medium.com\/ux-power-tools\/sketch-libraries-how-they-work-and-the-crazy-stuff-you-can-do-with-them-fc10f142ac80\">Описание работы библиотек<\/a><\/span><\/aside><\/p>\n<p>С момента написания того поста вышла новая версия Скетча — 47, в ней появились библиотеки символов. Иконки стало удобно вставлять в макеты именно с помощью библиотеки.<\/p>\n<p>Чтобы сделать один символ на одну иконку и не плодить их для каждого цвета, нужно сделать так:<\/p>\n<ol start=\"1\">\n<li>Сделайте по символу для каждого цвета, в которые у вас можно красить иконки.<\/li>\n<\/ol>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/mikeozornin.ru\/blog\/pictures\/icons-BG@2x.png\" width=\"557\" height=\"183\" alt=\"\" \/>\n<\/div>\n<ol start=\"2\">\n<li>Нарисуйте иконку и наложите её маской на символ цвета:<\/li>\n<\/ol>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/mikeozornin.ru\/blog\/pictures\/icon-in-library@2x.png\" width=\"368\" height=\"158\" alt=\"\" \/>\n<\/div>\n<p>После этого вы сможете менять цвет иконок через оверрайды:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/mikeozornin.ru\/blog\/pictures\/icon-in-library-2@2x.png\" width=\"376\" height=\"229\" alt=\"\" \/>\n<\/div>\n<p>Образец файла: <a href=\"https:\/\/github.com\/mikeozornin\/icon-font-public\/raw\/master\/iconset.sketch\">iconset.sketch<\/a><\/p>\n<p>К сожалению такой скетч-файл плохо собирался в шрифт. Это происходит из-за маски.<\/p>\n<p>Маска правильно экспортируется в СВГ-файл, но в шрифте нет понятия маски, в итоге иконки ломались. Долгое время я вел скетч-файл в двух вариантах: простые артборды для шрифта и такие же с маской для библиотеки. Но это плохой способ: нужно добавлять и изменять пары одинаковых иконкок. Из-за этого иногда были ошибки — я что-то где-то забывал.<\/p>\n<p>Неожиданно Акронис <a href=\"https:\/\/habrahabr.ru\/company\/acronis\/blog\/344454\/\">написал пост на Хабре<\/a> про свою дизайн-систему. С помощью их поста удалось сделать правильно. Сейчас наш сборщик регулярными выражениями вырезает лишнее из СВГ-файла и превращает файл с маской в обычный файл.<\/p>\n<p>Для вырезания лишнего я подключил grunt-text-replace, вот его конфиг:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">replace: {\r\n  remove_mask: {\r\n    src: [PATH_BUILD_ICONS + '\/*.svg'],\r\n     overwrite: true,  \/\/ overwrite matched source files\r\n     replacements: [\r\n      {from : \/ fill=&quot;(.*?)&quot;\/m,             to : ''},\r\n      {from : \/(\\s*)&lt;\\\/defs[\\s\\S]*&lt;\\\/g&gt;\/m,  to : ''},\r\n      {from : \/(\\s*)&lt;defs&gt;\/m,               to : ''},\r\n      {from : \/ id=&quot;(.*?)&quot;\/m,               to : ''},\r\n      {from : \/xmlns:xlink=&quot;(.*?)&quot;\/m,       to : ''},\r\n      {from : \/(\\s*)&lt;g[\\s\\S]*?&gt;\/m,          to : ''},\r\n      {from : \/(\\s*)&lt;\\\/g&gt;\/m,                to : ''},\r\n      {from : \/&lt;svg\/m,                      to : '&lt;svg fill=&quot;#000&quot;'},\r\n      {from : \/ transform=&quot;(.*?)&quot;\/m,        to : ''},\r\n      {from : \/ fill-rule=&quot;(.*?)&quot;\/m,        to : ''},]            \r\n    }\r\n  }<\/code><\/pre><h3>Поставка иконок в виде ТТФ-файла<\/h3>\n<p>У нас в компании есть команда технических писателей, которые пишут руководства по продуктам. В руководствах техписатели вставляют иконки, чтобы проиллюстрировать на что нужно нажать в интерфейсе.<\/p>\n<p>Раньше для вставки иконки они делали скриншот экрана, вырезали из него иконку и вставляли в руководство. Сказать, что это неудобный способ — ничего не сказать: растровые скриншоты плохо выглядят при печати; при изменении иконки нужно внимательно заменить все скриншоты с ней; сложно менять цвет иконок в растре, их приходится снимать в каждом цвете — это все увеличивает и без того большую работу.<\/p>\n<p>Редактор документации поддерживает вставку через СВГ, она решила бы почти часть этих проблем, но СВГ легко не перекрасить, и СВГ-картинки плохо выглядит при редактировании.<\/p>\n<p>Идеальным вариантом для них было бы использовать ТТФ-шрифт: он отлично рендерится на экране и при печати, хорошо автоматизируется и удобно перекрашивается.<\/p>\n<p>Однако недостаточно просто включить генерирование ТТФ-файла: по умолчанию коды символов формируются сборщиком автоматически. При следующем формировании шрифта код иконки мог замениться на произвольный. Если технический писатель вставлял иконку в документ, и обновлял шрифт, часть иконок могла непредсказуемо поменяться на другие. Так, конечно, никуда не годилось.<\/p>\n<p>В новой версии вместо автоматического выбора кодов символов я задаю их руками. Меинтейнер шрифтового репозитория (это я) выдает каждой новой иконке новый код и следит за тем, чтобы коды не менялись. Для этого используется секция codepoints в grunt-webfont. Каждой иконке нужно задать номер символа. У нас используется последовательная нумерация начиная с 0xF501.<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">codepoints : {\r\n   'upload-to-cloud_24' : 0xF501,\r\n   'upload-to-cloud_64' : 0xF502,\r\n},\r\nstartCodepoint: 0xF701,<\/code><\/pre><p>Иконкам, которым номер не выставлен вручную, код выберется автоматически. Вообще это неправильно и лучше следить, чтобы таких иконок не было. Чтобы найти такие иконки их пространство имен отличается, им выдается номер из пространства 0xF701+, поэтому они всегда будут в конце шрифта и их легко найти.<\/p>\n<p>Посмотреть коды символов можно с помощью <a href=\"http:\/\/opentype.js.org.\">http:\/\/opentype.js.org.<\/a> Выбираете Glyph Inspector, загружаете шрифт и смотрите:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/mikeozornin.ru\/blog\/pictures\/opentype.js@2x_1.png\" width=\"1886\" height=\"1398\" alt=\"\" \/>\n<\/div>\n<p>Наши писатели перешли на ТТФ-файл и довольны.<\/p>\n<div class=\"post-summary\"><div class=\"post-summary__header\"><p>Скачать и посмотреть<\/p>\n<\/div><div class=\"post-summary__text\"><p>Я обновил репозиторий решения, там есть пример файла и сборщик. Чтобы понять как и что — прочитайте README.md. Скачивайте и смотрите:<br \/>\n<a href=\"https:\/\/github.com\/mikeozornin\/icon-font-public\">https:\/\/github.com\/mikeozornin\/icon-font-public<\/a><\/p>\n<\/div><\/div><h2>Что дальше<\/h2>\n<p>Наша схема сейчас покрывает нужны дизайнеров, фронтендеров и технических писателей. Немного не хватает нормального предпросмотра шрифта (пока не хватает времени доделать). Напишу, если будет что показать.<\/p>\n<p>С вопросами, багрепортами или советами — в телеграм <a href=\"http:\/\/t.me\/mikeozornin\">@mikeozornin<\/a> или на почту <a href=\"mailto:mike.ozornin@gmail.com\">mike.ozornin@gmail.com<\/a>.<\/p>\n",
            "date_published": "2018-01-09T12:12:15+03:00",
            "date_modified": "2018-12-16T01:05:57+03:00",
            "image": "https:\/\/mikeozornin.ru\/blog\/pictures\/icon-font-cover-2@2x.png",
            "_date_published_rfc2822": "Tue, 09 Jan 2018 12:12:15 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "79",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": [
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/icon-font-cover-2@2x.png",
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/icons-BG@2x.png",
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/icon-in-library@2x.png",
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/icon-in-library-2@2x.png",
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/opentype.js@2x_1.png"
                ]
            }
        },
        {
            "id": "54",
            "url": "https:\/\/mikeozornin.ru\/blog\/all\/how-to-build-icon-font-from-sketch\/",
            "title": "Как собрать шрифт с иконками",
            "content_html": "<p class=\"lead\">Разработчики иногда просят иконки не в пнг или свг, не пнг-спрайтом, а в шрифте. Им проще его так использовать. В посте я расскажу как из скетч-файла собрать шрифт, чтобы всем было удобно в будущем.<\/p>\n<p class=\"lead\">UPD. Появилась <a href=\"http:\/\/mikeozornin.ru\/blog\/all\/how-to-build-icon-font-from-sketch-2\/\">более новая версия сборки<\/a><\/p>\n<h2>Зачем это делать, есть же фонтелло<\/h2>\n<p>Есть много сервисов, которые собирают шрифт из загруженных свг-файлов. Зачем придумывать что-то своё?<\/p>\n<ul>\n<li><b>Чтобы случайно не сломать шрифт<\/b>. Если забыть иконку, которую уже выдавал и не загрузить в фонтеллу, то она не попадет в новый шрифт и где-то в интерфейсе пропадет иконка.<\/li>\n<li><b>Подойдет, если дизайнеров несколько<\/b>. Если дизайнеров в команде несколько, то нужна синхронизация между ними. Было бы круто, если бы каждый мог добавлять в шрифт иконки и не сломать чужой результат.<\/li>\n<li><b>Легко встраивается в ваш CI-процесс<\/b>.<\/li>\n<li><b>Не всех устраивает внешний сервис<\/b>. Внешний сервис может быть недоступен, он может внезапно обновиться, стать платным, и вообще не все готовы отдавать наружу свои иконки.<\/li>\n<li><b>Разработчику сразу формируется и less-файл<\/b>. Обмениваться кодами символов неудобно, они генерируются автоматически и могут измениться, использовать css-класс надежно, он не поменяется.<\/li>\n<\/ul>\n<h2>Вариант 1. Собираем и отдаём шрифт<\/h2>\n<p>Я выложил все необходимые файлы в репозиторий <a href=\"https:\/\/github.com\/mikeozornin\/icon-font-public\">icon-font-public<\/a>, скачайте его и распакуйте куда-нибудь.<br \/>\nСкачать: <a href=\"https:\/\/github.com\/mikeozornin\/icon-font-public\/archive\/master.zip\">https:\/\/github.com\/mikeozornin\/icon-font-public\/archive\/master.zip<\/a><\/p>\n<h3>Шаг 1. Настраиваем среду<\/h3>\n<p>Все эти заклинания надо произнести один раз, дальше не понадобятся<\/p>\n<p>1) Установить <a href=\"http:\/\/brew.sh\/index_ru.html\">brew<\/a>. Brew — это такой менеджер пакетов, который легко позволяет ставить программы и библиотеки. Выполнить в терминале команду:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">\/usr\/bin\/ruby -e &quot;$(curl -fsSL https:\/\/raw.githubusercontent.com\/Homebrew\/install\/master\/install)&quot;<\/code><\/pre><p>2) С помощью brew установить шрифтообработчики:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">brew install ttfautohint fontforge --with-python<\/code><\/pre><p>3) Установить node.js, скачать тут <a href=\"https:\/\/nodejs.org\/en\/\">https:\/\/nodejs.org\/en\/<\/a> current-версию.<\/p>\n<p>4) Установить SketchTool<br \/>\nПри установленном Sketch выполнить в терминале команду:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">\/Applications\/Sketch.app\/Contents\/Resources\/sketchtool\/install.sh<\/code><\/pre><p>5) Установить grunt<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">npm install -g grunt-cli<\/code><\/pre><h3>Шаг 2. Сборка файла шрифта<\/h3>\n<p>Для сборки шрифт нужно произнести в терминале заклинание:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">.\/build.sh<\/code><\/pre><p>Если в первый раз не сработает, сделайте скрипт выполняемым:<\/p>\n<pre class=\"e2-text-code\"><code class=\"\">chmod +x build.sh<\/code><\/pre><p>В итоге при добавлении иконки нужно будет сделать:<\/p>\n<ol start=\"1\">\n<li>Нарисовать иконку.<\/li>\n<li>Собрать шрифт: .\/build.sh<\/li>\n<li>Отдать файл шрифта и less-файл разработчику<\/li>\n<\/ol>\n<h3>Что все это было?<\/h3>\n<p>Всё, что описано выше работает так:<\/p>\n<ol start=\"1\">\n<li>С помощью SketchTool всё, что может быть экспортировано, экспортируется из скетча в СВГ-файлы.<\/li>\n<li>СВГ-файлы, полученные на шаге выше собираются в шрифт. Для этого запускается сборщик шрифта, который все СВГ-файлы собирает в шрифт, конвертирует его в нужный формат и формирует хтмл-страницу с превью.<\/li>\n<\/ol>\n<h2>Вариант 2. Собираем и отдаём шрифт npm-пакетом<\/h2>\n<h3>Это ещё что такое?<\/h3>\n<p>Фронтенд-разработчики подключают библиотеки через npm-пакеты. Это привычная и удобная для них среда, кроме этого, npm-пакет сделает передачу шрифта удобней.<\/p>\n<h3>Шаг 1. Настраиваем среду<\/h3>\n<p>Нужно настроить среду как в первом варианте, и дополнительно настроить локальный npm-репозиторий. Спросите вашего фронтендера как это сделать у вас в команде. Попросите его исправить файл package.json.<\/p>\n<h3>Шаг 2. Сборка файла шрифта<\/h3>\n<ol start=\"1\">\n<li>Нарисовать иконку.<\/li>\n<li>Собрать шрифт: .\/build.sh<\/li>\n<li>Изменить версию пакета в файле package.json<\/li>\n<li>Выполнить команду grunt publish<\/li>\n<li>Передать разработчику шифровку «Выпустил пакет версии xxx».<\/li>\n<\/ol>\n<h2>Вариант 3. Собираем и отдаём шрифт нугет-пакетом<\/h2>\n<p>К сожалению про нугет ничего не знаю, но если кто-то соберет рабочий вариант, добавлю.<\/p>\n<h2>На что обратить внимание при рисовании иконок<\/h2>\n<p>При экспорте иконок надо не забыть перевести все в кривые. Чего не должно быть:<\/p>\n<ul>\n<li>Радиусов скруглений<\/li>\n<li>Покрашенным рамок<\/li>\n<li>Текстовых надписей<\/li>\n<\/ul>\n<p>Если это будет, то иконка может выглядеть в шрифте плохо, даже если в СВГ-файле было всё ок.<\/p>\n<h2>Если что-то не работает<\/h2>\n<p>Я обкусывал наше решение, возможно что-то переобкусал. Пишите если что-то не работает, или попросите помочь вашего фронтендера, он разберется.<\/p>\n",
            "date_published": "2017-01-26T12:21:17+03:00",
            "date_modified": "2018-12-16T01:07:38+03:00",
            "image": "https:\/\/mikeozornin.ru\/blog\/pictures\/icon-font-cover@2x.png",
            "_date_published_rfc2822": "Thu, 26 Jan 2017 12:21:17 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "54",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": [
                    "https:\/\/mikeozornin.ru\/blog\/pictures\/icon-font-cover@2x.png"
                ]
            }
        }
    ],
    "_e2_version": 3798,
    "_e2_ua_string": "E2 (v3798; Aegea)"
}