Верстка сайту з використанням CSS Custom Properties (змінні)
CSS Custom Properties — це не просто «змінні в CSS». Це каскадуючись, наслідувані, змінювані з JavaScript значення, які живуть у DOM. На них будуються design systems, темізація, динамічні адаптивні стилі без єдиного рядка JS та анімації значень, які неможна анімувати напрямк.
Архітектура токенів дизайну
Професійний підхід — трирівнева система токенів:
/* Рівень 1: Примітиви (не використовуються напрямку в компонентах) */
:root {
--color-blue-50: #eff6ff;
--color-blue-500: #3b82f6;
--color-blue-600: #2563eb;
--color-blue-700: #1d4ed8;
--color-gray-50: #f9fafb;
--color-gray-100: #f3f4f6;
--color-gray-900: #111827;
--space-1: 0.25rem; /* 4px */
--space-2: 0.5rem; /* 8px */
--space-4: 1rem; /* 16px */
--space-8: 2rem; /* 32px */
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-full: 9999px;
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.125rem;
--font-size-xl: 1.25rem;
--font-size-2xl: 1.5rem;
--shadow-sm: 0 1px 2px rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
}
/* Рівень 2: Семантичні токени (тема) */
:root {
--color-bg-primary: var(--color-gray-50);
--color-bg-surface: #ffffff;
--color-text-primary: var(--color-gray-900);
--color-text-secondary: #6b7280;
--color-text-muted: #9ca3af;
--color-border: var(--color-gray-100);
--color-accent: var(--color-blue-600);
--color-accent-hover: var(--color-blue-700);
}
/* Тривра тема — тільки переопредилити семантичний рівень */
[data-theme="dark"] {
--color-bg-primary: #0f172a;
--color-bg-surface: #1e293b;
--color-text-primary: #f8fafc;
--color-text-secondary: #94a3b8;
--color-text-muted: #64748b;
--color-border: #1e293b;
--color-accent: var(--color-blue-500);
--color-accent-hover: #60a5fa;
}
/* Рівень 3: Компонентні змінні */
.button {
--btn-bg: var(--color-accent);
--btn-bg-hover: var(--color-accent-hover);
--btn-text: #ffffff;
--btn-radius: var(--radius-md);
--btn-padding-x: var(--space-4);
--btn-padding-y: var(--space-2);
background-color: var(--btn-bg);
color: var(--btn-text);
border-radius: var(--btn-radius);
padding: var(--btn-padding-y) var(--btn-padding-x);
transition: background-color 150ms ease;
}
.button:hover {
background-color: var(--btn-bg-hover);
}
/* Варіант кнопки — переопредилити тільки компонентні змінні */
.button--ghost {
--btn-bg: transparent;
--btn-bg-hover: var(--color-bg-primary);
--btn-text: var(--color-text-primary);
}
Зміна з JavaScript
Custom Properties доступні з JS — міст між логікою та стилями:
// Встановити змінну на :root
document.documentElement.style.setProperty('--color-accent', '#8b5cf6');
// Читати поточне значення
const accent = getComputedStyle(document.documentElement)
.getPropertyValue('--color-accent')
.trim();
// Застосувати до конкретного елемента
const card = document.querySelector('.card');
card.style.setProperty('--card-elevation', '3');
// Динамічний паралакс через прокрутку
window.addEventListener('scroll', () => {
const progress = window.scrollY / document.body.scrollHeight;
document.documentElement.style.setProperty('--scroll-progress', progress.toString());
});
/* Використання в CSS */
.progress-bar {
transform: scaleX(var(--scroll-progress, 0));
transform-origin: left;
}
Адаптивні значення без media queries
Custom Properties з clamp() для fluid typography:
:root {
/* Шрифт плавно масштабується від 320px до 1280px viewport */
--font-size-h1: clamp(1.75rem, 4vw + 0.5rem, 3.5rem);
--font-size-h2: clamp(1.375rem, 3vw + 0.25rem, 2.25rem);
--font-size-h3: clamp(1.125rem, 2vw + 0.25rem, 1.5rem);
--font-size-body: clamp(0.9375rem, 1vw + 0.75rem, 1.0625rem);
/* Відступи */
--section-padding: clamp(3rem, 8vw, 8rem);
--container-padding: clamp(1rem, 5vw, 3rem);
}
Наслідування та область
Custom Properties наслідуються за DOM — компонент може переопредилити їх в межах своєї облас
ти:
/* Глобальна карта */
.card {
--card-bg: var(--color-bg-surface);
--card-padding: var(--space-4);
background: var(--card-bg);
padding: var(--card-padding);
}
/* У sidebar карти компактніші */
.sidebar .card {
--card-padding: var(--space-2);
}
/* У hero — з іншим фоном */
.hero-section .card {
--card-bg: rgb(255 255 255 / 0.1);
}
CSS Houdini та @property
Для анімації числових значень custom properties браузер потрібно явно вказати тип:
/* Без @property анімація не працює — браузер не знає тип */
@property --progress {
syntax: '<number>';
initial-value: 0;
inherits: false;
}
@property --gradient-angle {
syntax: '<angle>';
initial-value: 0deg;
inherits: false;
}
.animated-border {
--gradient-angle: 0deg;
background: conic-gradient(
from var(--gradient-angle),
#6366f1, #8b5cf6, #a855f7, #6366f1
);
animation: rotate-gradient 3s linear infinite;
}
@keyframes rotate-gradient {
to { --gradient-angle: 360deg; }
}
Інтеграція з інструментами
PostCSS Custom Properties (для IE11)
// postcss.config.js
module.exports = {
plugins: [
require('postcss-custom-properties')({
preserve: true, // Залишити нативні змінні для сучасних браузерів
}),
],
};
Експорт токенів з Figma
Figma Variables → Style Dictionary → CSS Custom Properties. Формат для Style Dictionary:
{
"color": {
"accent": {
"$value": "#2563eb",
"$type": "color"
}
}
}
// style-dictionary.config.mjs
export default {
source: ['tokens/**/*.json'],
platforms: {
css: {
transformGroup: 'css',
files: [{
destination: 'src/styles/tokens.css',
format: 'css/variables',
options: { selector: ':root' },
}],
},
},
};
Терміни
Первинна установка системи токенів (примітиви + семантика + light/dark темізація): 4–6 годин. Міграція готового проекту з хардкодними значеннями на custom properties: 1–2 дні залежно від обсягу CSS. Підтримка оновлень токенів з Figma через Style Dictionary — налаштовується один раз, далі автоматично.







