Keyframes vs Transitions: Вечная битва за плавный интерфейс
Присаживайся, коллега. Давай честно: сколько раз ты проклинал CSS, когда пытался сделать простую анимацию появления модалки? Ты вешаешь transition: all 0.3s, меняешь display: none на block, и… ничего. Никакой плавности, просто резкий скачок. Ты лезешь в JavaScript, вешаешь setTimeout на 10 миллисекунд или слушаешь событие transitionend, превращая код в спагетти. Знакомая боль?
Сегодня мы разберем, когда пора перестать мучить transitions и переходить на keyframes, а также заглянем в светлое будущее, где старые костыли больше не нужны.
Как мы страдали раньше
Раньше мир CSS-анимаций был жестко разделен. Transitions были идеальны для простых состояний: навел мышкой — кнопка плавно поменяла цвет. Но как только дело доходило до многоэтапных движений или анимации свойств, которые не имеют промежуточных значений (типа того же display), начинался ад.
Мы использовали хаки с max-height: 0 и max-height: 1000px для раскрывающихся списков, потому что CSS не умел анимировать высоту в auto. Мы создавали невидимые элементы, чтобы браузер заранее просчитал их геометрию. А если нужно было сделать бесконечную сложную пульсацию, приходилось городить огромные конструкции в @keyframes, которые было чертовски сложно переиспользовать. Кстати, если ты до сих пор борешься с прыгающей версткой при изменении контента, посмотри, как свойство aspect-ratio решает проблемы с пропорциями — это база для стабильных анимаций.
Как делать правильно в 2026 году
Современный CSS (тот, что мы называем спецификацией 2024-2026 годов) наконец-то принес «киллер-фичи», которые стирают грань между переходами и ключевыми кадрами. Теперь мы можем анимировать даже те вещи, которые раньше считались невозможными.
Главные герои нашего времени: @starting-style и свойство transition-behavior: allow-discrete. Они позволяют анимировать переход элемента из состояния «его нет в DOM/он скрыт» в состояние «он виден». Это особенно круто работает, когда ты используешь нативные элементы dialog и popover. Теперь не нужно гадать, победит ли opacity в битве с display: none — браузер сам поймет, что нужно сначала дождаться завершения анимации, а потом уже скрывать элемент.
Transitions теперь выбираем, когда нужно простое взаимодействие «А в Б», даже если это сложные типы данных. А Keyframes оставляем для цикличных, нелинейных анимаций или когда элемент должен пройти через несколько опорных точек без участия пользователя.
Готовый сниппет кода
Давай посмотрим на магию в деле. Вот пример современной анимации появления элемента, который раньше потребовал бы кучу JS-кода, а теперь работает на чистом CSS:
/* Стили для элемента, который появляется плавно из display: none */
.modern-card {
display: none;
opacity: 0;
transform: translateY(20px) scale(0.9);
transition:
display 0.5s allow-discrete,
opacity 0.5s,
transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* Состояние, когда элемент открыт (например, через класс или [open]) */
.modern-card.is-visible {
display: block;
opacity: 1;
transform: translateY(0) scale(1);
}
/* Магия: задаем начальные значения для анимации ПЕРЕД тем, как элемент появится */
@starting-style {
.modern-card.is-visible {
opacity: 0;
transform: translateY(20px) scale(0.9);
}
}
/* Бонус: анимация высоты до auto (теперь в современных браузерах) */
.accordion-content {
height: 0;
overflow: hidden;
transition: height 0.3s interpolate-size;
}
.accordion-content.open {
height: auto;
}
Частая ошибка новичков
Самый частый «выстрел в ногу» — это анимация «тяжелых» свойств. Новички обожают анимировать width, height, top или margin. Проблема в том, что каждое изменение этих свойств заставляет браузер пересчитывать всю геометрию страницы (Layout/Reflow). Если у тебя на странице больше десяти элементов, анимация начнет безбожно тормозить, особенно на мобилках.
Золотое правило: анимируй только transform (scale, translate, rotate) и opacity. Эти свойства обрабатываются видеокартой (GPU) и не трогают макет страницы. Если тебе нужно изменить размер кнопки — используй transform: scale(). Если нужно передвинуть блок — transform: translate(). Твой интерфейс должен летать, а не ползать со скоростью 15 FPS.
🔥 Больше фишек, готовых сниппетов и передовых подходов к CSS мы публикуем в нашем Telegram-канале. Подписывайтесь, чтобы не пропустить!