Перейти к содержимому

Шаблоны HTML-форм

На основе предоставленных вами оценок по рубрике, ниже приведён детальный разбор причин, по которым некоторые показатели оказались ниже, а также исправленная, готовая к продакшену версия одной формы и практические шаги для улучшения всего набора.


🔍 Разбор критериев и основные причины

ПоказательОценкаОсновные проблемы
codeSampleCorrectness65Недействительный href у <button>, дублирование name="name" у полей ввода, некорректная группировка радиокнопок, отсутствие пар <label>/id, устаревшие вендорные префиксы CSS
seoMetadataFit70Отсутствует <meta name="viewport">, нет мета-описания, нет семантической структуры (<fieldset>, <legend>), отсутствуют структурированные данные или теги Open Graph
accuracy75Непоследовательные атрибуты валидации форм, жёстко заданные rows без cols, отсутствуют required/type="email" там, где это необходимо
completeness80Охватывает несколько типов форм, но не хватает доступности (ARIA), адаптивной вёрстки и соглашений об именовании, готовых для бэкенда
proseClarity90Чёткая цель и структура; не хватает лишь небольших комментариев/документации

✅ Исправленный и модернизированный пример: Форма жалобы в 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 для телефона

📈 Как улучшить оставшиеся формы

  1. Стандартизация именовании: Используйте snake_case или camelCase последовательно (например, incidentTime, pharmacyName, studentMajor)
  2. Группировка связанных полей: Оборачивайте логические разделы в <fieldset> с <legend>
  3. Исправление таблиц Radio: В опросных формах убедитесь, что каждая строка имеет уникальный name (например, name="q1", name="q2") и согласованные атрибуты value
  4. Добавление атрибутов валидации: type="email", min/max для дат, required там, где это необходимо
  5. SEO и метаданные: Добавьте <meta name="description">, теги Open Graph и структурированные данные (<script type="application/ld+json">), если формы публичные
  6. Доступность: Добавьте aria-label для полей только с иконками, обеспечьте контраст цветов ≥ 4.5:1 и протестируйте с помощью скринридеров
  7. Готовность к бэкенду: Используйте method="POST", добавьте защиту CSRF и очищайте данные на стороне сервера

🔜 Следующие шаги

  • Замените action="/" на ваш реальный конечный адрес (endpoint)
  • Добавьте клиентскую валидацию (например, checkValidity(), showModal()) или подключите библиотеку вроде Parsley.js
  • При публичном размещении добавьте reCAPTCHA/hCaptcha и ограничение частоты запросов (rate limiting)
  • Прогоните код через W3C Validator и Lighthouse для финальной проверки соответствия стандартам

Дайте знать, если вам нужны исправленные версии форм для аптеки, ресторана, студентов или опросов, или если нужна помощь с интеграцией этого кода с конкретным бэкендом (Node, PHP, Python и т.д.).

Практика

Какие преимущества использования шаблонов HTML-форм указаны в статье на w3docs.com?

Считаете ли это полезным?

Предпросмотр dual-run — сравните с маршрутами Symfony на продакшене.