← HTML & CSS/CSS-переменные: продвинутые техники#31 из 383← ПредыдущийСледующий →+20 XP
Полезно по теме:Гайд: старт в frontendПрактика: DOM и событияТермин: DOMМаршрут: старт с нуля

CSS-переменные: продвинутые техники

CSS custom properties — это не просто замена магических чисел в коде. Они обладают мощными возможностями, которые превращают их в инструмент для создания систем тем, адаптивных интерфейсов и даже анимаций.

Каскадирование переменных

Переменные следуют правилам CSS-каскада. Переменная, объявленная ближе к элементу, переопределяет более общую:

:root {
  --color-primary: #7b2ff7;
  --spacing: 16px;
}

.dark-theme {
  --color-primary: #a855f7;   /* Переопределяет для .dark-theme и его детей */
}

.card {
  background: var(--color-primary);
  padding: var(--spacing);
}

Это мощнее, чем переменные в SCSS: они меняются в runtime без перекомпиляции.

@property — типизированные переменные

Правило @property позволяет задать тип, начальное значение и наследование:

@property --progress {
  syntax: '<number>';       /* Тип: число */
  initial-value: 0;         /* Начальное значение */
  inherits: false;          /* Не наследуется */
}

@property --hue {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: true;
}

Главное преимущество @property: браузер знает тип, поэтому умеет анимировать переход между значениями через transition.

calc() с переменными

:root {
  --base-size: 4px;
  --columns: 12;
}

.grid-col-4 {
  width: calc(var(--base-size) * 4 * var(--columns));
}

.fluid-font {
  /* Масштабируемая типографика */
  font-size: calc(var(--min-font) + (var(--max-font) - var(--min-font)) * var(--progress));
}

Системы тем

Паттерн «семантические токены»:

/* Базовые цвета (primitive tokens) */
:root {
  --purple-400: #a855f7;
  --purple-600: #7b2ff7;
  --gray-100: #f7fafc;
  --gray-900: #1a202c;
}

/* Семантические токены */
:root {
  --color-bg: var(--gray-100);
  --color-text: var(--gray-900);
  --color-accent: var(--purple-600);
}

/* Тёмная тема — только переопределяем семантические токены */
[data-theme="dark"] {
  --color-bg: var(--gray-900);
  --color-text: var(--gray-100);
  --color-accent: var(--purple-400);
}

Управление через JavaScript

// Получить переменную
const root = document.documentElement
const color = getComputedStyle(root).getPropertyValue('--color-primary').trim()

// Установить переменную на :root
root.style.setProperty('--color-primary', '#ff0066')

// Установить на конкретном элементе (локально)
const card = document.querySelector('.card')
card.style.setProperty('--card-bg', '#f0f4ff')

// Удалить переменную (вернёт значение из :root)
card.style.removeProperty('--card-bg')

Трюк с анимацией через @property

Без @property CSS не умеет анимировать кастомные свойства. С ним — умеет:

@property --angle {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}

@keyframes rotate-gradient {
  to { --angle: 360deg; }
}

.spinning-border {
  background: conic-gradient(from var(--angle), #7b2ff7, #06b6d4, #7b2ff7);
  animation: rotate-gradient 3s linear infinite;
}

Браузер интерполирует <angle> так же, как transform: rotate().

Значения по умолчанию

var() принимает второй аргумент — запасное значение:

.button {
  background: var(--btn-color, var(--color-primary, #7b2ff7));
  /* Цепочка: --btn-color → --color-primary → #7b2ff7 */
}

Примеры

Получение и установка CSS custom properties через JS, динамическая смена темы

// Работа с CSS custom properties через JavaScript
const root = document.documentElement

// Задаём начальные переменные
root.style.setProperty('--color-bg', '#ffffff')
root.style.setProperty('--color-text', '#1a202c')
root.style.setProperty('--color-accent', '#7b2ff7')
root.style.setProperty('--spacing-base', '8px')

// Читаем переменные через getComputedStyle
function getCSSVar(name) {
  return getComputedStyle(root).getPropertyValue(name).trim()
}

console.log('--color-accent:', getCSSVar('--color-accent'))     // #7b2ff7
console.log('--spacing-base:', getCSSVar('--spacing-base'))     // 8px

// Создаём карточку, использующую переменные
const card = document.createElement('div')
card.style.cssText = `
  background: var(--color-bg);
  color: var(--color-text);
  border: 2px solid var(--color-accent);
  padding: calc(var(--spacing-base) * 2);
  border-radius: 8px;
  margin-bottom: 12px;
  font-family: sans-serif;
`
card.textContent = 'Карточка с CSS-переменными'
document.body.appendChild(card)

// Менеджер тем
const themes = {
  light: { '--color-bg': '#ffffff', '--color-text': '#1a202c', '--color-accent': '#7b2ff7' },
  dark:  { '--color-bg': '#1a202c', '--color-text': '#f7fafc', '--color-accent': '#a855f7' },
  ocean: { '--color-bg': '#0f172a', '--color-text': '#e2e8f0', '--color-accent': '#06b6d4' },
}

function applyTheme(themeName) {
  const theme = themes[themeName]
  Object.entries(theme).forEach(([key, value]) => {
    root.style.setProperty(key, value)
  })
  console.log(`Применена тема: ${themeName}`)
}

// Применяем темы с паузой, чтобы увидеть изменения
applyTheme('light')
console.log('После light — accent:', getCSSVar('--color-accent'))

applyTheme('dark')
console.log('После dark — accent:', getCSSVar('--color-accent'))

// Локальная переменная на конкретном элементе
card.style.setProperty('--color-accent', '#f59e0b')  // только для card
console.log('Локальный accent на card:', getComputedStyle(card).getPropertyValue('--color-accent').trim())
console.log('Глобальный accent на root:', getCSSVar('--color-accent'))  // dark тема

// Удаляем локальную переменную — вернётся глобальная
card.style.removeProperty('--color-accent')
console.log('После removeProperty — accent на card:', getComputedStyle(card).getPropertyValue('--color-accent').trim())

CSS-переменные: продвинутые техники

CSS custom properties — это не просто замена магических чисел в коде. Они обладают мощными возможностями, которые превращают их в инструмент для создания систем тем, адаптивных интерфейсов и даже анимаций.

Каскадирование переменных

Переменные следуют правилам CSS-каскада. Переменная, объявленная ближе к элементу, переопределяет более общую:

:root {
  --color-primary: #7b2ff7;
  --spacing: 16px;
}

.dark-theme {
  --color-primary: #a855f7;   /* Переопределяет для .dark-theme и его детей */
}

.card {
  background: var(--color-primary);
  padding: var(--spacing);
}

Это мощнее, чем переменные в SCSS: они меняются в runtime без перекомпиляции.

@property — типизированные переменные

Правило @property позволяет задать тип, начальное значение и наследование:

@property --progress {
  syntax: '<number>';       /* Тип: число */
  initial-value: 0;         /* Начальное значение */
  inherits: false;          /* Не наследуется */
}

@property --hue {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: true;
}

Главное преимущество @property: браузер знает тип, поэтому умеет анимировать переход между значениями через transition.

calc() с переменными

:root {
  --base-size: 4px;
  --columns: 12;
}

.grid-col-4 {
  width: calc(var(--base-size) * 4 * var(--columns));
}

.fluid-font {
  /* Масштабируемая типографика */
  font-size: calc(var(--min-font) + (var(--max-font) - var(--min-font)) * var(--progress));
}

Системы тем

Паттерн «семантические токены»:

/* Базовые цвета (primitive tokens) */
:root {
  --purple-400: #a855f7;
  --purple-600: #7b2ff7;
  --gray-100: #f7fafc;
  --gray-900: #1a202c;
}

/* Семантические токены */
:root {
  --color-bg: var(--gray-100);
  --color-text: var(--gray-900);
  --color-accent: var(--purple-600);
}

/* Тёмная тема — только переопределяем семантические токены */
[data-theme="dark"] {
  --color-bg: var(--gray-900);
  --color-text: var(--gray-100);
  --color-accent: var(--purple-400);
}

Управление через JavaScript

// Получить переменную
const root = document.documentElement
const color = getComputedStyle(root).getPropertyValue('--color-primary').trim()

// Установить переменную на :root
root.style.setProperty('--color-primary', '#ff0066')

// Установить на конкретном элементе (локально)
const card = document.querySelector('.card')
card.style.setProperty('--card-bg', '#f0f4ff')

// Удалить переменную (вернёт значение из :root)
card.style.removeProperty('--card-bg')

Трюк с анимацией через @property

Без @property CSS не умеет анимировать кастомные свойства. С ним — умеет:

@property --angle {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}

@keyframes rotate-gradient {
  to { --angle: 360deg; }
}

.spinning-border {
  background: conic-gradient(from var(--angle), #7b2ff7, #06b6d4, #7b2ff7);
  animation: rotate-gradient 3s linear infinite;
}

Браузер интерполирует <angle> так же, как transform: rotate().

Значения по умолчанию

var() принимает второй аргумент — запасное значение:

.button {
  background: var(--btn-color, var(--color-primary, #7b2ff7));
  /* Цепочка: --btn-color → --color-primary → #7b2ff7 */
}

Примеры

Получение и установка CSS custom properties через JS, динамическая смена темы

// Работа с CSS custom properties через JavaScript
const root = document.documentElement

// Задаём начальные переменные
root.style.setProperty('--color-bg', '#ffffff')
root.style.setProperty('--color-text', '#1a202c')
root.style.setProperty('--color-accent', '#7b2ff7')
root.style.setProperty('--spacing-base', '8px')

// Читаем переменные через getComputedStyle
function getCSSVar(name) {
  return getComputedStyle(root).getPropertyValue(name).trim()
}

console.log('--color-accent:', getCSSVar('--color-accent'))     // #7b2ff7
console.log('--spacing-base:', getCSSVar('--spacing-base'))     // 8px

// Создаём карточку, использующую переменные
const card = document.createElement('div')
card.style.cssText = `
  background: var(--color-bg);
  color: var(--color-text);
  border: 2px solid var(--color-accent);
  padding: calc(var(--spacing-base) * 2);
  border-radius: 8px;
  margin-bottom: 12px;
  font-family: sans-serif;
`
card.textContent = 'Карточка с CSS-переменными'
document.body.appendChild(card)

// Менеджер тем
const themes = {
  light: { '--color-bg': '#ffffff', '--color-text': '#1a202c', '--color-accent': '#7b2ff7' },
  dark:  { '--color-bg': '#1a202c', '--color-text': '#f7fafc', '--color-accent': '#a855f7' },
  ocean: { '--color-bg': '#0f172a', '--color-text': '#e2e8f0', '--color-accent': '#06b6d4' },
}

function applyTheme(themeName) {
  const theme = themes[themeName]
  Object.entries(theme).forEach(([key, value]) => {
    root.style.setProperty(key, value)
  })
  console.log(`Применена тема: ${themeName}`)
}

// Применяем темы с паузой, чтобы увидеть изменения
applyTheme('light')
console.log('После light — accent:', getCSSVar('--color-accent'))

applyTheme('dark')
console.log('После dark — accent:', getCSSVar('--color-accent'))

// Локальная переменная на конкретном элементе
card.style.setProperty('--color-accent', '#f59e0b')  // только для card
console.log('Локальный accent на card:', getComputedStyle(card).getPropertyValue('--color-accent').trim())
console.log('Глобальный accent на root:', getCSSVar('--color-accent'))  // dark тема

// Удаляем локальную переменную — вернётся глобальная
card.style.removeProperty('--color-accent')
console.log('После removeProperty — accent на card:', getComputedStyle(card).getPropertyValue('--color-accent').trim())

Задание

Создай систему тем с CSS-переменными. В `:root` объяви семантические токены: `--color-bg`, `--color-text`, `--color-accent`. Добавь светлую тему по умолчанию и тёмную тему через атрибут `[data-theme="dark"]`. Создай кнопку, которая переключает тему, устанавливая `data-theme` на корневом элементе.

Подсказка

Светлая тема: `--color-bg: #ffffff`, `--color-text: #1a202c`, `--color-accent: #7b2ff7`. Тёмная тема в `[data-theme="dark"]`: `--color-bg: #1a202c`, `--color-text: #f7fafc`, `--color-accent: #a855f7`. Переключение через `dataset.theme`.

Загружаем среду выполнения...
Загружаем AI-помощника...