Стилизуем кнопки правильно

Перевод статьи Styling buttons, the right way Флоренса Вёршелда.

Если вы создаёте сайт или веб-приложение, там наверняка есть кнопки. А может быть ссылки, выглядящие как кнопки? В любом случае важно правильно их сделать.

В этой статье мы создадим основные стили для тегов <a> и <button>, а также собственный CSS-компонент .btn. Для каждого этапа вы найдёте демо-страницу.

Этап 1: Сброс стилей у <button>.
Этап 2: Создание CSS-компонента «button».
Этап 3: Стилизация активного, ховер- и фокус-состояния.
Этап 4: Разбираемся с прилипшим фокусом.

Сброс стилей у <button>

Как правило, 99,99% кликабельных элементов на сайте или в приложении должны быть <a> или <button>. Что делать, если вы не уверены, какой элемент использовать в определённых ситуациях:

  1. Если при клике открывается другая страница или изменяется большая часть контента на странице — используйте ссылку (<a href=”some_url”>…</a>).
  2. В остальных случаях используйте обычную кнопку (<button type=”button”>…</button>).

Использование правильного элемента имеет ряд преимуществ: это благоприятно для SEO (особенно для ссылок!), хорошо для навигации с клавиатуры и улучшает доступность для всех пользователей.

Несмотря на это, разработчики редко используют <button>. По всему интернету можно встретить кучу кнопок, которые вызывают действия через JavaScript, но на деле оказывается, что это <div>, <span>или<a>.

Почему же <button> так редко используется?

  • Знание: многие разработчики просто не знают о нём (выучить более 100 HTML-элементов не так-то просто).
  • Стилизация: по умолчанию у <button> сложные стили, из-за которых трудно добиться собственного вида.

К счастью, стилизацию поправить не так уж сложно!

/**  
 * Сброс стилей у кнопки. 
 * Придётся немного поработать, чтобы получить нейтральный вид.  
 */
button {
  padding: 0;
  border: none;
  font: inherit;
  color: inherit;
  background-color: transparent;
  /* отображаем курсор в виде руки при наведении; некоторые  
   * считают, что необходимо оставлять стрелочный вид для кнопок 
   */
  cursor: pointer;
}

В итоге мы получаем кнопки, которые выглядят как обычный текст.

Ссылка на Codepen

Минус в том, что при таком подходе необходимо стилизовать все кнопки, или пользователь не распознает их (см.: возможности). Ещё один вариант — использовать этот стиль как миксин (Sass или другой препроцессор) и применять его выборочно:

@mixin button-reset {
  padding: 0;
  border: none;
  font: inherit;
  color: inherit;
  background-color: transparent;
  cursor: pointer;
}

.my-custom-button {
  @include button-reset;
  padding: 10px;
  background-color: skyblue;
}
<button type="button">
  У меня браузерные стили по-умолчанию.
</button>

<button type="button" class="my-custom-button">
  А я использую собственные стили.
</button>

Создание CSS-компонента «button»

Теперь, когда мы сбросили дефолтные стили, можем приступить к написанию своих. Вот что мы сделаем:

  • «кнопочный» стиль, который можно применять и к ссылкам и кнопкам;
  • применять мы хотим выборочно, потому что у нас есть другие ссылки и кнопки с разными стилями на странице.

Напрашивается CSS-компонент. CSS-компонент — это стиль или набор стилей, которые применяются, используя классы, к различным типам HTML-элементов. Вам должно быть знакомо это понятие, если вы используете CSS-фреймворки Bootstrap или Foundation.

Назовём этот компонент .btn (как в Bootstrap, но мы ограничимся одним цветом и размером, чтобы было проще).

.btn {
  /* по-умолчанию для <button>, но пригодится для <a> */
  display: inline-block;
  text-align: center;
  text-decoration: none;

  /* создаём маленькие отступы, если кнопки перенесутся на две строки */
  margin: 2px 0;

  /* невидимая граница (понадобится для цвета при наведении/фокусе) */
  border: solid 1px transparent;
  border-radius: 4px;

  /* размер строится из текста и отступов (без width/height) */
  padding: 0.5em 1em;

  /* убедитесь, что достаточно контраста! */
  color: #ffffff;
  background-color: #9555af;
}

Вот что у нас получилось:

Ссылка на Codepen

Вам, возможно, интересно, почему контраст так важен. Ведь кнопки на второй строке выглядят красиво: кому не нравятся пастельные цвета?

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

Стилизация активного, ховер- и фокус-состояния

Круто, когда ваша кнопка выглядит красиво, но… пользователи будут взаимодействовать с ней, и поэтому требуется визуальный отклик, когда состояние кнопки изменяется.

У браузеров свои стили по-умолчанию для «активного» (т.е. нажатого) и «фокусного» состояния, но, сбросив стили у кнопки, мы избавились от некоторых из них. Не хватает ещё стилей при наведении мышкой, да и в целом хочется, чтобы все стили были видны и соответствовали нашему дизайну.

Начнём со стилей для состояния :active, которое срабатывает при клике кнопки или ссылки:

/* олдскульный эффект «нажатия» + цветовые правки */
.btn:active {
  transform: translateY(1px);
  filter: saturate(150%);
}

Мы могли бы изменять цвет кнопки, но я хочу использовать этот эффект при наведении:

/* инвертируем цвета при наведении */
.btn:hover {
  color: #9555af;
  border-color: currentColor;
  background-color: white;
}

Давайте добавим теперь и фокусные стили. Пользователи вашего сайта или веб-приложения могут использовать обычную или виртуальную клавиатуру (на iOS и Android), чтобы «сфокусировать» и активировать поле в форме, кнопки, ссылки и другие интерактивные элементы.

  • Для некоторых пользователей это ускоряет заполнение форм.
  • Для других использование мышки или тач-указателя невозможно или затруднительно. Они пользуются клавиатурой или специальным устройством.

Во многих веб-проектах, которые я видел, дизайнеры задают стили только для наведения мышки, но не для фокуса. Что же нам делать? Простое решение — использовать :hover стили для :focus.

/* инвертируем цвета при наведении и фокусе */
.btn:hover,
.btn:focus {
  color: #9555af;
  border-color: currentColor;
  background-color: white;
}

И когда у нас будут стили для фокуса (только не до!), мы можем сбросить браузерные стили для кнопки:

.btn {
  /* ... */
  /* все браузеры: сбрасываем дефолтный outline,  
    так как у нас уже есть собственные стили */
  outline: none;
}

/* Firefox: сбрасываем внутреннюю границу при фокусе */
.btn::-moz-focus-inner {
  border: none;
}

Попробуйте сами: если вы за компьютером, используйте Tab и Shift+Tab для навигации между кнопками:

Ссылка на Codepen

Разбираемся с прилипшим фокусом

Есть небольшой недочёт. В некоторых браузерах, если кликнуть по ссылке или кнопке, применяются два псевдокласса:

  • :active
  • :focus

«Активный» псевдокласс убирается сразу после того, как отпускается кнопка мыши или трекпада после нажатия. Но в некоторых браузерах после этого остаётся :focus стиль, пока пользователь не кликнет где-нибудь на странице.

В моих тестах, такое происходит в Chrome (66), Edge (16), и Firefox (60, только для ссылок). Safari (11.1), кажется, поумнее, и там нет такой проблемы.

Мы можем исправить это, используя новый псевдокласс :focus-visible (черновик спецификации). Этот элемент ещё не полностью проработан, но идея в том, что браузеры должны применять состояние :focus-visible только при взаимодействии с клавиатуры или подобных устройств, а не при клике.

Так как :focus-visible ещё не внедрён в браузеры, нам придётся использовать решение на JavaScript, такой как этот полифилл. Он действует по всей странице и добавляет класс focus-visible только элементам, которые получают фокус при использовании клавиатуры.

Давайте изменим стили, чтобы отделить стили :hover от стилей :focus.

/* инвертируем цвета при наведении */
.btn:hover {
  color: #9050aa;
  border-color: currentColor;
  background-color: white;
}
/* убеждаемся, что есть видимые границы при фокусе */
.btn:focus {
  outline: none;
  box-shadow: 0 0 0 3px rgba(255, 105, 180, 0.5), 0 0 0 1.5px rgba(255, 105, 180, 0.5);
}

Теперь, после того как мы подключили к странице focus-visible.js, он будет добавлять класс js-focus-visible элементу <body>. Мы можем использовать это, чтобы убрать стили фокуса с элементов, которые не имеют класс _focus-visible:

/* скрываем фокусные стили, если используется не клавиатура */
.js-focus-visible .btn:focus:not(.focus-visible) {
  box-shadow: none;
}

Более простой способ — определить фокусные стили только для класса focus-visible, но если полифилл неактивен (например, если JS не смог загрузится), то стили не применятся.

Окончательный вариант:

Ссылка на Codepen

Окончательный вариант можно посмотреть на сайте Флоренса Вёршелда.