HTML

Модуль 1: HTML

В этом модуле вы освоите базовую разметку HTML: узнаете, как устроен каркас документа, какие теги бывают, как использовать атрибуты, семантические элементы и создавать формы.

Подготовительный этап (0.1–0.2): установка редактора (VS Code), браузеров (Chrome/Firefox), Git и GitHub, публикация «Hello, World!» на GitHub Pages.

Введение в HTML. Базовый каркас документа

Представьте, что вы строите дом. HTML — это как чертёж и каркас здания. Он не красит стены (это делает CSS) и не добавляет электричество (это JavaScript), но без него не было бы ни стен, ни комнат, ни самого дома. HTML (HyperText Markup Language) — это язык разметки, который создаёт структуру веб-страницы. Он не программирует логику, а просто говорит браузеру: "здесь заголовок", "здесь абзац", "здесь кнопка". Основные строительные блоки HTML — это элементы (теги) и их атрибуты.

Что такое HTML и зачем он нужен

Если бы интернет был книгой, HTML был бы её содержанием и структурой. Он определяет, где находится каждая глава, где начинается новый абзац, какие слова важные (заголовки), а какие обычные (текст). Браузер читает этот "чертёж" и превращает его в красивую страницу, которую вы видите на экране. Без HTML веб-страница просто не существовала бы — это фундамент, на котором строится всё остальное.

Давайте разберёмся с основными понятиями на простых примерах:

  • Элемент — это как контейнер с содержимым. Представьте коробку с подарком: <p> — это открывающая крышка, Текст абзаца — это сам подарок, а </p> — закрывающая крышка. Получается: <p>Текст абзаца</p>. Внутри элемента может быть текст, другие элементы (как матрёшка) или он может быть пустым.
  • Атрибут — это как наклейка на коробке, которая говорит: "хрупкое", "верх не переворачивать" или "адрес получателя". Например, в ссылке <a href="/about" target="_blank">О нас</a> атрибут href — это адрес, куда ведёт ссылка, а target="_blank" — инструкция открыть её в новой вкладке. Без атрибутов элементы были бы "слепыми" — они бы знали, что это ссылка, но не знали бы, куда она ведёт.
  • Структура документа — это как паспорт и адрес дома. Каждый HTML-документ должен иметь обязательные элементы: <!DOCTYPE html> говорит браузеру "это современный HTML", <html> — корневой контейнер (весь документ), <head> — "служебная информация" (метаданные, стили, скрипты), <body> — "видимое содержимое" (то, что видит пользователь). Без этой структуры браузер не поймёт, что делать с вашим кодом.

Как браузер превращает код в страницу: магия DOM

Вы написали HTML-код, нажали F5, и на экране появилась красивая страница. Что произошло за эти миллисекунды? Браузер проделал удивительную работу, превратив ваш текст в визуальную реальность. Давайте заглянем "под капот":

  1. Парсинг — чтение кода — браузер читает ваш HTML-код, как человек читает книгу, но со скоростью света. Он анализирует каждую строку, распознаёт теги, атрибуты и содержимое. Всё это преобразуется в дерево DOM (Document Object Model) — особую структуру данных, похожую на генеалогическое древо.
  2. Построение DOM — создание структуры — представьте семейное дерево: <html> — это прародитель, <body> — его ребёнок, <div> — внук, а <p> внутри — правнук. Каждый элемент становится узлом этого дерева со своими атрибутами (как характеристики человека) и дочерними элементами (как дети). Это создаёт иерархию, которую браузер понимает.
  3. Рендеринг — рисование на экране — теперь браузер берёт это дерево и "рисует" его на экране. Он смотрит на каждый элемент: "Это заголовок? Сделаю его большим и жирным. Это ссылка? Подчеркну и сделаю синим." CSS добавляет краски и стили, превращая чёрно-белую структуру в красочную страницу.
  4. Интерактивность — оживление — JavaScript может "шевелить ветками" этого дерева в реальном времени. Нажали кнопку? JavaScript добавил новый элемент в DOM. Ввели текст? DOM обновился. Всё это происходит без перезагрузки страницы — магия современного веба!

DOM — это не просто структура, это живой организм. Представьте, что DOM — это кукольный театр, а JavaScript — кукловод, который может двигать кукол, менять их костюмы и даже создавать новых персонажей прямо во время представления. Любые изменения в DOM мгновенно отражаются на экране — это и есть основа интерактивности современных сайтов.

Пустые элементы: теги-одиночки

Большинство HTML-элементов — это "контейнеры" с открывающим и закрывающим тегом. Но есть особые элементы, которые не могут содержать внутри себя ничего — они самодостаточные, как дверной звонок или выключатель. Их называют пустыми (void) элементами, и они не требуют закрывающего тега.

Почему они пустые? Потому что их "содержимое" — это не текст, а что-то другое: файл изображения, метаданные или просто действие (перенос строки). Вот основные "одиночки":

  • <br> — перенос строки. Как нажатие Enter в текстовом редакторе. Совет: используйте осторожно! Чаще лучше управлять отступами через CSS — это даёт больше контроля.
  • <img> — изображение. Его содержимое — это сам файл картинки, а не текст. Поэтому тег самозакрывающийся: <img src="photo.jpg" alt="Описание">.
  • <input> — поле ввода формы. Это как пустая коробочка, куда пользователь вводит данные. Само по себе поле не содержит текста — текст появляется, когда пользователь что-то печатает.
  • <meta> — метаинформация о странице. Это "заметки на полях" для браузера и поисковиков. Они не видны пользователю, но очень важны для SEO.
  • <link> — подключение внешних ресурсов. Как провод, который подключает CSS-файл или иконку сайта. Сам тег ничего не содержит, он просто указывает путь к файлу.
  • <hr> — горизонтальная линия-разделитель. Визуально разделяет контент, как линия в тетради между темами.

Интересный факт: В HTML5 можно писать эти теги как <br> или <br /> — оба варианта валидны. Но если вы работаете с XHTML (более строгий стандарт), обязательно нужен слэш: <br />. Большинство современных разработчиков используют короткий вариант без слэша.

Глобальные атрибуты: универсальные инструменты

Представьте, что у вас есть набор универсальных инструментов, которые работают с любым элементом на странице. Это и есть глобальные атрибуты — они как швейцарский нож веб-разработчика. Их можно применить к любому тегу, и они всегда работают одинаково. Давайте познакомимся с самыми полезными:

  • id — это как паспорт элемента, его уникальное имя. Представьте класс в школе: у каждого ученика уникальный номер в журнале. Так и здесь — id должен быть единственным на странице. Используется для создания якорей (ссылок внутри страницы типа #section1), для JavaScript-селекторов и CSS. Важно помнить: если два элемента имеют одинаковый id, браузер запутается — это как если бы у двух людей был один паспорт!
  • class — это как ярлык или категория. В отличие от id, класс можно использовать многократно. Представьте наклейки: наклейка "важное" может быть на нескольких документах. Один элемент может иметь несколько классов через пробел: class="button primary large" — это элемент одновременно и кнопка, и основная, и большая. Очень удобно для стилизации!
  • data‑* — это ваш личный "карман" для хранения информации. Можно создать любой атрибут вида data-название="значение", например data-user-id="123" или data-price="999". Это как заметки, которые вы приклеиваете к элементу. JavaScript может легко их прочитать: element.dataset.userId (обратите внимание: дефисы превращаются в camelCase). Очень полезно для передачи данных из HTML в JavaScript!
  • aria‑* — это "переводчик" для людей с ограниченными возможностями. Скринридеры (программы, которые читают страницу вслух) используют эти атрибуты, чтобы понять, что происходит на странице. Например, aria-label="Закрыть меню" подскажет, что кнопка закрывает меню, даже если на ней только иконка. Делая сайт доступным, вы помогаете миллионам людей!
  • hidden — это как невидимая шапка-невидимка. Элемент остаётся в коде (в DOM), но пользователь его не видит. Полезно, когда элемент должен быть скрыт по умолчанию, но может появиться при определённых условиях. В отличие от display: none в CSS, это семантический способ скрытия.
  • title — это как подсказка в игре. При наведении курсора появляется маленькое окошко с дополнительной информацией. Полезно для уточнений, но помните: не все пользователи видят подсказки (особенно на мобильных), поэтому не кладите туда критически важную информацию.
  • tabindex — это как номер в очереди для клавиатурной навигации. Когда пользователь нажимает Tab, фокус переходит от элемента к элементу. tabindex="0" — элемент в естественном порядке, tabindex="-1" — элемент пропускается при Tab, но может получить фокус программно. Осторожно: положительные значения (tabindex="1" и выше) — это как "пройти без очереди", они нарушают естественный порядок и могут запутать пользователей.
  • lang — это как флаг страны для текста. Браузер, поисковики и скринридеры используют его, чтобы правильно обработать текст. Например, lang="ru" для русского или lang="en" для английского. Это особенно важно для правильного произношения скринридерами и для SEO.
  • contenteditable — это как превращение обычного текста в блокнот. Элемент становится редактируемым прямо на странице, как в Google Docs. Используется редко, в основном для WYSIWYG-редакторов (What You See Is What You Get — "что видишь, то и получаешь").

Пути к файлам: как найти нужный файл

Представьте, что вы в большом городе и вам нужно найти адрес. В HTML тоже есть "адреса" — пути к файлам. Когда вы хотите подключить картинку, стили или перейти на другую страницу, нужно указать, где этот файл находится. Есть три способа "навигации" по файловой структуре сайта, и каждый подходит для своих задач:

1. Абсолютные пути — полный адрес с индексом

Это как сказать: "Москва, улица Ленина, дом 10, квартира 5" — полный адрес от страны до квартиры. В вебе это выглядит так: https://site.ru/page или http://example.com/images/logo.png.

Когда это полезно: когда вы ссылаетесь на внешние сайты (другие домены), подключаете ресурсы с CDN (Content Delivery Network — сеть доставки контента), или когда нужно гарантировать, что путь точно не изменится.

Подводные камни: если вы решите сменить домен (например, с mysite.com на newsite.com), все абсолютные пути сломаются, и вам придётся искать и заменять их по всему сайту. Также они не работают, если вы просто открываете HTML-файл в браузере без сервера — браузер не знает, что такое "site.ru".

2. Относительные пути — "от того места, где я нахожусь"

Это как сказать: "иди на два квартала вперёд, потом поверни направо" — инструкция относительно вашего текущего положения. Браузер ищет файл, начиная от места, где находится текущий HTML-файл.

Примеры "навигации":

  • about.html — "файл в той же папке, где я нахожусь" (как сосед по лестничной площадке)
  • images/logo.png — "зайди в папку images, там найдёшь logo.png" (как "спустись на этаж ниже")
  • ../img/cat.png — "поднимись на уровень выше (родительская папка), затем зайди в img" (две точки .. означают "на уровень выше")
  • ../../css/styles.css — "поднимись на два уровня выше, затем зайди в css" (как "поднимись на два этажа")

Когда это удобно: для всех внутренних ссылок на вашем сайте. Это самый гибкий вариант для локальной разработки — вы можете перемещать папки, и пути будут работать, пока структура относительно друг друга не изменится.

Важный нюанс: относительные пути — это как маршрут от точки A до точки B. Если вы переместите точку A (ваш HTML-файл), маршрут может сломаться. Например, если файл был в /pages/about.html и ссылался на ../images/logo.png, а вы переместили его в /about.html, путь сломается.

3. Пути от корня сайта — "от главного входа"

Это как сказать: "от главного входа в здание иди в комнату 205" — всегда от одной фиксированной точки. Путь начинается со слэша /, что означает "корень сайта" (главная папка вашего сайта). Примеры: /images/icon.ico, /css/styles.css, /about.html.

Когда это идеально: когда файл всегда находится в одном и том же месте относительно корня сайта, независимо от того, на какой странице вы находитесь. Это самый надёжный вариант для продакшена (готового сайта).

Преимущества: пути не ломаются, когда вы перемещаете страницы по структуре сайта. Легко понять структуру — сразу видно, что /images/ — это папка с изображениями в корне.

Ограничение: не работают, если вы просто открываете HTML-файл двойным кликом в браузере (без запущенного сервера). Браузер не знает, что такое "корень сайта" без сервера. Но это не проблема, если вы используете локальный сервер для разработки (что и рекомендуется).

Практический совет от опытных разработчиков: Используйте пути от корня (/) для продакшена — это надёжнее. Для локальной разработки можно использовать относительные пути, если вам так удобнее. Многие современные сборщики (Webpack, Vite, Parcel) автоматически преобразуют относительные пути в правильные при сборке проекта, так что вы можете писать как удобно, а они всё исправят.

Экранирование специальных символов: когда символы "бунтуют"

Представьте ситуацию: вы хотите написать в тексте "<div>", но браузер видит это и думает: "О! Это HTML-тег, нужно его обработать!" И вместо текста на странице появляется пустой блок. Проблема? Да! Решение? Экранирование.

Некоторые символы в HTML имеют "магические" свойства — браузер воспринимает их как команды, а не как обычный текст. Чтобы показать эти символы как текст, нужно их "экранировать" — заменить на специальные коды, которые браузер понимает как "это просто символ, не команда".

Вот основные "бунтари" и как с ними справиться (HTML-сущности):

  • &lt; или &#60;< (символ "меньше", открывающая скобка тега)
  • &gt; или &#62;> (символ "больше", закрывающая скобка тега)
  • &amp; или &#38;& (амперсанд, используется для начала сущности)
  • &quot; или &#34;" (двойная кавычка)
  • &apos; или &#39;' (одинарная кавычка, но можно использовать обычную)
  • &nbsp; → неразрывный пробел (полезен для предотвращения переноса слов)
  • &copy;© (символ копирайта)
  • &mdash; (длинное тире)
<!-- Примеры использования -->
<p>Чтобы написать &lt;div&gt; в тексте, используйте &amp;lt;div&amp;gt;</p>
<p>Цена: 1000&nbsp;₽ (неразрывный пробел)</p>
<p>Компания &copy; 2026</p>

Когда нужно экранировать — три главных случая:

  • Показываете код как текст — например, в учебных материалах (как сейчас в этом курсе!), в документации или в комментариях. Если вы пишете статью "Как использовать тег <div>", вам нужно экранировать угловые скобки, иначе браузер попытается создать реальный div.
  • Специальные символы в атрибутах — иногда в значении атрибута нужно использовать кавычки или другие специальные символы. Экранирование помогает браузеру понять, где заканчивается атрибут.
  • Гарантия безопасности — когда вы не уверены, как браузер интерпретирует символ, лучше перестраховаться и экранировать. Особенно это важно при динамической вставке контента через JavaScript.

Хорошая новость: В большинстве случаев современные браузеры умные и правильно обрабатывают символы даже без экранирования. Но для "большой тройки" — <, > и & — экранирование обязательно, если они не являются частью разметки. Это как правило дорожного движения: можно не всегда следовать, но лучше следовать всегда, чтобы избежать проблем.

<!DOCTYPE html>
<html lang="ru">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Моя первая страница</title>
    <meta name="description" content="Краткое описание страницы" />
    <link rel="icon" href="/images/icon.ico" type="image/x-icon" />
    <link rel="stylesheet" href="/css/styles.css" />
  </head>
  <body>
    <header><h1>О себе</h1></header>
    <main>
      <p>Привет! Это моя первая HTML‑страница.</p>
    </main>
    <footer>© 2025</footer>
    <script src="/js/script.js"></script>
  </body>
</html>

Важно: <meta name="viewport"> обеспечивает адаптивность. Атрибут lang="ru" у <html> помогает SEO и ассистивным технологиям.

Частые мета‑теги

<meta name="robots" content="index, follow" />
<meta name="author" content="Имя автора" />
<meta property="og:title" content="Заголовок для соцсетей" />

Практика

  • Создайте index.html с корректным доктайпом, lang, charset, viewport.
  • Подключите внешний CSS/JS, добавьте осмысленный <title> и description.

Работа с текстовым контентом

Текст — это душа любого веб-сайта. Без текста даже самый красивый дизайн останется пустой оболочкой. Но просто написать текст недостаточно — нужно сделать его структурированным, понятным и доступным. Представьте книгу без глав, параграфов и абзацев — читать её было бы невозможно! Так и с веб-страницами: правильная структура текста критически важна для читаемости, SEO (чтобы поисковики понимали ваш контент) и доступности (чтобы люди с ограниченными возможностями могли легко навигироваться).

Заголовки: создаём иерархию, как в книге

HTML предоставляет вам шесть уровней заголовков — от <h1> (самый важный, как название книги) до <h6> (наименее важный, как подпункт в подразделе). Это как система заголовков в учебнике: сначала название главы (h1), потом разделы (h2), подразделы (h3) и так далее. Заголовки создают "скелет" документа — они помогают и людям, и поисковым системам понять структуру и важность информации.

Правила использования заголовков:

  • <h1> — главный заголовок страницы. Должен быть только один на странице. Обычно содержит название страницы или основную тему.
  • <h2> — основные разделы страницы. Используйте для крупных тематических блоков.
  • <h3> — подразделы внутри h2. Для более детального деления контента.
  • <h4> — подпункты внутри h3. И так далее до h6.

Золотое правило: Не пропускайте уровни! Это как лестница — нельзя перепрыгнуть со второго этажа на четвёртый, минуя третий. Нельзя использовать h1, затем сразу h3, минуя h2. Представьте оглавление книги: сначала "Глава 1", потом "Раздел 1.1", затем "Подраздел 1.1.1" — всё по порядку. Правильная последовательность: h1h2h3h4... Это не просто формальность — нарушение иерархии сбивает с толку и людей, и поисковые системы.

<!-- Правильная структура заголовков -->
<h1>Главный заголовок страницы</h1>
<h2>Первый основной раздел</h2>
<h3>Подраздел первого раздела</h3>
<h2>Второй основной раздел</h2>
<h3>Подраздел второго раздела</h3>
<h4>Детали подраздела</h4>

Почему правильная иерархия заголовков — это не просто "хороший тон", а необходимость:

  • SEO — дружба с поисковиками: Google и другие поисковые системы "читают" заголовки, чтобы понять структуру и важность контента. Правильная иерархия помогает вашему сайту лучше ранжироваться в поиске. Это как правильно оформленная научная работа — её легче понять и оценить.
  • Доступность — помощь всем пользователям: скринридеры (программы для людей с нарушениями зрения) используют заголовки для навигации. Пользователь может "прыгать" от заголовка к заголовку, быстро находя нужный раздел. Представьте, что вы читаете книгу вслепую — оглавление с правильной структурой критически важно!
  • UX — удобство для всех: исследования показывают, что пользователи не читают страницы слово за словом — они сканируют их глазами, останавливаясь на заголовках. Правильная структура помогает людям быстро найти нужную информацию, не тратя время на чтение всего текста.
  • Стилизация — радость для разработчика: когда заголовки правильно структурированы, CSS-стили применяются логично и предсказуемо. Вы можете легко задать стили для всех h2 или всех h3, зная, что они используются по назначению.

Абзацы: создаём читаемый текст

Абзацы — это как дыхание в тексте. Они разбивают длинный текст на логические куски, делая его читаемым и понятным. В HTML абзацы создаются тегом <p>. Каждый абзац — это отдельный "блок" текста, который визуально отделён от других абзацев отступами (обычно сверху и снизу).

Представьте, что вы читаете статью без абзацев — сплошная стена текста. Глаза устают, сложно найти нужное место, если отвлёкся. Абзацы делают текст "дышащим" и структурированным.

<p>Это первый абзац текста. Он содержит несколько предложений, которые образуют законченную мысль. Когда мысль закончена, начинается новый абзац.</p>
<p>Это второй абзац. Он отделён от первого визуально (отступом) и логически (новой мыслью). Каждый абзац — это как отдельный "кирпичик" в здании текста.</p>

Перенос строки — инструмент, который требует осторожности: Тег <br> создаёт принудительный перенос строки внутри абзаца. Это как нажатие Enter в середине предложения. Используйте его очень осторожно! В 95% случаев лучше использовать отдельные абзацы или управлять отступами через CSS. Почему? Потому что <br> — это "жёсткий" перенос, который сложно контролировать. На разных экранах он может выглядеть по-разному, а CSS даёт вам полный контроль.

Когда <br> действительно нужен — редкие, но важные случаи:

  • Адреса — каждая строка адреса должна быть на новой строке: улица, дом, город. Это не разные абзацы, а части одного адреса.
  • Стихи или тексты с фиксированной структурой — когда важна именно структура строк, а не смысловые блоки.
  • Короткие подписи — например, под изображением, где нужен перенос, но это не полноценный абзац.

Во всех остальных случаях — используйте <p> для абзацев и CSS для управления отступами. Ваш будущий я (и ваши коллеги) скажут вам спасибо!

Когда НЕ использовать <br>:

  • Для создания отступов между абзацами (используйте <p> или CSS margin)
  • Для создания списков (используйте <ul> или <ol>)
  • Для структурирования контента (используйте семантические теги)
<!-- Плохо: много <br> для структуры -->
<p>Имя: Иван<br>Фамилия: Петров<br>Email: ivan@example.com</p>

<!-- Хорошо: используйте списки или структурированные данные -->
<dl>
  <dt>Имя:</dt>
  <dd>Иван</dd>
  <dt>Фамилия:</dt>
  <dd>Петров</dd>
</dl>

Списки

<ul>
  <li>Маркированный пункт</li>
  <li>Ещё пункт</li>
</ul>
<ol>
  <li>Нумерованный пункт</li>
  <li>Ещё пункт</li>
</ol>
<dl>
  <dt>Термин</dt>
  <dd>Определение</dd>
</dl>

Блочные и строчные элементы: два разных мира

Представьте, что HTML-элементы живут в двух разных "мирах" по способу отображения: блочные и строчные (inline). Это как разница между кирпичами и цементом: кирпичи (блочные) занимают целую строку и создают структуру, а цемент (строчные) заполняет пространство между кирпичами. Понимание этой разницы — это фундамент верстки. Без него вы будете бороться с элементами, которые ведут себя не так, как ожидаете.

Блочные элементы: "захватчики строк"

Блочные элементы — это "эгоисты" пространства. Они занимают всю доступную ширину родителя (как будто говорят: "Вся эта строка моя!") и всегда начинаются с новой строки, выталкивая следующий элемент вниз. Они создают "блок" контента — визуально отдельный прямоугольник.

Характеристики:

  • Занимают всю ширину контейнера
  • Начинаются с новой строки
  • Могут содержать другие блочные и строчные элементы
  • Можно задавать ширину, высоту, отступы (margin, padding)

Примеры блочных элементов: <div>, <p>, <section>, <article>, <header>, <footer>, <h1>-<h6>, <ul>, <ol>, <li>.

Строчные элементы: "скромные помощники"

Строчные элементы — полная противоположность блочным. Они скромные и занимают только столько места, сколько им нужно для содержимого. Они не создают новую строку и "встраиваются" в поток текста, как слова в предложении. Если блочный элемент — это отдельный абзац, то строчный — это выделенное слово внутри абзаца.

Характеристики:

  • Занимают только необходимое пространство
  • Не создают новую строку
  • Могут содержать только текст и другие строчные элементы
  • Нельзя задавать ширину и высоту (игнорируются)
  • Вертикальные отступы (margin-top, margin-bottom) не работают

Примеры строчных элементов: <span>, <a>, <em>, <strong>, <code>, <img>, <button>.

<!-- Блочные элементы -->
<div>Это блочный элемент</div>
<p>Это тоже блочный элемент</p>

<!-- Строчные элементы -->
<p>Это <strong>строчный</strong> элемент внутри <em>абзаца</em>.</p>
<span>Строчный элемент</span> <span>рядом со строчным</span>

Важно: В HTML5 появились inline-block элементы и можно изменить тип отображения через CSS свойство display. Но базовое понимание блочных и строчных элементов остаётся фундаментальным.

Пробелы и переносы: когда браузер "упрощает" ваш код

HTML имеет интересную особенность, которая часто удивляет новичков: браузер автоматически "схлопывает" множественные пробелы и переносы строк в один пробел. Представьте, что вы написали в коде:

<p>Это    текст    с    множественными    пробелами</p>

А браузер показывает: "Это текст с множественными пробелами" (все пробелы стали одним). Это сделано для удобства — вы можете форматировать код как угодно (с отступами, переносами), и браузер всё равно покажет текст правильно. Но иногда это создаёт неожиданные результаты, особенно когда вы хотите сохранить форматирование "как есть".

<!-- В коде много пробелов -->
<p>Это    текст    с    множественными    пробелами</p>

<!-- В браузере отобразится как: -->
<!-- Это текст с множественными пробелами (все пробелы схлопнуты в один) -->

Как сохранить форматирование:

  • <pre> — сохраняет все пробелы и переносы строк как есть. Используется для кода, стихов, ASCII-арта.
  • CSS white-space: pre — сохраняет пробелы и переносы
  • CSS white-space: pre-wrap — сохраняет пробелы, но переносит текст по ширине
  • HTML-сущность &nbsp; — неразрывный пробел (не схлопывается)
<!-- Сохранение форматирования -->
<pre>
function hello() {
    console.log('Hello, World!');
}
</pre>

<!-- Или через CSS -->
<p style="white-space: pre-wrap">Текст
    с    пробелами
        и переносами</p>

Семантические инлайн‑элементы для форматирования

HTML предоставляет семантические теги для выделения текста. Важно использовать их по назначению, а не только для визуального оформления (для этого есть CSS).

  • <strong>важный текст. Используется для логически важной информации. По умолчанию отображается жирным, но смысл — в важности, а не в оформлении.
  • <em>акцент/интонация. Используется для выделения интонации или акцента. По умолчанию отображается курсивом, но смысл — в интонации.
  • <mark>выделенный текст. Используется для выделения текста, как маркером (например, результаты поиска).
  • <small>мелкий текст. Используется для мелкого текста (например, копирайт, юридическая информация).
  • <code>код. Используется для фрагментов кода внутри текста.
  • <kbd>клавиши. Используется для обозначения клавиш клавиатуры.
  • <samp>пример вывода. Используется для примеров вывода программы.
  • <var>переменная. Используется для обозначения переменных в коде.
  • <span> — универсальный строчный контейнер без семантического значения. Используется для группировки элементов для стилизации или JavaScript.
<p>
    Это <strong>важный</strong> текст. 
    Обратите <em>особое внимание</em> на это.
    Для кода используйте <code>console.log()</code>.
    Нажмите <kbd>Ctrl</kbd> + <kbd>C</kbd> для копирования.
</p>

Правило: Используйте семантические теги по их назначению, а не только для визуального оформления. Если нужно просто сделать текст жирным или курсивом без смысловой нагрузки, используйте CSS (font-weight: bold или font-style: italic).

Практика

Добавьте на страницу «О себе» разделы с заголовками, абзацами, маркированным и нумерованным списками, а также списком определений.

Семантическая разметка

Семантические теги описывают смысл блоков: поисковики и ассистивные технологии лучше понимают структуру, пользователям проще навигация.

<header>
  <nav>Меню</nav>
</header>
<main>
  <article>
    <header><h1>Заголовок статьи</h1></header>
    <section aria-labelledby="s1">
      <h2 id="s1">Основной контент</h2>
      ...
    </section>
    <footer>Автор и дата</footer>
  </article>
  <aside>Боковая колонка</aside>
</main>
<footer>Подвал</footer>

Лендмарки доступности

  • <header>, <footer> — могут быть у страницы и у статьи/секции
  • <main> — только один на странице, основной контент
  • <nav> — группы ссылок (меню/хлебные крошки)
  • <aside> — боковой/вспомогательный контент
  • <article> — самостоятельный, самодостаточный блок (статья, карточка)

Если подходящего тега нет — используйте <div> (блочный) или <span> (строчный) и при необходимости ARIA‑атрибуты.

Практика

Преобразуйте страницу «О себе» в семантический макет с header, nav, main, article, aside, footer.

Формы: сбор данных от пользователей

Формы — это основной способ взаимодействия пользователей с веб-сайтом. Они позволяют собирать данные, отправлять запросы, выполнять поиск и многое другое. Правильная работа с формами критически важна для пользовательского опыта и безопасности.

Структура формы

Форма создаётся тегом <form>, который содержит поля ввода и кнопку отправки. Каждая форма должна иметь атрибуты action (куда отправлять) и method (как отправлять).

<form action="/submit" method="post" autocomplete="on" novalidate>
    <!-- Поля формы -->
    <button type="submit">Отправить</button>
</form>

Атрибуты формы

  • action — URL, куда отправляются данные. Может быть относительным (/submit) или абсолютным (https://api.example.com/submit). Если не указан, форма отправляется на текущую страницу.
  • method — HTTP-метод отправки: GET или POST (по умолчанию GET).
  • autocomplete — включает/выключает автозаполнение браузера. Значения: on (включено) или off (выключено).
  • novalidate — отключает встроенную HTML5-валидацию. Используйте, если валидация выполняется через JavaScript.
  • enctype — тип кодирования данных. По умолчанию application/x-www-form-urlencoded. Для загрузки файлов используйте multipart/form-data.

Типы полей ввода

HTML5 предоставляет множество типов полей <input>, каждый оптимизирован для своего типа данных:

Текстовые поля

<!-- Обычный текст -->
<input type="text" name="username" placeholder="Введите имя">

<!-- Email с валидацией -->
<input type="email" name="email" required>

<!-- Пароль (скрывается) -->
<input type="password" name="password" minlength="8">

<!-- Поиск -->
<input type="search" name="q" placeholder="Поиск...">

<!-- URL -->
<input type="url" name="website">

<!-- Телефон -->
<input type="tel" name="phone" pattern="[0-9]{10}">

<!-- Число -->
<input type="number" name="age" min="18" max="100" step="1">

<!-- Ползунок для чисел -->
<input type="range" name="volume" min="0" max="100" value="50">

Дата и время

<!-- Дата -->
<input type="date" name="birthday">

<!-- Время -->
<input type="time" name="meeting-time">

<!-- Дата и время -->
<input type="datetime-local" name="event-datetime">

<!-- Месяц -->
<input type="month" name="month">

<!-- Неделя -->
<input type="week" name="week">

Выбор значений

<!-- Чекбокс (можно выбрать несколько) -->
<label>
    <input type="checkbox" name="interests" value="coding" checked>
    Программирование
</label>
<label>
    <input type="checkbox" name="interests" value="design">
    Дизайн
</label>

<!-- Радио-кнопки (можно выбрать только одно) -->
<label>
    <input type="radio" name="gender" value="male" checked>
    Мужской
</label>
<label>
    <input type="radio" name="gender" value="female">
    Женский
</label>

<!-- Выпадающий список -->
<select name="country" required>
    <option value="">Выберите страну</option>
    <option value="ru" selected>Россия</option>
    <option value="us">США</option>
    <option value="de">Германия</option>
</select>

<!-- Множественный выбор -->
<select name="languages" multiple size="4">
    <option value="ru">Русский</option>
    <option value="en">Английский</option>
    <option value="de">Немецкий</option>
</select>

Специальные поля

<!-- Цвет -->
<input type="color" name="fav-color" value="#4f46e5">

<!-- Файл -->
<input type="file" name="avatar" accept="image/*">

<!-- Скрытое поле (не видно пользователю) -->
<input type="hidden" name="token" value="abc123">

<!-- Кнопка отправки -->
<button type="submit">Отправить</button>

<!-- Кнопка сброса -->
<button type="reset">Очистить</button>

<!-- Обычная кнопка (для JavaScript) -->
<button type="button">Нажми меня</button>

Многострочный текст: textarea

Для длинных текстов используйте <textarea>:

<label for="message">Сообщение</label>
<textarea 
    id="message" 
    name="message" 
    rows="6" 
    cols="50"
    placeholder="Введите ваше сообщение..."
    maxlength="500"
    required
></textarea>
<small>Осталось символов: <span id="char-count">500</span></small>

Атрибуты:

  • rows — количество видимых строк (можно изменить через CSS)
  • cols — количество видимых символов в строке (устаревший, лучше управлять через CSS width)
  • maxlength — максимальное количество символов
  • wrap — перенос строк: soft (по умолчанию) или hard

Связывание label и input

Каждое поле должно иметь связанную метку (<label>). Это критически важно для доступности и UX.

Способ 1: через атрибут for (рекомендуется)

<label for="email">Email адрес</label>
<input id="email" type="email" name="email" required>
<!-- При клике на label фокус переходит на input -->

Способ 2: обёртка

<label>
    Email адрес
    <input type="email" name="email" required>
</label>
<!-- label автоматически связан с input внутри -->

Почему это важно:

  • Скринридеры объявляют метку при фокусе на поле
  • Клик по метке активирует поле (особенно удобно для чекбоксов и радио)
  • Улучшает UX на мобильных устройствах
  • Помогает SEO и поисковым системам

Валидация форм

HTML5 предоставляет встроенную валидацию без JavaScript. Это первая линия защиты от некорректных данных.

Атрибуты валидации

  • required — поле обязательно для заполнения
  • minlength / maxlength — минимальная/максимальная длина текста
  • min / max — минимальное/максимальное значение для чисел
  • step — шаг для чисел (например, 0.5 для десятичных)
  • pattern — регулярное выражение для проверки формата
  • type — тип поля автоматически валидируется (email, url, number и т.д.)
<!-- Примеры валидации -->
<input 
    type="email" 
    name="email" 
    required 
    placeholder="example@mail.com"
>

<input 
    type="password" 
    name="password" 
    required 
    minlength="8"
    pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}"
    title="Пароль должен содержать минимум 8 символов, включая цифру, строчную и заглавную букву"
>

<input 
    type="number" 
    name="age" 
    min="18" 
    max="100" 
    step="1"
    required
>

<input 
    type="tel" 
    name="phone" 
    pattern="[0-9]{10}" 
    placeholder="1234567890"
    title="Введите 10 цифр"
>

Сообщения об ошибках

Браузер автоматически показывает сообщения об ошибках при невалидных данных. Можно кастомизировать через JavaScript или использовать атрибут title для подсказки.

<input 
    type="email" 
    name="email" 
    required
    oninvalid="this.setCustomValidity('Пожалуйста, введите корректный email')"
    oninput="this.setCustomValidity('')"
>

Группировка полей: fieldset и legend

Для логической группировки связанных полей используйте <fieldset> и <legend>:

<fieldset>
    <legend>Контактная информация</legend>
    
    <label for="name">Имя</label>
    <input id="name" type="text" name="name" required>
    
    <label for="email">Email</label>
    <input id="email" type="email" name="email" required>
</fieldset>

<fieldset>
    <legend>Предпочтения</legend>
    
    <label>
        <input type="checkbox" name="news">
        Подписаться на новости
    </label>
    
    <label>
        <input type="checkbox" name="promo">
        Получать промо-предложения
    </label>
</fieldset>

Преимущества: улучшает доступность (скринридеры объявляют группу), визуально группирует связанные поля, улучшает UX.

GET vs POST: когда что использовать

Выбор метода отправки формы критически важен для безопасности и правильной работы приложения.

GET — для получения данных

Характеристики:

  • Данные отправляются в URL как параметры: ?name=Иван&email=ivan@mail.com
  • Запрос можно закладкировать, отправить ссылкой
  • Ограничение длины URL (обычно ~2000 символов)
  • Данные видны в истории браузера, логах сервера
  • Идемпотентный — повторный запрос не изменяет состояние

Когда использовать:

  • Поисковые запросы
  • Фильтры и сортировка
  • Пагинация
  • Любые запросы, которые только читают данные
<!-- Форма поиска -->
<form action="/search" method="get">
    <input type="search" name="q" placeholder="Поиск...">
    <button type="submit">Найти</button>
</form>
<!-- Результат: /search?q=python -->

POST — для отправки данных

Характеристики:

  • Данные отправляются в теле запроса, не видны в URL
  • Нет ограничений по размеру (практически)
  • Безопаснее для чувствительных данных
  • Не идемпотентный — повторная отправка может создать дубликаты
  • Нельзя закладкировать результат

Когда использовать:

  • Регистрация и авторизация
  • Отправка форм обратной связи
  • Загрузка файлов
  • Любые действия, которые изменяют данные на сервере
<!-- Форма регистрации -->
<form action="/register" method="post" enctype="application/x-www-form-urlencoded">
    <input type="text" name="username" required>
    <input type="email" name="email" required>
    <input type="password" name="password" required>
    <button type="submit">Зарегистрироваться</button>
</form>
<!-- Данные отправляются в теле запроса, не видны в URL -->

Важно для безопасности:

  • Никогда не используйте GET для паролей, персональных данных, финансовых операций
  • Валидация на клиенте (HTML5) — это только удобство для пользователя. Всегда валидируйте данные на сервере
  • Для защиты от CSRF-атак используйте токены
  • Используйте HTTPS для всех форм с чувствительными данными

Атрибуты autocomplete: улучшение UX

Атрибут autocomplete помогает браузерам и менеджерам паролей правильно заполнять формы. Это значительно улучшает пользовательский опыт.

<form autocomplete="on">
    <input type="text" name="name" autocomplete="name">
    <input type="email" name="email" autocomplete="email">
    <input type="tel" name="phone" autocomplete="tel">
    <input type="text" name="address" autocomplete="street-address">
    <input type="text" name="city" autocomplete="address-level2">
    <input type="text" name="zip" autocomplete="postal-code">
    <input type="password" name="current-password" autocomplete="current-password">
    <input type="password" name="new-password" autocomplete="new-password">
</form>

Популярные значения autocomplete:

  • name, given-name, family-name — имя
  • email — email адрес
  • tel — телефон
  • street-address, address-level1 (регион), address-level2 (город), postal-code — адрес
  • current-password, new-password — пароли
  • cc-name, cc-number, cc-exp — данные кредитной карты

Загрузка файлов

Для загрузки файлов нужно:

  1. Использовать type="file"
  2. Установить enctype="multipart/form-data" в форме
  3. Использовать метод POST
<form action="/upload" method="post" enctype="multipart/form-data">
    <label for="avatar">Загрузить аватар</label>
    <input 
        id="avatar" 
        type="file" 
        name="avatar" 
        accept="image/png, image/jpeg, image/webp"
        multiple
    >
    <small>Максимальный размер: 5MB. Форматы: PNG, JPEG, WebP</small>
    <button type="submit">Загрузить</button>
</form>

Атрибут accept: ограничивает типы файлов, которые можно выбрать. Можно указать MIME-типы (image/*, application/pdf) или расширения (.pdf,.doc).

Практические советы по формам

  • Всегда используйте label: не заменяйте его на placeholder. Placeholder — это подсказка, label — это описание поля.
  • Группируйте связанные поля: используйте fieldset для визуальной и логической группировки
  • Показывайте прогресс: для длинных форм используйте индикаторы прогресса или разбивайте на шаги
  • Валидируйте в реальном времени: показывайте ошибки сразу, а не только при отправке
  • Используйте правильные типы полей: это улучшает UX на мобильных (правильная клавиатура) и валидацию
  • Предоставляйте обратную связь: показывайте сообщения об успешной отправке или ошибках
  • Защищайте от спама: используйте CAPTCHA, honeypot-поля или другие методы

Практика

Создайте полноценную форму регистрации со следующими полями:

  • Имя и фамилия (отдельные поля)
  • Email с валидацией
  • Пароль с требованиями (минимум 8 символов, буквы и цифры)
  • Подтверждение пароля
  • Дата рождения (date picker)
  • Пол (radio buttons)
  • Интересы (checkboxes — можно выбрать несколько)
  • Страна (select с несколькими опциями)
  • О себе (textarea)
  • Согласие с условиями (checkbox, обязательное)
  • Кнопка отправки

Требования:

  • Все поля должны иметь связанные label
  • Используйте правильные типы input
  • Добавьте валидацию через HTML5-атрибуты
  • Используйте fieldset для группировки
  • Добавьте autocomplete атрибуты
  • Используйте метод POST

Доступность (A11y) и ARIA

Доступность — это про то, чтобы интерфейсом могли пользоваться люди с разными возможностями и устройствами. Следуйте стандартам WCAG, используйте нативную семантику и дополняйте её ARIA, когда это необходимо.

Фокус и клавиатурная навигация

  • Порядок табуляции следует логике документа. Не используйте tabindex > 0, предпочитайте нативный порядок.
  • Кнопки должны быть <button>, а не <div role="button">. Если всё же нужен кастом — добавляйте role, tabindex="0", обрабатывайте клавиши Enter/Space.
  • Добавляйте ссылку «пропустить к содержимому» в самом начале: <a class="skip-link" href="#content">К основному содержимому</a>.

ARIA-атрибуты и лендмарки

  • role для уточнения роли, когда нет подходящего тега.
  • aria-label, aria-labelledby, aria-describedby — альтернативные/связанные подписи.
  • aria-expanded, aria-controls — состояние раскрывающихся областей.
  • aria-live="polite|assertive", aria-busy — живые области и загрузка.
<button aria-expanded="false" aria-controls="acc-1" id="acc-btn-1">Развернуть</button>
<section id="acc-1" role="region" aria-labelledby="acc-btn-1" hidden>
  <p>Скрываемый контент</p>
</section>

Формы и ошибки

  • Каждое поле связано с <label for>. Не используйте placeholder вместо метки.
  • Ошибки сообщайте текстом, связывайте с полем через aria-describedby, фокусируйте первую ошибку.
<label for="email">Email</label>
<input id="email" type="email" aria-describedby="email-err" required>
<p id="email-err" role="alert" hidden>Введите корректный email</p>

Контраст и медиа

  • Контраст текста к фону: минимум 4.5:1 для обычного текста.
  • У изображений обязателен осмысленный alt (декоративным — пустой alt="").
  • Для иконок SVG: добавляйте role="img" и aria-label при необходимости.

Правило: Сначала используйте семантические теги. ARIA — только если нельзя иначе.

SEO для HTML

  • Title/Description: уникальные на страницу; title ≈ 50–60 знаков, description ≈ 140–160.
  • Заголовки: один h1, далее иерархия h2–h3.
  • Open Graph/Twitter: корректные превью при шаринге.
  • Canonical: укажите канонический URL, чтобы избежать дублей.
  • Structured Data: JSON‑LD для статей, курсов, хлебных крошек (см. в <head>).
  • robots.txt / sitemap.xml: помогайте поисковикам индексировать сайт.
  • Core Web Vitals: LCP, CLS, INP влияют на ранжирование — оптимизируйте критический рендеринг и изображения.
<link rel="canonical" href="https://example.com/page" />
<meta property="og:title" content="Заголовок" />
<meta property="og:description" content="Описание" />
<meta property="og:image" content="https://example.com/preview.jpg" />

Адаптивные изображения и ретина

Используйте правильные форматы и отдавайте изображения под размер экрана и плотность пикселей. Указывайте явные width/height для предотвращения CLS.

<picture>, srcset и плотности

<picture>
  <source type="image/avif" srcset="/img/hero@1x.avif 1x, /img/hero@2x.avif 2x">
  <source type="image/webp" srcset="/img/hero@1x.webp 1x, /img/hero@2x.webp 2x">
  <img src="/img/hero@1x.jpg" srcset="/img/hero@2x.jpg 2x" sizes="(max-width: 768px) 100vw, 1200px" alt="Обложка" width="1200" height="600" loading="lazy" decoding="async" fetchpriority="high">
</picture>

SVG и иконки

  • Для одноцветных иконок используйте SVG‑спрайты, меняйте цвет через CSS.
  • Добавляйте aria-hidden="true" декоративным иконкам.

Фоновые изображения и ретина в CSS

.hero {
  background-image: image-set(
    url('/img/hero-bg@1x.jpg') 1x,
    url('/img/hero-bg@2x.jpg') 2x
  );
  aspect-ratio: 2 / 1;
  object-fit: cover;
}

Подсказка: Для LCP‑изображения используйте fetchpriority="high" и не лените загрузку.

Практика

Замените изображения на главной на AVIF/WebP с ретина‑вариантами, добавьте width/height и корректные alt.

Итоговый проект модуля 1

Сверстайте главную страницу портфолио без стилей. Обязательные секции: «Обо мне», «Навыки», «Проекты», «Контакты»; шапка с меню, подвал. Используйте семантические теги и добавьте форму обратной связи.

  • Структура: <header>, <nav>, <main> с 3–4 <section>, <footer>
  • Доступность: корректные alt у изображений, иерархия заголовков
  • Проверка: валидируйте документ на W3C Validator