Кнопка — элемент, который, кажется, разработчики научились оформлять ещё лет десять назад. Поправил отступы, цвет фона, ховер-состояние, и готово. На практике этого мало: на тач-устройстве кнопка дёргается при двойном тапе, на iOS выделяется текст, после клика в Chrome остаётся неаккуратный outline, у системного <input type="file"> вообще нельзя поменять стиль кнопки... — и каждая такая мелочь портит впечатление.
Соберём пять небольших CSS-настроек, которые закрывают эти раздражители и делают кнопку приятнее в реальной жизни — на телефоне, на ноуте с трекпадом и для пользователей, которые ходят по сайту с клавиатуры.
Тап без задержки и без зум-жеста
На тач-устройствах браузер по умолчанию ждёт около 300 мс после первого касания, проверяя, не последует ли второй тап для зума. Этот резерв ощущается как «залипание» кнопки: ткнули — и ничего не происходит, потом происходит. Свойство touch-action: manipulation разрешает браузеру обрабатывать только базовые жесты (скролл и pinch-зум) и сразу отдавать клик, без ожидания второго тапа.
.button {
touch-action: manipulation;
}
Кнопка станет отзывчивее, а пользователю не нужно отказываться от системного зума на странице. Универсальный приём для любых интерактивных элементов, по которым предполагается одиночный тап — кнопок, табов, ссылок-кнопок.
Не выделяем текст по случайности
Когда пользователь быстро двойным тапом нажимает кнопку (например, нетерпеливо отправляет форму), мобильный Safari может посчитать второй тап жестом выделения и подсветить текст внутри. У обычного <button> такого поведения нет, но у кнопкоподобной ссылки (<a class="button">) или у кастомных компонентов на <div> — запросто.
.button {
user-select: none;
}
Один нюанс: Safari исторически требовал префикс -webkit-user-select, и хотя современные версии понимают и стандартное свойство, для подстраховки удобно писать обе формы:
.button {
-webkit-user-select: none;
user-select: none;
}
Применять только к интерактивным элементам, не к контентным абзацам — иначе пользователь не сможет скопировать текст.
Стилизуем кнопку у <input type="file">
Системная кнопка «Выбрать файл» долгое время была одним из самых раздражающих элементов в вебе: внешний вид прибит к движку браузера, а заменить её обычно предлагали через костыль с label и спрятанным инпутом. Теперь есть прямой способ — псевдоэлемент ::file-selector-button, который позволяет применить любые свои стили к кнопке внутри файлового инпута.
.upload-input::file-selector-button {
padding: 8px 16px;
border: none;
border-radius: 8px;
background: #4f46e5;
color: #fff;
font-weight: 600;
cursor: pointer;
margin-right: 12px;
}
Удобный паттерн — написать одно правило сразу для обычной кнопки и для системной, чтобы не дублировать стили:
.button,
.upload-input::file-selector-button {
/* единое оформление кнопки */
}
Аккуратный фокус для клавиатурных пользователей
Самый частый запрос на ревью — «убери, пожалуйста, эту синюю обводку, которая появляется после клика». И самый частый ответ из стиля 2010-х — outline: none. Это плохо: outline — единственный визуальный сигнал для тех, кто навигирует с клавиатуры, и его потеря ломает доступность сайта.
Решение — псевдокласс :focus-visible. Он применяет стиль только тогда, когда браузер считает, что фокус «заслуживает» визуальной подсказки: при навигации с клавиатуры (Tab, стрелки), но не при обычном клике мышью или тапе. То есть outline появится у клавиатурного пользователя и не будет мозолить глаза тому, кто пришёл с трекпада.
.button:focus-visible {
outline: 2px solid #4f46e5;
outline-offset: 2px;
}
Свойство outline-offset добавляет зазор между обводкой и границей элемента — полезно, когда у кнопки скруглённые углы или своя рамка, и обводка вплотную смотрится грязно.
Чтобы стало ещё яснее: разница между :focus и :focus-visible — не во внешнем виде, а в условии срабатывания. :focus ловит факт фокуса как такового, независимо от способа. :focus-visible опирается на эвристику движка «показывать ли подсказку этому конкретному пользователю». На практике достаточно использовать только :focus-visible — обнулять :focus отдельно не нужно.
Логический размер вместо width
Привычное width: fit-content — нормально, пока сайт работает только в горизонтальном письме слева направо. Если завтра потребуется поддержка арабского, иврита или вертикальной восточноазиатской типографики, привязка к физической оси width создаст проблемы: при writing-mode: vertical-rl ширина становится высотой, и стили начинают вести себя неожиданно.
CSS-логические свойства решают это сразу:
.button {
inline-size: fit-content;
}
inline-size — это размер вдоль оси текста, без привязки к экранной ширине. Для обычной горизонтальной верстки результат идентичен width, для вертикальной или RTL — правильно адаптируется автоматически. Подробный разбор самих ключевых слов fit-content, min-content и max-content — в отдельной статье про размеры по содержимому.
Всё вместе
Соберём пять приёмов в одну компактную декларацию, которую можно применять как базовый стиль для любых кнопок и кнопкоподобных ссылок на сайте:
.button,
.upload-input::file-selector-button {
inline-size: fit-content;
padding: 8px 16px;
border: none;
border-radius: 8px;
background: #4f46e5;
color: #fff;
font-weight: 600;
cursor: pointer;
-webkit-user-select: none;
user-select: none;
touch-action: manipulation;
}
.button:focus-visible,
.upload-input::file-selector-button:focus-visible {
outline: 2px solid #4f46e5;
outline-offset: 2px;
}
Полный интерактивный пример с кнопкой и файловым инпутом:
Итог
Базовое оформление кнопки — это не только padding и фон. Пять небольших настроек — touch-action: manipulation, user-select: none, ::file-selector-button, :focus-visible с outline-offset и inline-size: fit-content — убирают мелкие, но регулярные раздражители и делают кнопку дружелюбнее на всех платформах и для всех способов навигации. Все они не требуют JS, не плохо ложатся на существующие стили и не ломают совместимость со старыми браузерами — стоят того, чтобы добавить их в базовый стиль кнопок один раз и больше не возвращаться.
Комментарии (0)