Последнее обновление: 01
Переключатели выглядят просто. Один щелчок — один выбор. Однако они также являются одним из самых простых способов снизить удобство использования и доступность. Скройте нативное поле ввода, пропустите fieldset и legendили проводить проверку слишком агрессивно, и вы отправите форму, которая будет сбивать с толку пользователей клавиатуры, раздражать программы чтения с экрана и снижать конверсию.
Это руководство покажет вам, как правильно создавать радио в 2025 году: быстро выбирать, легко просматривать, полностью доступно и измерять. Мы начнём с чистой семантической основы, а затем добавим современный CSS, чтобы радио выглядели как карточки или сегментированные элементы управления, не нарушая работу клавиатуры или программы чтения с экрана.
Вы увидите практичные шаблоны валидации, которые не вызывают нареканий, адаптивные макеты, обеспечивающие большой размер целевых страниц на мобильных устройствах, и готовые фрагменты кода, которые можно вставить в любой стек. Мы также рассмотрим аналитику, идеи A/B-анализа и настройку Formspree для мгновенного получения результатов.
Для кого это: дизайнеры, разработчики интерфейсов и маркетологи, которым важны оба подхода доступность и Преобразование. Не требуется никаких сложных фреймворков, только надежный HTML, целенаправленный CSS и немного прогрессивных улучшений.
К концу вы узнаете:
- Когда радиостанции бьют селекторы, флажки или переключатели
- Как создать доступную радиогруппу (
fieldset,legend, маркировка, фокус) - Как оформить пользовательские «карточные» радиоприемники, не скрывая вход
- Как выполнить проверку с помощью HTML Constraint API (и сообщения)
- Как подключить радио к Formspree и измерить выборки
TL, д-р
- Используйте радио именно для one выбор в рамках четко определенного набора (в идеале 2–6 вариантов) это должно быть видно сразу.
- Всегда заворачивайте радиоприемники в
<fieldset>+<legend>, дайте им то жеname, и сделать каждый вариант кликабельны<label>. - Не скрывайте входные данные с помощью
display:none; сохраните их фокусируемый (например,opacity:0; position:absolute; inset:0) так Tab/Стрелки/Пробел Работа. - Ставьте большие цели: по крайней мере 44 × 44 пикселей, С кольцо фокусировки это хорошо видно.
- Подтвердите с помощью API ограничений HTML (
required,reportValidity()), а затем улучшайте сообщения, а не наоборот. - Если у вас много вариантов или они вам незнакомы, добавьте вспомогательный текст под каждой меткой и рассмотрите поиск или группировка .
- На мобильном устройстве, в стеке один столбец; на широких экранах, 2–3 столбца через CSS-сетку.
- Мера: трек впечатления, выбор и отправкаи A/B-тестирование опционный заказ и копия.
- Для Formspree сохраните собственный POST как запасной вариант; добавить получать только как прогрессивное улучшение.
Когда использовать радио (дерево решений)
В1. Выбирает ли пользователь только один вариант?
- Нет → Используйте Флажки (0–много) или тумблер (бинарный) или переосмыслите задачу.
- Да → Перейти к вопросу 2.
В2. Должны ли опции быть видны с первого взгляда?
- Да → Используйте радио (лучше всего подходит для сканирования и быстрого выбора).
- Нет (мало места / много вариантов) → Рассмотрим выберите (раскрывающийся список). Если выбор критически важен и сравнение выгодно, отдайте предпочтение радиоприемникам и измените макет.
В3. Сколько вариантов?
- 2-6 → Радиоприемники — идеальный вариант.
- 7-10 → Радиостанции могут по-прежнему работать с группировкой или двухколонный сетку; в противном случае используйте выборку или список с возможностью поиска.
- > 10 → Используйте выбрать с помощью поиска или шаг который в первую очередь сужает набор.
В4. Являются ли эти варианты взаимоисключающими?
- Да → Радио.
- Нет / комбинации имеют смысл → Флажки.
В5. Повлечет ли выбор другие дополнительные вопросы?
- Да → Радио всё ещё в порядке. Показать продолжение условно, но сохраняйте скрытые поля вне порядка вкладок и поток SR пока не будет раскрыт.
- Нет → Простая радиогруппа.
В6. Знакомый или описательный?
- Знакомые этикетки (например, «Маленький / Средний / Большой») → достаточно коротких меток.
- Незнакомый или с высокими ставками (например, «Техническое SEO против контентного SEO») → добавить микрофотокопия (описание в одну строку) под каждой меткой.
В7. Мобильные устройства в первую очередь?
- Да (всегда) → Использовать стек из одной колонки с большим интервалом и размером текста 16–18 пикселей. Продвигайте наиболее выбираемый вариант по направлению к верху.
Практические примеры
- Ваша форма: «Какой сервис?» → Радиостанции лучше, чем Select, потому что пользователям приходится сравнивать варианты бок о бок. Оставьте 3 карточки (Линкбилдинг, SEO, Веб-разработка) с кратким подтекстом.
- Скорость доставки: 3–4 взаимоисключающие скорости → Радио (с разницей в цене в подтексте).
- Частота рассылки новостей: Ежедневно/Еженедельно/Ежемесячно → Радио.
- Интересы (можно выбрать несколько) → Не радио; используйте флажки.
Анатомия правильной радиогруппы
Скопировать-вставить семантическую базовую линию (пока без замысловатого CSS)
<form id="quote-form" action="/ru/submit" method="post">
<fieldset id="service-group">
<legend>Which service do you need?</legend>
<p id="service-hint">Pick one option that best matches your goal.</p>
<div class="field">
<label>
<input type="radio" name="service" id="svc-link" value="link-building" required>
<span>Link Building</span>
</label>
<label>
<input type="radio" name="service" id="svc-seo" value="seo">
<span>Search Engine Optimization</span>
</label>
<label>
<input type="radio" name="service" id="svc-web" value="web-dev">
<span>Web Design / Development</span>
</label>
</div>
<p id="service-error" class="error" role="alert" hidden>
Please choose a service to continue.
</p>
</fieldset>
<button type="submit">Continue</button>
</form>
Почему каждая часть важна
<fieldset>+<legend>дает группе название для программ чтения с экрана и задает контекст для всех.- Одна и та же
nameделает входы взаимоисключающими. - Clickable
<label>вокруг каждого входа обеспечивает огромную область нажатия/щелчка (44px+). requiredна первом радио позволяет браузеру автоматически проверять группу.- Вспомогательный текст (
service-hint) и расширение текст ошибки (service-error) разделены, поэтому вы можете показывать/скрывать ошибки, не перемещая элементы. - Роли ARIA не требуются. Родные радиостанции уже демонстрируют правильную семантику.
Минимальный CSS для удобства использования (нативный вид, крупные цели, четкая направленность)
/* Layout & spacing */
.field label {
display: flex;
align-items: center;
gap: .6rem;
padding: .65rem .8rem; /* big tap target */
border-radius: .6rem;
cursor: pointer;
}
/* Keyboard focus: highlight the whole label when the input is focused */
.field label:has(input:focus-visible) {
outline: 3px solid #3b82f6;
outline-offset: 2px;
}
/* Selected state (optional, native dot still shows) */
.field label:has(input:checked) {
background: #f1f5ff;
}
/* Error text */
.error { color: #b91c1c; margin-top: .5rem; font-size: .9rem; }
Наконечник:
:has()Это позволяет вам использовать «кольцо фокусировки по всей карте», не скрывая при этом фактический ввод. Если старый браузер не поддерживает эту функцию, пользователи всё равно видят встроенное кольцо фокусировки на самом радиоприемнике.
Мягкая и доступная проверка (сначала используется браузер)
Позвольте браузеру обработать часть «вы должны выбрать один», а затем при необходимости отобразите встроенную ошибку.
const form = document.getElementById('quote-form');
const group = document.getElementById('service-group');
const error = document.getEementById('service-error');
form.addEventListener('submit', (e) => {
// Trigger native validation UI (works because the first radio has `required`)
if (!form.reportValidity()) {
// Optional: surface a friendly inline error too
error.hidden = false;
group.setAttribute('aria-invalid', 'true');
e.preventDefault();
} else {
error.hidden = true;
group.removeAttribute('aria-invalid');
}
});
Поведение, которое следует ожидать (и тестировать):
- Клавиатура: Нажмите Tab один раз, чтобы войти в группу; используйте Клавиши со стрелками двигаться; Space выбирает.
- Программы чтения с экрана: Легенда читается как метка группы; каждый вариант объявляется с указанием состояния «выбрано/не выбрано».
- Сенсорный: Нажатие на метку переключает радио (благодаря переносу меток).
Индивидуально разработанные «карточные» радиоприемники (без нарушения доступности)
Скопировать-вставить HTML
<form id="plan" action="/ru/submit" method="post">
<fieldset>
<legend>Choose a plan</legend>
<p id="plan-hint">Pick one option. You can change anytime.</p>
<div class="card-group" role="group" aria-describedby="plan-hint">
<label class="card">
<input type="radio" name="plan" value="starter" required>
<span class="card-title">Starter</span>
<span class="card-sub">Good for small sites</span>
</label>
<label class="card">
<input type="radio" name="plan" value="growth">
<span class="card-title">Growth</span>
<span class="card-sub">Most popular</span>
</label>
<label class="card">
<input type="radio" name="plan" value="pro">
<span class="card-title">Pro</span>
<span class="card-sub">Advanced features</span>
</label>
</div>
<p id="plan-error" class="error" role="alert" hidden>Select one to continue.</p>
</fieldset>
</form>
Минимальный, надежный CSS
:root{
--brand:#0b5cff; --ring: rgba(11,92,255,.35);
--border:#e5e7eb; --bg:#fff; --muted:#6b7280;
--radius:14px;
}
/* Grid */
.card-group{
display:grid; gap:16px;
grid-template-columns: repeat(3, minmax(0,1fr));
}
@media (max-width:900px){ .card-group{ grid-template-columns:1fr 1fr; } }
@media (max-width:640px){ .card-group{ grid-template-columns:1fr; } }
/* Card label */
.card{
position:relative;
display:grid; place-items:center; text-align:center;
gap:6px; padding:18px;
border:1px solid var(--border); border-radius:var(--radius);
background:var(--bg);
cursor:pointer; user-select:none;
transition: box-shadow .15s, border-color .15s, transform .02s;
}
.card:hover{ box-shadow:0 8px 20px rgba(0,0,0,.06); }
/* Keep the native input focusable: no display:none */
.card input{
position:absolute; inset:0; opacity:0;
}
/* Keyboard focus ring on the whole card */
.card:has(input:focus-visible){
box-shadow:0 0 0 3px var(--ring);
border-color: var(--brand);
}
/* Selected state */
.card:has(input:checked){
border-color: var(--brand);
box-shadow:0 0 0 3px var(--ring);
}
/* Content */
.card-title{ font-weight:700; color:#111; }
.card-sub{ font-size:14px; color:var(--muted); }
/* Error text */
.error{ color:#b91c1c; margin-top:.5rem; font-size:.9rem; }
Заметки о поведении (почему эта модель работает)
- Нет
display:noneна вводе → клавиатура пользователи могут нажать Tab; Клавиши со стрелками выбор хода; Space выбирает. - Вся карта кликабельна, поскольку входные данные находятся внутри
label. :has()позволяет нам стиль фокус и проверил состояния на родительской карте без хаков ARIA.- Адаптивная сетка дает 1–3 столбца автоматически; цели касания остаются большими.
Необязательно: мягкая проверка (сначала нативная)
const form = document.getElementById('plan');
const err = document.getElementById('plan-error');
form.addEventListener('submit', (e) => {
if (!form.reportValidity()) { // uses the `required` on first radio
err.hidden = false;
e.preventDefault();
} else {
err.hidden = true;
}
});
Стратегии проверки, которые не раздражают
Отличная проверка незаметна, пока она не понадобится. Вот подход, разработанный специально для радио: быстрый, доступный и комфортный для пользователей.
Принципы
- Сначала местные. Позвольте HTML обрабатывать «один должен быть выбран».
- Отсроченные ошибки. Не кричите на пользователей, прежде чем они попытаются продолжить.
- Укажите на проблему. Сообщайте об ошибках, грамотно перемещайте фокус и делайте текст кратким.
- Ясно осознавая изменения. Как только будет сделан правильный выбор, устраните ошибку.
Базовый уровень (только нативный)
Только HTML может обеспечить соблюдение правила required на одном радио в группе.
<fieldset id="svc" aria-describedby="svc-hint">
<legend>Which service do you need?</legend>
<p id="svc-hint">Pick one option.</p>
<label>
<input type="radio" name="service" value="link" required>
<span>Link Building</span>
</label>
<label>
<input type="radio" name="service" value="seo">
<span>Search Engine Optimization</span>
</label>
<label>
<input type="radio" name="service" value="web">
<span>Web Design / Development</span>
</label>
</fieldset>
Это уже дает вам: стрелки клавиатуры, пробел для выбора и проверку на уровне браузера.
Дружественные встроенные ошибки (прогрессивное улучшение)
Отключите всплывающие окна браузера и обрабатывайте сообщения в строке, чтобы они соответствовали вашему дизайну.
HTML (добавить область ошибки)
<form id="quote" novalidate>
<!-- fieldset from above -->
<p id="svc-err" class="error" role="alert" hidden>Please choose one option.</p>
<button type="submit">Continue</button>
</form>
CSS (простой стиль)
.error { color:#b91c1c; margin-top:.5rem; font-size:.9rem; }
JS (откладывать ошибки, правильно объявлять)
const form = document.getElementById('quote');
const group = document.getElementById('svc');
const err = document.getElementById('svc-err');
const radios = form.querySelectorAll('input[name="service"]');
let attempted = false; // show errors only after user tries to continue
function hasSelection() {
return [...radios].some(r => r.checked);
}
function showError(msg='Please choose one option.') {
err.textContent = msg;
err.hidden = false;
group.setAttribute('aria-invalid', 'true');
// Append error to describedby so SRs announce it next
group.setAttribute('aria-describedby', 'svc-hint svc-err');
}
function clearError() {
err.hidden = true;
group.removeAttribute('aria-invalid');
group.setAttribute('aria-describedby', 'svc-hint');
}
form.addEventListener('submit', (e) => {
attempted = true;
if (!hasSelection()) {
e.preventDefault();
showError();
// Bring the group into view and place focus on the first radio
radios[0].focus();
group.scrollIntoView({ behavior: 'smooth', block: 'center' });
} else {
clearError();
}
});
// Don’t nag early; clear once they pick something
radios.forEach(r => {
r.addEventListener('change', () => {
if (attempted) clearError();
});
});
Почему это работает
- Нет всплывающих подсказок в браузере; пользователи видят ошибки встроенный рядом с группой.
- Читатели экрана слышат легенда, то ошибка, Благодаря
aria-describedby. - Ошибки появляются только после попытка отправки (или при возврате для исправления).
Мягкая пошаговая проверка (многошаговые формы)
Если вы выбираете кнопку «Далее», подтвердите только этот шаг.
document.getElementById('nextBtn').addEventListener('click', () => {
if (!hasSelection()) {
showError();
} else {
clearError();
// advance to next step...
}
});
Пользовательские сообщения с помощью Constraint API (необязательно)
Если вы предпочитаете сохранить модель валидности браузера, установите пользовательское сообщение на первый радио в группе.
const firstRadio = radios[0];
form.addEventListener('submit', (e) => {
if (!hasSelection()) {
firstRadio.setCustomValidity('Please choose one option.');
// Triggers native UI; or call form.reportValidity()
e.preventDefault();
form.reportValidity();
} else {
firstRadio.setCustomValidity('');
}
});
Используйте этот метод, когда вам нужен собственный «пузырь» и ваш собственный текст, в противном случае придерживайтесь метода inline, описанного выше, чтобы обеспечить единообразие пользовательского интерфейса.
Сводка ошибок (для длинных форм)
Если ваша форма длинная, добавьте верхнее резюме которая ведет к проблемной области.
<div id="error-summary" class="error" role="alert" hidden>
There’s a problem: <a href="#svc">Choose a service</a>.
</div>
function showSummary() {
const sum = document.getElementById('error-summary');
sum.hidden = false;
sum.querySelector('a').focus(); // SRs announce the alert
}
Резервные решения на стороне сервера и в сети
Проверка на стороне клиента улучшает UX, но всегда Проверьте также на сервере (или с помощью вашего сервиса отправки). Если отправка не удалась, верните страницу с:
- Предыдущий выбор сохранялся и
- The та же встроенная ошибка рядом с группой.
Интеграция Formspree (прогрессивное улучшение)
Заставьте радио работать даже при сбоях в работе JavaScript, а затем улучшите работу для более плавного выражения благодарности и лучшей аналитики.
Собственный HTML (всегда работает)
<form
id="quote"
action="https://formspree.io/f/your_form_id"
method="POST"
>
<fieldset>
<legend>Which service do you need?</legend>
<p id="svc-hint">Pick one option.</p>
<label class="card">
<input type="radio" name="service" value="link-building" required>
<span class="card-title">Link Building</span>
<span class="card-sub">Earn high-quality backlinks.</span>
</label>
<label class="card">
<input type="radio" name="service" value="seo">
<span class="card-title">Search Engine Optimization</span>
<span class="card-sub">Boost rankings & traffic.</span>
</label>
<label class="card">
<input type="radio" name="service" value="web-dev">
<span class="card-title">Web Design / Development</span>
<span class="card-sub">Fast, mobile-first sites.</span>
</label>
</fieldset>
<label>
<span>Full Name</span>
<input type="text" name="name" autocomplete="name" required>
</label>
<label>
<span>Email</span>
<input type="email" name="email" autocomplete="email" required>
</label>
<!-- Optional: subject line in your inbox -->
<input type="hidden" name="_subject" value="New lead from Contact form">
<!-- Simple honeypot -->
<input type="text" name="company" tabindex="-1" autocomplete="off" style="position:absolute; left:-9999px" aria-hidden="true">
<button type="submit">Get My Free Proposal</button>
<p id="form-error" class="error" role="alert" hidden>Something went wrong. Please try again.</p>
<p id="form-success" class="success" role="status" hidden>Thanks! We’ll be in touch shortly.</p>
</form>
Что это вам дает:
- Работает с отключенным JS (хорошо для SEO и надежности).
- Браузер обрабатывает «один должен быть выбран» (
requiredна первом радио). - Formspree получает
service,name,emailи_subject.
Улучшение JS (хороший UX, лучшее отслеживание)
Перехватите отправку, проверьте, отправьте с помощью fetchи отобразить встроенную благодарность (или перенаправление).
<script>
(function(){
const form = document.getElementById('quote');
const okMsg = document.getElementById('form-success');
const errMsg = document.getElementById('form-error');
const submitBtn = form.querySelector('button[type="submit"]');
// Optional: capture UTM + page context
function appendMeta(fd){
const url = new URL(location.href);
['utm_source','utm_medium','utm_campaign','utm_term','utm_content'].forEach(k=>{
const v = url.searchParams.get(k);
if(v) fd.append(k, v);
});
fd.append('page_url', location.href);
if (document.referrer) fd.append('referrer', document.referrer);
}
form.addEventListener('submit', async (e) => {
// Keep native fallback if JS fails
e.preventDefault();
// Use native validity (includes radio required)
if (!form.reportValidity()) return;
const fd = new FormData(form);
// Don’t send honeypot if filled (treat as spam)
if (fd.get('company')) return; // silently drop bots
appendMeta(fd);
submitBtn.disabled = true;
const original = submitBtn.textContent;
submitBtn.textContent = 'Submitting…';
okMsg.hidden = true; errMsg.hidden = true;
try{
const res = await fetch(form.action, {
method: 'POST',
body: fd,
headers: { 'Accept': 'application/json' }
});
if(res.ok){
form.reset();
okMsg.hidden = false;
// Optional: fire analytics
if (window.gtag) gtag('event','generate_lead',{method:'formspree', service: fd.get('service')});
// Optional: redirect after success
// setTimeout(()=> location.href = '/thank-you/', 1200);
} else {
errMsg.hidden = false;
}
} catch {
errMsg.hidden = false;
} finally {
submitBtn.disabled = false;
submitBtn.textContent = original;
}
});
})();
</script>
Заметки
- Поставьте
Accept: application/jsonпоэтому Formspree возвращает JSON, и вы можете однозначно определить успех/неудачу. - Используйте
form.reportValidity()поэтому браузер принудительно выбирает радио и формат адреса электронной почты перед отправкой. - Сохраните родной
action/methodпоэтому форма все равно работает без JS.
Контроль спама без ущерба для конверсий
- Горшок меда (указано выше) ловит множество ботов без каких-либо проблем.
- тайминг: добавить скрытый
started_atпри загрузке страницы; игнорировать отправленные данные быстрее, скажем, 2 секунд. - Сигналы контента: если вы видите повторяющиеся нежелательные значения, отфильтруйте их в правилах Formspree или при постобработке.
<input type="hidden" name="started_at" id="started_at">
<script>
document.getElementById('started_at').value = Date.now();
</script>
// in submit handler before sending:
const started = Number(fd.get('started_at'));
if (Date.now() - started < 2000) return; // looks like a bot
Темы писем, отражающие выбор радиостанции (чистый почтовый ящик)
Вы можете адаптировать _subject с выбранного радио:
const service = fd.get('service'); // e.g., "seo"
fd.set('_subject', `New ${service} lead from Contact form`);
Распространенные ошибки, которых следует избегать
- Скрытие радио с помощью
display:none. Используйтеopacity:0; position:absolute; inset:0внутри этикетки, чтобы клавиатура продолжала работать. - Забыв
fieldset/legend. Программы чтения с экрана полагаются на них для определения контекста. - Слишком ранняя перепроверка. Показывать ошибки после «Отправить/Далее», а затем очищать, как только пользователь исправит группу.
- Не учитывается контекст. Добавить
page_url,referrerи поля UTM, вы потом сами себе спасибо скажете.
Интернационализация и инклюзивность
Язык, ярлыки и тон
- Используйте страницу
lang(и переключение страниц в зависимости от локали):<html lang="en"> … </html> - Делайте этикетки короткими и описательный, избегайте жаргона, специфичного для данной культуры.
- предпочитать нейтральный формулировки («Выберите план») вместо предположений («Какой у вас бюджет?»).
- локализировать Ошибки и вспомогательный текст с простым словарем, набранным
document.documentElement.lang.
const i18n = {
en: { choose: 'Please choose one option.' },
es: { choose: 'Elige una opción, por favor.' },
ar: { choose: 'يُرجى اختيار خيار واحد.' }
};
const t = (k) => (i18n[document.documentElement.lang] || i18n.en)[k];
errorEl.textContent = t('choose');
Длинные метки и многострочные параметры
- Пусть этикетки заворачивать; никогда не сокращайте важные слова.
- Сохраняйте область нажатия большой, даже если текст переносится: используйте отступы этикетка, а не только входные данные.
.card { inline-size: 100%; padding: 16px; }
.card .card-title { font-weight: 700; line-height: 1.25; }
.card .card-sub { font-size: 14px; line-height: 1.4; }
Поддержка письма справа налево (RTL)
Использовать CSS логические свойства и :dir() поэтому вам не нужны отдельные стили.
/* Spacing that flips in RTL automatically */
.card { padding-inline: 16px; padding-block: 16px; }
/* Focus ring that respects direction */
.card:has(input:focus-visible) {
outline: 3px solid var(--ring);
outline-offset: 2px;
}
/* Optional: direction-specific tweaks */
:dir(rtl) .card-group { direction: rtl; }
Доступность за пределами ARIA
- Обеспечивать контрастность ≥ 4.5:1 для меток и состояний фокуса.
- Цели касания ≥ 44 × 44 px (отступ, а не размер шрифта).
- Не сообщайте о состоянии только цветом; используйте границы, значки или текст.
Контрольный список тестирования (для печати)
- Семантика: Радиоприемники внутри.
fieldsetс видимымlegend. - клавиатура: Tab входит в группу; Стрелки двигаться; Space выбирает.
- Программы чтения с экрана: Объявлена легенда; для каждого варианта указано выбрано/не выбрано.
- Фокус: Видимое кольцо на целая карта (и на родном вводе).
- Зона поражения: Вся этикетка кликабельна; работает на мобильных устройствах.
- Проверка: Ошибок нет до отправки/далее; ошибки исчезают при внесении изменений.
- Копировать: Варианты краткие; вспомогательный текст поясняет различия.
- Планировка: 1 столбец на телефонах; 2–3 на настольных компьютерах; без обрезки переполнения.
- i18n: Ошибки/метки локализованы; длинный текст переносится корректно; RTL в порядке.
- Аналитика: События для показа, выбора, отправки.
- Эффективности: Никаких тяжелых библиотек пользовательского интерфейса; минимум CSS; отсутствие изменения макета.
Производительность и ремонтопригодность
- Фавор собственные входы + легкий CSS; избегайте замены радиоэлементов на div.
- Сохраняйте модульность CSS (пользовательские свойства для цветов, радиуса, кольца).
- Избежать
display:noneна входах; использоватьopacity:0; position:absolute; inset:0поэтому доступность остается неизменной. - Отложите некритический JS-код; по возможности старайтесь, чтобы размер улучшений не превышал ~2–5 КБ.
- Используйте контейнерные запросы (при наличии) вместо сложных контрольных точек мультимедиа для встроенных форм.
@container (min-width: 680px) {
.card-group { grid-template-columns: 1fr 1fr 1fr; }
}
Аналитика и эксперименты
Отслеживайте намерения и точки трения:
// Fire when the radio group becomes visible
gtag?.('event','form_step_view', { step: 1, form: 'contact' });
// Fire on selection
document.querySelectorAll('input[name="service"]').forEach(r => {
r.addEventListener('change', () => {
gtag?.('event','radio_select', { group: 'service', value: r.value });
});
});
// Fire on successful submit
gtag?.('event','generate_lead', { form: 'contact', service: form.service.value });
Тест A/B:
- Заказ опциона (сначала наиболее часто выбираемые)
- копия призыва к действию («Получить мое бесплатное предложение» против «Получить мою цитату»)
- Вспомогательный текст присутствие
Анти-шаблоны (не делайте этого)
- Скрытие входных данных с помощью
display:none(ломает клавиатуру и SR). - Отсутствующий
fieldset/legend. - Крошечные области воздействия (кликабельна только маленькая точка).
- Состояние выбора только цвета; невидимый фокус.
- Пользователи до пользователь пытается продолжить.
- Упаковывая 10–20 вариантов в радиоприемники, вместо этого используйте функцию поиска.
Окончательный контрольный список
fieldset+legendпредставить- Одна и та же
nameуникальныйvalues - Метки оборачивались входными данными; вся карточка была кликабельна
- Кольцо фокусировки хорошо видно
requiredна первом радио; небольшие встроенные ошибки- Адаптивная сетка из 1–3 колонок, большие сенсорные элементы
- Локализованные строки; RTL-безопасность благодаря логическим свойствам
- Аналитика по просмотру/выбору/отправке
- Проверка на стороне сервера отражает клиентские правила
Ресурсы
Спецификации и шаблоны разработки
- Стандарт HTML –
<input type="radio">: собственная семантика, группировка поname, формировать поведение. - Авторские практики WAI-ARIA 1.2 – Радиогруппа: ожидаемое поведение клавиатуры (Tab в группу, Arrow двигаться, Space для выбора), шаблоны маркировки.
- ВКАГ 2.2 необходимое для радио:
- 2.1.1 клавиатура (все управляется с помощью клавиатуры)
- 2.4.7 Фокус Видимый (четко видимый фокус)
- 2.5.5 Целевой размер (рекомендуется ≥44×44 пикселей)
- 1.4.3 Контрастность (минимальная)
Справочные руководства
- MDN: Радиовход,
label,fieldset/legend, проверка форм (API проверки ограничений). - Система проектирования GOV.UK – Радиоприемники: превосходное, проверенное в боях руководство по формулировкам и ошибкам.
- Инклюзивные компоненты (Хейдон Пикеринг): практические модели и критика.
Ознакомьтесь с нашим последним блогом о Как искать возможности для гостевых постов на Ahrefs (пошаговое руководство)
Тестирование и оснастка
- axe DevTools (расширение для браузера) – автоматизированные проверки a11y.
- Обзор доступности – быстрые пошаговые тесты.
- Маяк – сигналы производительности + доступности.
- Средство проверки контрастности WebAIM – проверьте цветовой контраст.
- Программы чтения с экрана для выборочной проверки реального поведения:
- NVDA (Windows), JAWS (Windows), VoiceOver (macOS/iOS), Отвечать (Android).
- Ручной проход по клавиатуре: Tab для входа в группу → Стрелки для перемещения → Пробел для выбора → кольцо фокусировки остается видимым.
Проверьте наши обзор Notions Marketing
Часто задаваемые вопросы (краткие и практические)
Стремиться к 2-6Если у вас 7–10, сгруппируйте или используйте сетку из двух столбцов. Если больше 10, переключитесь на выбрать (с поиском) или сначала перепроектируйте, чтобы сузить выбор.
Только если есть явный дефолт большинства и не искажает данные. Предварительный выбор ускоряет завершение, но может снизить осознанный выбор. Если вы не уверены, не выбирать заранее.
Радио: единственный выбор, варианты должны быть видимый бок о бок.
Выберите: единственный выбор, когда места мало или вариантов много.
Флажки: 0–много выборы (не исключающие друг друга).
Да. Оставьте собственный ввод с фокусировкой (не использовать display:none). Введите входные данные внутри <label>, установите его на opacity:0; position:absolute; inset:0;и стилизовать этикетку. Используйте :has(input:focus-visible) и :has(input:checked) для управления фокусом/выбранными состояниями.
Сначала используйте HTML: поместите required на первом радио и вызов form.reportValidity() (или позвольте браузеру обработать отправку). Для более удобного интерфейса отобразите короткий встроенная ошибка рядом с группой после первая попытка, и ясность в отношении изменений.
role="radiogroup"? Не для родных радиоприемников. Правильный fieldset + legend ARIA уже предоставляет программам чтения с экрана нужный контекст. Добавляйте ARIA только если вы создаёте полностью индивидуальный виджет (по возможности избегайте).
Показать/скрыть последующие действия после выбор. Когда скрыто, также запрещать Эти элементы управления не попадают в порядок вкладок и не отправляются. При открытии перемещайте фокус разумно и следите за тем, чтобы подписи оставались чёткими.
Поток нажатия клавиш, видимый фокус, оповещения программы чтения с экрана (легенда, озвучивание выбранного состояния), области нажатия на мобильные устройства, время проверки (без раннего ворчания) и выбранные значения упорствовать при ошибке на стороне сервера.