Приходилось ли верстать кастомные чекбоксы и радиокнопки
Что проверяют
Умеете ли вы стилизовать элемент формы, не сломав его для клавиатуры и скринридера. Тема маленькая, но по ней сразу видно, думаете ли вы о доступности и поведении формы, а не только о картинке из макета.
Как отвечать
Есть два рабочих подхода. Первый: стилизовать сам input через appearance: none и рисовать отметку псевдоэлементом. Второй: оставить нативный input в потоке, визуально скрыть его и рисовать соседний элемент, связав подпись через label.
.checkbox input {
appearance: none;
width: 1.25rem;
height: 1.25rem;
border: 2px solid currentColor;
border-radius: 4px;
}
.checkbox input:checked { background: currentColor; }
.checkbox input:focus-visible { outline: 2px solid; outline-offset: 2px; }Проговорите набор состояний: отмечен, в фокусе, отключён, неопределённое состояние у чекбокса, наведение, а также режим высокой контрастности, где фон может не примениться и отметку нужно рисовать границей или символом. Для радиокнопок добавьте, что стрелками внутри группы переключается выбор, и это поведение даёт браузер, если у всех полей одинаковый name.
Если по макету меняется только цвет отметки, вся задача закрывается свойством accent-color без собственной разметки. Сказать об этом полезно: часть требований решается одной строкой, и понимание этого ценится выше умения написать лишний компонент.
Где спотыкаются
Скрывают поле через display: none или visibility: hidden. Элемент пропадает из дерева доступности и перестаёт получать фокус: мышью по подписи кликнуть ещё можно, клавиатурой добраться нельзя. Скрывают иначе: нулевая прозрачность с абсолютным позиционированием либо класс визуального скрытия.
Теряют фокус. Родные стили браузера убрали вместе с контуром, свой не нарисовали, и пользователь клавиатуры перестаёт понимать, где находится. Нужен явный стиль на :focus-visible, причём заметный на фоне и в тёмной теме.
Строят переключение на скрипте: клик по обычному блоку, состояние в классе. Тогда форма не отправляет значение, required не работает, автозаполнение и восстановление состояния при возврате назад тоже. Состояние всегда держит input, скрипт максимум реагирует на событие change.