Цветовая схема без помощи дизайнера

от автора

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

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

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

Задача

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

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

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

Идея и решение

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

Многие знают что есть множество разнообразных алгоритмов подбора цветов:

Расписывать их не вижу смысла, это уже сделали сотни раз до меня. Но есть несколько ключевых моментов:

  1. Для нас они избыточны.
  2. Хотелось подбирать цвета под себя.

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

Для начала описали базовые цвета:

export const getColors = (projectColor, inverse) => {   ...   const BASE_SUCCESS = '#00985f';   const BASE_WARNING = '#ff9900';   const BASE_PROGRESS = '#fe5c05';   const BASE_ALERT = '#ff3333';   const BASE_SYSTEM = '#778a9b';   const BASE_NORMAL = '#dde3e5';   const BASE_WHITE = '#ffffff';   const BASE_BLACK = '#000';   const TYPO_BASE_BLACK = '#242629';   const TYPO_LINK = '#33BDFF';   ... } 

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

  import color from 'color-js';    export const getColors = (projectColor, inverse) => {     ...     const baseWhite = color(BASE_WHITE);     const baseBlack = color(BASE_BLACK);     const baseTypoBlack = color(TYPO_BASE_BLACK);     ...   } 

Думаю, нет смысла полностью описывать весь подбор цветов, но для примера приведу пару строчек:

export const getColors = (projectColor, inverse) => {   ...   const bgBrand = baseProject;   const bgHard = baseColor.setLightness(0.4);   const bgSharp = baseColor.setLightness(0.18);   const bgStripe = baseColor.setLightness(0.1);   const bgGhost = baseColor.setLightness(0.07);   ... } 

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

А каким цветом отображать текст на элементах?

Задача оказалась не такой сложной, как казалось. Для её решения из hex-значения нам нужно получить яркость элемента. Сделали мы это двумя вспомогательными функциями. Первая переводит hex в RGB:

const hexToRgb = (hex) => {   const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;   const newHex = hex.replace(shorthandRegex, (     magenta,     red,     green,     blue   ) => red + red + green + green + blue + blue);    const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(newHex);    return result ? {     red: parseInt(result[1], 16),     green: parseInt(result[2], 16),     blue: parseInt(result[3], 16)   } : null; }; 

Вторая функция из RGB получает яркость фона и решает, темным будет цвет или светлым:

export const getBgTextColor = (bgColor) => {   const rgb = hexToRgb(bgColor);   const light = (rgb.red * 0.8 + rgb.green + rgb.blue * 0.2) / 510 * 100;    return light > 70 ? '#000000' : '#ffffff'; }; 

Вы можете подумать, что теперь всё готово к следующему этапу. Но нет, мы ведь ещё хотим поддержку тёмной темы из коробки? Да! Хотим!

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

import color from 'color-js';  export const getColors = (projectColor, inverse) => {   ...   const BASE_SUCCESS = '#00985f';   const BASE_WARNING = '#ff9900';   const BASE_PROGRESS = '#fe5c05';   const BASE_ALERT = '#ff3333';   const BASE_SYSTEM = '#778a9b';   const BASE_NORMAL = '#dde3e5';   const BASE_WHITE = '#ffffff';   const BASE_BLACK = '#000';   const TYPO_BASE_BLACK = '#242629';   const TYPO_LINK = '#33BDFF';    ...    const baseWhite = color(BASE_WHITE);   const baseBlack = color(BASE_BLACK);   const baseTypoBlack = color(TYPO_BASE_BLACK);   const baseColor = inverse ? baseWhite : baseBlack;   const typoColor = inverse ? baseWhite : baseTypoBlack;    ...    const bgHard = inverse ? baseColor.setLightness(0.4) : baseColor.lightenByAmount(0.85);   const bgSharp = inverse ? baseColor.setLightness(0.18) : baseColor.lightenByAmount(0.95);   const bgStripe = inverse ? baseColor.setLightness(0.1) : baseColor.lightenByAmount(0.96);   const bgGhost = inverse ? baseColor.setLightness(0.07) : baseColor.lightenByAmount(0.99);    ... } 

Вот и всё. Можем отдать список цветов:

return {   ...     // BG     'color-bg-hard': bgHard.toString(),     'color-bg-sharp': bgSharp.toString(),     'color-bg-stripe': bgStripe.toString(),     'color-bg-ghost': bgGhost.toString(),     'color-bg-primary': bgDefault.toString(),   ... } 

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

Следующий вопрос: как этим пользоваться?

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

  const colors = getColors(color, themeKey === 'dark');   const colorsVars = Object.keys(colors).map((key) => `--${key}: ${customColors[key]}`).join(';');      const link = document.createElement('style');   const headTag = document.getElementsByTagName('head')[0];    link.type = 'text/css';   link.id = 'project-theme-scope';   const stylesBody = `:root {${colorsVars}}`;    link.innerText = stylesBody;    headTag.append(link); 

А теперь самое вкусное. Внимание-внимание! Сейчас с помощью нескольких строчек когда мы добавим поддержку тёмной темы для половины элементов:

  body {     background: var(--color-bg-ghost);     color: var(--color-typo-primary);   } 

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

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

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

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

Спасибо за внимание.

ссылка на оригинал статьи https://habr.com/ru/company/domclick/blog/502978/


Комментарии

Добавить комментарий

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