css-magic-gradient — генерация CSS-градиентов с реактивностью для Vue 3

07.03.2026
css-magic-gradient — генерация CSS-градиентов с реактивностью для Vue 3

В веб-интерфейсах градиенты то и дело нужны и «по месту»: от фона карточки до кнопок и декоративных полос. Писать вручную linear-gradient(to right, #aabbcc, #ddeeff) и подставлять цвета из темы или из формы — неудобно, легко ошибиться в формате, а при смене цвета (пользователь выбрал другой акцент, переключилась тема) приходится заново собирать строку и обновлять стиль. Пакет css-magic-gradient как раз про то, чтобы один раз описать градиент (линейный, радиальный или конический), получить готовую CSS-строку и во Vue 3 — реактивно пересчитывать её при изменении базового цвета или опций, без ручных подписок и watchers.

В этом посте — зачем выносить генерацию градиентов в отдельный слой, что поддерживается (линейные, радиальные, конические градиенты, кастомные остановки, многослойность, радужный конический), как пользоваться ядром без фреймворка и как подключить реактивные хуки для Vue 3. Упор на реактивность: передаёте ref цвета и опций — градиент обновляется сам при любом изменении.


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

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

  • Единый источник правды: хочется один раз описать «как строится градиент от базового цвета» (смещение яркости, направление, количество шагов) и во всём приложении получать валидную CSS-строку. Иначе в одном месте градиент «светлее вверху», в другом — «светлее справа», и визуал расходится.
  • Реактивность без костылей: при изменении базового цвета (тема, выбор пользователя, проп) строка градиента должна пересчитываться сама. Вручную в watch или при каждом рендере вызывать генератор и вешать результат в style — дублирование и риск забыть обновить при смене опций.
  • Гибкие сценарии: не только «один цвет + авто-осветление», но и кастомные остановки цветов, многошаговые линейные градиенты, радиальные слои, конические с поворотом оттенка или готовый радужный конус. Собирать такие строки вручную — неудобно и легко ошибиться в синтаксисе.

css-magic-gradient даёт единый слой: вы вызываете createLinearGradient, createRadialGradient или createConicGradient с базовым цветом и опциями (или с массивом остановок), получаете готовую CSS-строку. Во Vue 3 композиции useLinearGradient, useRadialGradient, useConicGradient принимают ref цвета и опций и возвращают computed-строку: при изменении ref градиент пересчитывается автоматически, компонент перерисовывается — никакой ручной подписки. В итоге и дизайн-систему держать проще (одни правила генерации), и динамические темы/формы с выбором цвета работают «из коробки».


Что внутри: ядро и плагин для Vue 3

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

Что подключать Назначение
css-magic-gradient (ядро) createLinearGradient, createMultiStepLinearGradient, createRadialGradient, createConicGradient, createRainbowConicGradient. Работает в любом окружении (Node, браузер).
Vue 3 VueGradientPlugin (опционально, для глобальных свойств) и useLinearGradient, useRadialGradient, useConicGradient — реактивные ComputedRef<string> при передаче ref'ов.

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

bash Copy
npm install css-magic-gradient

Для Vue-проекта нужна peer-зависимость vue (^3.5.27) — как правило, она уже есть. Типы поставляются из коробки (TypeScript). Внутри для работы с цветом используется пакет color-value-tools (нормализация hex, яркость, hue rotation, rgba и т.д.).


Как устроено ядро: линейный градиент

Всё строится вокруг вызова функции с базовым цветом (или массивом остановок) и опциональными опциями. Результат — готовая строка вида linear-gradient(...), radial-gradient(...) или conic-gradient(...), которую можно подставить в style.background или в CSS.

createLinearGradient

Два режима:

  1. От одного цвета: передаёте строку (hex или CSS-переменную) и опции — градиент строится из автоматически осветлённого цвета в начало и базового в конец.
  2. Кастомные остановки: передаёте массив { color, position?, opacity? } и опции направления — строка собирается как есть.
Поле Описание
baseColor (или массив остановок) Цвет в формате hex, CSS-переменная var(--accent) и т.д.; либо массив LinearGradientColorStop[].
options.offsetPercent Смещение яркости для начального цвета, -100..100 (по умолчанию 15).
options.direction Направление: 'to bottom', 'to top', 'to right', 'to left' или своя строка (по умолчанию 'to bottom').
options.angle Угол в градусах (перекрывает direction).
options.fallbackColor Цвет подстановки, если база — CSS-переменная и она не задана (по умолчанию '#f5e477').

Пример от одного цвета:

ts Copy
import { createLinearGradient } from 'css-magic-gradient';

const gradient = createLinearGradient('#3b82f6', {
  direction: 'to right',
  offsetPercent: 20,
});
// linear-gradient(to right, осветлённый синий, #3b82f6)

Пример с кастомными остановками:

ts Copy
const gradient = createLinearGradient(
  [
    { color: '#00f', position: '0%' },
    { color: '#fff', opacity: 0.5, position: '50%' },
    { color: '#f00', position: '100%' },
  ],
  { direction: 'to right' }
);

createMultiStepLinearGradient

Строит линейный градиент из нескольких шагов по яркости от одного базового цвета — удобно для полос, разделителей, фонов с мягкими переходами.

ts Copy
import { createMultiStepLinearGradient } from 'css-magic-gradient';

const gradient = createMultiStepLinearGradient('#10b981', 5, {
  direction: 'to bottom',
  offsetPercent: 25,
});

Радиальный и конический градиенты

createRadialGradient

Принимает базовый цвет и опции: форма (circle/ellipse), размер, позиция центра. Можно задать свои остановки через useCustomColors и colors или собрать несколько слоёв через layers — в итоге получится строка из нескольких radial-gradient(...) через запятую (как в CSS).

Опция Описание
shape 'circle' | 'ellipse' (по умолчанию 'ellipse').
size Строка ('farthest-corner', '50% 50%') или объект { width, height }.
position Позиция центра, например 'center', '50% 50%'.
useCustomColors + colors Свой массив остановок { color, opacity?, position? }.
layers Массив слоёв, у каждого — shape, size, position, colors.

Пример с кастомными цветами:

ts Copy
import { createRadialGradient } from 'css-magic-gradient';

createRadialGradient('#00f', {
  useCustomColors: true,
  colors: [
    { color: '#00f', position: '0%' },
    { color: '#fff', opacity: 0.5, position: '100%' },
  ],
});

createConicGradient

Конический градиент от базового цвета: либо автоматические шаги по яркости (или по hue при hueRotation: true), либо полностью свои остановки через colors.

Опция Описание
fromAngle Начальный угол в градусах (по умолчанию 0).
position Позиция центра (по умолчанию '50% 50%').
colors Кастомные остановки { color, opacity?, position? }.
hueRotation true — шаги по кругу оттенков от базового цвета.
steps Количество шагов при авто-генерации (по умолчанию 8).
offsetPercent Смещение яркости для шагов (по умолчанию 20).

createRainbowConicGradient

Готовый радужный конический градиент (HSL по кругу). Удобно для спиннеров, декоративных элементов.

ts Copy
import { createRainbowConicGradient } from 'css-magic-gradient';

const rainbow = createRainbowConicGradient({
  fromAngle: 0,
  position: '50% 50%',
  saturation: 80,
  lightness: 60,
  steps: 12,
});

Использование без Vue: только ядро

В «голом» JS или в любом фреймворке без композиций достаточно импортировать функции и подставлять результат в стили:

ts Copy
import {
  createLinearGradient,
  createRadialGradient,
  createConicGradient,
  createRainbowConicGradient,
} from 'css-magic-gradient';

const linear = createLinearGradient('#ef4444', { direction: 'to right' });
const radial = createRadialGradient('#22c55e', { shape: 'circle', position: '30% 70%' });
const conic = createConicGradient('#8b5cf6', { hueRotation: true, steps: 6 });
const rainbow = createRainbowConicGradient({ steps: 8 });

document.querySelector('.panel').style.background = linear;

При смене цвета или опций вызываете функцию заново и снова присваиваете строку — реактивность в таком сценарии вы реализуете сами (например, через подписку на тему или обработчик формы).


Реактивность во Vue 3: главный акцент

Во Vue-приложении не хочется в каждом компоненте вручную вызывать генератор в watch или при каждом обновлении пропа. Композиции useLinearGradient, useRadialGradient, useConicGradient принимают реактивные аргументы: базовый цвет и опции могут быть как обычными значениями, так и ref (или reactive). Возвращают они ComputedRef<string> — то есть при изменении любого из ref'ов градиент пересчитывается автоматически, Vue перерисовывает шаблон. Никакой ручной подписки и отписки.

Внутри используется одна и та же логика: computed(() => createXxxGradient(resolve(baseColor), resolve(options))), где resolve разворачивает ref. Поэтому:

  • передали ref цвета из темы или из формы — градиент обновляется при смене цвета;
  • передали ref опций (направление, угол, количество шагов) — при смене опций строка пересчитывается сама.

Регистрация плагина (опционально)

Плагин вешает на app.config.globalProperties методы $useLinearGradient, $useRadialGradient, $useConicGradient — удобно, если хотите вызывать из Options API. Для реактивности в <script setup> плагин не обязателен: достаточно импортировать хуки.

ts Copy
import { createApp } from 'vue';
import { VueGradientPlugin } from 'css-magic-gradient';
import App from './App.vue';

const app = createApp(App);
app.use(VueGradientPlugin);
app.mount('#app');

Использование в компоненте: ref и computed

В любом компоненте подключаете композицию и передаёте ref цвета и при необходимости ref опций — получаете реактивную строку градиента:

vue Copy
<script setup lang="ts">
import { ref } from 'vue';
import { useLinearGradient, useRadialGradient, useConicGradient } from 'css-magic-gradient';

const color = ref('#3b82f6');
const direction = ref<'to right' | 'to bottom'>('to right');

const linearGradient = useLinearGradient(color, {
  direction,
  offsetPercent: 15,
});

const radialGradient = useRadialGradient(color, { shape: 'circle' });
const conicGradient = useConicGradient(color, { hueRotation: true, steps: 6 });
</script>

<template>
  <div
    class="card"
    :style="{ background: linearGradient }"
  />
  <div
    class="spot"
    :style="{ background: radialGradient }"
  />
  <div
    class="wheel"
    :style="{ background: conicGradient }"
  />
</template>

Как только пользователь или тема меняет color или direction, linearGradient, radialGradient и conicGradient пересчитываются, Vue обновляет DOM — реактивность «из коробки».

Опции тоже реактивные

Опции можно передать целиком из ref — например, настройки градиента приходят из store или из формы:

vue Copy
<script setup lang="ts">
import { ref, computed } from 'vue';
import { useLinearGradient } from 'css-magic-gradient';

const color = ref('#f59e0b');
const options = ref({
  direction: 'to bottom' as const,
  offsetPercent: 20,
  angle: undefined as number | undefined,
});

const gradient = useLinearGradient(color, options);

// Меняем направление по клику — градиент обновится сам
function setDirection(dir: 'to right' | 'to bottom') {
  options.value = { ...options.value, direction: dir };
}
</script>

<template>
  <div :style="{ background: gradient }">
    <button @click="setDirection('to right')">To right</button>
    <button @click="setDirection('to bottom')">To bottom</button>
  </div>
</template>

Никаких watch на color или options: композиция внутри использует computed, поэтому зависимости собираются автоматически.

Композables и производные значения

Удобно комбинировать градиент с другими вычислениями — например, два варианта (обычный и для hover) оба реактивны от одного цвета:

vue Copy
<script setup lang="ts">
import { ref } from 'vue';
import { useLinearGradient } from 'css-magic-gradient';

const color = ref('#6366f1');
const baseGradient = useLinearGradient(color, { direction: 'to right', offsetPercent: 15 });
const hoverGradient = useLinearGradient(color, { direction: 'to left', offsetPercent: 25 });
</script>

<template>
  <div
    class="card"
    :style="{ background: baseGradient }"
    @mouseenter="($event.currentTarget as HTMLElement).style.background = hoverGradient.value"
    @mouseleave="($event.currentTarget as HTMLElement).style.background = baseGradient.value"
  />
</template>

На практике hover лучше реализовать через CSS-переменную или класс; здесь важно то, что оба градиента реактивны и пересчитываются при смене color.


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

1. Градиент от темы (CSS-переменная или ref)

Задача: фон блока должен быть линейным градиентом от акцентного цвета темы. Тема меняется — градиент обновляется сам.

vue Copy
<script setup lang="ts">
import { computed } from 'vue';
import { useLinearGradient } from 'css-magic-gradient';

const themeColor = computed(() => {
  return getComputedStyle(document.documentElement).getPropertyValue('--color-accent').trim() || '#3b82f6';
});
const gradient = useLinearGradient(themeColor, { angle: 135, offsetPercent: 20 });
</script>

<template>
  <header :style="{ background: gradient }">...</header>
</template>

Если акцент хранится в ref (например, из Pinia или provide/inject), передаёте этот ref — при смене темы ref обновляется, градиент пересчитывается.

2. Выбор цвета в форме

Задача: пользователь выбирает цвет в <input type="color"> или в пикере — карточка сразу показывает градиент от этого цвета.

vue Copy
<script setup lang="ts">
import { ref } from 'vue';
import { useLinearGradient } from 'css-magic-gradient';

const pickedColor = ref('#8b5cf6');
const gradient = useLinearGradient(pickedColor, {
  direction: 'to bottom',
  offsetPercent: 25,
});
</script>

<template>
  <div>
    <input v-model="pickedColor" type="color" />
    <div class="preview" :style="{ background: gradient }" />
  </div>
</template>

Реактивность обеспечивает связку v-model → ref → computed внутри useLinearGradient: при смене цвета градиент обновляется без единого watch.

3. Список карточек с разными градиентами

Задача: у каждой карточки свой акцентный цвет, градиент строится от него по одному правилу. Выносим карточку в отдельный компонент — тогда у каждого экземпляра свой реактивный градиент от своего ref.

vue Copy
<!-- CardItem.vue -->
<script setup lang="ts">
import { toRef } from 'vue';
import { useLinearGradient } from 'css-magic-gradient';

const props = defineProps<{ color: string; title: string }>();
const gradient = useLinearGradient(toRef(props, 'color'), { direction: 'to right', offsetPercent: 15 });
</script>

<template>
  <div class="card">
    <div class="card-bg" :style="{ background: gradient }" />
    <h3>{{ title }}</h3>
  </div>
</template>

В родителе передаёте color и title; при смене color градиент в карточке обновится сам. Для списка с фиксированными цветами можно и без ref — передать просто item.color, тогда градиент пересчитается при смене пропа.

4. Радужный спиннер и декоративный конус

Задача: индикатор загрузки или декоративный круг с радужным коническим градиентом — без Vue достаточно один раз вызвать createRainbowConicGradient; если параметры (steps, fromAngle) должны меняться по UI — оборачиваете в ref и используете хук (для радужного градиента своего хука в пакете нет, но опции можно держать в ref и вызывать createRainbowConicGradient внутри computed).

ts Copy
import { createRainbowConicGradient } from 'css-magic-gradient';

const rainbow = createRainbowConicGradient({ steps: 8, fromAngle: 0 });
document.querySelector('.spinner').style.background = rainbow;

Краткий итог

css-magic-gradient закрывает типичные задачи при работе с градиентами: один раз описать правило (линейный от цвета с offset, многошаговый, радиальный с слоями, конический с hue rotation или кастомные остановки), получать валидную CSS-строку из ядра и во Vue 3 — реактивно через useLinearGradient, useRadialGradient, useConicGradient. Передаёте ref цвета и при необходимости ref опций — градиент пересчитывается при любом изменении, без ручных подписок и watchers. Кастомизация через полный набор опций (направление, угол, форма, размер, позиция, свои остановки, слои), при необходимости — готовый радужный конический градиент через createRainbowConicGradient. В итоге логика «как строится градиент» живёт в одном месте, код остаётся предсказуемым, а реактивность во Vue берёт на себя пакет.

Пакет на npm: css-magic-gradient
Репозиторий: macrulezru/css-magic-gradient

Читать далее

07.03.2026

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

Библиотека color-value-tools — один набор утилит для разбора, конвертации и манипуляции цветом: hex, RGB, HSL, HSV, Lab, LCH, CMYK, яркость, смешивание и контраст по WCAG. В посте — зачем выносить работу с цветом в отдельный слой, обзор API и примеры под реальные задачи (темы, градиенты, доступность, печать).

Метки
color-value-toolsцветаконвертацияTypeScriptдоступность (WCAG)
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