Привет, Хабр.

Мне нравится, когда красивые и дружелюбные интерфейсы радуют пользователя. Хоть я уже не создаю их, но я стараюсь помочь коллегам создавать более качественный пользовательский опыт. И, конечно, с вами тоже всегда рад поделиться идеями.

Сегодня хочу рассказать про простые CSS-техники, которые помогут сделать любой интерфейс более дружелюбным к пользователю. Не важно, какие фреймворки и библиотеки вы используете. Мои советы универсальны и могут быть реализованы везде.

Давайте посмотрим, что я вам подготовил.

Функция image-set() поможет адаптировать фоновые изображения для экранов с разной плотностью пикселя

Мы привыкли использовать элемент picture для адаптации изображений под экраны с разной плотностью пикселя. Но почему-то мало кто так делает с декоративными изображениями. Очень зря!

Вы можете оптимизировать загрузку фоновых изображений с помощью функции image-set(). Для демонстрации сделаем это для экранов с обычной плотностью пикселя и с двойной.

.awesome-screen-hero {
  background-image: image-set(
    url("awesome-hero-background-1x.webp") type("image/webp") 1x,
    url("awesome-hero-background-2x.webp") type("image/webp") 2x
  );
  background-color: #919191;
}

Псевдо-класс ::spelling-error поможет пользователю найти опечатки в словах

Я постоянно делаю опечатки. Думаю, если вы читаете мои статьи, то заметили это. По этой причине я особенно люблю новый псевдо-класс ::spelling-error. Он позволяет выделить слово, если в нём есть опечатка.

::spelling-error {
  background-color: rgba(255, 42, 81, 0.2);
  text-decoration: underline 1px wavy rgba(255, 42, 81, 1);
  text-underline-offset: 3px;
}

Псевдо-классы :user-valid и :user-invalid помогают стилизовать элементы формы при валидации

Стилизация элементов форм при валидации — важная штука. Нужно же показывать сообщения об ошибке или выделять поля соответствующим цветом. Раньше приходилось использовать JavaScript, а теперь для этой задачи отлично подходят псевдо-классы :user-valid и :user-invalid.

input:user-invalid {
  box-shadow: 0 0 1rem red;
}

input:user-valid {
  box-shadow: 0 0 1rem green;
}

Свойство background-color не позволяет спрятаться контенту при загрузке фоновых изображений

Фоновые изображения могут очень долго загружаться. Или вообще не отобразиться. По этой причине стандартный набор стилей может привести к тому, что контент сольётся с основным фоном страницы.

В итоге пользователи получат игру, в которой нужно найти белые буквы на белом фоне.

.page {
  color: #fefefe;
}

.page__banner {
  background-repeat: no-repeat;
  background-size: cover;  
  background-image: url("awesome-hero-background.jpg");
}

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

.page {
  color: #fefefe;
}

.page__banner {
  background-repeat: no-repeat;
  background-size: cover;  
  background-image: url("awesome-hero-background.jpg");
  background-color: #222;
}

Анимации без необязательной перерисовки можно сделать с помощью свойств трансформации

Анимации могут привести к проблемам в производительности, если вы используете свойства, вызывающие перерисовку элемента. Например, так произойдёт со свойством left.

.awesome-block {
  width: 2rem;
  height: 2rem;
  background-color: lightblue;
  position: absolute;
  animation: move 5s infinite alternate;
}

@keyframes move {
  0% {
    left: 0;
  }

  100% {
    left: 2rem;
  }
}

Это отлично видно, если включить в инструментах разработчика функцию «Paint flashing». Это поможет нам заметить, что элемент постоянно отображается зелёным, т.е. перерисовывается.

Хорошо, что эту ошибку можно исправить. Достаточно использовать свойства трансформации, такие как transformtranslatescale и rotate. Браузеры не перерисовывают их при анимации.

В нашем примере отлично подойдёт свойство translate.

.awesome-block {
  width: 2rem;
  height: 2rem;
  background-color: lightblue;
  position: absolute;
  animation: move 5s infinite alternate;
}

@keyframes move {
  0% {
    translate: 0;
  }

  100% {
    translate: 2rem;
  }
}

Медиа-функция prefers-reduced-motion спасает пользователей от неожиданной тошноты и головной боли

Многие любят плавную прокрутку, и хорошо, что её реализуют с помощью свойства scroll-behavior.

html {
  scroll-behavior: smooth;
}

Я уверен, что вы встречали такую реализацию. Только есть нюанс. В данном решении плавная прокрутка будет всегда, даже если пользователи её отключили в операционной системе.

Давайте уважать их выбор. Добавим плавную прокрутку так, чтобы она работала только тогда, когда она не отключена в настройках операционной системы. Для этого достаточно объявить медиа-функцию prefers-reduced-motion со значением no-preference.

@media (prefers-reduced-motion: no-preference) {

  html {
    scroll-behavior: smooth;
  }
}

Также медиа-функция будет полезна, если вы делаете анимацию приближения и отдаления элемента с помощью свойства scale или свойства transform с функцией scale().

Ключевое слово system-ui делает альтернативный шрифт красивым

Многие привыкли указывать семейство sans-serif в качестве альтернативного шрифта.

body {
  font-family: 'Roboto', sans-serif; 
}

В итоге пользователи операционной системы Windows видят известный мемный шрифт Times New Roman. Не мне рассказывать, насколько он страшный.

Уже давно в операционных системах есть более красивые шрифты. Давайте их использовать. Для этого объявите ключевое слово system-ui. Тогда браузер будет использовать красивый шрифт из операционной системы, если по какой-то причине основной не загрузится.

body {
  font-family: 'Roboto', system-ui; 
}

Привычка дублировать стили при наведении для сфокусированного состояния мешает пользователям клавиатуры

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

a:hover,
a:focus-visible {
  background-color: #fed330;
}

/*
или так 

a:is(:hover, :focus-visible) {
  background-color: #fed330;
}
*/

Пожалуйста, не надо так делать. Я как пользователь клавиатуры могу сказать, что, когда вы используете стили при наведении для сфокусированного состояния, их очень часто сложно заметить. Также такой подход приводит к тому, что постоянно нужно думать: «А почему ссылка поменяла фон?»

Лучше добавьте обводку с помощью свойства outline, которая будет для всех интерактивных элементов одинаковой. Также используйте свойство outline-offset. Оно не даст обводке слиться с фоном элемента.

a:hover {
  background-color: #fed330;
}

a:focus-visible {
  outline: 2px solid #fed330;
  outline-offset: 3px;
}

Медиа-функция forced-colors помогает правильно использовать свойство outline со значением none

Буду честен. Обводка из предыдущего способа может не понравится дизайнеру или менеджменту. Скорее всего, вас заставят сделать нестандартную обводку с помощью свойства box-shadow. В итоге вы добавите свойство outline со значением none.

a:focus-visible {
  outline: none;
  box-shadow: 0 0 2px 3px lightblue;
}

Можно спорить с ними, что такой способ не очень хорош. Я на вашей стороне. Да, обводка не отобразится в специальных режимах операционных систем, используемых людьми с особенностями зрения. Это, безусловно, плохо.

Поэтому давайте поступим хитрее. Мы отключим обводку, созданную свойством outline, для дизайнера, а для специальных режимов добавим её с помощью медиа-функции forced-colors. Так мы угодим коллегам и пользователям.

a:focus-visible {
  outline: none;
  box-shadow: 0 0 2px 3px lightblue;
}

@media (forced-colors: active) {
  a:focus-visible {
    outline: 2px solid #fed330;
    outline-offset: 3px;
  }
}

Заключение

Давайте подведём итог. В этой статье мы рассмотрели:

  • дублирование стилей при наведении для сфокусирования состояния не заменяет стандартную обводку, созданную с помощью свойства outline;

  • правильное использование свойства outline со значением noneбез создания проблем длядля пользователей специальных режимов высокой контрастности;

  • функцию image-set, позволяющая загружать специальные изображения для экранов с разной плотностью пикселей;

  • добавление свойства background-color к элементу с фоновым изображением;

  • почему свойства трансформации transformtranslatescale и rotate лучше подходят для создания анимации;

  • псевдо-класс ::spelling-error, помогающий подсветить слово с опечаткой;

  • использование свойства scroll-behavior без создания неприятных сюрпризов для пользователей;

  • стилизацию элементов форм при валидации с помощью псевдо-классов :user-valid и :user-invalid;

  • ключевое слово system-ui позволяющее использовать стандартный шрифт из операционной системы в качестве альтернативы к основному.

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

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

На этом всё. Спасибо за чтение!

P. S. Помогаю больше узнать про CSS и дружелюбные интерфейсы в своих закрытых ТГ-каналах CSS isn't magic и UX + Dev = a11y. Присоединяйтесь. Как вступить, написано в профиле.

© 2026 ООО «МТ ФИНАНС»