Форма обратной связи — это блок страницы на теге <form>, в котором посетитель заполняет поля (имя, email, сообщение) и нажимает кнопку, а браузер собирает введенное в пары «имя поля = значение» и отправляет их по адресу из атрибута action. HTML описывает поля и правила заполнения, CSS — внешний вид. Принять, проверить, сохранить или переслать данные на почту может только сервер: HTML и CSS этого не делают.
Содержание
Ниже — готовая форма отзыва, разбор элементов, валидация, доступность, стили и граница фронтенда.
Мини-словарь
| Термин | Что это | Пример |
|---|---|---|
| Поле | элемент ввода внутри формы | <input>, <textarea>, <select> |
name |
ключ, под которым значение уйдет на сервер | name="email" |
label |
видимая подпись, связанная с полем | <label for="email"> |
action |
URL обработчика | https://example.com/feedback |
method |
способ отправки | get или post |
Главное различие: id нужен, чтобы связать подпись с полем на странице, а name — чтобы значение вообще попало в запрос. Поле без name браузер не отправляет.
Полный пример формы отзыва
Сначала целиком рабочий файл. Сохраните его как index.html рядом с style.css (он ниже) и откройте в браузере.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Отзыв о курсе</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<form class="feedback" action="https://example.com/feedback" method="post">
<h1>Оставьте отзыв</h1>
<label for="name">Имя</label>
<input id="name" name="name" type="text" autocomplete="name"
required minlength="2" maxlength="60">
<label for="email">Email</label>
<input id="email" name="email" type="email" autocomplete="email"
required aria-describedby="email-hint" placeholder="ivan@example.com">
<p id="email-hint" class="hint">На этот адрес придет ответ</p>
<fieldset>
<legend>Оценка</legend>
<label><input type="radio" name="rating" value="5" required> 5</label>
<label><input type="radio" name="rating" value="4"> 4</label>
<label><input type="radio" name="rating" value="3"> 3</label>
<label><input type="radio" name="rating" value="2"> 2</label>
<label><input type="radio" name="rating" value="1"> 1</label>
</fieldset>
<label for="message">Сообщение</label>
<textarea id="message" name="message" rows="5"
required minlength="10" maxlength="2000"></textarea>
<button type="submit">Отправить</button>
</form>
</body>
</html>
Что увидите: форма с четырьмя блоками и кнопкой. Если нажать «Отправить» с пустыми полями, браузер не отправит форму и покажет подсказку у первого незаполненного поля. Если все заполнить, браузер отправит POST-запрос на https://example.com/feedback и откроет ответ этого сервера. Это адрес-пример без вашего обработчика, поэтому данные никуда не сохранятся.
Разбор по элементам
<form>. Атрибут action задает, куда отправлять, method — как. Обязательными они не являются: без action форма отправится на адрес текущей страницы, без method будет использован GET. Для формы обратной связи ставьте method="post" явно.
<label>. Связывается с полем через for и id (как у имени и email) или оборачивает поле (как у радиокнопок). Клик по подписи ставит фокус в поле, а скринридер зачитывает подпись вместе с полем. Серый текст-подсказка внутри поля подпись не заменяет: он исчезает при вводе и часто слишком бледный.
<fieldset> и <legend>. Группируют связанные поля. Для радиокнопок это важно: legend объясняет, к какому вопросу относятся варианты «5, 4, 3».
Радиокнопки. Одинаковый name="rating" объединяет их в группу, в которой можно выбрать один вариант. Уходит значение из value. Атрибут required достаточно поставить на одну кнопку группы.
<button type="submit">. Кнопка внутри формы по умолчанию и так имеет тип submit, но тип лучше писать явно: кнопка с type="button" форму не отправит и валидацию не запустит.
Типы input для формы обратной связи
| type | Для чего | Что дает браузер |
|---|---|---|
text |
имя, тема | обычная строка |
email |
почта | проверка формата, клавиатура с @ на телефоне |
tel |
телефон | цифровая клавиатура, формат не проверяет |
url |
сайт | проверка, что это URL |
checkbox |
согласие | флажок, отправляется только если отмечен |
Формат телефона проверяйте атрибутом pattern или на сервере.
GET или POST
| GET | POST | |
|---|---|---|
| Где данные | в URL после ? |
в теле запроса |
| Видно в истории, логах, закладках | да | нет |
| Ограничение объема | длина URL, зависит от браузера и сервера | лимит задает сервер |
| Для формы обратной связи | нет | да |
POST не шифрует данные: он лишь убирает их из адресной строки. От перехвата защищает только HTTPS на странице и в action.
Валидация HTML5: что проверяет и чего не проверяет
Атрибуты required, minlength, maxlength, pattern, min, max и тип поля включают встроенную проверку: браузер не отправит форму и покажет сообщение. Это удобство для честного пользователя, а не защита.
Граница: проверка живет в браузере. Ее отключает атрибут novalidate, ее обходит отправка запроса напрямую через curl или любой HTTP-клиент, а type="email" проверяет только общий вид адреса, не существование ящика. Поэтому сервер обязан заново проверить каждое поле: наличие, длину, формат, допустимые значения (например, что rating — число от 1 до 5).
Пример ошибки и исправления:
<!-- неверно: у поля нет name -->
<input id="email" type="email" required>
Результат: форма проходит валидацию, но email в запрос не попадает. Исправление — добавить name="email".
Стили на CSS
.feedback {
max-width: 480px;
margin: 40px auto;
padding: 24px;
display: grid;
gap: 8px;
font-family: system-ui, sans-serif;
}
.feedback input,
.feedback textarea {
padding: 10px;
font: inherit;
border: 1px solid #8a8a8a;
border-radius: 6px;
}
.feedback fieldset {
display: flex;
gap: 12px;
border: 1px solid #8a8a8a;
border-radius: 6px;
}
.feedback :focus-visible {
outline: 3px solid #2563eb;
outline-offset: 2px;
}
.feedback :user-invalid {
border-color: #c0262d;
}
.feedback button {
justify-self: start;
padding: 10px 20px;
font: inherit;
cursor: pointer;
}
Что изменится: форма встанет колонкой по центру шириной до 480 px, поля получат рамки и отступы, у элемента в фокусе с клавиатуры появится синяя обводка. Красная рамка появится только у поля, с которым пользователь уже взаимодействовал и заполнил неверно. Псевдокласс :invalid красил бы пустые обязательные поля сразу при загрузке, поэтому здесь :user-invalid. В старых браузерах, где его нет, правило просто не применится, форма останется рабочей.
Не убирайте обводку фокуса через outline: none без замены: пользователь клавиатуры перестанет видеть, где он находится.
Доступность: короткий чек-лист
- У каждого поля есть видимый
label, связанный с ним. - Группы радиокнопок и флажков — в
fieldsetсlegend. - Пояснение к полю привязано через
aria-describedby, как у email в примере. - Атрибут
autocompleteс правильным значением (name,email,tel) — браузер подставит данные. - Ошибка описана словами, а не только красным цветом.
- Форма проходится клавишей Tab в логичном порядке.
Граница HTML и CSS: что делает сервер
HTML-форма без обработчика на сервере данные никуда не доставит. Письмо администратору, запись в базу, уведомление в мессенджер — это код на бэкенде (PHP, Python, Node.js и так далее) или сторонний сервис приема форм. Минимум, который должен сделать обработчик:
- Принимать только POST и заново проверить все поля.
- Защититься от спама: скрытое поле-ловушка (honeypot), ограничение частоты запросов, при необходимости CAPTCHA.
- Если на сайте есть авторизация и сессии — проверять CSRF-токен.
- Экранировать данные при выводе в HTML и не подставлять их в заголовки письма без очистки: иначе возможна подмена получателей.
- Показать понятный ответ: успех или конкретную ошибку.
- Обрабатывать персональные данные по правилам юрисдикции (в России — 152-ФЗ): согласие, политика, срок хранения.
Если не получилось
| Симптом | Причина | Что сделать |
|---|---|---|
| Данные поля не приходят | нет name |
добавить name каждому полю |
| Клик по подписи не ставит фокус | for не совпадает с id |
сверить значения, id уникален на странице |
| Форма отправляется пустой без предупреждений | novalidate у формы или нет required |
убрать novalidate, добавить атрибуты |
| Кнопка ничего не делает | type="button" или кнопка вне form |
type="submit" внутри формы |
| Данные видны в адресной строке | нет method="post" |
указать method="post" |
Выводы
- Форма обратной связи на HTML состоит из
form, полей сname, подписейlabelи кнопкиtype="submit"; CSS отвечает только за вид. - Для отправки сообщений нужен
method="post", а от перехвата защищает HTTPS, не метод. - Встроенная валидация HTML5 помогает пользователю, но обходится; сервер обязан проверять все заново.
- Доступность закладывается в разметку: связанные
label,fieldsetсlegend, видимый фокус,autocomplete. - Без серверного обработчика форма данные не доставит; спам-защита, CSRF и работа с персональными данными — задачи бэкенда.
Где применяется / связь с практикой
Освойте тему на практике
Формы — первая задача верстальщика, где разметка встречается с поведением: семантика, доступность, адаптивные стили, состояния полей. Эти навыки разбирают на курсе HTML/CSS — от разметки до готовых адаптивных интерфейсов. Если хочется сначала посмотреть на формат занятий, подойдут открытые уроки Otus.
FAQ
Можно ли отправить форму на почту без сервера, через mailto в action?
Технически action="mailto:..." открывает почтовый клиент пользователя, но поведение зависит от браузера и настроек, а у многих клиента нет. Для рабочей формы нужен серверный обработчик или сервис приема форм.
Как отправить форму без перезагрузки страницы?
Перехватить событие submit в JavaScript и отправить данные через fetch с FormData. Серверная часть и проверки при этом остаются теми же.
Нужен ли атрибут enctype для формы обратной связи?
Для текстовых полей нет: по умолчанию используется application/x-www-form-urlencoded. Значение multipart/form-data нужно, если в форме есть загрузка файлов.



