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

Недавно у меня появилась мысль поделиться распространёнными HTML- и CSS-ошибками, которые я вижу у коллег. Только мне хотелось выглядеть убедительно, чтобы не было вкусовщины. И тут я сильно задумался.

На HTML и CSS очень сложно сделать критическую ошибку. Чтобы интерфейс не заработал. По этой причине сложно говорить об ошибках. Очень легко скатиться в субъективщину. Но всё же я собрал список.

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

Объявление свойства scroll-behavior со значением smooth без медиа-функции prefers-reduced-motion

Когда меня просят провести аудит проекта, сначала я всегда проверяю один момент. Смотрю какие стили для элемента <html> в инструментах разработчика. Классическая история — объявлено свойство scroll-behavior со значением smooth.

html {
  scroll-behavior: smooth;
}

Круто, что вы используете CSS для плавной прокрутки, а не городите код на JavaScript. Только она будет работать для всех пользователей. Даже если они не хотят её видеть.

В операционных системах есть настройки, позволяющие отключить анимацию в интерфейсах. Это критически важная штука. Есть люди, у которых анимация движения вызывает тошноту, головокружение и головную боль. Они увидят плавную прокрутку страницы, и их укачает. И это не самый худший вариант.

В общем, есть правило. Нельзя использовать свойство scroll-behavior со значением smooth для всех пользователей. Как вариант решения проблемы, используйте медиа-функцию prefers-reduced-motion.

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

  html {
    scroll-behavior: smooth;
  }
}

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

Кнопка «Закрыть» находится перед основным содержимым модального окна

К моему сожалению, многие специалисты не задумываются о порядке элементов в разметке. Казалось бы, какая разница? Браузерам же всё равно.

Да, это так. Порядок элементов мало влияет на отображаемый результат, потому что CSS всегда сделает так, как мы хотим. Только есть другие технологии, при работе с которыми важно учесть порядок. Это скринридеры.

Рассмотрим пример с модальным окном. У него будет какой-то контент (форма, текст, картинка и т. д.) и кнопка «Закрыть».

<body>
  <dialog class="modal">
    <button type="button" aria-label="Закрыть">
      <!-- здесь иконка --->
    </button>
    <!-- здесь контент модального окна -->
  </dialog>
</body>

Как думаете, где в этом коде проблема? Как только модальное окно откроется, скринридер озвучит кнопку «Закрыть» раньше, чем любой другой элемент. Почему так происходит?

Пользователи скринридера переключаются по элементам с помощью клавиш стрелок и по интерактивным элементам с помощью клавиши Tab. После того, как откроется модальное окно, он нажмёт клавишу со стрелкой вниз (↓) или Tab. Скринридер честно перенесёт пользователя на следующий элемент по разметке, а это кнопка «Закрыть».

Чтобы вы не подумали, что я придумываю, я спросил об этом у незрячих коллег. Один из них вот что сказал:

«Представь, что заходишь в магазин. Открываешь дверь и сразу видишь стрелку, показывающая на выход. Ощущение, что хотят, чтобы ты быстрее ушёл оттуда. Точно так же такое поведение интерфейса настраивает пользователя. Первая мысль, что надо закрыть. Я просто привык к такому, поэтому попробую пойти вниз по элементам, чтобы посмотреть, есть ли там что-то или нет».

Пользователям скринридеров приходится дополнительно постараться, чтобы стать клиентом бизнеса. Ведь без регистрации, а потом авторизации они не смогут воспользоваться сервисом.

Поэтому, пожалуйста, задумывайтесь о порядке элементов. Например, в случае с модальным окном достаточно сделать кнопку последним элементом.

<body>
  <dialog class="modal">
    <!-- здесь контент модального окна -->
    <button type="button" aria-label="Закрыть">
      <!-- здесь иконка --->
    </button>
  </dialog>
</body>

Значение свойства font-size для элемента <html> в пикселях

Я читаю многих авторов, пишущих о HTML и CSS. Пару недель назад в одном канале я увидел пост о единицах измерения в CSS. Читаю, читаю и тут я очень сильно удивился. Автор пишет о преимуществах единиц измерения rem и делится лайфхаком.

html {
  font-size: 16px;
}

.container {
  padding-left: 1rem;
  padding-right: 1rem;
}

.hero {
  padding-top: 2rem;
}

Я не буду вдаваться в вечный спор, какие единицы измерения лучше — px или rem. Проблема в другом. Если вы выбрали использовать единицы измерения rem, то сохраните их преимущества.

Возможно, вам кажется удобным переопределить значение свойства font-size в одном месте и при разных разрешениях экрана его менять, но у этой идеи есть несколько проблем.

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

Если вы используете единицы измерения px для элемента html, вы отключаете пользователю возможность увеличить размер текста с помощью настроек браузера.

Лучше вместо пикселей использовать единицы измерения %, em или rem. Правда, я не использую такое решение. Мой подход — не устанавливать значение для свойства font-size.

.container {
  padding-left: 1rem;
  padding-right: 1rem;
}

.hero {
  padding-top: 2rem;
}

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

Отключение свойства outline без создания альтернативы

Многим не нравится свойство outline. Некрасивая обводка, получается. Хорошо, что сейчас современные браузеры используют псевдо-класс focus-visible. Так стандартная обводка не появляется при клике, а для пользователей клавиатуры она есть.

Только что делать, если для них надо сделать красивую обводку? Конечно же, вы подумаете о свойстве box-shadow. Ваш код будет примерно таким.

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

В этом решении я убрал стандартную обводку. И это проблема. В режиме принудительных цветов многие свойства не отображаются. Свойство box-shadow в частности. В итоге пользователи будут без обводки.

Для демонстрации ошибки мы будем использовать режим высокой контрастности Windows. Его можно включить сочетанием клавиш левый Alt + левый Shift + Print Screen. Вот так в нём отображается ссылка из нашего примера.

Давайте разбираться, что можно сделать. Если всё же нужна красивая обводка, то сложно обойтись без box-shadow. Только для режима принудительных цветов нужна альтернативная обводка. Её можно добавить с помощью свойства outline. Его нужно написать так, чтобы оно применилось в принудительных цветах.

Например, я в этой задаче привык использовать медиа-функцию forced-colors.

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

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

И теперь нет никаких проблем, даже если обводка сделана с помощью свойства box-shadow. Красота!

Свойство background при стилизации активного состояния радиокнопки

В продолжение предыдущего раздела, кроме свойства box-shadow в режиме принудительных цветов изменяется свойство background. Браузеры берут значение из настройки темы операционной системы и добавляют его везде, где разработчик объявил свойство. В итоге они сливаются с фоном страницы.

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

Я подготовил пример, который запустил в режиме высокой контрастности Windows.

<body>
  <div class="uia-radio-button">
    <input id="rb-1" class="uia-toggle uia-radio-button__input" type="radio" name="radio" checked>
    <label for="rb-1" class="uia-radio-button__label">Вариант №1</label>
  </div>
  <div class="uia-radio-button">
    <input id="rb-2" class="uia-toggle uia-radio-button__input" type="radio" name="radio">
    <label for="rb-2" class="uia-radio-button__label">Вариант №2</label>
  </div>
</body>
.uia-toggle[type="radio"]::before {
  content: "";
  width: 0.5rem;
  height: 0.5rem;
  background-color: #242424;
  /* оставшиеся CSS */
}

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

Нам остаётся реализовать точку, используя свойство border. Для этого нужно убрать свойства width, height и background-color.

.uia-toggle[type="radio"]::before {
  content: "";
  border: 0.25rem solid #242424;
  /* оставшиеся CSS */
}

Заключение

Давайте подведём итог. В этой статье я рассмотрел следующие ошибки:

  • Использование свойства scroll-behavior со значением smooth без медиа-функции prefers-reduced-motion.

  • Положение кнопки «Закрыть» в разметке модального окна, влияющее на опыт пользователей скринридера.

  • Решение устанавливать единицы измерения px для элемента <html>.

  • Удаление обводки вокруг интерактивного элемента с помощью свойства outline без создания альтернативы для режима принудительных цветов.

  • Стилизацию нестандартных радиокнопок с использованием свойства background.

Надеюсь, вы не будете делать такие ошибки. Ещё хочу попросить вас поделиться ошибками, которые вы считаете критическими. Буду ждать их в комментариях.

На этом я прощаюсь. Спасибо за чтение!

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

Комментарии (2)


  1. dom1n1k
    22.07.2025 16:10

    Рисовать точку бордером – некрасивый костыль.
    Кроме того, если на сайте кастомные радио-кнопки, то и наверняка и чекбоксы тоже.
    Поэтому логичнее выглядит обернуть стилизацию в медиа-запрос not forced-colors и рисовать там как удобно. А вне запроса – просто ничего не делать.


  1. niktariy
    22.07.2025 16:10

    Хорошие пункты и не банальные! Я бы ещё сказала про динамический контент, например, при открытии модальных окон, переключении вкладок или отображения уведомлений - важно правильно управлять фокусом и использовать ARIA-атрибуты, такие как aria-live, для оповещения об изменениях в интерфейсе.

    Отдельно хочу подчеркнуть необходимость реализации фокус-трапа в модальных окнах. Несмотря на очевидность, многие разработчики всё ещё упускают этот момент, что негативно сказывается на доступности.

    Для демонстрации ошибки мы будем использовать режим высокой контрастности Windows. Его можно включить сочетанием клавиш левый Alt + левый Shift + Print Screen.

    Стоит добавить, что в браузерах на базе Chromium (Chrome, Edge и др.) есть возможность эмулировать режим принудительных цветов прямо в DevTools:
    Открываем DevTools (F12), нажимаем Ctrl + Shift + P, вводим Rendering, находим и активируем опцию Emulate CSS media feature forced-colors. Там ещё много других параметров, которые позволяют тестировать доступность без необходимости менять настройки ОС.