
Привет, Хабр!
Недавно у меня появилась мысль поделиться распространёнными 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
.
Надеюсь, вы не будете делать такие ошибки. Ещё хочу попросить вас поделиться ошибками, которые вы считаете критическими. Буду ждать их в комментариях.
На этом я прощаюсь. Спасибо за чтение!
P. S. Помогаю больше узнать про CSS в своём ТГ-канале CSS isn't magic. Присоединяйтесь. Ссылка в профиле.
© 2025 ООО «МТ ФИНАНС»
Комментарии (32)
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. Там ещё много других параметров, которые позволяют тестировать доступность без необходимости менять настройки ОС.
vanxant
22.07.2025 16:10При всём уважении к тем, кто вынужден этими в буквальном смысле костылями пользоваться...
По мне, так большинство пунктов это перекладывание с больной головы на здоровую. Если уж у юзера стоит в настройках prefers-reduced-motion, то браузер - или даже драйвер видеокарты - должен просто тупо запрещать быстрые движения, любые. А медиа-правило нужно для реализации адекватной замены, а не для исправления багов браузера, скринридера или чего там у юзера.
И уж тем более не стОит воевать с кривыми браузерами хаками типа эмуляции бэкграунда бордером.
melnik909 Автор
22.07.2025 16:10как реализовывать ваше предложение будем? Я без сарказма. Мне интересны, конкретные шаги
vanxant
22.07.2025 16:10Я не знаю "как будем", я знаю "как есть". По факту все на пункты из вашей статьи забивают. Не знаю ни одного реального сайта за пределами домена w3.org, где это бы использовалось. Включая производителей браузеров(
alexnozer
22.07.2025 16:10Не знаю ни одного реального сайта за пределами домена w3.org, где это бы использовалось
Это, отнюдь, не значит, что их нет. Я пункты из статьи стараюсь применять на практике. В моём окружении хватает людей, которые тоже стараются их применять. Также я работаю с крупным SaaS вендором, у которого есть требования и подобные решения есть в кодовой базе их продуктов.
vanxant
22.07.2025 16:10откройте первый десяток сайтов по посещаемости в мире или России. Фейсбук с гуглом и экс-твиттером, яндекс с вк и ок. Ничего из этого там нет. 'nuff said
Да что там, откройте озон и амазон. Деньги как бы решили, что нет.
urvanov
22.07.2025 16:10По-моему, тут как раз примеры того, когда мы боремся с проблемами, которые сами же и создаём своими таблицами стилей, где меняем всё стандартное оформление зачем-то.
Foxtail25
22.07.2025 16:10Значение свойства font-size для элемента <html> в пикселях
У меня на этот счёт "особое" мнение)) Как общеизвестно:
1) В браузерах значениеfont-size
для тега HTML по умолчанию, если оно не задано, обычно равно 16 пикселям (16px).
2) Значение 1rem соответствует размеру шрифта корневого элемента.
Получается что если для корневого элемента мы поменяем значение "по умолчанию 16px", на значение 10px. То далее, нам будет удобнее использовать единицы rem. Т.е. 1.5 rem у нас будет не 24px, а 15px. Что по моему скромному мнению, довольно удобно.vanxant
22.07.2025 16:10Я долго топил за rem-ы, пока не увидел, как их реально используют. А реально их ставят на :root в долях от vw (ширины вьюпорта) и дальше у людей типа резиновый дизайн.
Нет, спасибо.
Браузеры отлично масштабируют что rem-ы, что пиксели.
mayorovp
22.07.2025 16:10Представь, что заходишь в магазин. Открываешь дверь и сразу видишь стрелку, показывающая на выход.
А что не так? Отличное решение, гораздо лучше чем альтернативное "пройди весь магазин прежде чем мы разрешим тебе выйти". Ну, для покупателя лучше.
Spaceoddity
22.07.2025 16:10Браузеры сами знают, что по умолчанию у элемента будет использоваться значение из настроек. Чаще всего это
16px
. На это я и надеюсь при написании кода."Чаще всего", "надеюсь"? Серьёзно? Вы же там что-то про дизайнеров и макеты - и тут такая лютая неопределённость... А ну как текст в контейнер не влезет? Надежды не оправдались? Базовый размер шрифта надо "гвоздями прибивать"! Ну или делать его вычисляемым из размеров вьюпорта!
melnik909 Автор
22.07.2025 16:10Не надо от вьюпорта. Зум сломается
Spaceoddity
22.07.2025 16:10Надо! "Зум" - это не дефолтное событие браузера. Все риски и связанные с этим проблемы ложатся на "зуммирующего". Или у вас, например, вся графика векторная? Как же так - а вдруг пользователь зуммировать начнёт?
А вот тот факт, что от "базового размера шрифта" могут плясать не только размеры шрифта, но и все остальные - ненавязчиво намекает, что "не единой типографикой веб жив" ;)
Если же вы хотите сделать возможность "масштабировать шрифт" - то озаботьтесь этой задачей специально, а не оставляйте её на откуп этой недокументированной "фиче" - я вот только что попробовал на этой странице "позуммировать" - в итоге Вивальди повис наглухо, благо набранный текст успел сохранить... Как видите, проблема с зуммированием вовсе не в "несовпадении масштаба текста"...
P.S. Ну и спасибо за очередной минус - всегда так "приятно" комментировать ваши статьи... Чувствуется фидбэк ;)
xl1me
22.07.2025 16:10Не надо ломать зум, пожалуйста. Я люблю зум и пользуюсь им. Благодарен тем разработчикам, которые это учитывают.
urvanov
22.07.2025 16:10При этом для того, чтобы его учитывать, ничего делать не нужно, скорее наоборот, поменьше извращаться, тогда всё зуммироваться будет нормально.
Spaceoddity
22.07.2025 16:10А чего вы мне это пишете? Шпанкову отпишите, чтобы Вивальди зум не "ломал", а заодно в W3C - чтобы сделали спецификацию для этого самого "зума". Я лично просто вообще его в учёт не принимаю.
dom1n1k
22.07.2025 16:10Ломающие зум верстальщики отправляются в один котел с фронтами, ломающими открывание ссылок в новом окне.
Spaceoddity
22.07.2025 16:10Там уже занято теми юзерами, которые про "клик колёсиком" не слышали ;)
P.S. Очень забавляют такие категоричные комментарии от юзеров, которые свою вкусовщину возводят в разряд "догм для всех". А я вот терпеть не могу 100500 открытых вкладок и никогда не использую "браузерный зум" - такое вам в голову, конечно, не приходит, да?
dom1n1k
22.07.2025 16:10Я не уловил логику: как из факта "мне не нужно X и Y" вытекает "значит X и Y можно ломать?"
Кто-то не пользуется, ну и ради бога, а другие пользуются.Spaceoddity
22.07.2025 16:10Я тоже не уловил логику: как из факта "понятия не имею что вы подразумеваете под зумом" вытекает "ты ломаешь зум"? Я лично спецом ничего не ломаю - я просто не обращаю внимания на не документированные особенности (ну только если это особо не оговаривается в ТЗ).
alexnozer
22.07.2025 16:10В этом и заключается суть универсального доступа. Дядя Тим завещал, что веб должен быть для всех. Для тех, кто не любит 100500 вкладок и не пользуется браузерным зумом, и для тех, кто им пользуется.
У меня с рождения не лучшее зрение, поэтому некоторые страницы я немного увеличиваю браузерным зумом. А ещё у людей с возрастом падает зрение, но люди всё равно продолжают пользоваться Интернетом.
Хороший интерфейс без проблем удовлетворяет и тем, и другим. И ничего особого для этого делать не надо.
Spaceoddity
22.07.2025 16:10У меня с рождения не лучшее зрение, поэтому некоторые страницы я немного увеличиваю браузерным зумом. А ещё у людей с возрастом падает зрение, но люди всё равно продолжают пользоваться Интернетом.
У меня тоже. Но этим "зумом" я пользуюсь настолько редко... Обычно какое-то изображение просто увеличить, но уж никак не зуммировать весь контент - оно же по кд будет ломать лэйаут...
Ну и для таких нюансов давно придуманы "пользовательские таблицы стилей", всякие там высококонтрастные темы и т.п. Самому можно даже написать.
sfi0zy
22.07.2025 16:10Этот персонаж сам создает проблему и героически ее решает. Он рассчитывает размер шрифта, вычитая из него размер шрифта. Естественно там все плывет не туда. В традиционных подходах такой проблемы никогда не было. Это не проблема с rem или вьюпортом, это беда с логикой в коде. Люди научились делать шлюзы модно-молодежно через clamp вместо media-запросов, а по дороге потеряли весь смысл. Чик-чик и в продакшен, как говорится.
melnik909 Автор
22.07.2025 16:10Иван, если человек заблуждается — это не повод его называть персонажем. Ваши действия показывают ваш профессионализм.
sfi0zy
22.07.2025 16:10Мое обращение адресовано не конкретному человеку, как личности, а медийной персоне, которая ломает индустрию. Это публичный образ. Там некому и не на что обижаться. Их много таких. Игнорируют предыдущий опыт, ломают работающее, делают реакции, реакции на реакции... Контент ради контента. Он имеет спорную ценность и при этом выставляется как супер-умный, на передовой нашей индустрии. Новички на это ведутся. Они учатся на проблемном коде, не осознавая, насколько там все печально, и что все эти проблемы уже имели решения.
В данном конкретном контексте ситуация вдвойне занятна, потому что все эти формулы были разобраны не где-то, а в этом же самом рецензируемом журнале, в этом же самом разделе. А теперь там вот это. Ладно бы нашли проблемы со старыми решениями, там есть, что обсудить, но нет. Просто все разломали и пилим контент. Пора бы нам перестать играть в снежинок, и признать, что у нас серьезная проблема в индустрии.
Psychosynthesis
22.07.2025 16:10Если вы используете единицы измерения
px
для элементаhtml
, вы отключаете пользователю возможность увеличить размер текста с помощью настроек браузера.Это бред. У всех браузеров есть свойство "Масштаб по умолчанию".
А плавная прокрутка это вообще срань дикая, зачем это в макеты ставят непонятно.
Со стилизацией радио норм замечание. Жаль только большая часть "верстальщиков" слишком тупа чтоб до таких нюансов вообще добраться. Я тут недавно пытался объяснить на QnA Хабра почему везде использовать :before не надо, это как горохом в стенку кидаться.
dom1n1k
22.07.2025 16:10Масштаб это масштаб, размер текста это размер текста. Вещи взаимодополняющие, но не эквивалентные.
Psychosynthesis
22.07.2025 16:10В цитате, дословно написано "исключаете возможность увеличить размер текста". Я вам буквально назвал настройку которая меняет размер текста даже если в html размер шрифта указан в пикселях.
Spaceoddity
22.07.2025 16:10Масштаб это масштаб
Ну так если вам нужен масштаб - юзайте transform: scale(). Зачем вы для этого используете недокументированные особенности разных браузеров? Которые, как оказалось из этой ветки, ещё и размеры шрифта некорректно рассчитывают...
dom1n1k
Рисовать точку бордером – некрасивый костыль.
Кроме того, если на сайте кастомные радио-кнопки, то и наверняка и чекбоксы тоже.
Поэтому логичнее выглядит обернуть стилизацию в медиа-запрос not forced-colors и рисовать там как удобно. А вне запроса – просто ничего не делать.
melnik909 Автор
Да, тоже хорошее решение