Сколько раз в своей практике ты ловил себя на мысли: «Чёрт возьми, почему в CSS до сих пор нельзя просто сгенерировать случайное число?»
Каждый раз, когда дизайнеры просят оживить сайт органическими эффектами — разбросать снежинки на праздничном лендинге, слегка повернуть стикеры с отзывами на случайный угол в 2–3 градуса, добавить мерцание звёздам на фоне или разнести задержки появления карточек в каталоге, — фронтендер упирается в глухую стену.
Исторически у нас было ровно два костыля:
- Препроцессоры (Sass / SCSS): в них есть функция
random(), но она вычисляется ровно один раз во время сборки проекта на сервере. В итоговом CSS ты получаешь жестко захардкоженные числа, которые одинаковы для всех посетителей и требуют раздувания стилей сотнями громоздких селекторов:nth-child(n). - JavaScript-костыли: вычислять
Math.random()в коде компонента и пробрасывать значения в инлайн-стилиstyle="--delay: ...". Это загрязняет разметку, нагружает память виртуального DOM и ломает декларативную красоту стилей.
Ситуация наконец-то меняется: рабочая группа CSSWG включила нативную функцию random() в официальную рабочую версию спецификации CSS Values and Units Module Level 5.
Давай подробно разберём, как устроена эта функция, какие грабли поджидают верстальщиков и почему её появление в браузерах — это долгожданный праздник для веб-анимации.
Синтаксис и грамматика: единицы измерения, диапазоны и шаг
В отличие от стандартного Math.random() в JavaScript, который возвращает безразмерное число с плавающей точкой от 0 до 1, CSS-функция random() изначально спроектирована под физические единицы веба: пиксели, проценты, градусы, секунды и относительные шрифтовые единицы.
Базовая сигнатура функции принимает минимальное и максимальное значение, а также опциональный шаг дискретизации:
/* Базовый случайный угол от -5 до 5 градусов */
.sticky-note {
transform: rotate(random(-5deg, 5deg));
}
/* Случайный размер шрифта с дискретным шагом 4px */
.tag-cloud-item {
font-size: random(12px, 28px, 4px);
}
/* Случайная задержка анимации с шагом в одну десятую секунды */
.animated-card {
animation-delay: random(0s, 1.5s, 0.1s);
}
Обрати внимание на третий аргумент — шаг (step). Если ты указываешь random(10px, 50px, 10px), браузер выберет строго одно из дискретных значений: 10px, 20px, 30px, 40px или 50px. Это позволяет легко привязывать случайные отступы и размеры к модульной сетке дизайн-системы, исключая появление дробных размытых пикселей на экранах стандартной плотности.
Главный архитектурный вызов: проблема мерцания и ключ детерминизма
Казалось бы, всё просто? Но когда инженеры браузерных движков начали продумывать интеграцию случайности в рендеринг, они столкнулись с фундаментальной проблемой: жизненным циклом перерисовки (Repaint / Reflow).
Представь: ты задал случайный угол поворота кнопке. Пользователь наводит курсор мыши (:hover), страница скроллится или изменяется размер окна. Браузер запускает перерасчёт стилей. Если бы random() каждый раз генерировал новое число, кнопка при каждом движении мыши дёргалась бы в припадке эпилепсии! А при перерисовке таблицы всё оформление непрерывно скакало бы, вызывая катастрофический сдвиг макета (Cumulative Layout Shift).
Чтобы решить эту проблему, в спецификацию добавили гениальное концептуальное понятие: ключ кэширования (caching key / seed identifier), о котором детально рассказывает разработчик Google Chrome Брэм Ван Дамм в разборе в блоге Bramus.
Синтаксис с явным ключом выглядит так:
/* Значение генерируется один раз и стабильно привязано к конкретному элементу */
.gallery-photo {
transform: rotate(random(--photo-seed, -4deg, 4deg));
}
Как это работает под капотом:
- Первый аргумент с двойным дефисом (
--photo-seed) служит идентификатором генератора. - Браузер вычисляет псевдослучайное значение один раз для конкретного элемента в дереве DOM и кэширует его.
- Никакие ховеры, скроллы или изменения родительских классов больше не сбивают это значение: элемент остаётся визуально стабильным на протяжении всей жизни страницы.
Более того, если тебе нужно синхронизировать несколько свойств одного элемента (например, чтобы ширина и высота случайного пузыря были строго пропорциональны), ты используешь один и тот же ключ:
.particle {
--size: random(--bubble-seed, 10px, 60px);
width: var(--size);
height: var(--size);
/* Но задержка должна быть уникальной, поэтому ключ другой! */
animation-delay: random(--time-seed, 0s, 3s);
}
Родственная функция random-item() для работы со списками значений
Помимо числового диапазона, спецификация вводит парную функцию random-item(). Она позволяет выбирать случайное значение не из непрерывного отрезка, а из произвольного перечня ключевых слов, цветов или идентификаторов:
/* Случайный выбор акцентного цвета из палитры дизайн-системы */
.badge {
background-color: random-item(--badge-color, var(--color-blue), var(--color-emerald), var(--color-purple));
}
Это полностью избавляет от необходимости писать километровые каскады селекторов :nth-child(4n + 1) только для того, чтобы раскрасить теги в блоге в разные фирменные цвета.
Где random() идеален, а где категорически противопоказан
Функция random() открывает второе дыхание для чистого CSS, но требует профессионального чувства меры:
Идеальные сценарии применения:
- Органические фоновые паттерны (звёздное небо, дождь, частицы пыли).
- Рассинхронизация зацикленных анимаций (чтобы 20 плавающих элементов не двигались строем, как солдаты).
- Лёгкие рукописные микро-отклонения (имитация напечатанных на машинке букв, естественные наклоны карточек).
Опасные сценарии, которых стоит избегать:
- Размеры колонок в сетках Grid и Flexbox: случайная ширина блоков сломает предсказуемость адаптивной верстки и вызовет неконтролируемые переносы строк.
- Цвета критического текста: случайные оттенки неизбежно приведут к нарушению контрастности WCAG на светлом или тёмном фоне.
Если ты следишь за новыми стандартами веб-платформы, обязательно изучи наш материал про новинки Chrome 156: случайные числа в CSS,
