Перевод статьи Styling buttons, the right way Флоренса Вёршелда.
Если вы создаёте сайт или веб-приложение, там наверняка есть кнопки. А может быть ссылки, выглядящие как кнопки? В любом случае важно правильно их сделать.
В этой статье мы создадим основные стили для тегов <a>
и <button>
, а также собственный CSS-компонент .btn. Для каждого этапа вы найдёте демо-страницу.
Этап 1: Сброс стилей у <button>
.
Этап 2: Создание CSS-компонента «button».
Этап 3: Стилизация активного, ховер- и фокус-состояния.
Этап 4: Разбираемся с прилипшим фокусом.
Сброс стилей у <button>
Как правило, 99,99% кликабельных элементов на сайте или в приложении должны быть <a>
или <button>
. Что делать, если вы не уверены, какой элемент использовать в определённых ситуациях:
- Если при клике открывается другая страница или изменяется большая часть контента на странице — используйте ссылку (
<a href=”some_url”>…</a>
). - В остальных случаях используйте обычную кнопку (
<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;
}
В итоге мы получаем кнопки, которые выглядят как обычный текст.
Минус в том, что при таком подходе необходимо стилизовать все кнопки, или пользователь не распознает их (см.: возможности). Ещё один вариант — использовать этот стиль как миксин (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;
}
Вот что у нас получилось:
Вам, возможно, интересно, почему контраст так важен. Ведь кнопки на второй строке выглядят красиво: кому не нравятся пастельные цвета?
Проще говоря: с хорошим контрастом, вы охватите больше пользователей. Некоторые пользователи — слабовидящие. Другие могут просматривать ваш сайт с телефона, на ходу, а под дневным светом труднее читать. Вы можете проверить коэффициент контрастности и почитать о контрастности текста.
Стилизация активного, ховер- и фокус-состояния
Круто, когда ваша кнопка выглядит красиво, но… пользователи будут взаимодействовать с ней, и поэтому требуется визуальный отклик, когда состояние кнопки изменяется.
У браузеров свои стили по-умолчанию для «активного» (т.е. нажатого) и «фокусного» состояния, но, сбросив стили у кнопки, мы избавились от некоторых из них. Не хватает ещё стилей при наведении мышкой, да и в целом хочется, чтобы все стили были видны и соответствовали нашему дизайну.
Начнём со стилей для состояния :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 для навигации между кнопками:
Разбираемся с прилипшим фокусом
Есть небольшой недочёт. В некоторых браузерах, если кликнуть по ссылке или кнопке, применяются два псевдокласса:
- :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 не смог загрузится), то стили не применятся.
Окончательный вариант:
Окончательный вариант можно посмотреть на сайте Флоренса Вёршелда.