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 --progress {
syntax: '<number>'; /* Тип: число */
initial-value: 0; /* Начальное значение */
inherits: false; /* Не наследуется */
}
@property --hue {
syntax: '<angle>';
initial-value: 0deg;
inherits: true;
}Главное преимущество @property: браузер знает тип, поэтому умеет анимировать переход между значениями через transition.
: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);
}// Получить переменную
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 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 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 --progress {
syntax: '<number>'; /* Тип: число */
initial-value: 0; /* Начальное значение */
inherits: false; /* Не наследуется */
}
@property --hue {
syntax: '<angle>';
initial-value: 0deg;
inherits: true;
}Главное преимущество @property: браузер знает тип, поэтому умеет анимировать переход между значениями через transition.
: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);
}// Получить переменную
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 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`.