Ховер

Добавляем базовый интерактив и реакцию статичным элементам через наведение курсора

3 минуты чтения

Содержание

О модуле

В предыдущих модулях все анимации запускались при загрузке страницы и шли своим чередом, не реагируя на зрителя. Ховер это меняет. Псевдокласс :hover активируется, когда курсор оказывается над элементом, и выключается, когда курсор уходит. Плакат перестаёт быть просто движущейся картинкой и становится пространством, которое откликается на присутствие зрителя.

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

Transition и плавность

Свойство transition задаёт, как именно браузер будет интерполировать изменение между двумя состояниями. Оно принимает четыре параметра: какое свойство анимировать, как долго, с какой функцией замедления и с какой задержкой.

Запись transition: transform 0.3s ease означает, что любое изменение transform на этом элементе будет происходить плавно за 0.3 секунды с замедлением в конце. Можно перечислить несколько свойств через запятую, если нужно анимировать каждое с разными параметрами. А можно написать transition: all 0.3s ease, чтобы анимировались все изменяемые свойства сразу, хотя у этого подхода есть побочный эффект: если вы случайно изменили на ховере padding или color, они тоже начнут плавно переключаться, что может быть неожиданно.

Что можно анимировать

Свойства CSS делятся на три группы по стоимости для браузера.

Самые дешёвые для рендеринга transform и opacity. Они обрабатываются на GPU и не вызывают пересчёт раскладки страницы. Если у вас есть выбор, всегда отдавайте предпочтение этим двум.

Свойства средней стоимости требуют перерисовки, но не пересчёта раскладки. Сюда входят color, background-color, box-shadow, text-shadow, border-color, filter и clip-path. Они работают плавно в большинстве ситуаций.

Дорогие свойства вызывают пересчёт раскладки всей страницы. Это width, height, padding, margin, top, left и подобные. Анимировать их можно, но на сложных страницах переходы могут подтормаживать.

Есть свойства, которые не анимируются вовсе. Среди них display, font-family, position и overflow. Если вам нужно плавно показать скрытый элемент, используйте opacity и visibility вместо display: none.

Асимметрия входа

Тонкий приём, который сразу поднимает качество ховер-эффектов. Идея в том, чтобы задать разный тайминг для наведения и для ухода курсора.

transition на самом элементе в обычном состоянии управляет выходом, то есть возвратом в исходное состояние, когда курсор ушёл. А transition внутри :hover управляет входом. Быстрый вход (0.15s) и медленный выход (0.4s) создают ощущение, что элемент мгновенно откликается на присутствие курсора, но расслабленно возвращается обратно.

Триггер и эффект

Если :hover меняет позицию самого элемента, например через translateY(-10px), возникает проблема на границе зоны наведения. Курсор наводится на элемент, тот уезжает вверх, курсор оказывается за пределами элемента, ховер выключается, элемент возвращается обратно под курсор, ховер включается снова. Получается дёргание.

Решение в том, чтобы разделить триггер и эффект. Ховер вешается на обёртку, которая остаётся неподвижной, а визуальные изменения применяются к дочернему элементу внутри неё. Обёртка не двигается, значит курсор не теряет контакт, а дочерний элемент спокойно анимируется.

Базовый отклик

Три свойства на одном элементе: лёгкое увеличение через scale, появление тени через box-shadow и небольшое смещение вверх через translateY. Вместе они создают ощущение, что объект приподнимается над поверхностью. Этот паттерн демонстрирует асимметрию входа и выхода, где вход быстрый, а выход мягкий и замедленный.

Объяснение

На элементе в обычном состоянии задан transition: all 0.4s ease, который управляет возвратом. Внутри :hover стоит transition: all 0.15s ease, который управляет реакцией на наведение. Разница ощущается тактильно, хотя на первый взгляд кажется малозаметной.

Три свойства перечислены в одном transform через пробел: translateY(-6px) scale(1.03). Тень добавляется отдельно через box-shadow, потому что она не является частью transform и анимируется как самостоятельное свойство.


<div class="demo-basic-card"></div>
      

.demo-basic-card {
  width: 140px;
  height: 100px;
  background: #fff;
  border-radius: 8px;
  cursor: pointer;
  /* Выход — медленный */
  transition: all 0.4s ease;
}
 
.demo-basic-card:hover {
  transform: translateY(-6px) scale(1.03);
  box-shadow: 0 12px 30px rgba(0,0,0,0.3);
  /* Вход — быстрый */
  transition: all 0.15s ease;
}
      

Раскрытие содержимого

Карточка, при наведении на которую выезжает скрытый текстовый блок. Текст изначально невидим через opacity: 0 и смещён вниз через translateY(20px). На ховере он плавно поднимается на место и становится видимым. Этот паттерн показывает, как ховер на родителе может управлять дочерним элементом через конструкцию .card:hover .card-info.

Объяснение

На элементе в обычном состоянии задан transition: all 0.4s ease, который управляет возвратом. Внутри :hover стоит transition: all 0.15s ease, который управляет реакцией на наведение. Разница ощущается тактильно, хотя на первый взгляд кажется малозаметной.

Ховер применяется к родительскому контейнеру .demo-reveal-card, а визуальные изменения задаются на дочернем элементе .demo-reveal-info через составной селектор .demo-reveal-card:hover .demo-reveal-info. Это и есть принцип разделения триггера и эффекта. Обёртка остаётся неподвижной, а внутренний блок анимируется.

Сама карточка тоже реагирует на ховер лёгким затемнением фона, чтобы текст на ней читался лучше. Два эффекта на одном ховере создают ощущение цельного, продуманного взаимодействия.

Наведи
Ты открыл секрет, молодец! Держи огурец!

<div class="demo-reveal-card">
  <div class="demo-reveal-title">Плакат</div>
  <div class="demo-reveal-info">Скрытое описание</div>
</div>
      

.demo-reveal-card {
  border: 1px solid rgba(255,255,255,0.2);
  border-radius: 10px;
  overflow: hidden;
  cursor: pointer;
  transition: background 0.3s ease;
}
 
.demo-reveal-info {
  opacity: 0;
  transform: translateY(15px);
  transition: opacity 0.3s ease, transform 0.3s ease;
}
 
/* Ховер на родителе, эффект на дочернем */
.demo-reveal-card:hover .demo-reveal-info {
  opacity: 1;
  transform: translateY(0);
}
      

Подчёркивание-линия

Текстовая ссылка, у которой при наведении вырастает подчёркивание. Линия реализована через псевдоэлемент ::after с scaleX(0) в обычном состоянии и scaleX(1) на ховере. Свойство transform-origin управляет направлением роста: left и линия выезжает слева, center — расходится от центра, right — зеркально справа налево.

Объяснение

На элементе в обычном состоянии задан transition: all 0.4s ease, который управляет возвратом. Внутри :hover стоит transition: all 0.15s ease, который управляет реакцией на наведение. Разница ощущается тактильно, хотя на первый взгляд кажется малозаметной.

Псевдоэлемент ::after позиционируется абсолютно относительно ссылки, занимает всю ширину через width: 100% и имеет высоту в 2—3px. В обычном состоянии он сжат до нуля через transform: scaleX(0).

На :hover масштаб возвращается к scaleX(1), и линия вырастает вдоль текста. Меняя одно лишь свойство transform-origin, вы получаете три визуально разных эффекта.

Слева Центр Справа

<span class="demo-underline-link">Слева</span>
<span class="demo-underline-link from-center">Центр</span>
<span class="demo-underline-link from-right">Справа</span>
      

.demo-underline-link {
  position: relative;
  font-size: 20px;
  font-weight: 600;
  color: #fff;
  cursor: pointer;
}
 
.demo-underline-link::after {
  content: '';
  position: absolute;
  bottom: -4px;
  left: 0;
  width: 100%;
  height: 2px;
  background: #fff;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.3s ease;
}
 
.demo-underline-link:hover::after {
  transform: scaleX(1);
}
 
/* Варианты направления */
.from-center::after { transform-origin: center; }
.from-right::after  { transform-origin: right; }
      

Попробуйте использовать этот приём с transform-origin: left на ховере и transform-origin: right в обычном состоянии. Линия будет выезжать слева направо при наведении и утекать в правую сторону при уходе курсора. Получается ощущение непрерывного движения.

Затемнение группы

Ряд элементов, при наведении на один из которых остальные затемняются. Этот приём создаёт визуальный фокус и помогает зрителю сосредоточиться на конкретном объекте. Технически ховер применяется к родительскому контейнеру, который снижает opacity всех дочерних элементов, а на конкретном ребёнке :hover возвращает opacity: 1.

Объяснение

Когда курсор наводится на родительский контейнер, все дочерние элементы получают opacity: 0.3 через селектор .container:hover .item. Затем конкретный элемент, на который наведён курсор, восстанавливает свою видимость через .container:hover .item:hover { opacity: 1 }.

Специфичность второго правила выше, поэтому оно выигрывает. Важно, чтобы transition стоял на самих дочерних элементах, а не на контейнере, тогда затемнение и проявление будут плавными для каждого элемента индивидуально.


<div class="demo-dim-row">
  <div class="demo-dim-item"></div>
  <div class="demo-dim-item"></div>
  <div class="demo-dim-item"></div>
  <div class="demo-dim-item"></div>
  <div class="demo-dim-item"></div>
</div>
      

.demo-dim-item {
  width: 80px;
  height: 100px;
  background: #fff;
  border-radius: 6px;
  cursor: pointer;
  transition: opacity 0.3s ease, transform 0.3s ease;
}
 
/* Ховер на контейнере затемняет все элементы */
.demo-dim-row:hover .demo-dim-item {
  opacity: 0.3;
}
 
/* Наведённый элемент восстанавливается */
.demo-dim-row .demo-dim-item:hover {
  opacity: 1;
  transform: scale(1.05);
}
      

Вместо opacity попробуйте filter: blur(2px) для затемнённых элементов. Размытие соседей фокусирует взгляд на активном элементе ещё сильнее. Но учтите, что filter: blur может тормозить на мобильных устройствах, если элементов много.

Заливка фона

Кнопка, фон которой при наведении заливается слева направо цветом. Эффект строится на псевдоэлементе ::before, который изначально сжат через scaleX(0) и раскрывается на ховере. Текст остаётся поверх заливки через position: relative и z-index, чтобы не оказаться под цветным слоем.

Объяснение

Псевдоэлемент ::before позиционируется абсолютно внутри кнопки и занимает всю её площадь через inset: 0. В обычном состоянии он невидим через transform: scaleX(0) и transform-origin: left.

На :hover он раскрывается до scaleX(1), заполняя фон кнопки цветом. Текст кнопки должен иметь position: relative и z-index: 1, чтобы оставаться поверх псевдоэлемента. Цвет текста тоже можно менять на ховере, чтобы он оставался контрастным на новом фоне.

Наведи

<div class="demo-fill-btn">
  <span class="demo-fill-label">Наведи</span>
</div>
      

.demo-fill-btn {
  padding: 16px 48px;
  border: 2px solid #fff;
  background: transparent;
  overflow: hidden;
  position: relative;
  cursor: pointer;
}
 
.demo-fill-btn::before {
  content: '';
  position: absolute;
  inset: 0;
  background: #fff;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.35s ease;
  z-index: 0;
}
 
.demo-fill-btn:hover::before {
  transform: scaleX(1);
}
 
/* Текст остаётся поверх заливки */
.demo-fill-label {
  position: relative;
  z-index: 1;
  color: #fff;
  transition: color 0.35s ease;
}
 
.demo-fill-btn:hover .demo-fill-label {
  color: #1a1a2e;
}
      

Перспективный наклон

Карточка, которая при наведении слегка наклоняется в 3D. Мягкий rotateX и rotateY на 3–5° создают тактильное ощущение, словно объект отзывается на прикосновение. Этот паттерн перекликается с Перспективным наклоном из модуля Трансформации, но здесь он управляется ховером, а не бесконечной анимацией.

Объяснение

Контейнер получает perspective: 600px, чтобы задать глубину 3D-пространства. Сама карточка на :hover получает transform: rotateX(-5deg) rotateY(5deg), что наклоняет её в пространстве.

Значения в 3–5° достаточно для тактильного ощущения. Большие углы в 10–15° делают эффект драматичнее, но на маленьких элементах могут выглядеть чрезмерно.


<div class="demo-tilt-scene">
  <div class="demo-tilt-card"></div>
</div>
      

.demo-tilt-scene {
  perspective: 600px;
}
 
.demo-tilt-card {
  width: 160px;
  height: 120px;
  background: #fff;
  border-radius: 10px;
  cursor: pointer;
  transition: transform 0.4s ease, box-shadow 0.4s ease;
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
 
.demo-tilt-card:hover {
  transform: rotateX(-5deg) rotateY(5deg);
  box-shadow: -8px 12px 30px rgba(0,0,0,0.25);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
      

Для чистого CSS-решения наклон одинаков в любой точке карточки. Чтобы направление наклона зависело от позиции курсора, понадобится JavaScript.

Магнитный эффект

Группа элементов, при наведении на один из которых соседние отталкиваются в стороны. Это создаёт ощущение магнитного поля, где элементы реагируют друг на друга. Технически эффект строится на комбинаторе ~ (общий сиблинг), который позволяет стилизовать элементы, следующие за наведённым, и на :hover родителя для элементов, стоящих до него.

Объяснение

Когда курсор наводится на элемент, все его последующие соседи через комбинатор ~ получают translateX(15px), сдвигаясь вправо. Предыдущие соседи не поддаются стилизации через CSS напрямую, поэтому используется обходной путь: на ховере родителя все элементы сдвигаются влево, а сам наведённый элемент и его последующие соседи компенсируют этот сдвиг.

В результате элементы расходятся от наведённого. Этот приём ограничен чистым CSS и работает только для горизонтальных рядов. Для более сложных магнитных взаимодействий, где элементы отталкиваются во всех направлениях, понадобится JavaScript.


<div class="demo-magnetic-row">
  <div class="demo-magnetic-dot"></div>
  <div class="demo-magnetic-dot"></div>
  <div class="demo-magnetic-dot"></div>
  <div class="demo-magnetic-dot"></div>
  <div class="demo-magnetic-dot"></div>
  <div class="demo-magnetic-dot"></div>
  <div class="demo-magnetic-dot"></div>
</div>
      

.demo-magnetic-dot {
  width: 50px;
  height: 50px;
  background: #fff;
  border-radius: 50%;
  cursor: pointer;
  transition: transform 0.4s ease, opacity 0.4s ease;
}
 
/* Ховер на контейнере сдвигает всех влево */
.demo-magnetic-row:hover .demo-magnetic-dot {
  transform: translateX(-8px);
  opacity: 0.5;
}
 
/* Наведённый элемент возвращается */
.demo-magnetic-row .demo-magnetic-dot:hover {
  transform: translateX(0) scale(1.2);
  opacity: 1;
}
 
/* Соседи справа сдвигаются вправо */
.demo-magnetic-row .demo-magnetic-dot:hover ~ .demo-magnetic-dot {
  transform: translateX(8px);
  opacity: 0.5;
}
      

Важно помнить, что комбинатор ~ работает только с последующими сиблингами, которые идут после наведённого в HTML-разметке. Предыдущие соседи недоступны через CSS. Если вам нужно, чтобы элементы расходились симметрично, используйте трюк с ховером на родителе или переключитесь на JavaScript.