label и fieldset
<label>связывает текстовую подпись с элементом формы.<fieldset>и<legend>группируют связанные поля и дают им общий заголовок.
Зачем нужно
<label> -- критичный элемент доступности. Без него screen reader не знает, что означает поле ввода. Клик по label фокусирует связанный input -- это увеличивает область клика (особенно важно для checkbox/radio на мобильных).
<fieldset> группирует связанные поля (например, группу radio), а <legend> даёт группе название, которое screen reader объявляет перед каждым полем.
Где используется
- Каждый элемент формы должен иметь label
- Группы radio/checkbox обязательно в fieldset
- Логические секции форм (личные данные, адрес, оплата)
Предпосылки
<label> -- подпись элемента
Явная связь (explicit) -- через for
<label for="email">Email:</label>
<input type="email" id="email" name="email">
Атрибут for у <label> должен совпадать с id элемента формы.
Неявная связь (implicit) -- обёртка
<label>
Email:
<input type="email" name="email">
</label>
Input вложен в label -- связь автоматическая, for и id не нужны.
Что лучше?
| Способ | Плюсы | Минусы |
|---|---|---|
Explicit (for/id) |
Работает везде, гибкая вёрстка | Нужны уникальные id |
| Implicit (обёртка) | Не нужны id | Некоторые AT хуже поддерживают, менее гибко |
Рекомендация: используй explicit-связь (for/id) -- она надёжнее.
Label и разные элементы
<!-- input -->
<label for="name">Имя:</label>
<input type="text" id="name" name="name">
<!-- textarea -->
<label for="msg">Сообщение:</label>
<textarea id="msg" name="message"></textarea>
<!-- select -->
<label for="city">Город:</label>
<select id="city" name="city">
<option value="msk">Москва</option>
<option value="spb">Санкт-Петербург</option>
</select>
<!-- checkbox (label оборачивает для увеличения области клика) -->
<label>
<input type="checkbox" name="agree"> Согласен с условиями
</label>
<!-- radio -->
<label>
<input type="radio" name="size" value="s"> Маленький
</label>
<label>
<input type="radio" name="size" value="m"> Средний
</label>
Почему placeholder -- не замена label
<!-- ПЛОХО: только placeholder -->
<input type="email" placeholder="Email">
<!-- Проблемы:
- Исчезает при вводе (пользователь забывает что вводить)
- Серый цвет -- плохой контраст
- Screen reader может не прочитать
- Нет кликабельной области
-->
<!-- ХОРОШО: label + placeholder -->
<label for="email">Email:</label>
<input type="email" id="email" name="email" placeholder="user@example.com">
<fieldset> и <legend>
Группировка radio кнопок
<fieldset>
<legend>Способ доставки:</legend>
<label>
<input type="radio" name="delivery" value="courier" checked>
Курьер
</label>
<label>
<input type="radio" name="delivery" value="pickup">
Самовывоз
</label>
<label>
<input type="radio" name="delivery" value="post">
Почта
</label>
</fieldset>
Screen reader прочитает: "Способ доставки, группа. Курьер, радиокнопка, выбрана. Самовывоз, радиокнопка..."
Группировка checkbox
<fieldset>
<legend>Тип кухни:</legend>
<label>
<input type="checkbox" name="cuisine" value="italian"> Итальянская
</label>
<label>
<input type="checkbox" name="cuisine" value="japanese"> Японская
</label>
<label>
<input type="checkbox" name="cuisine" value="georgian"> Грузинская
</label>
</fieldset>
Секции формы
<form action="/api/order" method="POST">
<fieldset>
<legend>Контактные данные</legend>
<div class="field">
<label for="name">Имя:</label>
<input type="text" id="name" name="name" required autocomplete="name">
</div>
<div class="field">
<label for="phone">Телефон:</label>
<input type="tel" id="phone" name="phone" required autocomplete="tel">
</div>
</fieldset>
<fieldset>
<legend>Адрес доставки</legend>
<div class="field">
<label for="street">Улица:</label>
<input type="text" id="street" name="street" autocomplete="street-address">
</div>
<div class="field">
<label for="apt">Квартира:</label>
<input type="text" id="apt" name="apartment">
</div>
</fieldset>
<fieldset>
<legend>Способ оплаты</legend>
<label>
<input type="radio" name="payment" value="card" checked> Картой
</label>
<label>
<input type="radio" name="payment" value="cash"> Наличными
</label>
</fieldset>
<button type="submit">Оформить заказ</button>
</form>
Вложенные fieldset
<fieldset>
<legend>Настройки уведомлений</legend>
<fieldset>
<legend>Email-уведомления</legend>
<label><input type="checkbox" name="email_news"> Новости</label>
<label><input type="checkbox" name="email_promo"> Акции</label>
</fieldset>
<fieldset>
<legend>Push-уведомления</legend>
<label><input type="checkbox" name="push_orders"> Заказы</label>
<label><input type="checkbox" name="push_chat"> Сообщения</label>
</fieldset>
</fieldset>
disabled на fieldset
disabled на <fieldset> блокирует все вложенные элементы формы:
<fieldset disabled>
<legend>Адрес (заблокировано)</legend>
<label for="city">Город:</label>
<input type="text" id="city" name="city">
<!-- Все input внутри будут disabled -->
</fieldset>
Стилизация
<style>
fieldset {
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 1.5rem;
margin-bottom: 1.5rem;
}
legend {
font-weight: 600;
font-size: 1.1rem;
padding: 0 0.5rem;
}
/* Убрать рамку fieldset (для визуальной группировки без рамки) */
.no-border {
border: none;
padding: 0;
}
.no-border legend {
font-size: 1.25rem;
margin-bottom: 0.75rem;
}
label {
display: block;
margin-bottom: 0.25rem;
font-weight: 500;
}
.field {
margin-bottom: 1rem;
}
</style>
Частые ошибки
| Ошибка | Почему плохо | Как правильно |
|---|---|---|
| Input без label | Screen reader не знает, что вводить | Каждый input должен иметь label |
placeholder вместо label |
Исчезает при вводе, плохой контраст | label + placeholder |
Radio/checkbox без <fieldset> |
Screen reader не знает контекст группы | Группируй в fieldset + legend |
for не совпадает с id |
Связь не работает | Проверяй совпадение for и id |
| Несколько input на один label | Невалидно, непредсказуемо | Один label на один input |
<legend> вне <fieldset> |
Невалидно | <legend> только внутри <fieldset> |
Практика
- Создай форму с explicit label (
for/id) -- кликни по label и проверь фокус - Группируй radio-кнопки в fieldset с legend
- Создай форму с двумя fieldset: "Личные данные" и "Настройки"
- Добавь
disabledна fieldset и убедись, что все поля внутри заблокированы - Проверь форму через screen reader (NVDA) -- legend должен читаться перед каждым radio
Связанные темы
- input типы -- элементы форм
- Формы в HTML -- контейнер формы
- Валидация форм -- валидация с label-ошибками
- ARIA атрибуты -- aria-labelledby, aria-describedby
- Семантика для screen readers -- как screen readers читают формы