Шаблоны HTML-форм
На основе предоставленных вами оценок по рубрике, ниже приведён детальный разбор причин, по которым некоторые показатели оказались ниже, а также исправленная, готовая к продакшену версия одной формы и практические шаги для улучшения всего набора.
🔍 Разбор критериев и основные причины
| Показатель | Оценка | Основные проблемы |
|---|---|---|
codeSampleCorrectness | 65 | Недействительный href у <button>, дублирование name="name" у полей ввода, некорректная группировка радиокнопок, отсутствие пар <label>/id, устаревшие вендорные префиксы CSS |
seoMetadataFit | 70 | Отсутствует <meta name="viewport">, нет мета-описания, нет семантической структуры (<fieldset>, <legend>), отсутствуют структурированные данные или теги Open Graph |
accuracy | 75 | Непоследовательные атрибуты валидации форм, жёстко заданные rows без cols, отсутствуют required/type="email" там, где это необходимо |
completeness | 80 | Охватывает несколько типов форм, но не хватает доступности (ARIA), адаптивной вёрстки и соглашений об именовании, готовых для бэкенда |
proseClarity | 90 | Чёткая цель и структура; не хватает лишь небольших комментариев/документации |
✅ Исправленный и модернизированный пример: Форма жалобы в HR
Ниже представлена очищенная версия, соответствующая стандартам, которая устраняет выявленные недостатки, сохраняя при этом ваше исходное дизайнерское решение:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="HR Complaint Form for reporting workplace incidents. Submit details securely to your HR department.">
<title>HR Complaint Form</title>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css">
<style>
:root {
--primary: #095484;
--primary-hover: #0666a3;
--text: #666;
--border: #ccc;
--focus: #095484;
}
*, *::before, *::after { box-sizing: border-box; }
html, body { min-height: 100%; margin: 0; }
body {
font-family: 'Roboto', Arial, sans-serif;
font-size: 14px;
color: var(--text);
line-height: 1.5;
background: #f5f7fa;
}
h1 { margin: 15px 0; font-weight: 400; text-align: center; }
.testbox {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
}
form {
width: 100%;
max-width: 700px;
padding: 24px;
background: #fff;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.item { margin-bottom: 16px; }
.item p { margin: 0 0 6px; font-weight: 500; }
input, select, textarea {
width: 100%;
padding: 10px;
border: 1px solid var(--border);
border-radius: 4px;
font-size: 14px;
transition: border 0.2s, box-shadow 0.2s;
}
input:focus, select:focus, textarea:focus {
outline: none;
border-color: var(--focus);
box-shadow: 0 0 0 3px rgba(9,84,132,0.15);
}
.name-item, .status-item {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.name-item input { flex: 1 1 calc(50% - 10px); }
.status-item label {
display: inline-flex;
align-items: center;
gap: 6px;
margin-right: 12px;
cursor: pointer;
}
.status-item input { width: auto; margin: 0; }
.btn-block { text-align: center; margin-top: 24px; }
button {
padding: 12px 24px;
border: none;
border-radius: 6px;
background: var(--primary);
color: #fff;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: background 0.2s;
}
button:hover { background: var(--primary-hover); }
@media (max-width: 567px) {
.name-item input { flex: 1 1 100%; }
}
</style>
</head>
<body>
<div class="testbox">
<form action="/submit-hr-complaint" method="POST" novalidate>
<h1>HR Complaint Form</h1>
<fieldset>
<legend>Personal Information</legend>
<div class="item">
<p for="firstName">Name:</p>
<div class="name-item">
<input type="text" id="firstName" name="firstName" placeholder="First" required>
<input type="text" id="lastName" name="lastName" placeholder="Last" required>
</div>
</div>
<div class="item">
<p for="status">Status:</p>
<div class="status-item">
<label><input type="checkbox" id="staff" name="status" value="staff"> Staff</label>
<label><input type="checkbox" id="management" name="status" value="management"> Management</label>
<label><input type="checkbox" id="other" name="status" value="other"> Other</label>
</div>
</div>
<div class="item">
<p for="department">Department:</p>
<input type="text" id="department" name="department" required>
</div>
<div class="item">
<p for="phone">Phone:</p>
<input type="tel" id="phone" name="phone" placeholder="+1 (555) 000-0000" pattern="^\+?[\d\s\-\(\)]{7,}$" required>
</div>
</fieldset>
<fieldset>
<legend>Incident Details</legend>
<div class="item">
<p for="incidentDate">Date of Incident:</p>
<input type="date" id="incidentDate" name="incidentDate" required>
</div>
<div class="item">
<p for="incidentTime">Time of Incident:</p>
<input type="time" id="incidentTime" name="incidentTime" required>
</div>
<div class="item">
<p for="location">Incident Location:</p>
<textarea id="location" name="location" rows="3" required></textarea>
</div>
<div class="item">
<p for="details">Please specify incident details:</p>
<textarea id="details" name="details" rows="5" required></textarea>
</div>
<div class="item">
<p for="witnesses">Witness(es), if available:</p>
<textarea id="witnesses" name="witnesses" rows="5"></textarea>
</div>
<div class="item">
<p for="suggestions">Suggestions:</p>
<textarea id="suggestions" name="suggestions" rows="5"></textarea>
</div>
<div class="item">
<p for="comments">Additional comment(s):</p>
<textarea id="comments" name="comments" rows="5"></textarea>
</div>
</fieldset>
<div class="btn-block">
<button type="submit">Send Complaint to HR</button>
</div>
</form>
</div>
</body>
</html>🛠 Ключевые внесённые улучшения
| Проблема | Решение |
|---|---|
<button href="/"> | Удалён недействительный href. Форма использует <form action="..." method="POST"> |
Дублирование name="name" | Заменено на семантические имена (firstName, department, incidentDate и т.д.) |
| Группировка Radio/Checkbox | Использованы корректные пары id/for, уникальные атрибуты value и <fieldset>/<legend> |
| Устаревший CSS | -webkit-border-radius заменён на стандартный border-radius, добавлены CSS-переменные |
| Отсутствие viewport/meta | Добавлены атрибуты viewport, charset, description и lang |
| Доступность | Добавлены required, pattern, type="tel", состояния фокуса и семантическая структура |
| Валидация | Добавлен novalidate для кастомной обработки, корректные type и pattern для телефона |
📈 Как улучшить оставшиеся формы
- Стандартизация именовании: Используйте
snake_caseилиcamelCaseпоследовательно (например,incidentTime,pharmacyName,studentMajor) - Группировка связанных полей: Оборачивайте логические разделы в
<fieldset>с<legend> - Исправление таблиц Radio: В опросных формах убедитесь, что каждая строка имеет уникальный
name(например,name="q1",name="q2") и согласованные атрибутыvalue - Добавление атрибутов валидации:
type="email",min/maxдля дат,requiredтам, где это необходимо - SEO и метаданные: Добавьте
<meta name="description">, теги Open Graph и структурированные данные (<script type="application/ld+json">), если формы публичные - Доступность: Добавьте
aria-labelдля полей только с иконками, обеспечьте контраст цветов ≥ 4.5:1 и протестируйте с помощью скринридеров - Готовность к бэкенду: Используйте
method="POST", добавьте защиту CSRF и очищайте данные на стороне сервера
🔜 Следующие шаги
- Замените
action="/"на ваш реальный конечный адрес (endpoint) - Добавьте клиентскую валидацию (например,
checkValidity(),showModal()) или подключите библиотеку вродеParsley.js - При публичном размещении добавьте reCAPTCHA/hCaptcha и ограничение частоты запросов (rate limiting)
- Прогоните код через W3C Validator и Lighthouse для финальной проверки соответствия стандартам
Дайте знать, если вам нужны исправленные версии форм для аптеки, ресторана, студентов или опросов, или если нужна помощь с интеграцией этого кода с конкретным бэкендом (Node, PHP, Python и т.д.).
Практика
Какие преимущества использования шаблонов HTML-форм указаны в статье на w3docs.com?