Считайте каждое состояние полной цветовой парой
Начинайте с семантических ролей состояний, а не с одного фирменного образца. У заполненной основной кнопки есть надпись, заливка, возможная граница, значок и окружающая поверхность. Hover и active меняют заливку, а focus добавляет индикатор, соприкасающийся и с контролом, и со страницей. Вторичная или контурная кнопка создаёт другой набор соседних цветов. Запишите каждый передний план, фон и границу. Нельзя заключать, что более тёмный hover проходит только потому, что прошёл default: отношение текста, видимость границы и фокуса пересчитывают для реально отрисованного состояния.
- Перечислите default, hover, active, focus, disabled, loading и selected, которые компонент действительно отображает.
- Для каждого варианта запишите надпись, значок, заливку, границу, индикатор фокуса и окружающую поверхность.
- Считайте primary, secondary, destructive и quiet отдельными контрактами, а не перекрашенным образцом.
- Сохраняйте нативную семантику button, чтобы клавиатура и доступное имя не зависели от палитры.
Измеряйте текст, границу и фокус по их назначению
Текст надписи сравнивают с отрисованной заливкой кнопки. Критерий WCAG 2.2 1.4.3 обычно требует не менее 4,5:1 для обычного текста и 3:1 для крупного согласно определениям и исключениям критерия. Нетекстовый контраст относится к визуальной информации, необходимой для распознавания компонентов и состояний; если эту функцию выполняет заливка или граница, её проверяют относительно соседних цветов по 1.4.11. У индикатора фокуса отдельные требования, и он должен восприниматься в focused-состоянии. Одно отношение не подтверждает все задачи: в каждом сравнении участвуют другие пиксели и смысл.
| Часть | С чем сравнить | Вопрос |
|---|---|---|
| Надпись или значок | Отрисованная заливка | Содержание читается? |
| Существенная граница | Соседняя поверхность | Контрол распознаётся? |
| Индикатор фокуса | Цвета соприкосновения | Клавиатурный фокус виден? |
| Смена состояния | Предыдущее и текущее оформление | Обратная связь воспринимается? |
Реализуйте состояния без потери фокуса
В примере цвета заданы явно, чтобы каждую пару можно было измерить. Это иллюстрация, а не универсально соответствующая палитра: свойства шрифта, окружающие поверхности, рендеринг браузера и варианты компонента всё равно тестируют. :focus-visible показывает заметный клавиатурный фокус в поддерживающих браузерах, а политика совместимости должна сохранять подходящий фолбэк. Outline смещён от границы, чтобы не сливаться с ней, но в плотном макете он может касаться нескольких фонов. Никогда не удаляйте пользовательский outline без замены, не менее заметной во всех состояниях и условиях обрезки.
.button {
color: #ffffff;
background: #4338ca;
border: 2px solid #4338ca;
}
.button:hover { background: #3730a3; }
.button:active { background: #312e81; }
.button:focus-visible {
outline: 3px solid #6d28d9;
outline-offset: 3px;
}
.button[disabled] {
color: #64748b;
background: #e2e8f0;
border-color: #cbd5e1;
}Проектируйте hover, active, loading и disabled осознанно
Hover — обратная связь для указателя, он не заменяет клавиатурный focus. Active или pressed может быть кратким, но надпись не должна исчезать во время активации. Loading сохраняет доступное имя и сообщает о прогрессе не только сменой цвета; появление спиннера не должно двигать макет. Для disabled существуют исключения в отдельных критериях контраста, однако чрезмерно бледная кнопка мешает понять доступные действия и причину блокировки отправки. Проверьте, действительно ли контрол нужно отключать, и покажите рядом валидацию или объяснение, если не выполнено предварительное условие.
- Пересчитывайте контраст надписи после каждой смены заливки: более тёмное не всегда лучше для любого текста.
- Оставляйте hover для указателя, а focus делайте отдельно видимым для клавиатурной навигации.
- Сочетайте loading-визуал с программным состоянием и стабильным доступным именем, а не одной анимацией цвета.
- Используйте disabled только для действительно недоступного действия и объясняйте исправимое условие рядом.
- Для selected или pressed у переключателя применяйте семантику вроде aria-pressed.
Учитывайте alpha, градиенты, значки и вложенные поверхности
У полупрозрачной заливки и границы нет единственного итогового цвета независимо от страницы. Выполните композитинг для каждой допустимой поверхности до расчёта контраста. У градиентной кнопки проверяют значимые точки под надписью и значком, а не только края градиента. Значку, передающему действие, нужны достаточная видимость и доступное имя через видимый текст или другой механизм; декоративный значок не должен создавать повторное объявление. Если кнопка находится в баннере, карточке, диалоге, поверх изображения или в disabled-контейнере, для границы и фокуса используют настоящий соседний цвет. Общий токен действителен лишь в документированных контекстах.
Перед выпуском прогоните матрицу компонентов
Отрисуйте каждый вариант кнопки на каждой поддерживаемой поверхности с реалистичными надписями, значками, переносами и локализацией. Навигируйте клавиатурой, указателем, касанием и подходящей вспомогательной технологией. Проверьте focus при обычном и увеличенном масштабе, особенно у края контейнера, где overflow обрезает outline. Включите high contrast или forced-colors и убедитесь, что нативная семантика сохраняет контролы и состояния. Автоматические проверки полезны для детерминированных пар, но градиенты, alpha, динамические фоны, анимация и обрезка требуют просмотра. Храните матрицу рядом с токенами для повторения после изменений.
- Отрисуйте существующие primary, secondary, quiet, destructive, icon-only и toggle-варианты.
- Пройдите default, hover, active, focus, loading, disabled, error-adjacent и selected.
- Проверьте все поверхности, включая карточки, диалоги, баннеры, таблицы и области поверх изображений.
- Повторяйте контраст и focus после изменений типографики, отступов, границ, теней и общих токенов.
Первоисточники
Фактические утверждения статьи проверены по этим опубликованным спецификациям.