Как сделать анимированное подчеркивание ссылок на CSS

Реализовать подчеркивание на CSS непросто. Сложности возникают, если вам нужно нечто более оригинальное, чем стандартный text-decoration: underline. Существует немало различных методов — но, к сожалению, все они имеют серьезные недостатки.

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

Неприятность, с которой я столкнулся, заключалась в следующем — в моем блоге ссылки часто переносятся на следующую строчку — а это означает, что часть ссылки остается неподчеркнутой. К слову, в блоге Кэсси впечатляют не только ссылки — там прекрасно все, от кода до контента.

Задача

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

Применение фона

Существует сразу несколько различных способов подчеркнуть текстовый фрагмент. Я остановился на методе, который удовлетворяет всем критериям, и использует CSS свойство background-image. Фон background-image можно залить сплошным цветом, применяя функцию linear-gradient так, чтобы первый цвет перетекал во второй такой же.

Зачем использовать background-image, а не background-color, если предполагается заливка сплошным цветом? Дело в том, что background-image предоставляет больше свойств для манипуляций с фоном.

Размер фона ограничен по высоте и ширине и полностью заполняет ширину элемента ссылки — с помощью указания параметров background-size 2px и 100% соответственно. Это приводит к сплошному заполнению фона, поэтому я установил no-repeat для background-repeat.

Подчеркивание окажется сверху ссылки. Расположение линии меняем на нижнюю левую границу, устанавливая 0 100% для background-position — тогда она окажется внизу, под текстом ссылки.

Два фона

Для использования нескольких фоновых изображений и манипулирования их свойствами, установите для background-* несколько значений, разделенных запятыми. Первая запись из списка окажется верхним слоем, последующие будут нижними слоями. Фон элемента анкора будет сплошным черным (#000000). Белый (#FFFFFF) фон скрыт под ним:

a {
  background-image: linear-gradient(#000000, #000000), linear-gradient(#ffffff, #ffffff);
}

В следующем примере используются два фона — оба находятся внизу, первый перекрывает второй:

a {
  color: #dfe5f3;
  text-decoration: none;
  background-image: linear-gradient(rgb(176, 251, 188), rgb(176, 251, 188)),
    linear-gradient(#feb2b2, #feb2b2);
  background-size: 100% 2px, 100% 2px;
  background-position: 100% 100%, 0 100%;
  background-repeat: no-repeat, no-repeat;
}

Переход для background-size

Обратили внимание, чем отличаются параметры background-position, хотя визуально разницы нет? Один параметр привязан к левой стороне, второй — к правой.

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

a {
  color: #dfe5f3;
  text-decoration: none;
  background-image: linear-gradient(rgb(176, 251, 188), rgb(176, 251, 188)),
    linear-gradient(#feb2b2, #feb2b2);
  background-size: 100% 2px, 0 2px;
  background-position: 100% 100%, 0 100%;
  background-repeat: no-repeat;
  transition: background-size 2s linear;
}

a:hover {
  background-size: 0 2px, 100% 2px;
}

Три фона

Задача почти выполнена — осталось добавить промежуток между подчеркиваниями. Пробел между линиями можно имитировать, передвигая разделительный блок, цвет которого совпадает с фоновым. Что собой представляет подобный цветной разделитель? Верно, еще один фон. Что может быть лучше, чем два фона? Только три фона.

Этот фон я размещу поверх двух первых, указывая его первым в списке свойств для background-image. Не забудьте, что первое значение в списке свойств background-* теперь также относится к новому фону background-image. Высоту и ширину задаем с помощью background-size. Высота идентична параметрам предыдущих фонов (2px в нашем случае), а ширина установлена небольшая — 20px.

Переход для background-position

Разделительный фрагмент должен быть невидимым до наведения курсора, поэтому позиция фона background-position установлена отрицательной — это разместит разделитель слева от анкора, и таким образом полностью его скроет. В момент наведения курсора разделитель должен сдвинуться по подчеркиванию в противоположном направлении — и снова исчезнуть из вида.

Для вычисления обеих позиций воспользуемся функцией calc():

a {
  color: #dfe5f3;
  text-decoration: none;
  background-image: linear-gradient(#222b40, #222b40), linear-gradient(
      rgb(176, 251, 188),
      rgb(176, 251, 188)
    ), linear-gradient(#feb2b2, #feb2b2);
  background-size: 20px 2px, 100% 2px, 0 2px;
  background-position: calc(20px * -1) 100%, 100% 100%, 0 100%;
  background-repeat: no-repeat;
  transition: background-size 2s linear, background-position 2s linear;
}

a:hover {
  background-size: 20px 2px, 0 2px, 100% 2px;
  background-position: calc(100% + 20px) 100%, 100% 100%, 0 100%;
}

Теперь анимированное подчеркивание работает так, как и требовалось. Мы благодарим Джея Томпкинса — настоящего волшебника в сфере CSS анимации. Именно он предложил метод анимации, основанный на манипуляции со свойством background-position.

Пожалуйста, опубликуйте свои отзывы по текущей теме материала. Мы очень благодарим вас за ваши комментарии, подписки, лайки, дизлайки, отклики!

Пожалуйста, оставьте свои отзывы по текущей теме материала. За комментарии, подписки, дизлайки, отклики, лайки низкий вам поклон!

Сергей Бензенкоавтор-переводчик статьи «A CSS-only, animated, wrapping underline»