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

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

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

Основные моменты

Часть 1. Темизация. История, причины, реализация [статья]

  • Имена переменных строятся по принципу kebab-case.
    Например --shared-primary-hover.
    Здесь стоит дополнить, что это ожидаемый вид на уровне кода, в figma для подобного созданы папки. То есть данная переменная будет храниться по пути shared/primary/hover.

  • Имена переменных должны отражать их назначение и не содержать информацию о конкретных оттенках.
    Например --button-primary (не --blue-200).

  • Темизацию можно настроить посредством внешних файлов стилей, средствами css-in-js и/или css-переменных.

  • В случае с css-переменными:

    1. Для каждой темы создаются классы, содержащие переменные с цветами;

    2. Выбирается тема по умолчанию. Класс с данной темой добавляется корневому элементу;

    3. При смене тем происходит изменение класса у корневого элемента;

    4. Все стили пишутся с использованием css-переменных.

  • Тема сохраняется в cookies или в локальном хранилище. При следующем входе должна быть отрисована страница с нужной темой.

Часть 2. Новые браузерные API. Темизация при SSR. Выбор между SPA, SSR и SSG [статья]

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

  • На сервере можно получить тему пользователя (в том числе нового) и отрисовать страницу сразу с нужной цветовой схемой.

Дополнительно стоит уделить внимание ещё двум css-свойствам

color-scheme

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

normal

Значение по умолчанию, означает, что сайт не поддерживает цветовых схем. Элементы интерфейса браузера будут оформлены в светлой цветовой схеме.

light

Означает, что сайт поддерживает светлую (дневную) тему. Элементы интерфейса браузера будут оформлены в светлой цветовой схеме.

dark

Означает, что сайт поддерживает тёмную (ночную) тему. Элементы интерфейса браузера будут оформлены в тёмной цветовой схеме.

only

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

<custom-ident>

Также в качестве свойства можно указать любое другое значение. Все значения, не описанные выше, не будут иметь никакого действия (логика добавлена для обратной совместимости в будущем).

В случае темизации, свойство должно быть оформлено следующим образом:

:root {
  color-scheme: light dark;
}
.light-theme-example {
  color-scheme: light;
}
.dark-theme-example {
  color-scheme: dark;
}

Где color-scheme: light dark; означает, что страница поддерживает светлую и тёмную схемы, с предпочтением светлой темы.

Источник: caniuse.com. Поддержка css-свойства color-scheme

Так как на загрузку и добавление стилей браузеру нужно время, возможны мерцания стилей при переопределение цветовой схемы. Для того, чтобы данная ситуация не возникала, можно использовать метатег <meta name="color-scheme">, который сразу сообщит пользовательскому агенту о поддерживаемых схемах.

Например, <meta name="color-scheme" content="dark light"> говорит о том, что страница поддерживает тёмную и светлую тему, с предпочтением тёмной темы.

Источник: caniuse.com. Поддержка метатега color-scheme

accent-color

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

В качестве значения может быть:

авто – устанавливает акцентный цвет платформы (если таковой имеется);

<цвет> – цвет в любом формате.

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

На данный момент поддерживаются следующие HTML элементы:

  • <input type="checkbox">

  • <input type="radio">

  • <input type="range">

  • <progress>

Источник: caniuse.com. Поддержка css-свойства accent-color

Посмотреть, как работает данное свойство можно на странице, созданной Джоуи Архаромaccent-color.glitch.me.

После закрепления основных моментов, можно переходить к следующему разделу – к процессам и предпосылкам.

Шаги при реализации темизации

Для полноценной темизации нужно предпринять 3 шага:

  1. Дизайн;

  2. Вёрстка посредством переменных;

  3. Настроить логику смены тем.

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

  1. Продумывание и создание новых цветовых схем;

  2. Создание макетов под каждую схему.

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

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

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

Дизайн и Figma

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

Figma-токены

Несмотря на общий концепт (все стили описываются в виде объектов) общей структуры у Figma токенов нет. Например объект цвета выглядит следующим образом:

{
	description: "",
	id: "S:8c32367364cb87031fe4e21199c240a3f8c79dd9,",
	key: "8c95767364cb87031fe4e21199c2a3f8c39dd9",
	name: "dark/primary",
	paints: [
		{
			blendMode: "NORMAL",
			color: {r: 0.3999999761581421, g: 0.7120000123977661, b: 1},
			opacity: 1,
			type: "SOLID",
			visible: true
		}
	],
	remote: false,
	type: "PAINT"
}

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

Так как основная цель – улучшить жизнь дизайнеру при внедрении темизации – из всего многообразия токенов стоит выделить именно цвета. Теперь, поняв что именно стоит модифицировать, остаётся разобраться с вопросом “Как?”

Figma для разработчиков

Для этого обратимся к документации figma в раздел “Figma for developers”. Согласно нему для разработчиков есть следующие возможности:

  • REST APIs

  • Plugins

  • Widgets

  • Integrations

Виджеты и интеграции слабо интересны в контексте данной задачи, а вот 2 других варианта заслуживают внимания.

REST APIs

Rest API – интерфейс, предостовляющий доступ к чтению и взаимодействию с макетами из Figma. Благодаря данному api можно получать все узлы дизайна, извлекать из них стили и их свойства. На данный момент по Figma APIs можно получить “файлы” (объекты, описывающие всё содержимое макета) и их версии, изображения, пользователей, комментарии и стили, а также отправлять комментарии к файлам.

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

Куда приятнее дела обстоят с плагинами.

Plugins

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

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

После погружения в тему расширения функционала в Figma, можно вернуться к поставленной задаче – упростить жизнь дизайнеру и разработчикам.

Оптимизация процессов на этапе дизайна

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

Можно выделить несколько основных условий, которым должно соответствовать итоговое решение:

Основные условия:

  • возможность изменять цветовую схему элемента;

  • простота в использовании;

  • скорость работы и доступность из любой точки.

Плагин Themeizer

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

Папки в Figma

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

Это первая задача, которую решает плагин.

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

Выбор тем в настройках плагина

Все темы, которые не выбраны в качестве светлых или тёмных считаются shared, то есть доступными для любой темы.

Плагин знает все цветовые схемы. Это означает, что у него есть доступ к основному источнику правды, а если так, то это возможность сделать этот источник не просто основным, но и единственным.

Обмен с клиентской частью

Для перехода стилей Figma в статус единственного источника правды, нужно создать общее для дизайна и клиента хранилище. Для этого все стили нужно выгрузить на сервер.

Как уже говорилось, плагин не должен ничего навязывать, поэтому в роли сервера может выступать всё, что угодно. Единственное правило – адрес должен принимать POST-запрос. Этот адрес нужно указать в настройках плагина и в дальнейшем, при публикации, по данному адресу будет отправляться запрос, содержащий темы со следующей схемой:

{
  [theme: string]: {
    list: [
      {
        name: string,
        value: string,
        type: "solid" | "linear" | "radial"
      }
    ],
    type: "light" | "dark" | "shared"
  }
}

Всё же, перед тем как загружать на сервер, стоит посмотреть все изменения и убедиться, что всё верно. API плагинов для подобных задач предоставляют доступ к клиентскому хранилищу (альтернатива local storage) и к хранилищу внутри текущего дизайна. Клиентское хранилище однозначно не подходит, так как над проектом работают несколько людей и доступ к сохранённым данным должен быть у каждого из них. Хранилище внутри текущего дизайна также ограничивает возможности (например данные потеряются при копировании проекта или при переустановке плагина).

Есть ещё одно место, знающее последние сохранённые стили – сервер, на который плагин уже умеет записывать цветовые схемы. Соответственно из него же можно и считывать изменения. Для этого в настройках нужно указать адрес, по которому можно получить данные (с той же схемой) посредством GET-запроса.

Конфигурация публикации и взаимодействия с сервером в настройках плагина

Дополнительно для выполнения запросов могут потребоваться специальные заголовки. Для них создано отдельное поле – headers.

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

Просмотр изменений в темах перед публикацией на сервер

Теперь данные всех тем хранятся на сервере и при необходимости обновляются. Следующим шагом нужно настроить получение на клиенте.

Оптимизация процессов на этапе разработки

Что должно происходить на клиенте:

  • Получение стилей.

  • Преобразование в формат css-переменных.

  • Встраивание классов с цветами в стили.

Получение стилей

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

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

Преобразование

Плагин сохраняет все имена в удобном формате для создания css-переменных - kebab-case. Благодаря этому остаётся получить все имена, сделать из них переменные и создать класс для каждой темы. Дополнительно в классы нужно добавить color-scheme, чтобы для темных тем браузер отображал ui-элементы в тёмном интерфейсе.

Встраивание

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

Для решения этих задач был создан Themeizer.

Пакет “themeizer”

Пакет получает нужные настройки из переменных окружения, а именно – url сервера, headers и revalidate. Так как обращений быть очень много, пакет поддерживает опцию revalidate из коробки, которая определяет периодичность отправки запросов.

Пакет поддерживает 2 варианта встраивания стилей – во время сборки, путём замены метатега тегом style, содержащим классы и стили и ручной режим, используемый в основном для SSR.

Подробнее о работе пакета – www.npmjs.com/package/themeizer

Пакет “next-themeizer”

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

Добавить пакет в конфигурацию next.js можно следующим образом:

// next.config.js
const withThemeizer = require('next-themeizer').default;
module.exports = withThemeizer();

Подробнее о работе пакета – www.npmjs.com/package/next-themeizer

Оптимизация процессов на этапе внедрения

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

Пакет “themeizer-cli”

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

Автоматическая замена посредством утилиты

Пример использования:

npm install themeizer-cli -g
themeizer-cli –c ./themeizer.config.json
// or
themeizer-cli –u https://server-url.com/themes -t light

Подробнее о работе утилиты – www.npmjs.com/package/themeizer-cli

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

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

Пакет “stylelint-themeizer”

В работе со стилями и их проверкой основным инструментов выступает Stylelint (eslint для стилей), который может показывать ошибки и автоматически их исправлять.

Stylelint-themeizer – плагин, добавляющий новое правило, при наличии которого стили будут автоматически проверяться и в случае, если цвет присутствует в опубликованных стилях и записан не в виде css-переменной – покажет ошибку и подскажет имя переменной. При использовании с аргументом --fix Stylelint автоматически исправит все стили.

Подсказки в файлах стилей и сообщения об ошибках в консоли

www.npmjs.com/package/stylelint-themeizer

Прим. К сожалению, полноценной поддержки sass в stylelint все ещё нет (напр. существующий парсер postcss-sass не умеет корректно обрабатывать стили без потерь “#”).

Заключение

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

Ссылки: