Как сверстать кастомный чекбокс без JavaScript с кастомным стилем и кликом по тексту
Что проверяют
Знаете ли вы связку label и input и умеете ли стилизовать состояние через CSS, не ломая доступность. Следом обычно спрашивают, как пользователь с клавиатурой или скринридером поймёт, что чекбокс отмечен.
Как отвечать
Основа: настоящий <input type="checkbox"> внутри <label> или связанный с ним через for и id. Клик по тексту переключает чекбокс штатно, без единой строки JS, а значение уходит вместе с формой.
Дальше два современных пути:
appearance: noneна самом инпуте: убирает системную отрисовку, но оставляет элемент в дереве доступности и в порядке табуляции. Размер, рамку и фон задаёте прямо ему, галочку удобнее всего рисовать черезbackground-imageс SVG;- визуально скрытый инпут и соседний элемент: инпут прячется классом вроде
.visually-hidden(position: absolute, размер 1px,clip-path: inset(50%)), а рядом стоитspan, который стилизуется черезinput:checked + span.
Состояния, которые стоит показать: :checked, :disabled и :focus-visible для заметной обводки при навигации с клавиатуры. Если стиль нужен родителю, есть label:has(input:checked): селектор :has() сейчас поддерживают все основные браузеры. Для простой смены цвета достаточно accent-color, и это стоит назвать первым, прежде чем строить свой чекбокс.
Размер лучше задавать в em, чтобы чекбокс рос вместе с текстом и настройками шрифта пользователя.
Где спотыкаются
Прячут инпут через display: none или visibility: hidden. Мышью всё работает, но элемент выпадает из табуляции и из дерева доступности: с клавиатуры чекбокс не отметить, скринридер его не видит.
Делают чекбокс из div и span без инпута. Получается картинка, которая не отправляется с формой, не фокусируется и не сообщает своё состояние вспомогательным технологиям.
Забывают про режим высокой контрастности Windows: при appearance: none нарисованные фоном рамка и галочка могут пропасть. Правила внутри @media (forced-colors: active) с рамкой системным цветом CanvasText возвращают видимость.