← HTML & CSS/Правила хорошего HTML#15 из 430← ПредыдущийСледующий →+15 XP
Полезно по теме:Гайд: старт в frontendПрактика: DOM и событияТермин: DOMМаршрут: старт с нуля
← НазадДалее →

Правила хорошего HTML

Ты уже знаешь все основные теги и концепции HTML. Теперь поговорим о том, что отличает профессионала от новичка: правильная структура, именование, форматирование и валидация. Хороший HTML — это не просто «работает», это «работает, понятно и поддерживается».

1. Валидная структура документа

Каждый HTML-документ должен начинаться с корректной базовой структуры:

<!DOCTYPE html>
<html lang="ru">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Описательный заголовок страницы</title>
  </head>
  <body>
    <!-- Контент -->
  </body>
</html>

2. Правильная вложенность тегов

<!-- ПЛОХО: нарушена вложенность -->
<p>Текст <strong>жирный <em>жирный-курсив</strong> курсив</em></p>

<!-- ХОРОШО: теги закрываются в обратном порядке -->
<p>Текст <strong>жирный <em>жирный-курсив</em></strong> <em>курсив</em></p>

HTML — это дерево. Каждый открытый тег должен быть закрыт до того, как закроется его родитель.

3. Строчные буквы везде

<!-- ПЛОХО: заглавные теги -->
<DIV CLASS="header">
  <H1>Заголовок</H1>
</DIV>

<!-- ХОРОШО: строчные теги -->
<div class="header">
  <h1>Заголовок</h1>
</div>

HTML нечувствителен к регистру, но конвенция — всегда строчные.

4. Самозакрывающиеся теги

Теги без содержимого (void elements) должны правильно закрываться:

<!-- В HTML5 оба варианта корректны, но <tag /> предпочтительнее для JSX -->
<img src="photo.jpg" alt="Фото" />
<br />
<hr />
<input type="text" />
<meta charset="UTF-8" />

5. Атрибуты в двойных кавычках

<!-- ПЛОХО: без кавычек или одинарные кавычки -->
<img src=photo.jpg alt=Фото />
<a href='https://example.com'>Ссылка</a>

<!-- ХОРОШО: двойные кавычки -->
<img src="photo.jpg" alt="Фото" />
<a href="https://example.com">Ссылка</a>

6. Осмысленные имена классов и id

<!-- ПЛОХО: непонятные имена -->
<div class="d1 red big">...</div>
<div id="x">...</div>

<!-- ХОРОШО: имена описывают назначение, не внешний вид -->
<div class="product-card featured">...</div>
<div id="checkout-modal">...</div>

Используй kebab-case для классов и id. Называй по назначению, а не по виду: не red-text, а error-message.

7. Отступы и форматирование

<!-- ПЛОХО: всё в одну строку -->
<div class="card"><img src="prod.jpg" alt="Товар" /><h3>Nike Air Max</h3><p class="price">7 990 ₽</p><button>Купить</button></div>

<!-- ХОРОШО: отступы 2 пробела, каждый элемент на своей строке -->
<div class="product-card">
  <img src="product.jpg" alt="Кроссовки Nike Air Max 90" />
  <h3 class="product-title">Nike Air Max 90</h3>
  <p class="product-price">7 990 ₽</p>
  <button class="btn btn-primary" type="button">Купить</button>
</div>

8. Комментарии — умеренно

<!-- Начало секции каталога -->
<section class="catalog">
  ...
</section>
<!-- /Конец секции каталога -->

<!-- Временно скрыто до запуска -->
<!-- <div class="promo-banner">...</div> -->

Комментарии полезны для разделения больших секций. Не комментируй очевидные вещи.

9. Не смешивать семантику и представление

<!-- ПЛОХО: используем теги для стилизации, не для семантики -->
<b>Важный текст</b>   <!-- b = просто жирный -->
<i>Иностранное слово</i>  <!-- i = просто курсив -->
<br /><br />  <!-- Отступы через br — нет! -->

<!-- ХОРОШО: теги по смыслу, отступы через CSS -->
<strong>Важный текст</strong>  <!-- strong = важность -->
<em>Иностранное слово</em>    <!-- em = акцент -->

10. Валидация — проверяй результат

Инструмент: validator.w3.org — официальный валидатор W3C. Вставь URL или HTML-код и получи список ошибок. В реальных проектах валидацию интегрируют в CI/CD.

Чеклист перед сдачей HTML

☐DOCTYPE есть
☐lang задан у html
☐charset UTF-8 первым в head
☐viewport для мобильных
☐title уникальный, до 60 символов
☐Все img имеют alt
☐Все input имеют label
☐Один h1 на странице
☐Заголовки в правильной иерархии
☐Теги строчные
☐Атрибуты в двойных кавычках
☐Корректная вложенность

В реальных проектах

В командах используют линтеры для HTML: HTMLHint, html-validate. Prettier форматирует HTML автоматически. Обзор кода (code review) всегда включает проверку HTML-качества. Хороший HTML — это уважение к коллегам, поисковикам и пользователям.

Примеры

Генерация и форматирование валидного HTML документа

// Генерируем корректный HTML-документ программно
function createHTMLDocument(config) {
  const lines = []

  lines.push('<!DOCTYPE html>')
  lines.push('<html lang="' + (config.lang || 'ru') + '">')
  lines.push('  <head>')
  lines.push('    <meta charset="UTF-8" />')
  lines.push('    <meta name="viewport" content="width=device-width, initial-scale=1.0" />')
  lines.push('    <title>' + config.title + '</title>')
  if (config.description) {
    lines.push('    <meta name="description" content="' + config.description + '" />')
  }
  lines.push('  </head>')
  lines.push('  <body>')
  lines.push('    <header>')
  lines.push('      <nav>')
  lines.push('        <ul>')
  config.navLinks.forEach(link => {
    lines.push('          <li><a href="' + link.href + '">' + link.text + '</a></li>')
  })
  lines.push('        </ul>')
  lines.push('      </nav>')
  lines.push('    </header>')
  lines.push('    <main>')
  lines.push('      <h1>' + config.h1 + '</h1>')
  lines.push('    </main>')
  lines.push('    <footer>')
  lines.push('      <p>' + config.footer + '</p>')
  lines.push('    </footer>')
  lines.push('  </body>')
  lines.push('</html>')

  return lines
}

const html = createHTMLDocument({
  lang: 'ru',
  title: 'Sneaker Shop — Кроссовки онлайн',
  description: 'Лучший магазин кроссовок',
  h1: 'Каталог кроссовок',
  navLinks: [
    { href: '/', text: 'Главная' },
    { href: '/catalog', text: 'Каталог' },
    { href: '/about', text: 'О нас' },
  ],
  footer: '© 2024 Sneaker Shop',
})

html.forEach(line => console.log(line))
console.log('Строк в документе:', html.length)

HTML-линтер — проверка типичных ошибок

// Простой HTML-линтер (проверяем правила через строки)
function lintHTML(htmlString) {
  const errors = []
  const warnings = []

  // Проверка DOCTYPE
  if (!htmlString.includes('<!DOCTYPE html>')) {
    errors.push('Нет <!DOCTYPE html>')
  }

  // Проверка lang
  if (!htmlString.includes('lang=')) {
    errors.push('Нет атрибута lang у <html>')
  }

  // Проверка charset
  if (!htmlString.includes('charset=')) {
    errors.push('Нет meta charset')
  }

  // Проверка title
  if (!htmlString.includes('<title>')) {
    errors.push('Нет тега <title>')
  }

  // Проверка заглавных тегов
  if (/<[A-Z]/.test(htmlString)) {
    warnings.push('Найдены теги в верхнем регистре — используй строчные')
  }

  // Проверка атрибутов без кавычек
  if (/=w+[s>]/.test(htmlString.replace(/="[^"]*"/g, ''))) {
    warnings.push('Возможны атрибуты без кавычек')
  }

  // Проверка img без alt
  const imgWithoutAlt = (htmlString.match(/<img(?![^>]*alt=)[^>]*>/g) || []).length
  if (imgWithoutAlt > 0) {
    errors.push('Найдено ' + imgWithoutAlt + ' тег(ов) img без alt')
  }

  return { errors, warnings }
}

const badHTML = `<HTML>
  <head>
    <title>Мой сайт</title>
  </head>
  <body>
    <IMG src="photo.jpg" />
    <div class=wrapper>Контент</div>
  </body>
</HTML>`

const result = lintHTML(badHTML)
console.log('Ошибок:', result.errors.length)
result.errors.forEach(e => console.log('ОШИБКА: ' + e))
console.log('Предупреждений:', result.warnings.length)
result.warnings.forEach(w => console.log('ПРЕДУПРЕЖДЕНИЕ: ' + w))

Правила хорошего HTML

Ты уже знаешь все основные теги и концепции HTML. Теперь поговорим о том, что отличает профессионала от новичка: правильная структура, именование, форматирование и валидация. Хороший HTML — это не просто «работает», это «работает, понятно и поддерживается».

1. Валидная структура документа

Каждый HTML-документ должен начинаться с корректной базовой структуры:

<!DOCTYPE html>
<html lang="ru">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Описательный заголовок страницы</title>
  </head>
  <body>
    <!-- Контент -->
  </body>
</html>

2. Правильная вложенность тегов

<!-- ПЛОХО: нарушена вложенность -->
<p>Текст <strong>жирный <em>жирный-курсив</strong> курсив</em></p>

<!-- ХОРОШО: теги закрываются в обратном порядке -->
<p>Текст <strong>жирный <em>жирный-курсив</em></strong> <em>курсив</em></p>

HTML — это дерево. Каждый открытый тег должен быть закрыт до того, как закроется его родитель.

3. Строчные буквы везде

<!-- ПЛОХО: заглавные теги -->
<DIV CLASS="header">
  <H1>Заголовок</H1>
</DIV>

<!-- ХОРОШО: строчные теги -->
<div class="header">
  <h1>Заголовок</h1>
</div>

HTML нечувствителен к регистру, но конвенция — всегда строчные.

4. Самозакрывающиеся теги

Теги без содержимого (void elements) должны правильно закрываться:

<!-- В HTML5 оба варианта корректны, но <tag /> предпочтительнее для JSX -->
<img src="photo.jpg" alt="Фото" />
<br />
<hr />
<input type="text" />
<meta charset="UTF-8" />

5. Атрибуты в двойных кавычках

<!-- ПЛОХО: без кавычек или одинарные кавычки -->
<img src=photo.jpg alt=Фото />
<a href='https://example.com'>Ссылка</a>

<!-- ХОРОШО: двойные кавычки -->
<img src="photo.jpg" alt="Фото" />
<a href="https://example.com">Ссылка</a>

6. Осмысленные имена классов и id

<!-- ПЛОХО: непонятные имена -->
<div class="d1 red big">...</div>
<div id="x">...</div>

<!-- ХОРОШО: имена описывают назначение, не внешний вид -->
<div class="product-card featured">...</div>
<div id="checkout-modal">...</div>

Используй kebab-case для классов и id. Называй по назначению, а не по виду: не red-text, а error-message.

7. Отступы и форматирование

<!-- ПЛОХО: всё в одну строку -->
<div class="card"><img src="prod.jpg" alt="Товар" /><h3>Nike Air Max</h3><p class="price">7 990 ₽</p><button>Купить</button></div>

<!-- ХОРОШО: отступы 2 пробела, каждый элемент на своей строке -->
<div class="product-card">
  <img src="product.jpg" alt="Кроссовки Nike Air Max 90" />
  <h3 class="product-title">Nike Air Max 90</h3>
  <p class="product-price">7 990 ₽</p>
  <button class="btn btn-primary" type="button">Купить</button>
</div>

8. Комментарии — умеренно

<!-- Начало секции каталога -->
<section class="catalog">
  ...
</section>
<!-- /Конец секции каталога -->

<!-- Временно скрыто до запуска -->
<!-- <div class="promo-banner">...</div> -->

Комментарии полезны для разделения больших секций. Не комментируй очевидные вещи.

9. Не смешивать семантику и представление

<!-- ПЛОХО: используем теги для стилизации, не для семантики -->
<b>Важный текст</b>   <!-- b = просто жирный -->
<i>Иностранное слово</i>  <!-- i = просто курсив -->
<br /><br />  <!-- Отступы через br — нет! -->

<!-- ХОРОШО: теги по смыслу, отступы через CSS -->
<strong>Важный текст</strong>  <!-- strong = важность -->
<em>Иностранное слово</em>    <!-- em = акцент -->

10. Валидация — проверяй результат

Инструмент: validator.w3.org — официальный валидатор W3C. Вставь URL или HTML-код и получи список ошибок. В реальных проектах валидацию интегрируют в CI/CD.

Чеклист перед сдачей HTML

☐DOCTYPE есть
☐lang задан у html
☐charset UTF-8 первым в head
☐viewport для мобильных
☐title уникальный, до 60 символов
☐Все img имеют alt
☐Все input имеют label
☐Один h1 на странице
☐Заголовки в правильной иерархии
☐Теги строчные
☐Атрибуты в двойных кавычках
☐Корректная вложенность

В реальных проектах

В командах используют линтеры для HTML: HTMLHint, html-validate. Prettier форматирует HTML автоматически. Обзор кода (code review) всегда включает проверку HTML-качества. Хороший HTML — это уважение к коллегам, поисковикам и пользователям.

Примеры

Генерация и форматирование валидного HTML документа

// Генерируем корректный HTML-документ программно
function createHTMLDocument(config) {
  const lines = []

  lines.push('<!DOCTYPE html>')
  lines.push('<html lang="' + (config.lang || 'ru') + '">')
  lines.push('  <head>')
  lines.push('    <meta charset="UTF-8" />')
  lines.push('    <meta name="viewport" content="width=device-width, initial-scale=1.0" />')
  lines.push('    <title>' + config.title + '</title>')
  if (config.description) {
    lines.push('    <meta name="description" content="' + config.description + '" />')
  }
  lines.push('  </head>')
  lines.push('  <body>')
  lines.push('    <header>')
  lines.push('      <nav>')
  lines.push('        <ul>')
  config.navLinks.forEach(link => {
    lines.push('          <li><a href="' + link.href + '">' + link.text + '</a></li>')
  })
  lines.push('        </ul>')
  lines.push('      </nav>')
  lines.push('    </header>')
  lines.push('    <main>')
  lines.push('      <h1>' + config.h1 + '</h1>')
  lines.push('    </main>')
  lines.push('    <footer>')
  lines.push('      <p>' + config.footer + '</p>')
  lines.push('    </footer>')
  lines.push('  </body>')
  lines.push('</html>')

  return lines
}

const html = createHTMLDocument({
  lang: 'ru',
  title: 'Sneaker Shop — Кроссовки онлайн',
  description: 'Лучший магазин кроссовок',
  h1: 'Каталог кроссовок',
  navLinks: [
    { href: '/', text: 'Главная' },
    { href: '/catalog', text: 'Каталог' },
    { href: '/about', text: 'О нас' },
  ],
  footer: '© 2024 Sneaker Shop',
})

html.forEach(line => console.log(line))
console.log('Строк в документе:', html.length)

HTML-линтер — проверка типичных ошибок

// Простой HTML-линтер (проверяем правила через строки)
function lintHTML(htmlString) {
  const errors = []
  const warnings = []

  // Проверка DOCTYPE
  if (!htmlString.includes('<!DOCTYPE html>')) {
    errors.push('Нет <!DOCTYPE html>')
  }

  // Проверка lang
  if (!htmlString.includes('lang=')) {
    errors.push('Нет атрибута lang у <html>')
  }

  // Проверка charset
  if (!htmlString.includes('charset=')) {
    errors.push('Нет meta charset')
  }

  // Проверка title
  if (!htmlString.includes('<title>')) {
    errors.push('Нет тега <title>')
  }

  // Проверка заглавных тегов
  if (/<[A-Z]/.test(htmlString)) {
    warnings.push('Найдены теги в верхнем регистре — используй строчные')
  }

  // Проверка атрибутов без кавычек
  if (/=w+[s>]/.test(htmlString.replace(/="[^"]*"/g, ''))) {
    warnings.push('Возможны атрибуты без кавычек')
  }

  // Проверка img без alt
  const imgWithoutAlt = (htmlString.match(/<img(?![^>]*alt=)[^>]*>/g) || []).length
  if (imgWithoutAlt > 0) {
    errors.push('Найдено ' + imgWithoutAlt + ' тег(ов) img без alt')
  }

  return { errors, warnings }
}

const badHTML = `<HTML>
  <head>
    <title>Мой сайт</title>
  </head>
  <body>
    <IMG src="photo.jpg" />
    <div class=wrapper>Контент</div>
  </body>
</HTML>`

const result = lintHTML(badHTML)
console.log('Ошибок:', result.errors.length)
result.errors.forEach(e => console.log('ОШИБКА: ' + e))
console.log('Предупреждений:', result.warnings.length)
result.warnings.forEach(w => console.log('ПРЕДУПРЕЖДЕНИЕ: ' + w))

Задание

Напиши валидный HTML-документ по всем правилам хорошего HTML: правильная структура с DOCTYPE, lang, charset, viewport, уникальный title до 60 символов, h1 на странице один, все img с alt, теги строчные, атрибуты в двойных кавычках, классы в kebab-case.

Подсказка

lang="ru" для русскоязычного сайта. charset="UTF-8". viewport: width=device-width, initial-scale=1.0. Один h1 на страницу. img всегда с alt — пустой alt="" для декоративных. Теги строчные: <header>, <main>, <footer>. Классы в kebab-case: page-container, site-header, site-footer.

Загружаем среду выполнения...
Загружаем AI-помощника...