Добавить в корзинуПозвонить
Найти в Дзене
Vibecode Wiki

CSS анимации без библиотек: что умеет браузер — и это на удивление много

Это и так знают все, но стоит освежить — потому что именно на этом строится всё остальное. Transitions — плавное изменение одного состояния в другое при смене класса или псевдокласса: .button { background: #1a1a2e; transform: scale(1); transition: background 0.2s ease, transform 0.15s ease; } .button:hover { background: #e94560; transform: scale(1.03); } Правило которое экономит много времени: анимируй только transform и opacity. Они работают на GPU и не вызывают reflow. top, left, width, height — всё это дорого и заметно на мобильных устройствах. Keyframes — анимация по ключевым кадрам, запускается автоматически или по классу: @keyframes slideUp { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: translateY(0); } } .card { animation: slideUp 0.4s ease forwards; } forwards — важный параметр: без него элемент вернётся в исходное состояние после окончания анимации. Раньше если нужно было анимировать появление элемента при добавлении в DOM — это был JavaScript:
Оглавление

Основа: transitions и keyframes

Это и так знают все, но стоит освежить — потому что именно на этом строится всё остальное.

Transitions — плавное изменение одного состояния в другое при смене класса или псевдокласса:

.button {

background: #1a1a2e;

transform: scale(1);

transition: background 0.2s ease, transform 0.15s ease;

}

.button:hover {

background: #e94560;

transform: scale(1.03);

}

Правило которое экономит много времени: анимируй только transform и opacity. Они работают на GPU и не вызывают reflow. top, left, width, height — всё это дорого и заметно на мобильных устройствах.

Keyframes — анимация по ключевым кадрам, запускается автоматически или по классу:

@keyframes slideUp {

from {

opacity: 0;

transform: translateY(16px);

}

to {

opacity: 1;

transform: translateY(0);

}

}

.card {

animation: slideUp 0.4s ease forwards;

}

forwards — важный параметр: без него элемент вернётся в исходное состояние после окончания анимации.

@starting-style — появление элементов без вспышки

Раньше если нужно было анимировать появление элемента при добавлении в DOM — это был JavaScript: добавить класс через requestAnimationFrame, иначе браузер не видит разницу начального и конечного состояния.

@starting-style решает это в чистом CSS. Он задаёт стартовые значения свойств в момент когда элемент только появляется на странице:

.toast {

opacity: 1;

transform: translateY(0);

transition: opacity 0.3s ease, transform 0.3s ease,

display 0.3s allow-discrete;

}

@starting-style {

.toast {

opacity: 0;

transform: translateY(12px);

}

}

Элемент появляется — анимируется от @starting-style к основным значениям. Плавно, без JS, без setTimeout.

Кроссбраузерный базовый уровень достигнут в 2026 — Safari, Firefox, Chrome поддерживают.

display: none с анимацией выхода

Самая старая боль в CSS: анимировать исчезновение элемента нельзя — display: none отключает его мгновенно, раньше чем переход успевает сыграть. Стандартное решение — setTimeout на длину анимации. Хрупко и ненадёжно.

Теперь работает через transition-behavior: allow-discrete:

.dropdown {

opacity: 1;

transform: translateY(0);

transition: opacity 0.2s ease, transform 0.2s ease,

display 0.2s allow-discrete;

}

.dropdown[hidden] {

display: none;

opacity: 0;

transform: translateY(-8px);

}

@starting-style {

.dropdown {

opacity: 0;

transform: translateY(-8px);

}

}

allow-discrete говорит браузеру: дождись окончания перехода, только потом переключай дискретное свойство (display). Работает для тостов, дропдаунов, тултипов — любого элемента который появляется и исчезает.

interpolate-size — height: auto наконец анимируется

Аккордеон, раскрывающийся плавно — один из самых частых запросов. Раньше нужно было измерять scrollHeight в JavaScript и анимировать к конкретному значению в пикселях. Ломалось при изменении контента.

Одна строка в :root меняет это:

:root {

interpolate-size: allow-keywords;

}

.panel {

height: 0;

overflow: clip;

transition: height 0.35s ease;

}

.panel[data-open] {

height: auto;

}

interpolate-size: allow-keywords разрешает браузеру интерполировать к ключевым словам: auto, min-content, fit-content. Это свойство наследуется, поэтому одного объявления на :root достаточно для всего проекта.

Важно: overflow: clip вместо overflow: hidden — clip не создаёт случайный scroll-контейнер который мешает анимации высоты.

Scroll-driven animations — скролл как таймлайн

Это самое мощное из всего что появилось. Анимация прогрессирует не по времени, а по позиции скролла. Никакого JavaScript, никакого IntersectionObserver, никакого GSAP для базовых scroll-эффектов.

Два типа timeline:

scroll() — прогресс зависит от позиции в скролл-контейнере. Идеально для progress bar или parallax-фона:

@keyframes progressBar {

from { transform: scaleX(0); }

to { transform: scaleX(1); }

}

.reading-progress {

position: fixed;

top: 0;

left: 0;

width: 100%;

height: 3px;

background: #e94560;

transform-origin: left;

animation: progressBar linear;

animation-timeline: scroll(root);

}

Полоска прогресса чтения без единой строки JS. Реагирует на скролл плавно, работает на compositor thread.

view() — прогресс зависит от позиции элемента во вьюпорте. Идеально для reveal-эффектов при скролле:

@keyframes fadeInUp {

from {

opacity: 0;

transform: translateY(24px);

}

to {

opacity: 1;

transform: translateY(0);

}

}

.section {

animation: fadeInUp linear both;

animation-timeline: view();

animation-range: entry 0% entry 30%;

}

animation-range: entry 0% entry 30% — анимация играет пока элемент входит во вьюпорт, занимая первые 30% этого входа. После этого остаётся в конечном состоянии.

Поддержка в 2026 — кроссбраузерная. Firefox и Safari закрыли поддержку в 2025–2026, без префиксов.

Scroll-triggered animations — запуск по порогу скролла

В Chrome 145 появилось отдельное от scroll-driven: animation-trigger. Это не таймлайн где анимация скрабится скроллом — это однократный запуск когда элемент пересекает заданный порог. Прощай IntersectionObserver для этого паттерна:

@keyframes popIn {

from { opacity: 0; scale: 0.9; }

to { opacity: 1; scale: 1; }

}

.card {

animation: popIn 0.4s ease forwards paused;

animation-trigger: view(block 20%);

}

paused — анимация ждёт. animation-trigger: view(block 20%) — запускает когда элемент вошёл на 20% вьюпорта. Один раз, не скрабится.

На момент июня 2026 — Chrome 145+. Firefox и Safari в процессе. Добавляй @supports:

@supports (animation-trigger: view()) {

.card { animation-trigger: view(block 20%); }

}

@property — анимация градиентов и кастомных значений

Градиенты в CSS нельзя анимировать через transition — браузер не понимает как интерполировать между двумя linear-gradient(). Обходной путь был через псевдоэлементы с opacity. @property решает это напрямую, регистрируя кастомное свойство с типом:

@property --gradient-angle {

syntax: " ";

inherits: false;

initial-value: 0deg;

}

.card {

background: linear-gradient(var(--gradient-angle), #1a1a2e, #e94560);

transition: --gradient-angle 0.6s ease;

}

.card:hover {

--gradient-angle: 180deg;

}

Теперь при hover угол градиента плавно поворачивается — без JS, без псевдоэлементов. То же работает для анимации цветов внутри градиента, теней и любых значений которые раньше не поддавались переходам.

@property также открывает анимацию счётчиков, цветовых компонентов и любых числовых CSS-переменных. Поддержка — кроссбраузерная.

View Transitions — переходы между страницами

document.startViewTransition() — один из самых эффектных API которые появились за последние годы. Плавный переход между состояниями DOM или целыми страницами:

document.startViewTransition(() => {

updateDOM(); // любое изменение DOM

});

По умолчанию браузер делает crossfade. Кастомизация через CSS:

/* Переход между страницами — slide */

::view-transition-old(root) {

animation: slide-out 0.3s ease forwards;

}

::view-transition-new(root) {

animation: slide-in 0.3s ease forwards;

}

@keyframes slide-out {

to { transform: translateX(-100%); }

}

@keyframes slide-in {

from { transform: translateX(100%); }

}

Для многостраничных сайтов — автоматические переходы через @view-transition { navigation: auto; } в CSS без JavaScript вообще.

prefers-reduced-motion — обязательно

Все анимации выше нужно оборачивать в проверку. Часть пользователей отключает анимации в системных настройках — по медицинским причинам или личным предпочтениям:

@media (prefers-reduced-motion: reduce) {

*,

*::before,

*::after {

animation-duration: 0.01ms !important;

transition-duration: 0.01ms !important;

animation-iteration-count: 1 !important;

}

}

Одно правило в конце глобального CSS — и все анимации деградируют до мгновенных переходов у тех кто их отключил.

AI помогает: промпты под конкретные задачи

Весь код выше отлично генерируется агентами — CSS хорошо представлен в обучающих данных и новые свойства тоже подхватились. Три промпта под частые задачи:

Reveal-анимации при скролле без JS:

Добавь анимации появления при скролле для всех элементов

с классом .reveal на странице.

Используй только CSS: animation-timeline: view() и animation-range.

Анимация: opacity 0→1, translateY 20px→0, duration 0.5s ease.

Не использовать JavaScript, IntersectionObserver или сторонние библиотеки.

Добавь @media (prefers-reduced-motion: reduce) с отключением анимаций.

Аккордеон с плавным открытием до height: auto:

Создай компонент аккордеона на чистом HTML и CSS.

Требования:

- Открытие/закрытие плавно анимирует height до auto без JS-измерений

- Используй interpolate-size: allow-keywords на :root

- Анимация появления контента через @starting-style

- Переключение только через CSS :has() или details/summary без JavaScript

- Поддержка prefers-reduced-motion

Переход между страницами через View Transitions:

Добавь плавные переходы при навигации между страницами

в Next.js / React Router проекте.

Используй View Transitions API: document.startViewTransition().

Переход: текущая страница уходит влево, новая приходит справа.

CSS для ::view-transition-old и ::view-transition-new.

Добавь fallback: если API не поддерживается — обычная навигация без анимации.

Не использовать Framer Motion или другие библиотеки для этого эффекта.

Когда всё-таки нужна библиотека

CSS не заменяет всё. Три сценария где библиотека оправдана:

Физика и пружины — spring() easing в CSS пока не стандартизирован. Если нужен отскок с реальной физикой — React Spring или GSAP.

Сложные скрабируемые таймлайны — несколько анимаций синхронизированных на одном таймлайне с паузами, повторами и динамическим управлением — GSAP ScrollTrigger.

Интерактивные состояния с жестами — свайп, drag, pinch — это всегда JavaScript. CSS не обрабатывает gesture-события.

Во всём остальном — сначала браузер.

Источник и полная версия: VibeCode Wiki