В этом уроке соберём живую кнопку на чистом CSS: :hover и :focus меняют её вид, а transition делает изменение плавным. По шагам разберём разметку, какое состояние должно появляться при наведении и клавиатурном фокусе, и в какое именно место CSS-правил положить transition, чтобы анимация одинаково плавно работала в обе стороны - и при появлении эффекта, и при его исчезновении.
В этом уроке
Попробуй прямо сейчас: наведи курсор на кнопку - она поднимается и меняет цвет, а при уходе курсора так же плавно возвращается на место. Кликни рядом и переключайся между кнопкой и остальной страницей клавишей Tab - при получении фокуса клавиатурой кнопка реагирует точно так же. Перенеси строку transition внутрь блока .btn:hover - и наведение останется плавным, а вот возврат при уходе курсора станет мгновенным, без анимации.
Шаг 1: разметка и стартовые стили кнопки
Для кликабельной кнопки нужен настоящий тег button, а не div с обработчиком клика. У button из коробки есть то, что иначе пришлось бы дописывать вручную: элемент попадает в очередь переключения по Tab, реагирует на Enter и пробел как на клик, и скринридеры сами объявляют его как кнопку. Стартовые стили (padding, border-radius, цвет фона) задаются на этом же базовом селекторе - именно они будут меняться при наведении и фокусе.
<button class="btn">Нажми меня</button> - настоящая кнопка: фокусируется по Tab, кликабельна с клавиатуры без единой строчки JS.
Неправильное ожидание - что div с курсором pointer выглядит и ведёт себя как кнопка:
<div class="btn" onclick="...">Нажми меня</div> - визуально похоже, но div не попадает в очередь Tab и не реагирует на Enter/пробел без дополнительных tabindex и обработчиков клавиатуры - лишняя работа ради того, что button даёт бесплатно.
Шаг 2: что меняется при наведении - :hover, :focus и transform
Целевое состояние кнопки описывается в правиле .btn:hover, .btn:focus - именно через запятую, оба селектора сразу. :hover срабатывает при наведении мышью, а :focus - когда элемент получает фокус любым способом, включая клавишу Tab. Если указать только :hover, пользователи, которые работают с клавиатуры (а не только те, кто использует специальные технологии), просто не увидят никакой реакции кнопки на фокус - визуально она ничем не будет выделяться среди остальной страницы.
.btn:hover,
.btn:focus {
background: tomato;
transform: translateY(-4px);
} - одинаковый эффект и для мыши, и для клавиатуры: оба способа взаимодействия получают одинаковый визуальный отклик.
Неправильное ожидание - что :hover сам по себе покрывает всех пользователей:
.btn:hover {
background: tomato;
transform: translateY(-4px);
} - при переключении на кнопку клавишей Tab не произойдёт вообще ничего: не сработает ни :hover (курсора мыши на кнопке нет), ни визуальный отклик для фокуса, потому что :focus не описан.
Шаг 3: делаем переход плавным - куда положить transition
Место, где написан transition, решает, будет ли анимация работать в обе стороны. Если положить transition на базовый селектор .btn (тот, что действует и без наведения, и при наведении), переход плавно сыграет и при появлении эффекта, и при его исчезновении. Если же написать transition только внутри .btn:hover, .btn:focus - наведение действительно будет плавным, а вот при уходе курсора или потере фокуса кнопка мгновенно, без анимации, вернётся в исходное состояние: у базового правила .btn transition вообще не указан, а значит его длительность по умолчанию равна 0s, и переход назад происходит мгновенно.
.btn {
transform: translateY(0);
transition: transform .3s ease-out, background-color .3s;
}
.btn:hover, .btn:focus {
background: tomato;
transform: translateY(-4px);
} - transition на базовом селекторе: анимация плавно играет и вперёд, и назад.
Неправильное ожидание - что можно положить transition прямо туда же, где описано целевое состояние:
.btn:hover, .btn:focus {
background: tomato;
transform: translateY(-4px);
transition: transform .3s ease-out, background-color .3s;
} - наведение сыграет плавно, но уход курсора вернёт кнопку в исходное положение мгновенным скачком, без единой миллисекунды анимации.
Частые ошибки новичков
| Ошибка | Что происходит | Как исправить |
|---|---|---|
| Сделать кнопку из div с onclick вместо button | Элемент не попадает в очередь Tab и не реагирует на Enter/пробел - кнопка недоступна с клавиатуры | Использовать настоящий тег button - фокус и реакция на клавиатуру работают из коробки |
| Описать эффект только для :hover, забыв про :focus | Пользователи, переключающиеся по Tab, не получают вообще никакого визуального отклика на кнопке | Всегда описывать состояние взаимодействия как .btn:hover, .btn:focus вместе |
| Положить transition внутрь .btn:hover, а не на базовый .btn | Наведение анимируется плавно, а возврат при уходе курсора происходит мгновенным скачком без анимации | Всегда указывать transition на базовом селекторе - том, что действует и без наведения тоже |
| Указать в transition только одно из нескольких меняющихся свойств (например, только transform) | Свойства, не перечисленные в transition, меняются мгновенно, пока transform - плавно; эффект выглядит дёргано | Перечислять через запятую все свойства, которые меняются в целевом состоянии |
Шпаргалка
| Шаг | Что сделать |
|---|---|
| Разметка | Настоящий <button class="btn">...</button>, а не div с обработчиком клика |
| Стартовые стили | На базовом .btn - именно они изменятся при взаимодействии |
| Целевое состояние | .btn:hover, .btn:focus вместе - одинаковый отклик для мыши и клавиатуры |
| Где писать transition | На базовом .btn, не внутри :hover/:focus - иначе анимация не сыграет в обратную сторону |
| Что перечислить в transition | Через запятую все свойства, которые реально меняются (transform, background-color, box-shadow...) |
| Функция плавности | ease-out хорошо подходит для эффекта "кнопка поднимается навстречу" - быстрый старт, мягкое торможение |
Коротко: отвечаем на вопросы
Через что лучше делать анимацию при наведении - transition или @keyframes?
Для простого эффекта из двух состояний (обычное и при наведении), который должен плавно возвращаться назад при уходе курсора, лучше подходит transition - он "из коробки" анимирует и вперёд, и назад. @keyframes стоит приберечь для более сложных, цикличных или однонаправленных анимаций с несколькими промежуточными шагами, которые не обязаны плавно откатываться назад сами по себе.
Почему при уходе курсора с кнопки анимация иногда не проигрывается, а элемент просто дёргается обратно?
Почти всегда причина в том, что transition написан внутри правила :hover (или :focus), а не на базовом селекторе. Когда состояние наведения исчезает, снова начинает действовать базовое правило - а если в нём самом transition не указан, переход происходит мгновенно, без анимации. Решение - переносить transition на базовый селектор элемента.