Вёрстка сайта с использованием CSS Custom Properties (переменные)
Представьте: интернет-магазин с 50 000 товаров, цвета бренда меняются каждый сезон. Раньше нужно было переписывать 2000 строк SCSS, пересобирать стили и ждать деплоя. С CSS Custom Properties мы сократили это до изменения одной строки в JavaScript — и цвета обновились на всех страницах за миллисекунды. Экономия времени на каждом обновлении — 8 часов. Мы занимаемся вёрсткой с CSS Custom Properties более 5 лет, внедрили токены в 30+ проектах. Средняя экономия на поддержке стилей — 150 человеко-часов в год. Получите консультацию по вашему проекту — оценим возможность внедрения бесплатно.
Почему CSS Custom Properties лучше препроцессорных переменных?
Переменные SCSS/Sass компилируются в статические значения — их нельзя изменить в рантайме. Custom Properties живут в DOM: каскадируются, наследуются, доступны из JavaScript. Это единственный способ сделать полноценную темизацию без пересборки CSS. Согласно MDN, кастомные свойства наследуются по дереву элементов, что позволяет переопределять их в медиа-запросах и контекстах.
| Характеристика |
CSS Custom Properties |
SCSS-переменные |
| Изменение через JS |
Да |
Нет |
| Наследование по DOM |
Да |
Нет |
| Медиа-запросы |
Да |
Нет |
| Анимация |
Да (с @property) |
Нет |
| Тёмная тема |
Да — один переключатель |
Требуется несколько файлов |
Какие проблемы решают CSS Custom Properties на практике?
Проблема 1: Управление цветовой схемой. Хардкод цветов в каждом компоненте превращает обновление палитры в ад. С токенами меняем --color-accent в одном месте — и вся сетка, кнопки, иконки обновляются мгновенно. Обычно мы наблюдаем сокращение CSS на 30%.
Проблема 2: Адаптивные размеры без медиа-запросов. Раньше под каждый брейкпоинт нужно было писать отдельные правила. Используем clamp() с кастомными свойствами — получается fluid typography, которая подстраивается под любую ширину экрана. Количество медиа-запросов уменьшается на 50%.
Проблема 3: Анимация свойств, которые раньше были неанимируемыми. Например, вращение градиента или числовой прогресс. @property регистрирует тип, и браузер начинает интерполировать значение. Это открывает новые возможности для UI-эффектов.
Как мы строим систему токенов: кейс из практики
Один из проектов — интернет-магазин электроники. Изначально использовались SCSS-переменные с жёстко заданными цветами. Каждое обновление дизайна требовало пересборки и деплоя, занимало 2 дня. Мы внедрили трёхуровневую систему токенов:
- Примитивы: базовые цвета, отступы, радиусы.
- Семантические токены: --color-bg-primary, --color-text-primary.
- Компонентные переменные: --btn-bg, --card-padding.
:root {
--color-blue-500: #3b82f6;
--color-gray-50: #f9fafb;
--space-4: 1rem;
--radius-md: 8px;
--color-bg-primary: var(--color-gray-50);
--color-accent: var(--color-blue-500);
}
[data-theme="dark"] {
--color-bg-primary: #0f172a;
--color-accent: #60a5fa;
}
.button {
--btn-bg: var(--color-accent);
background-color: var(--btn-bg);
padding: var(--space-1) var(--space-4);
border-radius: var(--radius-md);
}
Результат: теперь дизайнер меняет цвета в Figma — Style Dictionary автоматически генерирует new CSS, а мы обновляем одну строку в коде. Время на ребрендинг сократилось с 2 дней до 2 часов (93% ускорение). Закажите аудит ваших стилей — узнайте, сколько можно сэкономить.
Пошаговый план внедрения CSS Custom Properties
- Аудит текущих стилей — ищем хардкод цветов, отступов, шрифтов. Определяем частоту изменений.
- Проектирование системы токенов — выделяем примитивы, семантику, компонентные переменные.
- Создание CSS-файла с токенами — :root для светлой темы, [data-theme="dark"] для тёмной.
- Рефакторинг компонентов — заменяем статические значения на var().
- Интеграция с Figma — подключаем Style Dictionary для автоматического экспорта.
- Тестирование контрастности и регрессии — проверяем WCAG AA, визуальное совпадение pixel-perfect.
Типичные ошибки при внедрении
- Забыли зарегистрировать числовые свойства через @property перед анимацией.
- Использовали var() без fallback — при ошибке свойство ломается.
- Переопределили токен на неправильном селекторе — сбой каскада.
- Не задали initial-value для @property — анимация не работает.
- Перепутали примитивы и семантику — система становится непрозрачной.
Как управлять стилями через JavaScript?
Custom Properties — мост между логикой и дизайном. Меняем цвет акцента программно:
document.documentElement.style.setProperty('--color-accent', '#8b5cf6');
// Динамический parallax
window.addEventListener('scroll', () => {
const progress = window.scrollY / document.body.scrollHeight;
document.documentElement.style.setProperty('--scroll-progress', progress);
});
Для плавного изменения значения используют requestAnimationFrame или CSS-переходы.
Как автоматизировать экспорт токенов из Figma?
Используем Style Dictionary: он читает JSON-файлы и генерирует CSS-переменные. Figma-плагины экспортируют значения цветов, отступов и шрифтов. Style Dictionary преобразует их в :root с кастомными свойствами. Это исключает ручное копирование и рассинхронизацию.
Что такое @property и как он расширяет возможности CSS?
Без регистрации типа браузер не интерполирует кастомное свойство. Документация CSS @property объясняет синтаксис. Пример:
@property --gradient-angle {
syntax: '<angle>';
initial-value: 0deg;
inherits: false;
}
.element {
--gradient-angle: 0deg;
background: conic-gradient(from var(--gradient-angle), blue, purple);
animation: rotate 3s linear infinite;
}
@keyframes rotate {
to { --gradient-angle: 360deg; }
}
Интеграция с инструментами
Для старых браузеров используем PostCSS с плагином postcss-custom-properties. Style Dictionary экспортирует токены из Figma:
// postcss.config.js
module.exports = {
plugins: [
require('postcss-custom-properties')({
preserve: true,
}),
],
};
// 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' },
}],
},
},
};
Как сделать адаптивные значения без media queries?
Fluid typography с помощью clamp():
:root {
--font-size-h1: clamp(1.75rem, 4vw + 0.5rem, 3.5rem);
--section-padding: clamp(3rem, 8vw, 8rem);
}
Это автоматически масштабирует элементы в зависимости от ширины вьюпорта без точек перелома.
Сроки и что вы получаете
Первичная настройка системы токенов (примитивы + семантика + light/dark) занимает от 4 до 6 часов. Миграция готового проекта — от 1 до 2 дней в зависимости от объёма CSS. Интеграция с Figma через Style Dictionary настраивается один раз и автоматизирует экспорт токенов.
| Этап |
Время |
Результат |
| Аудит стилей |
2-4 часа |
Список хардкода |
| Проектирование токенов |
4-6 часов |
Документация токенов |
| Рефакторинг CSS |
1-2 дня |
Замены var() |
| Интеграция с Figma |
2-3 часа |
Автоэкспорт |
Мы гарантируем, что каждый токен проверен на контрастность (AA) и единообразие. Свяжитесь с нами — оценим ваш проект и предложим оптимальное решение. Получите консультацию по внедрению — оценим ваш проект бесплатно.
Фронтенд-разработка React: от аудита до production
Бандл вырос до 3.1 MB gzip — это реальная цифра из проекта, который пришёл к нам на аудит. Причина: moment.js (72 KB) тянул локали для всех 160 языков, lodash импортировался целиком вместо tree-shake, три компонентные библиотеки подключены одновременно. TTFB отличный, но TTI (Time to Interactive) на мобильном — 14 секунд. Пользователи уходили, конверсия упала на 40%. Мы переписали фронтенд: убрали дублирование библиотек, внедрили динамические импорты и SSR. Результат — бандл уменьшился до 850 KB gzip, TTI — 2.1 секунды, LCP — 1.8 с.
Frontend — это не «нарисовать красиво». Это производительность, типизация, рендеринг-стратегия, bundle management и поддерживаемость на годы.
Почему Next.js — стандартный выбор для SEO?
React — наш основной UI-фреймворк для сложных интерфейсов. Next.js — стандартный выбор для проектов с SEO-требованиями или SSR. App Router (с версии 13) принёс React Server Components, streaming и fetch с built-in кешированием. Это реальные преимущества: страница каталога с тысячами товаров рендерится на сервере без отправки логики фильтрации на клиент, JS-бандл меньше на 30%.
Но App Router — другой способ мышления. "use client" нужно ставить осознанно. Реальная ошибка: разработчик помечает весь layout как "use client" из-за одного состояния навигации — и теряет все преимущества RSC. Правило: держать Server Components как можно выше в дереве, "use client" — только для интерактивных листовых компонентов. ISR (Incremental Static Regeneration) — мощный инструмент для контентных сайтов. На каталоге из 50 000 страниц с ISR и CDN — TTFB < 50 ms для любой страницы.
Как TypeScript предотвращает баги в продакшене?
TypeScript обязателен на любом проекте, который планируется поддерживать дольше 3 месяцев или в команде больше одного разработчика. Аргумент «пишем быстро без типов» работает только первые 2 недели. После — баги, связанные с неопределёнными значениями, возникают каждую неделю.
Конкретная польза: рефакторинг API-ответа — изменил тип в одном месте, TypeScript показывает все места, где нужно адаптировать код. Без типов — баг в продакшене через неделю. strict: true в tsconfig.json — обязательно. noImplicitAny, strictNullChecks, strictFunctionTypes. Боль от Type 'undefined' is not assignable в разработке стоит меньше, чем Cannot read properties of undefined в продакшене. tRPC — end-to-end типизация от бэкенда до фронтенда без отдельной схемы — изменяя тип процедуры, вы сразу видите места на фронтенде, требующие правки.
Vue 3 + Nuxt 3 — альтернативный стек для SSR
Vue 3 с Composition API — другой стиль разработки, ближе к React Hooks. <script setup> и composables делают код более переиспользуемым. Nuxt 3 — фреймворк для Vue с SSR/SSG, аналогичный Next.js. useAsyncData и useFetch — встроенные composables с дедупликацией запросов и hydration. Auto-imports удобны, но могут запутывать при debug. Nuxt Content — модуль для Markdown/MDX-файлов, идеален для документации.
Hydration mismatch — специфическая боль SSR на Vue и React. Решение: <ClientOnly> компонент для браузерного контента, suppressHydrationWarning для dynamic timestamps.
Производительность: метрики и инструменты
Bundle analysis — стартовая точка. @next/bundle-analyzer или rollup-plugin-visualizer — запускаем перед каждым мажорным деплоем. Цель: ни одна страница не должна требовать > 200 KB JS gzip для first paint.
Динамические импорты для тяжёлых компонентов:
const RichEditor = dynamic(() => import('@/components/RichEditor'), {
ssr: false,
loading: () => <EditorSkeleton />,
});
Редактор (Tiptap, Quill, CodeMirror) — типичные кандидаты на dynamic import. Без этого они попадают в основной бандл. React DevTools Profiler — для поиска лишних ре-рендеров. React.memo, useMemo, useCallback — точечные инструменты. Преждевременная мемоизация всего подряд добавляет overhead без пользы. Профилируйте сначала, оптимизируйте потом.
Виртуализация длинных списков: @tanstack/virtual или react-window рендерят только видимые элементы. Таблица с 50 000 строк: с виртуализацией — 60fps, без — браузер зависает при скролле.
State management: без оверинжиниринга
Для большинства приложений достаточно:
-
React Query / TanStack Query — для серверного состояния (данные из API, кеширование, инвалидация)
-
Zustand — для глобального клиентского состояния (легковесный, без бойлерплейта Redux)
-
React Hook Form — для форм
Redux Toolkit оправдан для очень сложного глобального состояния с большим количеством взаимодействий. Для большинства задач — это overkill. Recoil, Jotai — атомарные подходы для независимых кусков состояния.
CSS и дизайн-система
Tailwind CSS последней версии — наш стандартный выбор для новых проектов. Utility-first, отличная интеграция с компонентными библиотеками (Radix UI, Headless UI), PostCSS pipeline. CSS Modules — альтернатива когда нужна более явная изоляция стилей. Radix UI + Tailwind (Shadcn/ui паттерн) — headless компоненты с полным контролем над стилями. Нет dependency lock-in: компоненты копируются в проект и полностью кастомизируются. Storybook — для документирования компонентной библиотеки.
React DevTools Profiler — официальный инструмент от команды React.
Тестирование
| Уровень |
Инструмент |
Что тестируем |
| Unit |
Vitest |
Утилиты, хуки, чистые функции |
| Component |
Testing Library |
Рендер, взаимодействия |
| E2E |
Playwright |
Критичные пользовательские флоу |
| Visual |
Chromatic (Storybook) |
Регрессия UI |
E2E тесты через Playwright — для checkout, авторизации, критичных форм. Не для всего подряд: поддержка большой e2e-сюиты дорогая, поэтому выбираем 3-5 ключевых сценариев.
Ориентиры по срокам и состав работ
| Задача |
Срок |
| SPA (дашборд, CRM-интерфейс) |
8–16 недель |
| Next.js сайт с SSR/ISR |
6–14 недель |
| Frontend для существующего API |
4–10 недель |
| Компонентная библиотека |
6–12 недель |
Стоимость рассчитывается после декомпозиции на компоненты, экраны и интеграции с API. Мы используем N+1 оценку: прибавляем 20% на риски.
Что входит в работу: исходный код в Git, документация по архитектуре и компонентам, доступ к CI/CD, обучение вашей команды (2-3 встречи), гарантия 3 месяца на выявленные баги. Дополнительно — покрытие юнит-тестами ключевых модулей.
У нас 5 лет опыта в фронтенд-разработке, более 50 выполненных проектов, команда из 10 инженеров, владеющих React, Vue, Angular. Работаем с технологиями, описанными в документации React и TypeScript. Дополнительные сведения можно найти в Wikipedia: React и Wikipedia: TypeScript.
Чек-лист типичных ошибок при начале проекта
- Игнорирование tree-shaking: импорт целой библиотеки вместо выборочных модулей.
- Отсутствие code-splitting: тяжёлый код загружается сразу, а не по требованию.
- Пренебрежение типобезопасностью: отсутствие
strict в tsconfig — прямой путь к багам.
- Избыточная мемоизация:
useMemo и useCallback там, где они не нужны.
- Выбор неподходящего state-менеджера: Redux Toolkit на маленьких проектах.
Какой стек выбрать для фронтенд-разработки React?
Мы сравниваем инструменты по реальным метрикам. Next.js быстрее Nuxt в сборке SSR на 20–30% при одинаковом размере страницы. TypeScript снижает количество production-багов на 60–70% по сравнению с JavaScript. Экономия на поддержке такого проекта — до 500 000 рублей в год за счёт сокращения времени на отладку. Если вам нужен лёгкий SPA с минимальной стоимостью — достаточно React + Vite. Для контентного сайта с SEO — Next.js с ISR даёт TTFB ниже 50 мс даже при 50 000 страниц.
Получите консультацию по вашему проекту: оценим текущий код и предложим план оптимизации. Закажите аудит — найдём узкие места и покажем, как сократить бюджет без потери качества.