Контраст, анимация и размер кнопок: визуальная доступность сайта
Требования к контрасту, информация не только цветом, режим высокой контрастности, prefers-reduced-motion, размер целей нажатия и масштабирование

Часть 5 из 8 серии «Доступный веб на практике». Предыдущая часть: Клавиатура и фокус: как сделать сайт, которым можно пользоваться без мыши.
Низкий контраст текста — самая частая проблема в интернете: она есть на 83,9% популярных сайтов. В этой части — всё визуальное: контраст и цвет, режимы отображения, анимация, размер целей нажатия и масштабирование.
Цвет, контраст и режимы отображения
Требования к контрасту
| Что | Минимум (AA) | Критерий |
|---|---|---|
| Обычный текст | 4,5 : 1 | 1.4.3 |
| Крупный текст (от 24 px, или от ≈18,7 px жирным) | 3 : 1 | 1.4.3 |
| Компоненты интерфейса: границы полей, иконки, индикатор фокуса, состояния | 3 : 1 | 1.4.11 |
| Значимые части графики (линии графика, сегменты диаграммы) | 3 : 1 | 1.4.11 |
Требование не распространяется на неактивные (disabled) элементы, логотипы и чисто декоративные элементы.
Типичные проблемы:
- светло-серый текст
#999на белом: всего ≈2,8 : 1; - placeholder и подсказки слишком бледные;
- белый текст на ярком фирменном цвете;
- текст поверх фотографии без подложки;
- граница поля ввода
#dddна белом — поле «не видно» слабовидящему.
Проверить контраст можно прямо в DevTools: в Chrome при выборе цвета в панели Styles показывается коэффициент контраста и отметки AA/AAA. Есть и отдельные инструменты — например, WebAIM Contrast Checker.
Если цвета задаются в дизайн-системе, сразу заложите пары «фон — текст», проверенные на контраст, и запретите остальные комбинации.
Не только цветом
Критерий 1.4.1: цвет не должен быть единственным способом передать информацию. Около 8% мужчин имеют ту или иную форму нарушения цветовосприятия, а на солнце или на плохом мониторе цвета различает хуже любой.
<!-- ❌ Ошибка обозначена только красной рамкой -->
<input class="border-red">
<!-- ✅ Цвет + иконка + текст -->
<input aria-invalid="true" aria-describedby="err">
<p id="err" class="error"><svg aria-hidden="true">...</svg> Укажите дату в будущем</p>
То же со ссылками в тексте: если ссылка отличается от текста только цветом, контраст между ними должен быть не ниже 3 : 1 и нужен дополнительный признак при наведении и фокусе. Проще всего — оставить подчёркивание.
/* Ссылки в тексте статьи — с подчёркиванием */
.prose a {
text-decoration: underline;
text-underline-offset: 0.15em;
}
В графиках различайте линии не только цветом, но и маркерами, штриховкой или подписями прямо у линий.
Режим высокой контрастности и тёмная тема
В Windows есть режим контрастных тем (forced colors): браузер заменяет все цвета на системную палитру пользователя. Что в нём ломается:
- иконки, нарисованные фоном или
background-image, исчезают; - границы, нарисованные тенью, пропадают;
- фокус через
box-shadowне виден (решение — прозрачныйoutline, см. Клавиатура и фокус: как сделать сайт, которым можно пользоваться без мыши).
@media (forced-colors: active) {
.icon {
/* SVG с currentColor адаптируется сам; для масок — системный цвет */
background-color: CanvasText;
}
.card {
border: 1px solid CanvasText; /* вместо тени */
}
}
Проверить можно в Chrome DevTools: Rendering → Emulate CSS media feature forced-colors.
Тёмная тема через prefers-color-scheme тоже требует проверки контраста — пары цветов в ней свои.
Движение и анимация
Параллакс, масштабирование при прокрутке, большие сдвиги экрана вызывают у людей с вестибулярными нарушениями головокружение и тошноту. У людей с СДВГ бесконечная анимация мешает читать. В операционных системах есть настройка «Уменьшить движение», и браузер передаёт её через медиазапрос prefers-reduced-motion.
/* Базовый подход: анимации включаем только тем, кто не просил их убрать */
@media (prefers-reduced-motion: no-preference) {
.hero {
animation: slide-in 0.6s ease-out;
}
html {
scroll-behavior: smooth;
}
}
Или наоборот — глобальная страховка:
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
В JavaScript (например, для библиотек анимации):
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
element.animate(
[{ transform: 'translateY(20px)', opacity: 0 }, { transform: 'none', opacity: 1 }],
{ duration: reduceMotion ? 0 : 400, easing: 'ease-out', fill: 'forwards' }
);
«Уменьшить движение» не значит «убрать всё»: плавное появление прозрачностью обычно допустимо, проблемы вызывают движение, масштаб и вращение.
Ещё два правила:
- Всё, что движется дольше 5 секунд (карусели, бегущие строки, фоновое видео), должно иметь кнопку паузы (критерий 2.2.2).
- Ничего не мигает чаще трёх раз в секунду (2.3.1) — это может вызвать эпилептический приступ.
Размер целей, жесты и масштабирование
Размер цели нажатия
Критерий 2.5.8 из WCAG 2.2 (уровень AA): интерактивная цель — не меньше 24×24 CSS-пикселя, либо вокруг маленькой цели должно быть достаточно свободного места, чтобы круг диаметром 24 px с центром на ней не пересекался с соседними целями. Для мобильных интерфейсов разумнее ориентироваться на рекомендации платформ: Apple — 44×44 pt, Material Design — 48×48 dp. Уровень AAA WCAG (2.5.5) — 44×44 CSS-пикселя.
Увеличить зону нажатия можно, не меняя внешний вид:
/* Иконка 16px, кликабельная область 44px */
.icon-button {
display: inline-grid;
place-items: center;
min-width: 44px;
min-height: 44px;
padding: 0;
}
/* Или расширить зону псевдоэлементом */
.small-link {
position: relative;
}
.small-link::after {
content: "";
position: absolute;
inset: -10px;
}
Жесты и перетаскивание
- Сложные жесты (щипок, свайп двумя пальцами, рисование траектории) должны иметь альтернативу одиночным нажатием (2.5.1). Карта масштабируется щипком — значит, нужны кнопки «+» и «−».
- Перетаскивание (2.5.7, новое в 2.2): сортировка списка drag-and-drop, ползунки, загрузка файлов перетаскиванием — у всего этого должна быть альтернатива. Для сортировки — кнопки «Выше» / «Ниже» или меню «Переместить», для загрузки — обычная кнопка выбора файла.
- Срабатывание по отпусканию (2.5.2): действие выполняется при отпускании (
click,pointerup), а не при касании (pointerdown) — так человек может «передумать», уведя палец.
Масштабирование и перекомпоновка
-
Не запрещайте масштабирование:
<!-- ❌ --> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <!-- ✅ --> <meta name="viewport" content="width=device-width, initial-scale=1"> -
Перекомпоновка (1.4.10): при ширине 320 CSS-пикселей (это масштаб 400% на экране 1280 px) контент должен читаться без горизонтальной прокрутки. Адаптивная вёрстка обычно решает это сама — проверьте, уменьшив окно или увеличив масштаб.
-
Размеры шрифта — в
rem, а не вpx, чтобы учитывалась настройка размера шрифта в браузере. -
Межстрочные интервалы (1.4.12): вёрстка не должна ломаться, если пользователь увеличит межстрочный интервал до 1,5, межбуквенный — до 0,12 em, расстояние между словами — до 0,16 em. Главный враг — блоки с фиксированной высотой (
height: 40px) вместоmin-height.
Серия «Доступный веб на практике»
- Доступность сайта: для кого она на самом деле и с чего начать
- Семантический HTML и альтернативный текст: фундамент доступного сайта
- Доступные формы: подписи, подсказки, ошибки и автозаполнение
- Клавиатура и фокус: как сделать сайт, которым можно пользоваться без мыши
- Контраст, анимация и размер кнопок: визуальная доступность сайта — вы здесь
- ARIA и динамический контент: когда атрибуты помогают, а когда вредят
- Доступность в React: id, фокус при смене маршрута и объявления
- Как тестировать доступность: автоматика, ручная проверка и чеклист
Следующая часть: ARIA и динамический контент: когда атрибуты помогают, а когда вредят
Agama Labs — разработка сайтов и веб-приложений. Если хотите проверить доступность своего сайта, напишите нам: hello@agamalabs.com


