color-value-tools — конвертация и манипуляция цветами в любых форматах

07.03.2026
color-value-tools — конвертация и манипуляция цветами в любых форматах

В веб-разработке и в инструментах для дизайна цвета приходят отовсюду: hex из макетов, rgb() и hsl() из CSS, CSS-переменные из темы, значения из форм и API. То нужно осветлить кнопку на 15%, то смешать два цвета для градиента, то проверить контраст для доступности, то отдать в печать в CMYK — и каждый раз встаёт вопрос: в каком формате у нас цвет сейчас и в какой перевести. Писать конвертеры вручную или таскать копипасту из старых проектов неудобно, легко ошибиться в формулах и граничных случаях. Пакет color-value-tools как раз про то, чтобы один раз подключить библиотеку и получать парсинг, конвертацию между форматами (hex, RGB, HSL, HSV, Lab, LCH, CMYK) и утилиты для яркости, смешивания и контраста — без лишних зависимостей и с предсказуемым поведением.

В этом посте — зачем выносить работу с цветом в отдельный слой, что входит в API (определение типа, нормализация, конвертеры, смешивание, WCAG), и как этим пользоваться в повседневных задачах: темы, градиенты, доступность, печать. Цель — дать полную картину «что можно сделать» с примерами кода под реальные сценарии.


Зачем вообще отдельная библиотека для цветов

На практике с цветами часто получается так: в одном месте парсим hex и считаем яркость, в другом — смешиваем два rgb() через формулу из интернета, в третьем — проверяем контраст для текста. Типичные ситуации выглядят так:

  • Единый источник правды: хочется один раз описать «как мы работаем с цветом» (нормализация, конвертация, яркость) и во всём приложении использовать одни и те же функции. Иначе в одном модуле hex без решётки считают невалидным, в другом — дополняют до 6 символов по-своему, и баги всплывают не сразу.
  • Много форматов без головной боли: дизайн отдаёт hex, бэкенд иногда присылает rgba(), в CSS живут переменные var(--accent), для печати нужен CMYK — нужен единый вход (строка или объект) и выход в нужном формате без ручного разбора строк и граничных случаев.
  • Доступность и дизайн: контраст по WCAG, проверка «тёмный/светлый» фон, смешивание цветов для градиентов или hover-состояний — всё это опирается на одни и те же базовые операции (яркость, интерполяция). Выносить их в одну библиотеку удобно и для тестирования, и для консистентности.

Ручные парсеры под каждый формат и копипаста формул RGB↔HSL или Lab быстро обрастают нюансами (процентные значения в rgb(), короткий hex, прозрачность). color-value-tools даёт готовый набор: определение типа цвета, нормализация hex с fallback, конвертеры между hex / RGB / HSL / HSV / Lab / LCH / CMYK, смешивание в режиме RGB или HSL, относительная яркость и контраст. В итоге логику работы с цветом можно держать в одном месте и не плодить разрозненные хелперы по проекту.


Что внутри: один вход, полный набор утилит

Пакет — одно ядро без разделения на entry points: все функции экспортируются из корня. Нет зависимостей от фреймворков: подходит и для Node-скриптов, и для браузера, и для Vue/React — подключаете и используете там, где нужно.

Категория Назначение
Определение типа getColorType, isHexColor, isRgbColor, isHslColor, isCssVariable, extractCssVariableName
Нормализация и парсинг normalizeHex, normalizeColor, rgbaStringToRgba, hex8ToRgba
Конвертеры hex ↔ RGB ↔ HSL ↔ HSV ↔ Lab ↔ LCH ↔ CMYK, а также строки rgba() / rgb()
Манипуляция adjustHexBrightness, rotateHue, mixColors
Доступность (WCAG) relativeLuminance, contrastRatio, isDark, isLight

Установка — одна команда:

bash Copy
npm install color-value-tools

Типы поставляются из коробки (TypeScript). В коде можно импортировать только нужные функции — бандл остаётся предсказуемым.


Определение типа и нормализация

Прежде чем конвертировать или смешивать, нужно понять, с чем мы работаем: hex, rgb/rgba, hsl/hsla, CSS-переменная или именованный цвет. Для этого служат предикаты и общий определитель типа.

Функция Описание
isCssVariable(value) Возвращает true, если строка имеет вид var(--name) или var(--name, fallback).
isHexColor(value) Распознаёт 3- и 6-значный hex, с решёткой или без.
isRgbColor(value) Проверяет начало на rgb( или rgba(.
isHslColor(value) Проверяет начало на hsl( или hsla(.
getColorType(value) Возвращает `'hex'
extractCssVariableName(value) Из var(--accent) возвращает --accent.

normalizeHex(hex) приводит hex к виду: решётка в начале, 6 символов в нижнем регистре, короткий трёхсимвольный разворачивается удвоением (#f00#ff0000). Если строка невалидна — возвращается fallback #f5e477.

normalizeColor(input) — универсальный парсер. На входе: строка (hex, rgb/rgba, hsl/hsla, CSS-переменная, именованный цвет вроде transparent) или объект { r, g, b } / { h, s, l }. На выходе — объект с полями в зависимости от типа:

Поле Описание
type `'hex'
hex Нормализованный hex (если тип позволяет вычислить).
r, g, b, a Каналы 0–255 и альфа 0–1.
h, s, l Hue 0–360, saturation и lightness 0–100.
v Value из HSV (0–100).
raw Исходная строка для css-var (переменную не разобрать без значения).

Пример: разобрать цвет из формы или API и дальше работать с единой структурой.

ts Copy
import { getColorType, normalizeColor, normalizeHex } from 'color-value-tools';

getColorType('var(--accent)');        // 'css-var'
getColorType('#aabbcc');              // 'hex'
getColorType('rgba(255, 0, 0, 0.5)'); // 'rgb'

normalizeHex('f00');                  // '#ff0000'
normalizeHex('invalid');              // '#f5e477' (fallback)

const parsed = normalizeColor('#3498db');
// { type: 'hex', hex: '#3498db', r: 52, g: 152, b: 219, a: 1, h: 204, s: 70, l: 53, v: 86 }

const fromRgba = normalizeColor('rgba(100, 150, 200, 0.8)');
// type: 'rgb', hex: '#6496c8', r, g, b, a: 0.8, h, s, l, v

Для строк rgb() / rgba() полезны rgbToRgbaString (собрать строку из каналов и альфы) и rgbaStringToRgba (разобрать строку в объект { r, g, b, a }). Поддерживаются и процентные значения каналов.


Конвертеры между форматами

Библиотека покрывает цепочки hex ↔ RGB ↔ HSL ↔ HSV, плюс Lab, LCH и CMYK. Все конвертеры работают с числами в ожидаемых диапазонах; для hex на входе используется внутренняя нормализация.

Hex, RGB, RGBA

Функция Описание
hexToRgb(hex) Возвращает кортеж [r, g, b] (0–255).
hexToRgba(hex, opacity?) Строка rgba(r, g, b, opacity) (opacity по умолчанию 1).
rgbToHex({ r, g, b }) Строка #rrggbb.
rgbaToHex({ r, g, b, a }) 8-значный hex #rrggbbaa (альфа в 0–1).
hex8ToRgba(hex) Парсит #rrggbbaa или короткий #rgba в { r, g, b, a } или null.
rgbaToHex8({ r, g, b, a }) Собирает 8-значный hex из каналов.

HSL, HSV

Функция Описание
hexToHsl(hex) Кортеж [h, s, l]: h 0–360, s и l 0–100.
hslToHex(h, s, l) Строка hex.
rgbToHsl({ r, g, b }) [h, s, l].
hslToRgb(h, s, l) Объект { r, g, b }.
hexToHsv(hex) Кортеж [h, s, v].
hsvToHex(h, s, v) Строка hex.
rgbToHsv / hsvToRgb Аналогично HSL, для HSV.

Lab, LCH (восприятие и продвинутые сценарии)

Функция Описание
rgbToLab({ r, g, b }) Объект { L, a, b } (L 0–100, a и b примерно −128…127).
labToRgb({ L, a, b }) Объект { r, g, b }.
rgbToLch({ r, g, b }) Объект { L, C, H } (L, chroma, hue в градусах).
lchToRgb({ L, C, H }) Объект { r, g, b }.

Lab и LCH полезны, когда нужно работать с «визуально равномерными» изменениями (например, одинаковое воспринимаемое расстояние по яркости или насыщенности).

CMYK (печать)

Функция Описание
rgbToCmyk({ r, g, b }) Объект { c, m, y, k } (значения 0–1).
cmykToRgb({ c, m, y, k }) Объект { r, g, b }.

Пример: подготовка данных для печатного макета или экспорта в PDF.

ts Copy
import {
  hexToRgb,
  rgbToHsl,
  hslToHex,
  rgbToCmyk,
  rgbToLab,
  labToRgb,
} from 'color-value-tools';

const [r, g, b] = hexToRgb('#3498db');
const [h, s, l] = rgbToHsl({ r, g, b });
const backToHex = hslToHex(h, s + 10, l); // чуть насыщеннее

const cmyk = rgbToCmyk({ r, g, b });
// { c: 0.76, m: 0.31, y: 0, k: 0.14 }

const lab = rgbToLab({ r, g, b });
const rgbAgain = labToRgb(lab);

Яркость, оттенок и смешивание

Типичные задачи: осветлить/затемнить кнопку, сдвинуть оттенок для hover, получить промежуточный цвет между двумя значениями.

Функция Описание
adjustHexBrightness(hex, offsetPercent) Смещение яркости в процентах: положительное — светлее, отрицательное — темнее (−100…100).
rotateHue(hex, degrees) Поворот оттенка в градусах (0–360).
mixColors(c1, c2, t, opts?) Линейная интерполяция между двумя цветами (строками). t от 0 до 1. Опции: `mode: 'rgb'

mixColors принимает любые строки, которые умеет разбирать normalizeColor (hex, rgb, rgba, hsl, hsla). Режим rgb даёт привычное смешивание по каналам; режим hsl смешивает по оттенку, насыщенности и яркости — градиенты по HSL часто выглядят «цветовее». Формат выхода задаётся через format.

ts Copy
import { adjustHexBrightness, rotateHue, mixColors } from 'color-value-tools';

adjustHexBrightness('#3498db', 20);   // светлее на 20%
adjustHexBrightness('#3498db', -15); // темнее на 15%

rotateHue('#ff0000', 120);           // красный → зелёный по кругу HSL

mixColors('#ff0000', '#0000ff', 0.5);
// '#800080' (фиолетовый по умолчанию — hex, режим rgb)

mixColors('#f00', '#00f', 0.5, { mode: 'hsl', format: 'rgba' });
// rgba(128, 0, 128, 1) — тот же коэффициент, другой режим и вывод

Доступность: яркость и контраст (WCAG)

Для текста и фона важно соблюдать контраст. В пакете есть относительная яркость по формуле sRGB и коэффициент контраста по WCAG, плюс удобные предикаты «тёмный/светлый».

Функция Описание
relativeLuminance(color) Относительная яркость 0–1 (строка в любом поддерживаемом формате).
contrastRatio(colorA, colorB) Коэффициент контраста (1–21, для WCAG обычно нужно ≥ 4.5 или ≥ 7).
isDark(color, threshold?) true, если яркость меньше порога (по умолчанию 0.5).
isLight(color, threshold?) Противоположность isDark.

Пример: подбор цвета текста под фон или проверка пары «кнопка — подпись».

ts Copy
import {
  relativeLuminance,
  contrastRatio,
  isDark,
  isLight,
} from 'color-value-tools';

const bg = '#1a1a2e';
const textLight = '#eaeaea';
const textDark = '#111';

relativeLuminance(bg);           // ~0.02
contrastRatio(bg, textLight);     // ~15.6 — отлично для текста
contrastRatio(bg, textDark);      // ~1.1 — нечитаемо

isDark(bg);                      // true
isLight(textLight);              // true

// Подбор цвета подсказки под фон
const hintColor = isDark(bg) ? '#a0a0a0' : '#505050';

Примеры под реальные задачи

Ниже — несколько сценариев от простого к более развёрнутому. Каждый можно взять за основу и подстроить под свой проект.

1. Тема и CSS-переменные

Задача: цвет акцента приходит как CSS-переменная var(--accent), в рантайме нужно получить hex или rgba для графика/канваса или для передачи в другую библиотеку. Если переменная не задана — подставить fallback.

В normalizeColor для css-var возвращается только type и raw — значения переменной в JS по строке не вытащить. Зато можно проверить тип и извлечь имя переменной, а значение взять, например, из getComputedStyle и уже его передать в normalizeColor:

ts Copy
import { getColorType, extractCssVariableName, normalizeColor } from 'color-value-tools';

const value = 'var(--color-accent)';
if (getColorType(value) === 'css-var') {
  const varName = extractCssVariableName(value); // '--color-accent'
  // В браузере:
  const resolved = getComputedStyle(document.documentElement).getPropertyValue(varName).trim();
  const parsed = normalizeColor(resolved || '#3b82f6');
  console.log(parsed.hex, parsed.r, parsed.g, parsed.b);
}

2. Осветление кнопки и hover

Задача: по дизайну кнопка должна быть на 15% светлее базового акцента, а при наведении — ещё на 10% светлее. Один раз задали базовый цвет — получили два варианта.

ts Copy
import { adjustHexBrightness } from 'color-value-tools';

const accent = '#2563eb';
const buttonBg = adjustHexBrightness(accent, 15);
const buttonBgHover = adjustHexBrightness(accent, 25);

// Использовать в стилях или в :style

3. Градиент из двух цветов

Задача: сгенерировать несколько остановок градиента между цветом A и цветом B (например, для фона карточки или полосы). Режим HSL часто даёт более «гладкий» визуальный переход.

ts Copy
import { mixColors } from 'color-value-tools';

const from = '#6366f1';
const to = '#a855f7';
const stops = [0, 0.25, 0.5, 0.75, 1].map((t) => mixColors(from, to, t, { mode: 'hsl', format: 'hex' }));
// Использовать в linear-gradient или в палитре
const gradientCss = `linear-gradient(to right, ${stops.join(', ')})`;

4. Цвет подписи в зависимости от фона

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

ts Copy
import { isDark, contrastRatio, mixColors } from 'color-value-tools';

function getTextColors(backgroundColor: string) {
  const dark = '#1f2937';
  const light = '#f9fafb';
  const hintDark = '#6b7280';
  const hintLight = '#9ca3af';

  if (isDark(backgroundColor)) {
    const hint = contrastRatio(backgroundColor, hintLight) >= 4.5 ? hintLight : hintDark;
    return { text: light, hint };
  } else {
    const hint = contrastRatio(backgroundColor, hintDark) >= 4.5 ? hintDark : hintLight;
    return { text: dark, hint };
  }
}

const { text, hint } = getTextColors('#fef3c7');

5. Экспорт в печать (CMYK)

Задача: на сайте цвет в hex или rgb; для макета в печать нужно передать CMYK.

ts Copy
import { normalizeColor, rgbToCmyk } from 'color-value-tools';

function toCmyk(color: string) {
  const n = normalizeColor(color);
  if (n.type === 'unknown' || n.r == null) return null;
  return rgbToCmyk({ r: n.r, g: n.g, b: n.b });
}

toCmyk('#3498db');
// { c: 0.76, m: 0.31, y: 0, k: 0.14 }

6. Валидация и единый формат из формы

Задача: пользователь вводит цвет в поле (hex, rgb или вставляет из макета). Нужно проверить, что это распознано, и дальше хранить/отображать в едином виде (например, hex).

ts Copy
import { getColorType, normalizeColor } from 'color-value-tools';

function parseUserColor(input: string): { ok: true; hex: string } | { ok: false; error: string } {
  const type = getColorType(input);
  if (type === 'unknown' || type === 'css-var') {
    return { ok: false, error: 'Неподдерживаемый формат или CSS-переменная' };
  }
  const parsed = normalizeColor(input);
  if (parsed.type === 'unknown' || !parsed.hex) {
    return { ok: false, error: 'Не удалось разобрать цвет' };
  }
  return { ok: true, hex: parsed.hex };
}

Краткий итог

color-value-tools закрывает типичные задачи при работе с цветом: определение типа (hex, rgb, hsl, css-var, named), нормализация hex с fallback, конвертация между hex / RGB / HSL / HSV / Lab / LCH / CMYK, осветление и поворот оттенка, смешивание двух цветов в режиме RGB или HSL с выбором формата вывода, плюс относительная яркость и контраст по WCAG для доступности. Один пакет без зависимостей от фреймворков — подходит для скриптов, браузера и любого стека. Описываете вход (строка или объект) → получаете нужный формат или метрики; логика работы с цветом остаётся в одном месте, код — предсказуемым и удобным для тестов.

Пакет на npm: color-value-tools
Репозиторий: macrulezru/color-value-tools

Читать далее

13.03.2026

Toolz: генерируем полный набор фавиконок

Новый модуль Fav Icona в составе toolz.macrulez.ru закрывает задачу, с которой сталкивается каждый вебмастер при запуске проекта: подготовка полного набора favicon-файлов для всех платформ. Один SVG или PNG на входе — готовый пакет с десятком файлов, манифестом и HTML-кодом на выходе.

Метки
faviconвеб-разработкаPWAиконкиинструменты
13.03.2026

Тайлы, открытые данные и маршрутные сети авиакомпаний

Рассказываю, как с нуля построил интерактивную карту маршрутных сетей авиакомпаний: сгенерировал собственные тайлы из GeoJSON, собрал данные о 885 авиакомпаниях из открытых источников и написал REST API с PostgreSQL. Маршруты рисуются на canvas без картографических библиотек — только Vue 3 и кривые Безье.

Метки
vue3nodejspostgresqlgisopen-data
14.03.2026

3D-режим отображения маршрутных сетей авиакомпаний на основе Three.js

Проект airlines.macrulez.ru изначально строился вокруг 2D-карты: тайлы, canvas, кривые Безье. Всё это описано в предыдущей статье. Но плоская карта с проекцией Меркатора неизбежно искажает маршруты — особенно дальние перелёты, которые в реальности идут через Арктику. Логичным следующим шагом стало добавление интерактивного 3D-глобуса.

Метки
three.jswebglvue3gisdata-visualization