Transition хорошо справляется с переходом между двумя состояниями, но что делать, если анимация должна пройти через несколько промежуточных точек или зациклиться сама по себе, без внешнего триггера вроде наведения курсора? Для таких случаев в CSS есть правило @keyframes.
Внутри @keyframes описывается последовательность состояний в процентах от общей длительности анимации — 0% это начало, 100% конец, а между ними можно добавить сколько угодно промежуточных точек вроде 50%, каждая со своим набором свойств. Браузер сам плавно интерполирует значения между соседними точками.
Чтобы применить набор ключевых кадров к элементу, используется свойство animation-name с именем, которое было указано после @keyframes, вместе с animation-duration для длительности. Похожая логика, что и у transition, но с гораздо большей гибкостью за счёт нескольких промежуточных состояний.
Свойство animation-iteration-count управляет количеством повторений — можно задать конкретное число или infinite для бесконечного зацикливания, что часто используют для лоадеров, спиннеров и декоративных фоновых эффектов, которые должны крутиться постоянно, пока пользователь на странице.
Animation-direction добавляет ещё один слой гибкости: normal проигрывает анимацию от начала к концу каждый раз, а alternate заставляет её идти то вперёд, то назад поочерёдно, что удобно для пульсирующих или покачивающихся эффектов без резкого возврата к началу.
Как и в случае с transition, стоит следить, какие именно свойства анимируются через ключевые кадры — transform и opacity почти всегда безопасный выбор с точки зрения плавности, а вот анимация позиции через top и left может работать заметно менее гладко на слабых устройствах.
Полный синтаксис @keyframes и разбор всех связанных свойств animation можно посмотреть в статье про @keyframes на MDN.