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
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
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
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
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
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
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
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
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
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
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
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