HTML · Урок 3

Ссылки, изображения и таблицы

Научимся соединять страницы между собой и наполнять их мультимедиа.

Ссылки — <a>

Ссылка задаётся тегом <a> с атрибутом href. Ссылаться можно на внешние сайты, локальные файлы и элементы своей страницы.

<a href="https://developer.mozilla.org">MDN Docs</a>
<a href="about.html">О нас</a>
<a href="#contacts">Контактная информация</a>
Вид ссылкиПримерКуда ведёт
Абсолютнаяhref="https://…"На другой сайт
Относительнаяhref="about.html"На файл рядом с текущим
Якорнаяhref="#id"К элементу с этим id
Emailhref="mailto:hi@mail.ru"Открывает почтовый клиент
Телефонhref="tel:+79990000000"Звонок на мобильных
⚠️
Для внешних ссылок добавляйте target="_blank" и rel="noopener" — это безопаснее по открытию в новой вкладке.

Изображения — <img>

Изображение — одиночный тег с src (путь) и alt (текст-заменитель). Ширину и высоту можно управлять через CSS, а здесь уменьшим прямо атрибутами для примера:

<img src="cat.jpg" alt="Рыжий кот на подоконнике" width="300">

Атрибут alt показывается, если картинка не загрузилась. Скринридеры озвучивают его незрячим пользователям — это не формальность, а доступность.

Таблицы

Таблица строится из рядов <tr>, ячеек данных <td> и заголовков <th>. Для объединения ячеек есть атрибуты colspan и rowspan.

<table>
  <thead>
    <tr><th>Технология</th><th>Сложность</th></tr>
  </thead>
  <tbody>
    <tr><td>HTML</td><td>Низкая</td></tr>
    <tr><td>CSS</td><td>Средняя</td></tr>
    <tr><td>JS</td><td>Высокая</td></tr>
  </tbody>
</table>
💡
Таблицы — не для вёрстки макетов. Для расположения блоков используйте Flexbox и Grid (урок 6). Таблицы предназначены для данных.

Видео и аудио

Современные теги позволяют встраивать видео без сторонних плееров:

<video controls width="400">
  <source src="promo.mp4">
</video>
<audio controls>
  <source src="track.mp3">
</audio>

Пути: относительные и абсолютные

Путь к файлу может быть полным (от корня сайта) или относительным (от текущей папки). Разница критична при переносе сайта на хостинг:

<img src="images/cat.jpg">        <!-- в подпапке images/ -->
<img src="../images/cat.jpg">     <!-- на уровень выше -->
<a href="/blog/post.html">      <!-- от корня сайта -->
<img src="https://site.ru/i/cat.jpg"> <!-- внешний -->
ℹ️
../ — подняться на уровень вверх, ./ — остаться в текущей папке (пишут не всегда). Слеш в вебе всегда прямой /, даже в Windows.

Форматы изображений

ФорматПлюсыДля чего
JPEGмаленький вес для фотофотографии, фон
PNGпрозрачность без потерьлоготипы, скриншоты
WebPлучшее сжатие и прозрачностьсовременные сайты
SVGвектор, масштабируется без потери качестваиконки, иллюстрации
GIFпростая анимациямелкие анимации
💡
Иконки почти всегда делают в SVG — вектор теряет качество при увеличении, а как файл весит копейки. Веб-сервисы типа TinyPNG сжимают растровые картинки почти без потерь.

Ленивая загрузка и скорость сайта

Страница не обязана ждать загрузки всех картинок сразу. Атрибут loading="lazy" подгружает изображение, только когда пользователь докрутил до него:

<img src="gallery/photo-12.jpg"
     alt="Фото из поездки"
     loading="lazy" decoding="async">
⚠️
Всем картинкам ниже «первого экрана» ставьте loading="lazy". Первому экрану — наоборот, eager (по умолчанию): там лень только навредит, пользователь уже смотрит на эту картинку.

Семантика таблиц: caption и scope

Таблицам можно дать подпись <caption>, а заголовки колонок пометить scope="col" — так её понимают скринридеры и поисковики:

<table>
  <caption>Расписание занятий</caption>
  <tr>
    <th scope="col">День</th>
    <th scope="col">Тема</th>
  </tr>
  <tr><td>Пн</td><td>HTML</td></tr>
</table>
💡
Доступность — не прихоть: мировая статистика показывает, что корректные таблицы лучше ранжируются и читаются ассистентами.

Ссылка «наверх» и глубокие якоря

Якорь можно направить не только вниз, но и «наверх» — на любого предка, у которого есть id. Классика — кнопка возврата:

<body id="top">
  <a href="#top">↑ Наверх</a>

  <!-- или на любой блок -->
  <a href="#footer">К контактам</a>
  <footer id="footer">...</footer>
💡
Якорные ссылки работают и между страницами: <a href="contacts.html#map"> — откроет страницу и сразу прокрутит к блоку.

Безопасные ссылки: rel="noopener"

При открытии в новой вкладке передавайте rel="noopener noreferrer" — тогда вкладка-цель не получит ссылку на вашу страницу и не сможет ею манипулировать:

<a href="https://example.com"
   target="_blank"
   rel="noopener noreferrer">Внешняя ссылка</a>
ℹ️
Современные браузеры подразумевают noopener автоматически у target="_blank", но правило «отец и сын» не мешает: noreferrer ещё и отключает передачу реферера.

Адаптивные изображения: srcset и <picture>

Тег <picture> и атрибут srcset подсказывают браузеру несколько вариантов картинки — для экрана, плотности пикселей и формата:

<picture>
  <source srcset="hero.webp" type="image/webp">
  <img src="hero.jpg"
       srcset="hero@2x.jpg 2x"
       alt="Баннер">
</picture>
💡
Та же @2x-логика применима и в CSS — background-image: -webkit-image-set(url(...) 2x). На ретине телефон запросит картинку вдвое чётче, а на обычном экране — полегче.

Встраивание: iframe

В чужой контент — карты, видео, формы — встраивают через <iframe>:

<iframe src="https://www.openstreetmap.org/export/embed.html"
        width="500" height="300"
        loading="lazy"></iframe>
⚠️
Браузеры блокируют «открытые» iframe без разрешения сайта. Ограничение снимает атрибут sandbox или заголовок X-Frame-Options на стороне встраиваемого ресурса.

Мета-теги соцсетей (Open Graph)

Шаринг в мессенджерах и соцсетях показывает превью-карточку. Ею управляют OG-теги:

<meta property="og:title" content="WebDev Уроки">
<meta property="og:image" content="https://dev.ru/preview.png">
<meta property="og:description" content="Курс по вебу">
💡
Картинка должна лежать по абсолютной ссылке и быть ~1200×630. Проверить превью можно, вставив ссылку в мессенджер.

Скачивание файла: download

Ссылку можно превратить в кнопку «скачать» — браузер не откроет файл, а сохранит его:

<a href="files/resume.pdf" download>Скачать резюме</a>

<!-- можно задать своё имя загрузки: -->
<a href="files/report.pdf" download="Отчёт-2026.pdf">Скачать</a>

Ссылки mailto и tel

Контактные ссылки открывают почтовый клиент или приложение для звонков:

<a href="mailto:hi@site.ru?subject=Вопрос">Написать нам</a>
<a href="tel:+79990000000">Позвонить</a>
ℹ️
Do самому полю subject, body и прочие параметры передаются знаком ? после адреса.

colspan и rowspan

Ячейки таблицы объединяются по строкам и столбцам:

<table>
  <tr>
    <th colspan="2">Объединённый заголовок</th>
  </tr>
  <tr>
    <td rowspan="2">Радужка на две строки</td>
    <td>право 1</td>
  </tr>
  <tr>
    <td>право 2</td>
  </tr>
</table>

Размер изображений для скорости

Скорость загрузки — часть вёрстки. Держите под контролем:

<img src="hero.webp" alt="Баннер" width="1200" height="500">
💡
Поможет PageSpeed Insights (от Google): он покажет, какие картинки весят больше нормы.

Картинка без файла: data URI

Маленькую картинку можно встроить прямо в атрибут — идеально для иконок и прелоадеров:

<img alt="Иконка"
     src="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24'><circle r='10' fill='%2338bdf8'/></svg>">
⚠️
Техника хороша для крохотных файлов. Картинку в несколько сотен килобайт так кодировать не стоит — страница потяжелеет.

Редирект через meta refresh

Страница может «прыгнуть» на другой адрес без JavaScript:

<meta http-equiv="refresh" content="0; url=https://new-site.ru">
ℹ️
То же самое — через сервер (301/302) или JS location.href. Для постоянных переносов предпочтителен серверный 301: он «склеивает» в поиске.

video: autoplay, muted, loop

Встроенный плеер управляется атрибутами прямо в разметке:

<video autoplay muted loop controls>
  <source src="bg.mp4" type="video/mp4">
</video>
⚠️
Autoplay со звуком браузеры блокируют (анти-наглость). Поэтому фоновое видео «беззвучно» = muted обязательно.

SVG прямо в HTML

Вектор вставляется тегом — это текст, который можно красить из CSS и анимировать:

<svg width="80" height="80">
  <circle cx="40" cy="40" r="36" fill="#38bdf8"></circle>
</svg>
💡
SVG-иконку можно стилизовать: fill, stroke, переходы и hover работают как у обычного элемента.

Favicon: иконка вкладки

Вкладка и закладки украшает маленькая иконка — её подключают тегом link:

<link rel="icon" href="favicon.ico" sizes="any">
<link rel="icon" type="image/svg+xml" href="favicon.svg">

CORS: доступ к чужим ресурсам

Загружать можно многое, но манипулировать чужими данными — только по правилам. О них вам скажут в консоли:

<!-- свои/простые ресурсы грузятся свободно -->
<img src="https://cdn.site.ru/logo.png">

<!-- чтение данных (fetch и др.) требует CORS-заголовков -->
fetch("https://api.other.ru/data");
ℹ️
Ошибка «No Access-Control-Allow-Origin» — не о вас: чужой сервер не разрешил чтение. Если API ваш — добавьте заголовок или проксируйте через свой сервер.

Меню из списков

Классическая навигация строится на <ul> и <a> внутри <nav>:

<nav>
  <ul>
    <li><a href="/">Главная</a></li>
    <li><a href="/blog">Блог</a></li>
  </ul>
</nav>
💡
Списки по умолчанию показываются маркером; в CSS его убирают list-style: none. Так и делают все меню мира.

Query-параметры в ссылках

После ? передают данные между страницами — при фильтрах, поиске, пагинации:

<a href="/catalog?category=notebooks&page=2">Ноутбуки, стр. 2</a>
ℹ️
В HTML амперсанд пишут как &amp; — иначе часть параметра может потеряться при обработке.

alt и семантика изображений

Текст alt важен и когда картинка не загрузилась, и для незрячих пользователей. Что писать:

<!-- Содержательные картинки: опишите смысл -->
<img src="diagram.png" alt="График роста продаж за год">

<!-- Декоративные: пустой alt -->
<img src="line.png" alt="">
⚠️
Пусто — это alt="", а не пропуск. Пропущенный alt у скринридера превратится в «картинка» — шум. Фото рядом с названием в тексте можно писать и как alt="Фото кота".

rel=nofollow: не передавать вес

Ссылка без «рекомендации» поисковикам — для недоверенных источников:

<a href="https://spam.ru" rel="nofollow">Источник</a>

rel=sponsored и rel=me

Пометки для рекламы и подтверждения авторства:

<a href="https://shop.ru" rel="sponsored">Партнёр</a>
<a href="https://github.com/me" rel="me">Мой профиль</a>

hreflang: язык цели

Подсказывает поисковику язык страницы, на которую ведёт ссылка:

<a href="https://en.site.com" hreflang="en">English</a>

Ссылка-заглушка: href="#"

Пока адрес неизвестен — но лучше явно предупредить:

<a href="#">Скоро</a>          <!-- прыгает к началу -->
<a href="https://example.com" aria-disabled="true">Черновик</a>

Якорь к конкретному месту

Ссылка на любой элемент с id — не только «наверх»:

<a href="#reviews">К отзывам</a>
...
<section id="reviews">...</section>

Карта изображения: map и area

Разные ссылки на разных участках одной картинки:

<img src="map.png" alt="Карта" usemap="#m">
<map name="m">
  <area shape="rect" coords="0,0,60,40" href="/north" alt="Север">
</map>

Картинка как ссылка

Изображение прекрасно работает внутри ссылки:

<a href="/product"><img src="watch.jpg" alt="Часы"></a>

Относительные пути: ./ и ../

Как найти файл относительно текущей страницы:

<img src="photo.jpg">        <!-- рядом -->
<img src="./img/logo.png">     <!-- в подпапке -->
<img src="../img/logo.png">    <!-- на уровень выше -->

Абсолютные пути

От корня сайта — со слеша, либо с полным адресом:

<img src="/img/logo.png">
<img src="https://cdn.site.ru/logo.png">

Таблицы: colgroup и col

Общие настройки целых колонок одним махом:

<table>
  <colgroup>
    <col style="background:#1e293b">
    <col span="2">
  </colgroup>
</table>

Таблицы: thead, tbody, tfoot

Смысловые части таблицы помогают и стилям, и скринридерам:

<table>
  <thead><tr><th>Товар</th><th>Цена</th></tr></thead>
  <tbody>
    <tr><td>Хлеб</td><td>40 ₽</td></tr>
  </tbody>
  <tfoot><tr><td>Итог</td><td>40 ₽</td></tr></tfoot>
</table>

Скролл длинной таблицы

Оберните таблицу в контейнер — на узком экране появится прокрутка:

<div style="overflow-x:auto">
  <table>...</table>
</div>

Таблицы: рамки-границы

Убираем двойные линии между ячейками:

table { border-collapse: collapse; }
td, th { border: 1px solid #334155; padding: 8px; }

VIDEO: постер и подгрузка

Картинка-заставка и стратегия загрузки:

<video controls poster="cover.jpg" preload="metadata"></video>
ℹ️
preload="none" экономит трафик, "auto" грузит сразу, "metadata" — только длительность и первый кадр.

Субтитры: track

Подключаем файл субтитров к видео — доступность и иностранные языки:

<video controls>
  <source src="movie.mp4">
  <track kind="subtitles" src="ru.vtt" srclang="ru" label="Русский" default>
</video>

Несколько форматов видео

Браузер выберет первый поддерживаемый источник:

<video controls>
  <source src="clip.webm" type="video/webm">
  <source src="clip.mp4" type="video/mp4">
</video>

Запасной текст для видео

Если видео не поддерживается — покажется текст или ссылка:

<video controls>
  <source src="clip.mp4">
  Ваш браузер не умеет видео — <a href="clip.mp4">скачать файл</a>.
</video>

Аудио: форматы

Тот же принцип нескольких источников для звука:

<audio controls>
  <source src="song.mp3" type="audio/mpeg">
  <source src="song.ogg" type="audio/ogg">
</audio>

Встраивание: embed и object

Способы встроить внешнее содержимое, когда iframe не подходит:

<embed src="file.pdf" type="application/pdf" width="600" height="400">

iframe: песочница sandbox

Ограничиваем возможности встроенной страницы ради безопасности:

<iframe src="widget.html" sandbox></iframe>
⚠️
Пустой sandbox блокирует скрипты и формы. Разрешайте только нужное: sandbox="allow-scripts".

iframe: ленивая загрузка

Не грузить встроенную страницу, пока до неё не доскроллят:

<iframe src="map.html" loading="lazy"></iframe>

iframe: полный экран

Разрешение развернуть встроенное видео:

<iframe src="video.html" allowfullscreen></iframe>

iframe: подпись title

Каждый iframe нуждается в заголовке — его читает скринридер:

<iframe src="map.html" title="Карта офиса"></iframe>

srcset: выбор размера

Несколько версий картинки с дескрипторами ширины:

<img src="photo-800.jpg"
     srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1600.jpg 1600w"
     alt="Пейзаж">

sizes: сколько занимает место

Подсказка браузеру, какую версию выбрать под ширину экрана:

<img src="photo.jpg" alt="Фото"
     srcset="p-400.jpg 400w, p-800.jpg 800w"
     sizes="(max-width: 600px) 100vw, 50vw">

picture: смена композиции

Разные картинки под разные экраны — art direction:

<picture>
  <source media="(max-width: 600px)" srcset="vertical.jpg">
  <img src="wide.jpg" alt="Баннер">
</picture>

Приоритет загрузки: fetchpriority

Главную картинку можно поднять в очереди:

<img src="hero.jpg" alt="Главная" fetchpriority="high">

Декодирование: decoding

Не блокировать отрисовку тяжёлой картинкой:

<img src="big.jpg" alt="Обзор" decoding="async">

img: crossorigin и реферер

Для работы с картинками из скриптов и приватности:

<img src="avatar.png" alt="Аватар" crossorigin="anonymous" referrerpolicy="no-referrer">

SVG: img или внутри HTML

Вектор двумя путями — выбор зависит от задачи:

<img src="icon.svg" alt="Иконка">   <!-- проще, кэшируется -->
<svg viewBox="0 0 24 24">...</svg>   <!-- красится из CSS -->

Иконки для устройств

Favicon для вкладки и «яблочная» иконка на домашнем экране:

<link rel="icon" sizes="32x32" href="favicon-32.png">
<link rel="apple-touch-icon" href="apple-180.png">

Карточка для соцсетей

Отдельные теги для крупного превью:

<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Заголовок">

Open Graph: url и type

Дополняем карточку превью адресом и типом:

<meta property="og:url" content="https://site.ru/page">
<meta property="og:type" content="article">

Ссылка внутри ссылки

Вложенные <a> запрещены — браузер их разорвёт:

<!-- ТАК НЕЛЬЗЯ -->
<a href="/a">Текст <a href="/b">вложенная</a></a>

<!-- Вместо этого — две ссылки рядом -->

Мини-проект: страница визитка

Попробуйте собрать трёхстраничный мини-сайт. Простой вариант файловой структуры:

my-site/
├── index.html   <!-- главная -->
├── gallery.html <!-- галерея -->
├── contacts.html<!-- контакты -->
└── images/
    └── avatar.jpg
  1. На главной — заголовок, абзац и ссылка «Галерея».
  2. В галерее — три изображения в одну строку.
  3. В контактах — якорь вниз к блоку с mailto:-ссылкой.
  4. Между всеми страницами — взаимная навигация.

Ссылка как «прыжок»: что умеет a

Тег <a> — «переброс» на другой адрес: страницу, файл, раздел страницы или почту. Всё, что внутри ссылки, становится кликабельным.

<a href="about.html">О нас</a>
💡
Текст ссылки обязан быть понятен без контекста: «Курсы вёрстки» лучше, чем «тут».

Адрес ссылки: href и его форматы

В href можно положить: путь к файлу, полный URL, якорь (#...), телефон (tel:) или почту (mailto:). Атрибут необязателен: ссылка без href — это «заглушка».

<a href="contact.html">Контакты</a>
<a href="https://mdn.io">MDN</a>
<a href="#top">Наверх</a>
💡
Работающая «заглушка» href="#" — плохой тон для будущего кода: оставляйте href="" и помечайте TODO.

Куда вести: относительные пути на практике

Относительный путь считается от текущего файла: about.html — соседний файл, img/logo.png — папка рядом, ../ — шаг «наверх» из папки. Редко источник битых ссылок начинается именно отсюда.

<!-- из /lessons/lesson-3.html -->
<a href="lesson-2.html">Урок 2</a>
<a href="../index.html">На главную</a>
⚠️
../ из корня сайта «уходит» вверх мимо сайта — проверяйте, из какой папки считаете.

Ссылка-файл: скачивание и атрибуты

<a> может отдавать файл: архив, PDF, отчёт. Атрибут download предлагает сохранить файл, а не открыть, и даже задаёт новое имя.

<a href="price.pdf" download>Прайс-лист (PDF)</a>
💡
Рядом укажите вес файла («2,3 МБ») — людям приятно знать, сколько они «скачают».

target: где открывать результат

Атрибут target выбирает окно: _self — текущая вкладка, _blank — новая. Для внешних сайтов часто используют _blank.

<a href="https://example.org" target="_blank">Сайт партнёра</a>
⚠️
Новые вкладки удобны для «увести наружу», но злоупотребление раздражает: цель — предсказуемость.

Ссылка на раздел страницы: якорь

Якорь — скачок к элементу по id: «#faq» приведёт на кусок страницы с id="faq". Часто используется в длинных статьях и оглавлениях.

<a href="#faq">К вопросам</a>
<section id="faq">...</section>
💡
Якоря — отличный инструмент для «навигационной панели» в длинных инструкциях.

Почему ссылки не бывают «тут» и «жми»

«Подробнее тут» бесполезно и для кликов, и для поисковиков: непонятно, куда ведёт. Пользователь со скринридером часто «проходит по ссылкам» списком — такие тексты выглядят мусором.

<!-- плохо --><a href="...">жми здесь</a>
<!-- хорошо --><a href="...">Скачать прайс</a>
💡
Проверка: если читатель видит только ссылки без текста — ясно ли, куда они ведут?

Ссылка «назад»: как делать правильно

«Назад в оглавление» важна на длинных страницах. Стек для неё простой: список ссылок по урокам с якорем наверх или в содержание файла.

<!-- весь раздел вправо, ссылка — в начало -->
<a href="table-of-contents.html#lesson-3">← К оглавлению</a>
⚠️
«Назад», работающая через кнопку браузера, ок, но та, что ведёт по понятному пути, — всегда надёжней.

Картинка-ссылка: a вокруг img

Оборачивая картинку в <a>, получаем кликабельное изображение: обложку статьи, логотип-домой, миниатюру.

<a href="article.html">
  <img src="cover.jpg" alt="Обложка статьи«»">
</a>
💡
Белая рамочка вокруг «ссылки-картинки» исчезнет в CSS, но сначала — ссылка, потом стили.

Ссылка на почту: mailto

mailto: открывает почтовый клиент с заполненным адресом. Добавите ?subject=... — подставится и тема письма.

<a href="mailto:hello@site.ru">Написать нам</a>
<a href="mailto:hi@site.ru?subject=Вопрос%20по%20уроку">Вопрос по уроку</a>
⚠️
Пробелы в subject кодируйте как %20 — иначе тема может приехать с ошибками.

Ссылка на телефон: tel

tel: на телефоне сразу «звонит», на компьютере — предлагает приложение. Номер пишут в международном формате.

<a href="tel:+74950000000">+7 495 000-00-00</a>
💡
На мобильных это «однокликовый» контакт — добавляйте tel на страницы контактов и услуг.

Ссылка на фрагмент другой страницы

Комбинация «путь + якорь» переводит на конкретное место чужой страницы: help.html#советы откроет help.html и проскроллит к id="советы".

<a href="help.html#faq-payment">Как оплатить — сразу к ответу</a>
💡
Ставьте понятные id («faq-payment», а не «x42») — якоря станут читаемыми и для вас, и для ссылок.

Что происходит после клика: навигация

Клик по ссылке отправляет браузер по маршруту; сервер отвечает статусом. 200 — отлично, 404 — не нашли, 3xx — редирект («переезд»), 500 — ошибка сервера. «Редирект» — это не «сломалось», а договорённость о новом адресе.

ℹ️
Инструмент «Network» в DevTools показывает каждый переход и его код — полезно, когда «ссылка ведёт не туда».

URL, который понятен человеку: слаги

Пути вида /lessons/lesson-3.html — «человечные», в отличие от /p=42&id=7. Когда соберётесь в многостраничник, с самого начала делайте «говорящие» имена файлов и папок.

<!-- читаемо -->
/lessons/lesson-3.html
/lesson-3.html
<!-- не очень -->
/index.php?id=3&ref=utm_1
💡
Слаги «живут» и в копирайтинге: понятный адрес — часть текста ссылки.

Меню из ссылок: список и навигация

Меню почти всегда состоит из списка ссылок, обёрнутых в <nav>. Так и семантика чистая, и «пункты» стилизовать легко.

<nav>
  <ul>
    <li><a href="index.html">Главная</a></li>
    <li><a href="lessons.html">Уроки</a></li>
    <li><a href="about.html">Обо мне</a></li>
  </ul>
</nav>
💡
Пункт текущей страницы принято подсвечивать — классический приём — «вы здесь».

Хлебные крошки: путь «домой»

«Хлебные крошки» показывает, где вы находитесь: «Главная → Уроки → Урок 3». Это набор вложенных ссылок, обычно с разделителями.

<nav aria-label="Хлебные крошки">
  <ol>
    <li><a href="index.html">Главная</a></li>
    <li><a href="lessons.html">Уроки</a></li>
    <li>Урок 3</li>
  </ol>
</nav>
💡
Последняя «крошка» — текущая страница, ссылкой её не делают.

Оглавление в начале: якоря для длинного

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

<nav aria-label="Содержание">
  <ul>
    <li><a href="#start">Что понадобится</a></li>
    <li><a href="#steps">Пошагово</a></li>
  </ul>
</nav>
💡
Якори в оглавлении будут сами «следить» за позицией прокрутки — см. урок про события.

Внешние ссылки: открывать или уводить

Внешняя ссылка ведёт на чужой сайт. Решение «новая вкладка или туда же» — вопрос удобства: чужие сайты обычно открывают в новой вкладке, чтобы не «потерять» посетителя. Уважайте выбор пользователя — предсказуемость важнее.

⚠️
Для _blank крайне желателен rel="noopener" — защита от «кражи» окна.

Ссылка на mailto без спама: практика

Почта в коде «собирается» спамерами. Приёмы: писать адрес картинкой, «разбивать» @-символом на два слова, отдавать почту через форму. Начинайте с самого простого: показать адрес текстом и сделать телефон — звонить проще, чем писать.

💡
Заведите «технический» адрес для форм и «публичный» для людей — первый мельком гремит спамом, второй нет.

Стилизация ссылки: стандартные состояния

Псевдоклассы ссылки: :link (обычная), :visited (посещённая), :hover (наведение), :focus (фокус с клавиатуры). Браузер уже дал стили по умолчанию — поправлять их будете в уроке CSS.

/* в следующем уроке */
a { color: #0b7285; }
a:visited { color: #5f3dc4; }
⚠️
Не раскрашивайте ссылки как обычный текст — читатель должен знать заранее, что это «кликается».

Ссылки и доступность: ошибки понимания

Цветом ссылка отличаться должна не только «в цвете»: при дальтонизме или фокус-навигации нужен подчёркивающий и/или hover-эффект. Смешивайте форму и цвет — так «кликабельность» видна всем.

💡
Тест: сделайте скриншот страницы в градациях серого. Если ссылки «пропали» — те добавляют признак.

Проверка битых ссылок: инструменты

Битые ссылки проверяются автоматически: браузерные плагины и консольные «линтеры» просматривают все href на проекте. Найденные 404 — это «мусор» среди ваших страниц, который видит и поисковик.

💡
Проверяйте ссылки перед публикацией правки, а не раз в год: одна изменённая страница «сломает» пару href.

Изображение как ссылка: уточнение alt

Если картинка — это и есть ссылка, альтернативный текст должен описывать назначение перехода, а не «фотография кота». The alt на ссылке = подпись ссылки.

<a href="recipes.html">
  <img src="pie.jpg" alt="Наши рецепты выпечки">
</a>
💡
Если картинка декоративная и ссылка имеет текстовую дубликат — alt можно оставить пустым (alt="").

Ссылка-кнопка и кнопка-ссылка: разница

Ссылка «ходит по сайту», кнопка «делает действие»: отправка формы, отправка скрипта. Разметка разная (<a> против <button>), а выглядеть можно одинаково — хорошо, когда в CSS-урочил вы уже умеете.

⚠️
Скринридер озвучивает «кнопка» и «ссылка» по-разному: не меняйте их местами без причины.

Ссылка на «продолжение»: брежневый приём

Пагинация и «далее»: chunk-ссылки «следующая страница/следующий урок» удобны в конце контента. Их поддерживают поисковики (rel="next/prev" устарел, но «правильная» ссылка-контекст остаётся полезной).

<p class="next"><a href="lesson-4.html">Дальше: урок 4</a></p>
💡
Возвращайтесь «на шаг назад» с той же легкостью: «предыдущий» — пара «Дальше».

Изображение img: что под капотом

Тег <img> вставляет картинку: браузер запрашивает файл по адресу src и рисует его в потоке. Важно: у img нет содержимого — это «пустой» тег, закрывающего тега нет.

<img src="img/logo.png" alt="Логотип курса">
💡
img — строчный элемент: он «живёт» в строке текста до тех пор, пока вы не зададите ему другие правила.

Пути к картинкам: относительные и полные

Картинки подключают теми же путями, что и ссылки: img/pic.png — рядом-файл, ../img/pic.png — выше. Полный URL подходит для внешних CDN, но у «своих» файлов держите локальные пути — сайт не зависит от чужой работоспособности.

<img src="image/photo.jpg" alt="Фото из проекта">
⚠️
Переместили папку/файл — сразу проверьте все пути к картинкам: битое изображение равно потерянному контенту.

Рабочее место картинки: размеры по умолчанию

Без атрибутов картинка занимает столько места, сколько весит файл. Классы и CSS-правила зададут её размер позже; на старте привыкайте указывать width/height — это «резерв места» и защита от скачущего экрана.

<img src="ava.jpg" width="100" height="100" alt="Аватар">
💡
Числа задают логические «пиксели» на экране: браузер сам масштабирует файл.

alt: как писать полезный текст

alt — заменяет картинку для тех, кто её не видит: скринридер, поисковик, слабый интернет. Пишите «что важно» про картинку, а не «img src...». Назначение — это всегда мягкое описание содержания.

<!-- информативная -->
<img src="chart.png" alt="Диаграмма: рост продаж на 20% в июле">
<!-- декоративная -->
<img src="wave.png" alt="">
⚠️
Не пишите «картинка» или «фото котят» в начале — это не добавляет смысла.

Форматы изображений одним взглядом

JPEG — фото с потерей качества, PNG — «прозрачность» и графика, WebP — современный компромисс «маленький/красивый», SVG — вектор-код для иконок и логотипов. GIF остался в прошлом, кроме анимаций.

💡
Правило старта: SVG для «нарисованного», WebP для фотографий; PNG-подложки — только когда прозрачность нужна.

Семейство srcset и picture: картинка на каждый экран

Тег <picture> и атрибут srcset позволяют отдавать разные файлы для разных ширин: телефон получит маленькую, десктоп — большую. Начало пути — добавить srcset с парой размеров на главных картинки.

<img
  src="photo.jpg"
  srcset="photo-400.jpg 400w, photo-800.jpg 800w"
  sizes="(max-width: 600px) 400px, 800px"
  alt="Фото">
💡
Сначала — одна картинка и alt. srcset добавляйте, когда страница начинает тормозить на мобильных.

Фон или контент: когда CSS, а когда img

Если картинка — важная информация (фото товара, иллюстрация инструкции) — это <img> с alt. Если «декор» (кружок, градиент-«фон», узор) — это CSS-фон, alt не нужен.

<!-- контент: img + alt -->
<!-- декор: div с background в CSS -->
💡
Простой тест: «исчезнет ли смысл, если картинку уберут?» Если нет — скорее всего, это фон.

Ленивая загрузка: loading="lazy"

Атрибут loading="lazy" просит браузер не тянуть картинку, пока она не «подъедет» к видимой области. На длинных страницах это ощутимо ускоряет открытие.

<img src="gallery/3.jpg" loading="lazy" alt="Фото №3">
⚠️
Первой картинке «выше края» lazy не нужен; для критичных изображений используйте eager/умолчание.

Картинка и текст рядом: базовый приём

Картинка «в строке с текстом» получается свойством CSS float/разметки, но на старте не хватайте иголку: просто ставьте img и текст один под другим, обёрткой-карточкой. Раскладку даст урок про CSS.

<figure>
  <img src="hero.jpg" alt="Иллюстрация урока">
  <figcaption>Пояснение под картинкой</figcaption>
</figure>
💡
figure автоматически «держит» картинку вместе с подписью — переносить их порознь не придётся.

SVG: картинка-код

SVG — вектор, записанный текстом: иконка не «размывается» при любом размере и весит килобайты. Его можно класть прямо в HTML через <svg> либо подключить файлом.

<svg viewBox="0 0 24 24" width="24" height="24">
  <circle cx="12" cy="12" r="10"/>
</svg>
💡
Цвет SVG управляется CSS-свойством fill — «оживляет» иконку без нового файла.

Иконки: эмодзи, SVG или иконочный шрифт

Для декорирования списков чаще всего хватает эмодзи; для «значков-функций» (поиск, корзина) уместнее SVG. Иконочные шрифты живут в старых проектах. Любимое: эмодзи — быстро, SVG — правильно.

⚠️
Эмодзи не «видны» одинаково во всех ОС: проверяйте список из него на другом устройстве.

Подпись к картинке: история и данные

У картинок часто есть «пояснение»: источник, дата, автор. Кладите его в <figcaption>. Если подпись «плавает» отдельно — пересоберите: изображение и пояснение должны «ехать» вместе.

💡
Вместо «рис. 1» называйте уникально: «Схема урока 3» — якорь и оглавление станут точнее.

Галерея из картинок: заполнение in css

Галерея — список картинок, обычно с одинаковой миниатюрой. Разметка: <ul><li><img>... — и лёгкая CSS-сетка (см. урок 5–6). Каждая миниатюра — «ссылка на большую» или элемент «светового окна».

<ul class="gallery">
  <li><img src="thumb-1.jpg" alt="Проект №1"></li>
  <li><img src="thumb-2.jpg" alt="Проект №2"></li>
</ul>
💡
Картинке-миниатюре alt описывает смысл, а не порядок в списке: «Паспорт страницы рецепта».

Таблица: зачем вообще

Таблица выравнивает данные по строкам и столбцам: цены, расписание, характеристики, рейтинги. Её размечают в HTML, а выглядят «линии» благодаря CSS.

<table>
  <tr>
    <th>Формат</th>
    <th>Цена</th>
  </tr>
  <tr><td>Курс</td><td>0 ₽</td></tr>
</table>
💡
Таблица — НЕ инструмент вёрстки страниц. Её место — структурированные данные.

Основные теги таблицы: table, tr, th, td

<table> — сама таблица, <tr> — строка, <td> — «обычная» ячейка, <th> — ячейка-заголовок (по умолчанию жирная и по центру).

<tr>              <!-- строка -->
  <th>Урок</th>      <!-- заголовок -->
  <td>90 минут</td>  <!-- данные -->
</tr>
💡
Каждая строка — ровно <tr>, каждая ячейка — ровно <td>/<th>, порядок не путайте.

Заголовок и подпись: caption

<caption> — название таблицы видимое глазом: «Таблица 3. Прайс на 2026 год». Помогает и людям, и скринридерам понять, о чём таблица.

<table>
  <caption>Прайс на курсы</caption>
  <tr><th>Название</th><th>Цена</th></tr>
</table>
💡
caption идёт сразу после <table>, до первой строки.

Строки и ячейка-столбец: базовые действия

Данные «читаются» по строкам: каждая <tr> — это одна строчка. Столбцы складываются из ячеек «на одном месте»: так в каждой строке первая ячейка — первый столбец. Добавили ячейку — сдвинулись все «по этой позиции».

💡
Тренировка: начертите таблицу на бумаге в 3×3, потом разметьте — глаза и руки поймут связь.

Логические части: thead, tbody, tfoot

У большой таблицы части: <thead> — шапка, <tbody> — данные (может быть много), <tfoot> — итог. Для стилей и скриптов это «разделённые зоны».

<table>
  <thead><tr><th>Показатель</th><th>Январь</th></tr></thead>
  <tbody>
    <tr><td>Продажи</td><td>120</td></tr>
    <tr><td>Заявки</td><td>98</td></tr>
  </tbody>
  <tfoot><tr><th>Прирост</th><td>22</td></tr></tfoot>
</table>
💡
Порядок частей довольно гибкий; логика «шапка/данные/итог» — главная ценность, а не строгий порядок.

Объединение столбцов: colspan

colspan="N" растягивает ячейку на несколько столбцов — «заголовок на всю ширину», «общая ячейка итога».

<table>
  <tr><th colspan="2">Платежи</th></tr>
  <tr><td>Метод</td><td>Сумма</td></tr>
</table>
⚠️
Экономия ячеек: строки «под merged» содержат на (N-1) ячеек меньше — считайте аккуратно.

Объединение строк: rowspan

rowspan="N" — ячейка «вязка» через несколько строк: подпись одной клеткой сбоку или «промо-столбец».

<tr>
  <td rowspan="2">Категория А</td>
  <td>Первый пункт</td>
</tr>
<tr><td>Второй пункт</td></tr>
💡
rowspan «пропускает» место в следующих строках: не добавляйте там лишнюю ячейку.

Согласование заголовков: scope

Атрибут scope говорит скринридеру, над каким столбцом «властвует» заголовок: scope="col" — столбец, scope="row" — строка. Для простых таблиц достаточно th без scope, но привычка ценна.

<tr><th scope="row">Цена</th><td>100 ₽</td></tr>
💡
Со «сквозным» чтением таблицы скринридер называет заголовок перед каждым значением — теперь думает.

Ширина колонок: базовые правила

Колонки сами «подстраиваются» под содержимое. Не задавайте огромную width на каждую ячейку: достаточно пар общих «подписок» на колонку через CSS (см. 5 урок), а на старте — оставьте автоширину.

⚠️
Последняя колонка тянет всю таблицу на всю ширину родителя — иногда это удобно, но «вразброс» бывает удивляет.

Горизонтальный скролл для тесной таблицы

Узкий телефон и широкая таблица — конфликт. Обёртка div с прогоняющимся скроллом держит таблицу, не ломая остальную страницу. Понадобится CSS overflow, о котором — в уроке 5.

<div class="table-wrap">
  <table>... </table>
</div>
💡
Как альтернатива на мобильных — «карточный» вид таблицы, но это уже работа с медиа-запросами (урок 4).

Таблица против вёрстки: когда что

Таблица — для данных. Раскладка страниц (колонки, сетки) — для CSS-флекса/грида. «Разметили страницу таблицей» — это legacy-музей: скринридер читает её как данные, а не как дизайн, и все «ячейки» выстраиваются не так, как планировали.

⚠️
Встретили старый макет с table-раскладкой — не наследуйте подход, переносите на flex/grid.

Таблица с доступностью: сводные правила

Правила за 30 секунд: caption — название, th со scope для «сложных» таблиц, alt-и только для «своих» картинок-в-таблице, не использовать «пустые» ячейки для отступов. Тогда скринридер расскажет таблицу «связно».

💡
Попробуйте «услышать» свою таблицу скринридером — сразу станет ясно, где заголовки «потерялись».

Практика: таблица цен

Соберите таблицу «тарифы»: столбец «Название», «Цена», «Фишки», строки «Старт/Про/Бизнес». Подписи через th, последнюю строку — выводом. Разметили? Теперь добавьте по предложению у каждого тарифа: таблица читается как программа.

<table>
  <caption>Тарифы</caption>
  <tr><th scope="col">Тариф</th><th scope="col">Цена</th></tr>
  <tr><td>Старт</td><td>0 ₽</td></tr>
  <tr><td>Про</td><td>490 ₽</td></tr>
</table>
💡
Сам скорость: «цена» в конце строки удобнее глазу, чем в начале. Пробуйте оба.

Связанные разделы: перекрёстные ссылки

Ссылки «сложнее», чем навигация: свяжите «урок 3» с «уроком 2» фразой «вспомни списки — вот пример». Такие «перекрёстники» держат читателя на материале и помогают поиску связанных страниц.

<p>Списки-«меню» уже были в <a href="lesson-2.html#menu">уроке 2</a>.</p>
💡
Чередуйте «домашнюю» ссылку и «вглубь»: это создаёт «петлю» изучения.

Текст ссылки: полный и ожидаемый

Текст ссылки должен почти точно повторять название пары назначения: «Таблица сравнения тарифов» ведёт на страницу с таким заголовком. Пользователя не удивите, поисковик не обманите.

<a href="prices.html#compare">
  Сравнение тарифов
</a>
⚠️
«Узнать больше» везде — одинаковый текст для разных ссылок: и навигация, и SEO страдают.

Навигация по урокам: «предыдущий/следующий»

В конце каждого урока прямая пара: «← Урок 2» и «Урок 4 →». Это ожидаемое продолжение пути и верный улучшатель «глубины просмотра». Разметьте так свои страницы уроков.

<nav aria-label="Навигация по урокам">
  <a href="lesson-2.html">← Урок 2</a>
  <a href="lesson-4.html">Урок 4 →</a>
</nav>
💡
Текущую страницу в такой паре ссылкой не делают — она уже «открыта».

Всплывающая подсказка к картинке: title

Атрибут title на img показывает тултип при наведении. Это «больше деталей», а не замена alt: alt читается всегда, title — только при наведении. Дублируйте не всё, а важное.

<img src="city-map.jpg"
     alt="Карта города"
     title="Открыть в новом окне">
💡
Для скринридера title тоже «звучит» — но только при навигации по подсказкам, так что не делайте его главным.

Практика: галерея с подписями

Сделайте галерею из четырёх картинок: список ul, в каждом li — figure с img и figcaption «название проекта». У каждой картинки — полезный alt. Результат обязан «читаться списком», а не «кучей img» при голом HTML.

<ul class="works">
  <li>
    <figure>
      <img src="w1.jpg" alt="Сайт пекарни «Крендель»">
      <figcaption>Пекарня «Крендель»</figcaption>
    </figure>
  </li>
</ul>
💡
Файлы кладите в папку images/ — не «в корень», иначе файл с картинками растёт бесконтрольно.

Практика: таблица расписания

Расписание — идеальная таблица: столбцы «День», «Тема», «Домашка». Строки — дни. Добавьте scope="row" для дней и scroll-обёртку под мобильные. Одна таблица — и вы применили половину урока.

<table>
  <caption>Расписание недели</caption>
  <thead><tr><th scope="col">День</th><th scope="col">Тема</th></tr></thead>
  <tbody>
    <tr><th scope="row">Пн</th><td>Ссылки</td></tr>
    <tr><th scope="row">Вт</th><td>Картинки</td></tr>
  </tbody>
</table>
💡
Две «широкие» колонки на маленьком экране — повод подумать о порядке колонок на телефоне.

Практика: страница-«ссылочник»

Полезные ссылки «со всего курса» — мини-раздел: список ul ссылок, сгруппированных по темам в details/summary. Заодно потренируете href, якоря и «читаемый текст ссылки».

💡
Ссылочник — отличная «плюс-страница» для будущего резюме: покажет, что вы умеете структурировать.

Связанный контент внизу: «похожее»

Внизу статей «похожее» работает как миниатюры-карточки. Разметка — список из article-карточек: миниатюра-ссылка, заголовок-ссылка, пара слов. Не «мусорьте» карточками — три-четыре связанных записи хватает.

<section aria-label="Похожие статьи">
  <h2>Читайте также</h2>
  <ul class="related">...</ul>
</section>
💡
Связанные ссылки «схожей темы» полезны и для читателя, и для обхода поисковика по структуре.

Файл с картинками на диске: порядок

Имя файла — «кириллица?» лучше нет: ясные латинские имена и дефисы («lesson-3-pie.jpg»). Папка images/ — внутри урока, а не одна «просто картинки» на весь сайт без структуры.

⚠️
Пробелы и кириллические имена файлов иногда «роняют» пути на сервере и CDN — уходите от них заранее.

Что такое «атрибут loading» ещё раз

Итог коротко: loading="lazy" — «тяни, когда понадобится», loading="eager" — тяни сразу (по умолчанию). Сначала термины странные, но это самая простая оптимизация картинок, доступная без сервера.

💡
Проверьте свой “галерейный” файл: если там десяток ленивых img — вы уже ускорили открытие.

Нормальная структура «карточки»: мини-обзор

«Карточка» — частный случай: article (самостоятельность) + h3 (название) + img (иллюстрация) + p (описание) + a (подробнее). Применив это связно, вы почти профессионально структурируете контент.

<article class="card">
  <img src="img/t-3.jpg" alt="Урок про ссылки и картинки">
  <h3>Урок 3: ссылки и картинки</h3>
  <p>Краткое описание.</p>
</article>
💡
Такую карточку в уроке 4 можно будет «оживить» CSS-тенью и поворотами.

Итоги урока 3: что вы собрали

По итогам: ссылки (весь спектр href), картинки (src/alt/форматы/srcset/lazy), таблицы (table/thead/colspan/scope). Ваши страницы теперь связаны, иллюстрированы и структурированы данными. Дальше начнётся самое красивое — CSS: цвет, сетки, шрифты, анимации.

💡
Соберите из всего курса «сборник» ссылок и картинок — к 5 уроку он превратится в резюме-страницу.

Путь против URL: разница в «системе»

URL — «полный адрес» в интернете: протокол + домен + путь. Путь — «дорога» внутри сайта: /images/photo.jpg. В ссылках чаще пишут путь, а URL приходит из «наружи» или соцсетей.

<a href="images/photo.jpg">фото</a>  // путь
<a href="https://example.com/">сайт</a>  // URL
💡
URL всегда «снаружи», путь — «внутри»: различие ключевое для понимания href.

Протокол-относительные ссылки: //путь

Ссылка вида //example.com/page пропускает протокол: браузер берёт текущий (http/https). Удобно на «двухпротокольных» сайтах, но в вашем коде лучше явно https — без неожиданностей.

💡
На странице с https даже «//»-ссылка останется https — отсюда «ленивый» трюк, но не злоупотребляйте.

href: «что указать» в ссылке

В href кладут: путь к файлу, полный URL, якорь, mailto/tel. Браузер «соберёт» из этого переход. Относительный путь значит «от текущей страницы», абсолютный — «от корня сайта».

💡
Вопрос «куда вести?» решает не тег, а содержимое href — им и начинайте

Ссылка на скачивание с именем файла

Атрибут download подсказывает браузеру «качаем», а его значение задаёт имя сохраняемого файла. Идеально для PDF, архивов, шаблонов — вместо «открыть в окне».

<a href="kurs.pdf" download="Курс.pdf">
  Скачать программу</a>
💡
Контролируемое имя файла (+download) защищает от «курс(1).pdf» при повторных загрузках.

mailto: тема и текст письма

mailto может подставить тему и текст: mailto:mail@example.com?subject=Привет&body=текст. Аккуратно с «грузом» — длинный кодированный текст отпугивает.

<a href="mailto:hi@site.ru?subject=Вопрос">
  Написать нам</a>
💡
Тему в subject давайте непустой: читатель сразу понимает, о чём письмо.

tel: международный формат номера

Ссылка на вызов пишется tel:+7... (с кодом страны, без пробелов). На телефоне откроется набор номера, на десктопе — приложение. Формат «международный» работает везде.

<a href="tel:+79000000000">
  +7 900 000-00-00</a>
💡
Напишите в href именно «+7...», а в тексте покажите «+7 900...» — красота и техника не спорят.

Якорь с переходом: файл#ид

Ссылка «на другую страницу и сразу к разделу»: href="manual.html#chapter2". Адрес состоит из пути и якоря через #. Полезно для «FAQ» и «справки».

<a href="help.html#billing">
  К разделу оплаты</a>
💡
Раздел должен существовать: id="billing" на странице help.html — иначе «кусок» ссылки объявится пустым.

Служебная «заглушка»: куда вести href="#"

href="#" — «заглушка» на время разработки: клик уводит «в никуда» (наверх страницы) и добавляет «#» к адресу. Лучше ставить «real» адрес сразу или вешать обработчик, чтобы не пугать пользователя.

💡
Оставьте href="#" только до «рабочего» линка; закрыли вкладку — и весь юзеру «вы» — нет, это про другое.

Файловые ссылки: .pdf, .zip в href

Ссылка на файл (.pdf, .zip) скачать сработает «по умолчанию»: браузер откроет или загрузит его сам. Продумайте: документ открыть или скачать? Для скачивания используйте download.

💡
PDF лучше открывается вкладкой, ZIP — скачивается; явный download решает «правильный» сценарий.

Внешние и свои: с trusted-контекстом

Внешней ссылке «не надо верить» по умолчанию: она может «притащить» лишнее. rel="noopener noreferrer" обезопасит открытие в новой вкладке; для «своих» — обычные ссылки.

<a target="_blank"
   rel="noopener noreferrer">
   внешний сайт</a>
💡
target="_blank" почти обязан идти в паре с rel="noopener" — запомните «по-сложному»: пара выгоднее соло.

Картинка-превью: маленький размер, большой смысл

Превью — уменьшенная копия: thumbnail-версия картинки на карточке, а полная — по клику. Одна картинка, два размера, один замысел: «показать в обзоре, открыть в деталях».

<a href="big.jpg">
  <img src="thumb.jpg" alt="Фото">
</a>
💡
Превью «весит» меньше оригинала — страница ждёт от вас «лёгкой» сетки.

Изображение-спрайт: палитра для иконок

Спрайт — одна картинка с несколькими иконками; CSS «вырезает» нужную фоном. Один файл грузится быстрее N отдельных. Классика производительности для иконочных наборов.

<span class="icon icon-search"></span>
<span class="icon icon-user"></span>
💡
Современный «спрайт» — спрайт в SVG-коде: масштабируемый и без «лишних» запросов.

Формат AVIF: претендент на сжатие

AVIF — формат из экосистемы AV1: сильное сжатие без заметных потерь. Поддержка растёт, но «старый» браузер увидит запасной формат. Ставьте в с fallback на webp/jpeg.

💡
AVIF + «http accept» — «умная отправка»: сервер сам отдаёт формат, который понимает браузер.

Картинки «retina»: сколько пикселей нужно

Экран-retina плотнее: на «двукратном» дисплее картинка в 2 раза больше «логических» пикселей. Для чёткости давайте «2x»-размер через srcset. Тяжёлые «топ-картинки» — с расчётом.

<img src="photo.jpg"
     srcset="photo@2x.jpg 2x">
💡
Правило «2x»: дали 1000px места — отдайте 2000px картинку для retina.

Атрибут crossorigin у картинок

crossorigin позволяет браузеру «честно» обращаться к чужим картинкам (и для canvas, и для «правильного» кеша). Без него запрос «анонимный» и упасть может в специфичных сценариях.

💡
Пока не трогаете canvas и не «рисоваете» чужие фотографии — crossorigin обычно не нужен.

Картинка в .webp: когда выбирать

WebP — «середнячок»: хорошее сжатие, широкая поддержка. Выбор: большая фотография → webp (или avif с fallback), «простые» пиктограммы → svg. Всегда с проверкой «как выглядит».

💡
Сконвертируйте десяток фото в webp — «вес» галереи упадёт заметно, глаза «не скажут».

Галерея «легче»: ленивая загрузка каждого

В длинных галереях изображения «внизу» можно грузить по мере скролла: loading="lazy" на каждом. Первая «картинка» отдаётся сразу (lazy не для главной), а «остальные» — по касанию.

<img src="first.jpg" fetchpriority="high">
<img src="rest.jpg" loading="lazy">
💡
«Первое фото — быстро»: галле ель для «над облаком» — читается «сверху», lazy снизу.

Фокус-зона картинки: object-position

object-position выбирает, куда «смотреть» при кадрировании: «лучшее место» лица или продукта остаётся в кадре. «Классика» — кадрировать слева-top для «звёзд/слева».

.card-photo {
  object-fit: cover;
  object-position: center top;
}
💡
«Портрет» в квадратном кадре: object-position: center top оставит лицо «вверх-центр-заголовок».

«Изображение» SVG: документация внутри

SVG-файл — это код внутри разметки или файл .svg. Внутри можно вставить понятное описание (title/desc) — «текстовую» версию. Иконка, карта, график — «картинка-документ».

<svg role="img" width="100" height="100">
  <title>Иконка дома</title>
  <rect ... >
</svg>
💡
role="img" + title внутри svg = «alt» для векторных картинок.

Пиктограмма и иллюстрация: alt-разделение

Пиктограмма — «иконка-смысл»: ей довольно пустого alt. Иллюстрация — «картинка-повествование»: ей нужен alt с описанием. Различение определяет, что прочитает скринридер.

💡
Простая проверка: «что я потеряю, если картинки нет?» — потеря смысла = нужен alt, нет = пустой.

Таблица на телефоне: что делать

Узкая таблица на мобильном «ломается». Приёмы: горизонтальный скролл в обёртке, «карточки» из строк при <600px, или скрытие редко-нужных колонок по медиа-запросу. Начните со скролл-обёртки.

<div class="table-wrap">
  <table>...</table>
</div>
💡
.table-wrap с overflow-x: auto — быстрый «телефонный» щит для любой таблицы.

Таблица-«характеристики»: размещение слева

Технические характеристики читаются «название — значение»: dh — термин, dd — значение. В таблице — тоже «слева тег, справа значение». Разметки: и dl, и table жизнеспособны; выбирайте по ширине контента.

<dl>
  <dt>Диагональ</dt>
  <dd>27 дюймов</dd>
</dl>
💡
Две колонки «пара-значение» хорошо ложатся в dl, а «много колонок» — в table.

Видео-постер: кадр до воспроизведения

Атрибут poster показывает картинку-«стопкадр» до начала видео. Красивая обложка «приглашает» играть, а не «чёрный квадрат». Дайте постер — и источник «интриги» справится.

<video controls poster="cover.jpg">
  <source src="movie.mp4">
</video>
💡
Постер = «первое впечатление»: размер и alt постеру не дают, но превью «своё» — отличное вложение.

Плеер без контролов: autoplay и controls

controls показывают кнопки; без них видео «молчит» и не имеет управления. autoplay без muted почти всегда блокируется. Решение: controls с «честным» постером — и никаких «некликабельных» плееров.

💡
Заберите звук-автоплей из головы: muted+autoplay — единственная разрешённая «автоматическая» связка.

Звук «в фоне»: autoplay с muted

Фоновая музыка «сама собой» — прошлый век: и раздражает, и блокируется. Единственный автоплей, который браузер терпит, — muted. Любите звук — дайте экранную кнопку «включить».

💡
«Начинается с muted, включается человеком» — правило политики звука на мобильных.

Видео с переводом: track в действии

track приносит субтитры и перевод: каждый — «дорожка». kind=subtitles, lang=ru, файл .vtt. Пользователь включает субтитры кнопкой плеера.

<video controls>
  <source src="lesson.mp4">
  <track kind="subtitles" src="ru.vtt"
         srclang="ru" label="Русские">
</video>
💡
Субтитры — не только «глухим»: у них зрители в метро и в библиотеке.

Субтитры: формат файла .vtt

WebVTT (.vtt) — «простые» субтитры: таймкоды и текст, строки вида 00:01.500 --> 00:04.000. Файл текстовый, его пишут руками или конвертируют из .srt. Кодировка — UTF-8.

WEBVTT

00:01.500 --> 00:04.000
Привет! Добро пожаловать.
💡
t2a-lesson: имя файла русское? Лучше латиницей (ru.vtt), и реже «проблем с кодировкой».

Multi-формат: mp4, webm, ogg

Одно видео пишут в пару форматов: mp4 — «везде», webm — «легче», ogg — «старые». source перебираются браузером до первого «понятного». Правило: mp4 всегда «первым, если доступен».

<video controls>
  <source src="v.mp4" type="video/mp4">
  <source src="v.webm" type="video/webm">
</video>
💡
type просто ускоряет «прыжок» по списку: браузер пропускает неподдерживаемые без догадок.

Стоп-кадр: preload и «видео в ленте»

preload="none" говорит «не грузи видео до клика» — экономия в лентах. Когда нужен «первый кадр», прелоад metadata + постер. Время «тяжёлого видео» из главного потока — на паузу ответа.

💡
В ленте «видео-обзор» поставьте preload="none" — и страница «похудеет» на мегабайты.

SVG-анимация: маленький пример

Внутри SVG работают CSS-анимации и SMIL: . Оживить иконку (дыхание, вращение) можно без JS. Начните с CSS: целевому паттерну соответствуют transition и keyframes.

<circle r="20">
  <animate attributeName="r"
           values="20;25;20"
           dur="2s" repeatCount="indefinite">
</circle>
💡
animate «дышит» кружком; для серьёзной анимации иконок всё же удобнее CSS.

OG-теги: полная карточка

Open Graph описывает, как сайт «выглядит» в мессенджерах и соцсетях: og:title, og:description, og:image, og:type, og:url. Заполните пять базовых — карточка по ссылке станет «живой».

<meta property="og:title" content="Уроки веб-разработки">
<meta property="og:image" content="cover.png">
💡
og:image лучше 1200×630 — «стандартный» холст карточки-превью.

og:title против title

title — «вкладка» и поиск. og:title — «карточка» в выдаче соцсетей. Совмещать можно, но og:title суше и «со смайлом» часто дружелюбнее. Не путайте: file name vs «витрина».

💡
Один рассказывает «в поиске», другой «в ленте»: оба заполняются под свои площадки.

Иконки: apple-touch-icon

Apple-touch-icon — иконка для «домашнего экрана» iPhone/iPad. Файл 180×180, формат png. Без неё браузер «показывает» скриншот сайта. Мини-деталь «домашнего экрана».

<link rel="apple-touch-icon"
      href="icon-180.png">
💡
Иконку «.ico» и «apple» ставьте отдельными link: разные платформы, разные файлы.

Web App Manifest: «иконка на главном экране»

Manifest (.webmanifest) делает сайт «приложением»: имя, иконки, цвета, стартовый URL. Ссылка через link rel="manifest". Установка «на экран» — «нативная» без «мобильных» браузеров.

<link rel="manifest"
      href="manifest.webmanifest">
💡
JSON внутри manifest «правит» браузерным «дефолтом» — без него установка тоже работает, но «безлико».

Мета-тег theme-color

theme-color «красит» адресную строку телефона в цвет бренда. Поддерживается не всеми, но где работает — даёт «фирменный» штрих. Мелко, а «приятно».

<meta name="theme-color" content="#1a73e8">
💡
Цвет согласуйте с «главным» фоном страницы: «панель-телефон» станет «вашей».

dns-prefetch: заранее разогретое подключение

dns-prefetch говорит браузеру «разреши DNS для домена заранее»: внешние шрифты, стоки картинок, аналитика — «первый перелёт» выигрывает. Ставьте на часто заменяемые «внешние» домены.

<link rel="dns-prefetch"
      href="https://cdn.example.com">
💡
Домаин, где лежат шрифты и css — «кандидаты»; не «разогревайте» десятки бездумно.

preload: нужный ресурс раньше всех

preload «поднимает» ресурс в начало очереди загрузки: критический CSS, главную картинку, шрифт. «Тяжёлые», но нужные сразу — кандидаты; не «платите» всякой мелочи.

<link rel="preload" as="image"
      href="hero.jpg">
💡
Помните про as: без него предзагрузчик «не знает», что ему досталось (image/font/style/script).

Готовые «превью» сообщений: link-теги

Кроме OG есть «другие» campos превью: Twitter Card (twitter:card, twitter:title) и даже старые link rel="image_src". Заполняйте og + twitter-card — покрытие основным мессенджером.

<meta name="twitter:card" content="summary_large_image">
💡
«Твиттер-карточка» живет и в «телеграме»: базового og + twitter:card хватает почти всем.

Проверка OG-карточки: инструменты

Готовую карточку проверьте: подменю «Share debugger» Facebook / «Card validator» Twitter. Вставьте URL — посмотрите, что «вытащит» отладчик и какие поля «пустые». Быстрая правка — и карточка «красивая».

💡
Отладчик стучится «свежим» скрапером: после правки og-тегов «обновите» карточку ещё раз.

Кэш и версии файлов: «?v=2»

«?v=2» в ссылке на ресурс «ломает» кэш старой версии: браузер скачает файл заново. Удобно при правках: поменяли css → поднимите «версию». На старине — автоматические «хэши» имён (gulp/webpack).

<link rel="stylesheet"
      href="style.css?v=2">
💡
«?v=» безобиден, но настоящие сборщики считают версию по содержимому — имейте это в виду.

Практика: навигация «в три уровня»

Соберите меню глубиной три: главный пункт, в нём подменю с подпунктами. Используйте вложенные списки и, если страниц много, — относительные пути. Проверьте все цепочки кликов.

<ul>
  <li>Курсы
    <ul>
      <li><a href="html.html">HTML</a></li>
    </ul>
  </li>
</ul>
💡
Каждый пункт-меню — это ссылка; «активный» пометьте aria-current.

Практика: галерея с подписями и ссылками

Галерея где каждая картинка-превью ведёт на большую версию, а под ней — подпись figure/figcaption. «Клик-превью → большое → подпись». Это связывает картинки, ссылки и контент.

<figure>
  <a href="full1.jpg">
    <img src="th1.jpg" alt="Работа 1">
  </a>
  <figcaption>Первый проект</figcaption>
</figure>
💡
alt и подпись могут «рассказать» разное: alt — «что», подпись — «зачем».

Практика: страница-«линки» со всеми типами ссылок

Соберите «справочник ссылок»: на страницу, на файл, на якорь, в новую вкладку, mailto, tel, на скачивание. К каждому — комментарий в коде: «куда ведёт». Это ваш «линк-арсенал».

💡
Пройдитесь кликом по каждой: поймите, где «та» вкладка, а где «то» действие.

Практика: таблица расписания с объединениями

Расписание с «двойными» уроками: rowspan на объединение строк, colspan на объединение столбцов. Держите «логику» слияния в голове: видимая ячейка — «одна» логическая единица на 1–2.

<td rowspan="2">Физкультура</td>
<td colspan="2">Математика</td>
💡
После слияний проверьте, что каждая «виртуальная» ячейка имеет текст — пустоты «ломают» чтение.

Практика: видео-страница с субтитрами

Страница с видео: заголовок, плеер (video+source+track), описание, транскрипт-текст под видео. Транскрипт — бонус доступности и SEO. Всё — без JS, на чистом HTML.

💡
Транскрипт «под видео»: искатели увидят содержание, глухие поймут без звука.

Чек-лист медиа-контента

Проверьте медиа: фото имеют alt и размеры; превью «ленивые», главная — «быстрая»; форматы — webp/avif с fallback; видео с постером и субтитрами; iframe с title; галереи «живые» на click. Прогнали — «медиа» в порядке.

💡
Чек-лист = «дефолты» страницы: они и отличают аккуратную вёрстку от «по-быстрому».

Ошибки медиа: fileName без alt и sizes

Классика: картинка без alt, без sizes, без ленивости; видео без постара; iframe без title. Каждая ошибка ведёт к «неоптимальному»: доступности, скорости или загрузке «не на то».

💡
«Sizes» влияет на srcset-выбор: без него браузер «меряет» неверно и качает слишком много.

Как читать «сетевую» вкладку для медиа

DevTools → Network показывает, что и сколько грузится. Ищите: «толстые» изображения в «первом экране», последовательность загрузки, «продублированные» ресурсы. Знание «веса» ловит десятки мегабайт экономии.

💡
Сортируйте столбец Size по убыванию: первые строки — самые «тяжёлые» задачи оптимизации.

Доступность медиа: subtitles, alt, контраст

Медиа «для всех»: субтитры у видео, осмысленный alt у изображений, контраст текста поверх фото (плиточная плашка), кнопки-«слова» при иконках. Доступность медиа = «медиа понятен всем».

💡
Текст на фото без плашки — провал контраста: добавьте тёмный градиент снизу.

Итоги урока 3: «медиа-голос» вашей страницы

Ссылки, картинки, таблицы, видео/аудио, мета-соцсетей — «текстовая» и «не-текстовая» суть страницы собраны. Проверьте пятёрку своих страниц на чек-листе и выложите «медиа-визитку» на GitHub.

💡
«Медиа-богатый» сайт = «живой» сайт: семантика плюс «весомая» графика говорят вместе.

Что такое CSS на самом деле

CSS описывает, как выглядят элементы HTML: цвет, размер, отступы, раскладка. Отдельно от разметки — «одежда» для документа. Внешние стили подключаются через link, внутренние — через style.

💡
HTML — суть, CSS — вид. Один и тот же HTML можно «одеть» в миллионы стилей.

Правило, свойство, значение

Правило состоит из селектора и блока: селектор «находит» элемент, внутри — пары свойство: значение. Каждое свойство «своё»: color, margin, font-size. Точка с запятой разделяет свойства.

p {
  color: tomato;
  margin: 10px;
}
💡
Селектор — «кто», свойство — «что», значение — «насколько».

Селектор тега: штамп на всех

Селектор тега красит «все» элементы с этим тегом: p — «все абзацы», a — «все ссылки». Удобно для базовых настроек: базовый цвет, шрифт. «Точечные» правки — классами.

💡
Одно правило p — и весь текст страницы выровнен: база задаётся тегом.

Класс: метка для группы

Класс — «группа» элементов: .card красит все «карточки». Имя класса — ваше, строчными с дефисом. Один элемент может иметь несколько классов через пробел.

.card { border: 1px solid gray; }
💡
Имя класса — про смысл, а не про вид: .button, а не .red-wide.

id: единственный адрес элемента

id — уникальное имя элемента на странице: #header «достаёт» его одно. Используется для якорей и скриптов. Стили по id «перевешивают» классы — специфичность выше.

💡
Для стилей чаще удобнее класс (переиспользование), id — для «адресных» нужд.

Специфичность: кто сильнее

Специфичность «взвешивает» селекторы: id > класс > тег. Что специфичнее — то побеждает. «Счёт» ведут по типам: больше id — сильнее. Поняв вес, перестанете «ругаться» с каскадом.

💡
Комбинация (id+класс) ещё сильнее: не усложняйте без нужды селекторы.

Порядок: последнее побеждает

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

💡
Базовые стили сначала, конкретные — позже: «каскад» построен на порядке.

Каскад в трёх словах

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

💡
Поняли каскад — остановили половину CSS-головных болей.

Комбинация через пробел: потомок

Пробел — «потомок внутри»: .menu a — ссылка где-то внутри .menu. Работает на любую глубину. Строже — «ребёнок» через знак >.

.menu a { color: blue; }
💡
Пробел «глубокий», > «близкий»: выбирайте по структуре — и селектор точен.

Группировка правил: запятая

Запятая перечисляет несколько селекторов в одном правиле: h1, h2, h3 — «одно стилизуем всё». Сокращает код и держит «общее» в одном месте. Разные теги — одинаковые стили.

h1, h2, h3 { margin-bottom: 12px; }
💡
Одно место правки — и все заголовки «в курсе»: группируйте «семейства».

Наследование: что передаётся детям

Часть свойств наследуется: color, font-family, line-height — «уходят» потомкам. Другие — нет: margin, border, background. Настроите цвет на body — текст «внутри» унаследует.

💡
Свойства «текстовые» наследуются; «блочные» — живут «своим» элементом.

Наследование через inherit и initial

inherit — «возьми у родителя», initial — «верни значение по умолчанию». Полезно, когда «своё» наследование сломано. Точечная «отмена» стилей — через initial + inheritance.

a { color: inherit; }
💡
inherit у ссылок внутри текста — попадание в «цвет абзаца»: красиво и логично.

Сброс и нормализация стилей

Браузеры «по умолчанию» рисуют по-своему. reset «убирает» все стили в ноль, normalize «выравнивает» различия. Выберите «один» подход и подключите «первым» файлом.

💡
«С нуля» стилизовать проще, чем «из-под» браузерных умолчаний: начните с сброса.

Цвета в CSS: hex, rgb, слова

Цвет задаётся: #ff8800 (hex), rgb(255 136 0) (современный синтаксис), orа также «именованными» словами (tomato). Все они — одно и то же для браузера. Альфа-канал добавляет прозрачность.

💡
Hex привычнее у верстальщиков; rgb() читается «по числам»: красный, зелёный, синий.

Размеры: px, em, rem, проценты

px — «точки», фикс; em — «от родителя»; rem — «от корня»; % — «от контейнера». Для интерфейсов удобны rem (масштаб самого себя); em уместен в «локальных» компонентах.

💡
rem — «предсказуем», em — «контекстен». База — rem, «детали» — по вкусу.

Относительные единицы: vw, vh, ch

vw — «процент ширины экрана», vh — высоты; ch — «ширина нуля» (для шрифтов). Удобны для «полноэкранных» блоков и «мерной типографики». «Осторожно»: vw на «десктопе» может «сделать» широким.

💡
Баннер «во всю ширину» — 100vw; «строку» в текст — ch.

Прозрачность: opacity и альфа

opacity «гасит» весь элемент вместе с текстом; альфа-канал цвета — «только цвет». Прозрачный фон + явный текст — через rgb(0 0 0 / 0.5). Выбирайте по нужному эффекту.

💡
«Погасить» карточку — opacity; «полупрозрачный слой» под текстом — альфа-фон.

Комментарии в CSS: заметки в стилях

Комментарии пишутся /* текст */. Помечают «разделы» файла, объясняют «почему», «отключают» правила. Стили «читаются» кодом и «человеком» — комментируйте «намерения».

/* Шапка */
.header { ... }
💡
/* Комментарий */ — «заметка на полях», а не «шум»: держите их короткими.

Шорткаты: короткая запись нескольких свойств

margin: 10px 20px задаёт сразу четыре стороны; border: 1px solid black — три свойства. Шорткаты короче и «устойчивее». Но «пропуски» сбрасывают остальное — будьте внимательны.

💡
«margin: 0 auto» — «вертикально ноль, горизонтально авто»: классика центрирования.

Кастомные свойства: переменные CSS

Переменные CSS — значения «по имени»: --main-color: red; внутри :root. Используются через var(--main-color). Один «узел» — глобальный «запас»: смените в одном месте — обновитесь везде.

:root { --accent: #f80; }
.btn { background: var(--accent); }
💡
Темы оформления — это «набор переменных»: переопределили корень — сменили вид.

Коробка вокруг элемента: box-модель

Каждый элемент — «коробка»: контент, внутри — padding, вокруг — border, затем — margin. Браузер «считает» размеры по этой схеме. Понимание «коробки» — база всей раскладки.

💡
«Коробки» вкладываются: контент, начинка, рамка, «дистанция» до соседей.

Padding: внутренний запас

padding — «воздух» между контентом и рамкой/краем. Четыре стороны своя: padding-top, -right и др. Шорткат: padding: сверху право снизу слева. Не спутайте с полем «снаружи» — margin.

.card { padding: 16px; }
💡
Комфортные «поля» в карточке — 16–24px: текст «не прилипает» к краям.

Margin: внешний интервал

margin — «дистанция» до соседей: снаружи рамки. Вертикальный margin у блочных «схлопывается» (берётся больший). margin: 0 auto — «по центру». Понимание margin «спасает» раскладки.

💡
«Схлопывание» удивляет новичков: два margin по 20px дают 20px, а не 40px.

box-sizing: border-box экономит мозг

По умолчанию размер «контентный»; border-box включает padding и border в width/height. С border-box «100%» — ровно «ширина блока». Один глобальный box-sizing — и размеры предсказуемы.

💡
* { box-sizing: border-box; } — правило-«спаситель»: колонки «не выламывают» сетку.

Ширина и высота: width и height

width/height задают размеры «коробки» (с учетом box-sizing). auto — «само по контенту»; фикс — «точное» число/%. Высота редко «нужна»: контент «растёт» сам.

💡
Не «жёстко» фиксируйте высоту текстовых блоков — позвольте контенту дышать.

Границы размера: min-width и max-width

min-width «не даёт» сжаться, max-width — «не даёт» разрастись. Классика: max-width: 960px; центрируется; контент «не перегорает» на широких экранах. «Эластичные» границы — лучше фиксаций.

💡
max-width + margin: auto — «читаемые» строки в центре: столбец «дышит» на любом экране.

Обводка outline: без влияния на размер

outline — «огранка» вокруг рамки, не занимающая места в коробке. Это «индикатор фокуса» по умолчанию. Не «убирайте» его совсем: оставьте «видимый» стиль фокуса для доступности.

💡
Хотите «рамку-подсветку» без сдвига соседей — outline: идеальный «хайлайтер».

Переполнение: overflow и его значения

Когда контент «больше» коробки — приходит overflow. visible — «вылезает»; hidden — «обрезает»; auto — «скролл по нужде». Прячет боковые «паузы» горизонтального скролла.

💡
Модалки прячут «хвосты»: overflow: hidden на «подложке» — «не ломает» скролл.

Центр по горизонтали: margin auto

Блоковый элемент в «свободной» ширине центрируется: margin-left: auto; margin-right: auto. Условие — больший контейнер. Текст внутри — text-align: center; отдельная история.

.wrap { max-width: 600px; margin: 0 auto; }
💡
margin «auto» работает только для «горизонтали» блока: вертикаль — flex-центрирование.

Единица измерения отступов: разумный выбор

Отступы «свои» единицы: rem для «масштабируемых», px для «пиксель-точных», % для «резины» в родителе. Единообразие в проекте — читаемость. Определите «меру» и придерживайтесь.

💡
«Гайд» по единицам в проекте: rem для отступов, px для «своих» линий, % для сетки.

Шрифт: font-family и стек

font-family перечисляет шрифты «по цепочке»: если первого нет — берётся следующий. В конце — «запасной» класс: sans-serif. Имена с пробелами — в кавычках.

body { font-family: Arial, Helvetica, sans-serif; }
💡
Первый в списке — самый любимый; последний — «нижняя граница» любой машины.

Размер шрифта: font-size и масштаб

font-size задаёт высоту букв. База браузеров — 16px. Настроили базу — и всё в rem масштабируется вместе. Не мельчите: основной текст комфортнее 16px и выше.

💡
Заголовок чуть больше абзаца — иерархия в размерах читается с одного взгляда.

Жирность: font-weight и начертание

font-weight: 400 — нормаль, 700 — жирный, промежуточные — числами. bolder/lighter — относительно родителя. Жирность даёт акцент, но перебор делает текст «скомканным».

💡
Два веса на странице — достаточно: нормаль и акцент. Иерархия — в размерах.

Курсив и наклон: font-style

font-style: italic — курсивный облик шрифта; oblique — «наклонённый» механически. Курсив у терминов и названий — классический приём. Но семантику выделения держит em.

💡
Начертание — CSS, смысл — em. Не «перерисовывайте» смысл в стилях.

Интервалы букв: letter-spacing

letter-spacing «разряжает» буквы: логотипы, акценты, заголовки. Малые значения — шик, большие — «сумасшедшая разметка». Разряженный «капс» читается хуже.

💡
Заголовок с letter-spacing выглядит «дороже», но тексту «пространство» не обязательно.

Расстояние между словами: word-spacing

word-spacing меняет промежутки между словами. Небольшие значения «дышат», большие — «рассыпают» строку. Применяется реже, чем letter-spacing; «дозируйте» осторожно.

💡
Разрядка слов — «тихий» акцент ширины: маленькая доза, ясная цель.

Цвет текста и фона вместе

color — цвет букв, background-color — подложка. Эта пара отвечает за контраст: тёмное на светлом, светлое на тёмном. Настроена на блоке — текст наследует.

💡
Проверяйте пару «текст/фон»: серое на белом — частая ошибка новичков.

Оформление текста: text-decoration

text-decoration: underline, line-through, none. Ссылки по умолчанию подчёркнуты — узнаваемый сигнал. line-through — зачёркнутая старая цена. Оформление «линией» — аккуратное.

💡
Цвет ссылки менять можно, но подчёркивание в тексте оставьте: привычка читателя.

Выравнивание: text-align

text-align: left, center, right, justify. Заголовки по центру — торжественно; основной текст по левому — удобнее. justify выравнивает и вправо, и влево — возможны «дыры».

💡
В русских текстах justify растягивает пробелы: «по левому» надёжнее.

Высота строки: line-height

line-height — воздух между строками: 1.5 читается комфортно. Значение без единиц привязано к размеру шрифта. Заголовки «плотнее» (около 1.2), абзацы «воздушнее».

💡
line-height 1.5 — «золотой» минимум для длинного текста; заголовки сжимайте.

Фон блока: background-color

background-color красит подложку элемента. Прозрачный по умолчанию; можно с альфой: rgb(0 0 0 / 0.4). Фон «внутри» рамки, включая padding. «Пустое место» — тоже «покрашенное».

💡
Полупрозрачный фон + белый текст — классика «заголовка поверх картинки».

Фоновая картинка: background-image

background-image: url("путь") — картинка за элементом. По умолчанию «плитка» на весь блок. Хороша для «декора», не для «контента»: контентные картинки — через img.

.banner { background-image: url("header.jpg"); }
💡
«Фоновое» зерно, узор, «превью-баннер» — background; «важные» фото — тег img.

Повторение фона: background-repeat

background-repeat: repeat, no-repeat, repeat-x, repeat-y. Без no-repeat картинка «течёт» плиткой. Узоры — repeat; фото и баннеры — no-repeat.

💡
Бесшовный узор — repeat; «однократный» баннер — no-repeat, иначе «муар» краёв.

Позиция фона: background-position

background-position двигает картинку в блоке: left center, 50% 50%, px. Удобно «поставить» баннер «по центру». Связка с no-repeat — точечная «установка» фона.

💡
«center center» — штатное место для «обложки»: картинка по центру блока.

Размер фона: background-size

background-size: cover — «накрыть» весь блок, contain — «вписать целиком». Проценты и px — «свои» размеры. cover обрезает, contain оставляет «пустоты». Выбирайте по задаче.

💡
«Обложка» — cover; «вся картинка видна» — contain. «Резиновая» разница — в деталях обрезки.

Градиент фона: linear-gradient

linear-gradient(направление, цвета) — плавный переход. От «светлого к тёмному», из трёх цветов, поворотом. Градиенты «оживляют» кнопки и баннеры без картинок.

.btn { background: linear-gradient(180deg, #fff, #ddd); }
💡
Градиент — «задача браузеру», а не картинка: весит меньше и масштабируется.

Рамка: border и её стороны

border: ширина стиль цвет — рамка вокруг элемента. Можно отдельно: border-bottom, border-left. Стили: solid, dashed, dotted. Рамки «участвуют» в размерах (box-border учтёт).

💡
Нижняя линия под заголовком — border-bottom: «Вам» не нужна «коробка» целиком.

Скругление углов: border-radius

border-radius скругляет углы: 8px — «мягкие», 50% — «круг». У картинок и кнопок — «милое» лицо. Без radius — «резкие» коробки старого веба.

💡
Круглая аватарка — border-radius: 50% при квадратном img: классика профилей.

Тень блока: box-shadow

box-shadow: смещение-x смещение-y размытие цвет — «глубина» блока. Мягкие тени «поднимают» карточки. Не переборщите: «чёрная дымка» на каждом блоке — «грязный» вид.

💡
Тень «6px 6px 12px» — «вниз-вправо»: карточка «приподнята» над листом.

Кадрирование медиа: object-fit

object-fit управляет «вписыванием» картинки/видео в рамку: cover — «заполнить», contain — «вся видна». Для img и video в «обложках». Не путайте с background-size: тут — «элемент» медиа.

💡
Картинка «во всю кромку карточки» — img { width:100%; height:200px; object-fit: cover; }

Главное о display: block, inline, inline-block

display решает «поведение»: block — «весь ряд», inline — «в строке», inline-block — «в строке, но с размерами». Ссылки и strong — inline; p и div — block. Понимание display — база раскладки.

💡
Не «пинайте» inline-элементы margin-вертикалью: он «не слушается» — размеры ему не даны.

Скрытие: display none против visibility

display: none — «убирает» из потока (место освобождается); visibility: hidden — «прячет», но место остаётся. Для «вкладок» — display; для «тайной» метки — visibility. Выбор по поведению раскладки.

💡
«Исчезло и сжалось» — display none; «не видно, но держит место» — visibility hidden.

Позиционирование: static и relative

static — «по потоку» (по умолчанию). relative — «тот же», но сдвиг относительно «своего» места: top, left. Полезно для «прицела» абсолютного ребёнка и «тонких» смещений.

💡
relative без сдвигов — «якорь» для absolute-детей: «ноль» поведения, тонна пользы.

Абсолютное позиционирование: absolute

absolute вынимает элемент из потока и «привязывает» к ближайшему позиционированному предку. Положение — top/left/right/bottom. Меню-«выпадашки», иконки «по углу» — его дом.

💡
«Кнопка поверх карточки» — absolute в relative-родителе: положение по углу блока.

Закрепление к экрану: fixed

fixed «прибивает» элемент к окну: скроллите — он стоит. Меню-«закреп», кнопки «наверх» — классика. Уточнение: внутри transform-предка fixed ведёт себя иначе.

💡
«Плавающая» кнопка «звонка», липкая шапка — fixed: всегда на виду, никогда в потоке.

Липкая шапка: position sticky

sticky «держится» в контейнере, а потом «прилипает» к краю экрана. Выгоднее fixed: не «вылетает» за границы родителя. Оглавление, шапки секций — его стихия.

💡
sticky — «осторожное» закрепление: прилипает и «освобождается» на границе секции.

Слои: z-index и контекст наложения

z-index «поднимает» элемент над соседями (работает с position/display). Больший — «выше». Контексты наложения создаются opacity, transform и др. — «внутренние» порядки «не лезут наружу».

💡
z-index 1–10 обычно хватает; «9999» — признак хаоса: стройте «этажи» скромно.

Переполнение контейнера: overflow подробнее

overflow: visible/hidden/scroll/auto для «обеих осей»; overflow-x/y — по одной. auto показывает скролл «по нужде», scroll — «всегда». Горизонтальный «мусор» часто лечится hidden на родителе.

💡
«Ничего не ломается вбок» — overflow-x: hidden на wrapper: быстрое «лекарство» макета.

Центрирование: три классических способа

Текст — text-align: center; блок — margin: 0 auto; «по вертикали» — flex (align-items: center). Для «всего сразу» — flex на родителе. Три инструмента — все основные «центры».

💡
Не «пинайте» vertical-align по блокам: центрирование по вертикали — прерогатива flex/grid.

Поток документа: как ложатся элементы

Элементы ложатся «потоком»: блочные — столбиком, строчные — строкой. position и float «вынимают» из потока. «Пайплайн» чтения страницы — это и есть поток. Управляете потоком — управляете вёрсткой.

💡
Поток — «естественная» раскладка: сначала поймите её, потом «исправляйте» выборочно.

Видовое окно: viewport на мобильных

Без мета-тега viewport мобильные браузеры «сжимают» страницу. <meta name="viewport" content="width=device-width, initial-scale=1"> — «правило» адаптива. С него начинается мобильная жизнь сайта.

💡
Второй шаг после начала адаптива — тот самый «viewport» в head: без него «всё вкривь».

Медиазапросы: min-width и max-width

@media (min-width: 768px) применяет стили «от 768px». min-width — «от», max-width — «до». Правила «договариваются» между брейкпоинтами: строчьте «лесенку» размеров.

@media (min-width: 768px) {
  .cols { display: flex; }
}
💡
«Снизу вверх»: базовые стили — мобильные, поверх — «от широкого» через min-width.

Мобильный-first: от маленького к большому

Мобильный-first — сначала «узкие» стили, затем расширения для широких. Меньше переопределений, быстрее «база». Современный подход: базу пишем для телефона, «ширины» — дозами.

💡
«Сверху» — min-width-правила; «база» — универсальная. Так «раздувается» минимум кода.

Гибкая сетка: проценты и flex

Процентные ширины «дышат» с контейнером; flex растягивает «вдогонку». Сетка-«резина» без «пиксельных» дублей. Комбинация: flex-wrap + min-ширины — «гибкие» ряды.

💡
flex-wrap: wrap + базовые размеры детей — «карточки» сами перестраиваются по ширине.

Изображения и отзывчивость

img { max-width: 100%; height: auto; } — картинка «не вылезет» из колонки. Высота авто сохраняет пропорции. «Шина» картинок — первый шаг адаптива медиа.

💡
«Вылезла» картинка на мобильном — проверьте max-width: 100% у img. «Мгновенное» излечение.

Брейкпоинты: где «ломать» вёрстку

Точки перелома — около «типичных» ширин: 480, 768, 1024, 1280. Лучше «по контенту», чем «по магии»: ломается строка — вот ваш брейкпоинт. Не плодите лишние «переломы».

💡
Сужайте окно в DevTools: «скорбное» место макета и есть точка @media.

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

«Резина» — проценты: блок «растягивается-сжимается»; отзывчивость — @media: «меняет» раскладку по размерам. Совместно: «эластичная» основа + «изломы» по запросам. Полный адаптив — оба приёма.

💡
Резина — «постоянно», @media — «в точках». Хороший макет использует оба.

Проверка на устройствах: инструменты

DevTools «телефоны» (toggle device) имитируют экраны; «эмуляция» и «реальность» близки. Проверяйте «свои» брейкпоинты и «повороты» (портрет/ландшафт). Быстрая «оглядка» — в DevTools.

💡
Иконка-«телефон» в DevTools — «мгновенный» мобильный; портрет/ландшафт — тоже там.

Адаптив: принципы за пять шагов

1) viewport-мета; 2) базовые «узкие» стили; 3) img max-width; 4) flex-wrap-карточки; 5) @media «на изломах». Пять шагов — «скелет» адаптива любой страницы.

💡
«Пять шагов» — чек-лист: прошлись по всем — сайт «едет» на телефоне достойно.

Шрифты на экране: масштаб и comfort

Мобильным текстом «16px+» — комфорт чтения без зума. Заголовки — не «гиганты» в угоду «вау». font-size в «единицах rem» масштабирует «в связке». Текст — первичен, размер — на службе.

💡
«Основной» размер — не ниже 16px; крупнее на «широких», а не «героически» всегда.

Практика: цветовая схема страницы

Выберите «палитру»: акцент, основной, фоновый, текст. Задайте переменные в :root и «разложите» по странице. Одной правкой переменной «переоденете» сайт. Практика учит «системе цветов».

💡
«Три цвета + фон» — сдержанная формула; переменные хранят их под «говорящими» именами.

Практика: кнопка на чистом CSS

Соберите кнопку: padding, фон, скругление, тень, hover-эффект, :active «нажатие». Кнопка — мини-лаборатория box-model и псевдосостояний. Через час таких — «кнопочная» мускулатура натренирована.

💡
Эффект «нажат» — сдвиг translateY(1px) + тень меньше: «живой» отклик без JS.

Практика: карточка товара в стилях

Карточка: изображение (cover), название, цена, кнопка. Рамка, скругление, тень, padding. Плюс hover — «приподнять» карточку. Повторите три «экземпляра» в ряд — сетка начнёт работать.

💡
Карточка — «типовой» компонент: стили один раз, «экземпляры» — классами.

Практика: меню с эффектами

Меню из ul>li>a: распределить в ряд, убрать маркеры, задать отступы. Hover — подчёркивание или «подсветку» пункта. Резина: wrap на узких экранах. Меню — «полигон» каскада и псевдоклассов.

💡
Пункты на hover — border-bottom или background: «реакция» читается мгновенно.

Практика: тени и градиенты

Поиграйте «светом»: мягкая тень, радиальный градиент, «полупрозрачная» подложка. Настройте «своё» свечение/глубину на кнопках и баннере. Баланс: эффекты «по делу», а не «салют» на весь сайт.

💡
Тень «вниз» — «реализм»; градиент «от тёмного к светлому» — «объём» кнопки.

Практика: стильная статья

Оденьте «чистую» статью: титул, подзаголовки, списки, цитату, ссылки. Крупный заголовок, воздух line-height, аккуратные подчёркивания. Цель — «красиво читается», а не «пестро». Это — «цивильное» CSS.

💡
«Типографика» статьи делает 80% «красоты»: размеров, строк, воздуха достаточно.

Практика: центрирование всеми способами

Сотрите: текст по центру, блок по центру, «и текст, и блок» в flex. Потом — «по вертикали» в фиксированной высоте. Четыре «центра» — четыре инструмента: выучите «когда какой».

💡
«И по вертикали, и по горизонтали» — флекс-родитель с justify+align center: стандарт.

Практика: галерея на flex-wrap

Галерея «превью»: flex-wrap + процентные ширины; на «узком» — один столбец. clip-safe: картинки object-fit cover. Перестройка «по ширине» — «живая» демонстрация адаптива.

💡
«Превращение» ряда в столбец через @media — обязательная «сюжетная» сцена адаптива.

Оценка стилей: чек-лист качества

Проверьте: единицы «согласованы»; переменные в «одном месте»; классы «по смыслу»; нет «воинственных» важностей; контраст ок. Пять пунктов — «быстрый» осмотр таблицы стилей.

💡
Нашли «!important» — пересмотрите структуру: это «красный флаг» каскадной аварии.

Контрольная работа по уроку 3

Соберите «лендинг»: шапка, главный экран с фоном, карточки-преимущества, форма, футер. Адаптив через @media, палитра через переменные, без «голых» хаков. Контрольная «складывает» весь урок CSS.

💡
Лендинг — «учебный макет»: после него CSS «в руках» — для любых страниц.

Итоги урока 3: стили работают

Урок CSS «запустил» оформление: селекторы, каскад, box-модель, фоны, адаптив. Страницы получили «вид» и «отзывчивость». Дальше — тонкая типографика и шрифты.

💡
CSS «оживил» разметку: теперь «смысл» ещё и «красив». Двухчасовая «база» выучена.

Частые ошибки CSS: сборник

Классика: пропущенная точка с запятой, путаница id/class, «случайный» !important, фикс-высоты, забытый box-sizing. Каждая ошибка «лечится» пониманием каскада и boxmodelи. «Болевые точки» — в списке на память.

💡
«Почему не работает?» — начните с «счёт» специфичности и «порядок» правил.

Дальше: тонкая типографика (урок 4)

Следующий шаг — шрифты и текст: гарнитуры, кегли, интервалы, «хороший» облик. Типографика «правит» страницей в глазах читателя. Урок 4 — «продолжение» одежды, но «для букв».

💡
Хорошая типографика — «90% дизайна»; урок 4 даст инструменты «девяноста процентов».

Закрепление: селекторы без подсказок

Восстановите «по памяти»: тег, класс, id, потомок, ребёнок, группировка, атрибут. Напишите по два примера «своих». Селекторы — «словарь» CSS: чем богаче словарь, тем точнее стили.

💡
Карточки-тренажёр: селектор a[href^="https"] — «ссылки на внешние» — куда проще с примерами.

Домашнее задание: стиль профильной страницы

Оденьте «страницу-профиль» из урока 2: шапка, карточки, форма. Палитра в переменных, hover-эффекты, адаптив до 320px. Откройте на телефоне — «ходите» по брейкпоинтам. Задание объединяет уроки.

💡
«Профиль» — один проект из двух уроков: вторая половина (стили) накладывается на первую (смысл).

Каскад в реальности: ремонт «поломки»

Возьмите «сломанный» стиль: элемент «не такой». Включите DevTools — «Стили» покажет «виновника». Отключите правила по одному — «вычислите» конфликт. Реальный «ремонт» — лучший тренажёр каскада.

💡
Инспектор «показывает» все правила элемента и их «вес»: «отладка» — минутная.

Инструменты CSS: DevTools и препроцессоры

DevTools — «прогон» стилей вживую; препроцессоры (SCSS) — переменные, вложенность, миксины. Начинающим достаточно «чистого» CSS + переменных. Препроцессоры — «удобство», не «магия»: база нужна.

💡
Сначала «чистые» руки, потом — «инструменты»: препроцессор «приходит» сам, когда больно писать.

Скорость CSS: вес и производительность

CSS-файл — «вес» и «рender»: селекторы не «глубинные», картинок не «тяжёлых». Осознанность: избегайте «мультяшных» вложенностей и «проценты» масштабов. «Лёгкий» CSS — «быстрый» рендер.

💡
Селектор-«простой» читается за микросекунды; «ложка» правил — «порядка» в файле.

Семантика и классы: связка двух миров

Теги несут «смысл», классы — «стиль»: section «рассказывает», .card «одевает». Не «переименовывайте» смысл в классы (class="important-main"). Согласие двух миров — «чистая» система.

💡
Классы — «по одному назначению»; теги — «по одному смыслу». Разделение — «ключ» к масштабу.

Заключение урока 3: вид имеет значение

Стили «оживили» семантику: страница заиграла цветом, формой, отзывами на действия. CSS — «следующий полёт» вёрстки. Урок 4 развернёт «букву» и «шрифт» — финальный штрих красоты.

💡
Вид «вторичен» за смыслом, но «вторичность» — не «малость»: красиво — значит «уважаемо».

Сетка «на» ровных колонках: repeat и «дышащий» ряд курсов

grid-template-columns: repeat(4, 1fr) — четыре «равные» колонки каталога. Практика: ряд курсов в 4 карточки. Скринридер «прочтёт» по DOM-порядку; визуально «плотно» и ровно. Не «плодите» колонки «сверх» нужды — на мобильном схлопывай. Проверка: gap «вместо» хаоса margin’ов. Доступность «сетки». Дисциплина «четвертей». Осмысленный «ряд» продуктов. Практика: align-items: stretch. Уважение к «колонке». Профессиональный «каталог». Гармония «ячеек». Чистый «ритм» ряда.

💡
<div>? — <ul> с <li> `.grid { display:grid; grid-template-columns: repeat(4,1fr); gap:1rem }` — карточки курсов каталога.

Центр «для глаза»: place-content и герой-блок

Герой-секция: display:grid; place-items:center; min-height:60vh — «взгляд» падает в центр. Практика: лендинг курса с главным сообщением. Скринридер «не» о геометрии, но «фокус» смысла ясен. Не «мучайте» «ручным» позиционированием. Проверка: по вертикали стабильно. Доступность «вступления». Дисциплина «центра». Осмысленный «акцент» лендинга. Практика: заголовок + подзаголовок + кнопка CTA. Уважение к первому экрану. Гармония «пятна».

💡
.hero { display:grid; place-items:center; text-align:center } — «мгновенное» центрирование пары «заголовок+кнопка».

Вакансия-карточка: <article> внутри <ul> поста

Список вакансий: <ul><li><article>…</article></li>… — «карточки» — «самостоятельные» единицы. Практика: кадры в ленте. Скринридер «пройдёт» по списку и «внутри» — по вакансии. Не «плодите» div-обёртки ради стилей. Проверка: article автономен в li. Доступность «ленты карточек». Дисциплина «одна вакансия — одна статья». Осмысленный «пост-кадр». Профессиональная «витрина вакансий». Уважение к «ряду».

💡
<li> сама «не» обязана быть «простой»: article внутри — норма для карточных лент-вакансий.

Заголовок-«вынести» из сетки: order помнить про фокус

<h1> «не» «внутри» колонки? — «вынесите» семантически. Практика: заголовок над grid, «не» в flex-ряду «с» кнопкой. Скринридер «встретит» h1 «первым» в DOM-порядке. Не «переставляйте» визуалом (CSS order) так, чтобы чтение «прыгало». Проверка: Tab/чтение линейно. Доступность «порядка». Дисциплина «выноса» заголовков. Осмысленный «источник». Профессиональная композиция. Гармония «шапки».

💡
CSS order «меняет» вид, «не» поток: заголовок «первым» в DOM — «первым» прочтётся.

Ссылка «в» контексте: <a> «с» точным именем в каталоге

Ссылка на товар: «Кроссовки Nike Air 42 размер» — полное имя. Практика: карточка товара. Скринридер «прочтёт» имя ссылки отдельно в «списке ссылок». Не «называйте» «подробнее» — «плодите» неоднозначность. Проверка: имена ссылок осмысленны сами по себе. Доступность «контекста». Дисциплина «имени». Осмысленный «переход». Профессиональная «ссылка-витрина». Уважение к списку ссылок. Гармония «навигации».

💡
Вместо «Подробнее» — «Смотреть кроссовки Nike Air»: «понятно», «куда» приведёт.

Изображение-«декор» в кнопке: aria-hidden на SVG

Иконка-стрелка «внутри» кнопки «Далее»: <svg aria-hidden="true" focusable="false"> + текст. Практика: кнопки-пагинации. Скринридер «прочтёт» «Далее», «не» споткнётся о декорацию. Не «вешайте» aria-label «на» пустую. Проверка: имя кнопки — текст. Доступность «иконических» кнопок. Дисциплина «тихих» декораций. Осмысленная «миниатюра». Профессиональная «кнопка-стрелка». Гармония «внутренних» значков.

💡
Значимая иконка без текста? — aria-label; декоративная — aria-hidden="true".

Числа «в» таблице: выравнивание вправо для суммы

Таблица цен/остатков: числовые td text-align:right. Практика: бюджет, каталог с ценами. Скринридер «прочтёт» числа; выравнивание — «для глаза». Не «центрируйте» длинные числа. Проверка: колонка чисел читается «как» столбец. Доступность «сравнения сумм». Дисциплина «правого края». Осмысленная «колонка данных». Профессиональная «таблица». Уважение к цифрам. Гармония «ширины цифр».

💡
font-variant-numeric: tabular-nums — «одинаковой ширины» цифры, колонка «не прыгает».

Поле с подсказкой «снизу»: helper через small + aria-describedby

<small id="hint">Подсказка…</small> + aria-describedby="hint" на поле. Практика: пароль «8+ символов». Скринридер «прочтёт» подсказку после ввода. Не «плодите» шум: одна связанная подсказка. Проверка: фокус — подсказка озвучена. Доступность «контекста ввода». Дисциплина «помощи». Осмысленная «подсказка». Профессиональная «форма». Уважение к сомнению. Гармония «подсказок».

💡
aria-describedby «связывает», aria-labelledby «именует» — «не» путайте роли меток.

Состояние dotted: активный пункт «меню» тарифов

Карточка тарифа «выбрана»: aria-current="true" — «пункт». Практика: «Про» выделен в сравнении. Скринридер «прочтёт» состояние выбора. Не «красьте» только цветом без aria. Проверка: состояние озвучено. Доступность «выбора плана». Дисциплина «активного пункта». Осмысленный «акцент тарифа». Профессиональная «полка планов». Уважение к выбору. Гармония «сравнения».

💡
<li aria-current="true">Популярный</li> — «и» визуально (бейдж), «и» семантически отмечено.

Елка меток: связывание label "с" именем поля через for

<label for="email">Эл. почта</label><input id="email"> — «жёсткая» связь имя↔поле. Скринридер «прочтёт» название и «всё» поле. Практика: форма входа. Не «плодите» «пустые» for/id «без» цели. Проверка: клик по словам — фокус в поле. Доступность «имени» контрола. Дисциплина: один label на одно поле. Осмысленный «аккомпанемент» поля. Гармония «этикеток». Уважение к смыслу. Чистая «пара» имя-значение. Профессиональная «подпись».

💡
Не «заключайте» input «внутрь» label ради «лени» — связка «работает», но «странно» при вложенных.

Число у метки: порядковый «в» alt у nav точек каталога

Точки-индикаторы слайдера: <ol> «с» <li> и ссылками aria-label="Слайд 2 из 5". Практика: карусель-демо. Скринридер «прочтёт» и положение, «и» «сколько» всего. Не «прячьте» точки в «пустые» дивы. Проверка: имя «Слайд N». Доступность «пагинации» галереи. Дисциплина «счётчиков». Осмысленная «навигация» слайдов. Профессиональный «фильм-строк».

💡
<button aria-label="Слайд 3 из 5">·</button> — «точка» с понятным «адресом».

Основной контент «не» «в» div: <main> один на страницу

<main> — уникальный «центр» страницы. Практика: статья/каталог. Скринридер «прыгнет» в main по сочетанию (skip). Один main — на объекте. Не «вкладывайте» main «в» main. Проверка: навигация «вне» main. Доступность «мишени» контента. Дисциплина «главного». Осмысленный «фокус» страницы. Профессиональный «центр». Гармония «ядра».

💡
<main id="main"> + <a href="#main" class="sr-only">К контенту</a> — «прыжок» мимо меню.

Ячейка-«заголовок»: th со scope=col для колонок данных

<th scope="col">Прибыль</th> — этот заголовок «именует» колонку. Скринридер «привяжет» «при чтении строки» (чтение «знает» колонку). Практика: таблица отчёта. Не «забывайте» scope у смешанных таблиц. Проверка: заголовок «следует» за ячейкой. Доступность «шапки таблицы». Дисциплина «оси» th. Осмысленное «наименование» столбца.

💡
<th scope="row"> — для «строки» (левая ячейка-имя ряда), «а» scope="col" — для «колонки».

Инлайн-контраст: <strong> акцент для скринридера

<strong> — «важность» автоматически усилена голосом. Практика: «Не пропустите: скидка до 30%». Скринридер «прочтёт» с интонацией выделения. Не «путайте» «b» (визуальная жирность) со «смыслом» strong. Проверка: содержание без CSS все равно «важно». Доступность «акцентов». Гармония «новостей».

💡
<em> — интонация «логического» ударения, <strong> — «сильная» важность. Выбирайте «по смыслу».

Поле-пароль с «глазом»: aria-pressed на переключателе

Кнопка «показать/скрыть пароль» — aria-pressed «говорит» состояние. Практика: форма логина. Скринридер «озвучит» «нажато/не нажато». Не «двигайте» фокус неожиданно. Проверка: Tab после кнопки — в поле. Доступность «переключателя» видимости. Дисциплина «тумблеров». Осмысленное «состояние» кнопки.

💡
<button type="button" aria-pressed="false">Показать</button> — JS — меняет «pressed» «и» label «Скрыть».

Заголовок-«гнездо»: h2 + подзаголовок через hgroup

<hgroup><h2>Отчёт</h2><p>год спустя</p></hgroup> — пара «заголовок+подзаголовок». Практика: серия докладов. Скринридер «прочтёт» первый заголовок в навигации «заголовков». Не «дублируйте» подзаголовок в h3 «для» «красоты». Проверка: в дерево входит первый. Доступность «пары». Гармония «титулов».

💡
Подзаголовок — <p> или <h?>? — <p> «в» hgroup; «не» «городите» лишний уровень.

Поле с «маской»: pattern и подсказка формата

<input pattern="[0-9]{5}"> — валидация по шаблону + aria-describedby к подсказке. Практика: индекс. Скринридер «прочтёт» и поле «и» подсказку в контексте. Не «полагайтесь» только на placeholder (исчезает). Проверка: сообщение об ошибке. Доступность «формата». Дисциплина «маски».

💡
step/pattern «и» label «с» примером: <label>Индекс (12345)</label> — «я» и «значение», «и» формат.

Ссылка «бе» художества: обводка фокуса у карточек-ссылок

Карточка-ссылка: <a> «внутри» article — stretched-link плюс outline. Практика: новости. Скринридер «найдёт» ссылку; фокус «виден». Не скрывайте «outline» финально. Проверка: Tab — кольцо «очерчивает» карточку. Доступность «зоны клика». Профессиональный «фокус» на блоке.

💡
.card:focus-within { outline:2px solid } — кольцо «рисуется» на «всей» карточке «без» JS.

Числа «как» список: <ol> для последовательных шагов

<ol> «нумерует» шаги: Практика «Как оплатить» — 1,2,3. Скринридер «прочтёт» «первый шаг», «второй». Не «переделывайте» в «свой счёт» через CSS. Проверка: порядке важен для ol. Доступность «порядка операций». Гармония «гайда».

💡
start="3" — продолжить «с» другого числа «в» двух вложенных списках.

Кнопка «только» иконкой: aria-label обязателен

Кнопка с крестиком (закрыть): <button aria-label="Закрыть">×</button>. Скринридер «произнесёт» имя, «не» «корявый» символ. Не «полагайтесь» на title/тултип (не клавиатурен). Проверка: имя у кнопки есть. Доступность «иконических» кнопок. Дисциплина «меток».

💡
Значок «×» — aria-hidden, «а» имя — в aria-label. «Крестик» «не» «как» осмысленное слово.

Карточка «товара»: article + h3 + кнопка «корзины»

Карточка: <article><h3>Худи</h3><p>цена</p><button>В корзину</button>. Скринридер «прочтёт» «карточку» целиком как «статью». Практика: каталог. Не «распыляйте» на голые div. Проверка: названия продуктов — заголовки уровня 3. Гармония «витрины».

💡
aria-label на <button> «добавить» контекст: «В корзину: Худи» — «ун» «сетке» карточек «одна» кнопка.

Прелоадер «шрифта»: <link rel=preload> до стилей

<link rel="preload" as="font"> — шрифт грузится «раньше» CSS. Практика: иконочный шрифт логотипа. Скринридер «не» о «загрузке», но «чёткие» буквы на старте. Не «прелоадьте» «всё» подряд (лимит соединений). Проверка: crossorigin обязательно у шрифтов. Доступность «визуальной» готовности. Дисциплина «критичного» ресурса. Осмысленный «приоритет».

💡
Шрифты «preload» — только «если» влияют «на» первый экран: «№», «логотип», заголовок-горизонталь.

Табличная «связка»: aria-labelledby «на» table со «скрытой» caption

<table aria-labelledby="t-title"> + <h2 id="t-title" class="visually-hidden">Доходы</h2> — имя таблицы вне caption. Практика: дашборд, где «имя» — заголовок секции. Скринридер «назовёт» таблицу. Не «дублируйте» имя в caption «и» aria-labelledby. Проверка: одно «имя». Доступность «легенды данных».

💡
caption «предпочитается»: aria-labelledby — «запасной» «когда» «видимая» подпись «не» «в» таблице.

Кнопка «карусели»: пред/след «с» aria-label и «у» стрелки

Карусель: две кнопки «Назад»/«Вперёд» с aria-label. Практика: слайдер отзывов. Скринридер «прочтёт» «имя» — «не» «стрелку». Не «плодите» дубли «без» смысла. Проверка: фокус в кнопках; Tab-порядок. Доступность «вращения слайдов».

💡
Не «автопрокручивайте» без паузы: пользователь должен успеть «прочитать» отзыв.

Ошибка «свёрнута»: <details> для «не»обязательной детали

<details><summary>Почему это важно?</summary>…</details> — разворачиваемое «пояснение». Практика: FAQ в форме. Скринридер «прочтёт» summary-кнопку, состояние «свёрнуто/развёрнуто». Не «прячьте» «критичный» контент за свёрнутым. Проверка: ответ доступен. Доступность «спойлера».

💡
<details> «нативно» «говорит» aria-expanded — «без» JS-«плясок».

Состояние «в процессе» у кнопки: loading и анимация-«спин»

Кнопка «Отправить» при отправке: <button disabled aria-busy="true">Отправка…</button>. Практика: форма обратной связи. Скринридер «озвучит» busy? — «иногда», лучше aria-live «плюс» текст. Не «замораживайте» интерфейс молча. Проверка: повторный клик заблокирован. Доступность «ожидания».

💡
Спин-иконка — aria-hidden; текст «Отправка…» — осмысленное состояние.

Автозаполнение регионов: <datalist> для «названий» стран

<input list="regions">+<datalist id="regions"><option value="Москва">… — подсказки автокомплита. Практика: форма адреса. Скринридер «не» о datalist «всегда», но «ввод» удобен. Не «заменяйте» им «выбор» из списка (datalist «свободен»). Проверка: значение можно «ввести» любое. Доступность «умного ввода».

💡
datalist «не» валидирует «жёстко» — для «жёсткого» выбора нужен select.

Оверлей «модалки»: <dialog> нативный «с» фокус-ловушкой

<dialog> — модальное окно нативно: showModal + закрытие Esc, фокус «внутри». Практика: подтверждение удаления. Скринридер «поймёт» модалку (роль dialog), фон — inert. Не «пишите» «свою» ловушку «без» нужды. Проверка: Tab не «уходит» из окна. Доступность «диалогов».

💡
<dialog> + <form method="dialog"> — закрытие «через» кнопку «Отмена» без JS.

Аккордеон «чистый»: details+summary с фиксированной высотой

Контент «аккордеона»: <details>. Стиль «анимации» раскрытия — CSS transition «не» работает с height авто — обход через grid-template-rows. Практика: FAQ квартиры. Скринридер «услышит» расширяемое состояние. Не «прячьте» «нужное». Проверка: keyboard — Space. Доступность «свёрнутого» ряда.

💡
.acc > div { display:grid; grid-template-rows:0fr; transition:.3s } + details[open] > div { 1fr }.

Ссылка «на» файл: download + осмысленный текст «имени»

<a href="guide.pdf" download aria-label="Скачать руководство (PDF, 2 МБ)">Руководство</a>. Практика: материалы урока. Скринридер «прочтёт» «имя» «с» типом файла. Не «говорите» «нажми сюда». Проверка: размер и формат «в имени». Доступность «загрузок».

💡
Формат и вес «в» тексте ссылки — «и» про «медленный» интернет честно.

Инлайн-формула: <var> и курсив переменных

<var>x</var> + <sup>2</sup> — переменные «функции». Практика: лекция математики. Скринридер «прочтёт» как текст; скобки/степени — «через» разметку. Проверка: x² «не» «как» x2. Доступность «формул» (MathML — отдельно). Дисциплина «псевдо-степеней».

💡
<math> для настоящих формул: <mn>2</mn><mo>+</mo><mn>2</mn> — «осмысленно» для скринридера.

Результат «поиска»: отметка mark + синтаксис подсветки

<mark>вокруг «найденного» слова — «акцент» совпадения. Практика: страница результатов Google-подобная. Скринридер «не» «особо» о mark («просто» текст), но «глаз» видит. Не «подсвечивайте» «всё» — «только» совпадение. Проверка: контраст «маркера». Доступность «релевантности».

💡
aria-label «на» ссылке «результата»: <a aria-label="Курс HTML — страница 2"> — «контекст» «одиночного» списка.

Метка «сдвоенного» поля: одна на серию, но for/id обе

<label>Телефон:</label> + поле «страны» и «номера» — две пары. Практика: регистрация. Скринридер «прочтёт» первую метку и «перейдёт» к полям по порядку. Не «оборачивайте» «обе» одним label (путаница). Проверка: каждая часть «имеет» имя. Доступность «разбитого» поля — сложнее; проще два поля.

💡
Если «так» — свяжите через <span id> + aria-labelledby на «обоих» инпутах (комбинированное имя).

Тёмная «кнопка» на светлом: контраст и текстура

Тёмная кнопка-CTA на светлом фоне: контраст текста к кнопке 4.5:1. Практика: «Начать курс» — чёрная на белом. Скринридер «не» о «красоте», но «читаемость» важна. Проверка: белый текст на #222 — ок. Доступность «тёмного акцента». Не «уводите» в «pitch-black» без «контраста».

💡
Кнопка «тёмная» + :hover чуть светлее — «отклик» без «крика» контраста.

Таблица «слайдеров»: ul точек пагинации с aria-label на li

Точки-индикаторы: <ul><li aria-current="true">… — «позиция» карусели. Практика: галерея. Скринридер «прочтёт» текущую; «соседние» — ссылки. Не «делайте» точки «недоступными». Проверка: aria-current «текущей». Доступность «индикаторов».

💡
<button aria-label="Слайд 3 из 8"> — имя «точки» «в» пагинации карусели.

Поле «счет агрегата»: inputmode numeric и step отдельно

<input inputmode="numeric" pattern="[0-9]*"> — цифровая клавиатура для телефона. Практика: кол-во, код. Скринридер «не» о клавиатуре, но «ввод» удобен. Не «заменяйте» type=number «если» нужны «целые». Проверка: клавиатура цифр на мобильном. Доступность «быстрого ввода».

💡
Для телефонов: type="tel" — «умный» ввод; для «кода» — inputmode="numeric".

Сетка «на» псевдокнопке: ::before «фон» для «акцента»

Псевдо-украшение кнопки: <button class="accent"> — ::before имитирует «полосу» под текстом. Практика: CTA с подчёркиванием. Скринридер «не» о декоре (aria-hidden понимает? — `::before` не озвучивается). Не «кладите» смысл в псевдо. Проверка: текст кнопки осмыслен. Доступность «декоративных» линий.

💡
::before { content:""; bottom:0; height:3px } — «полоска»-усилитель, имя — «в» тексте.

Индексируемые списки: <ol> с таргет-«порядка» действий

<ol> для «шагов» инструкции; каждый <li> — этап. Практика: «Как записаться на курс». Скринридер «прочтёт» номера автоматически. Не «рисуйте» числа вручную. Проверка: последовательность «ясна» без CSS. Доступность «нумерации».

💡
Если шаги «не»важны по порядку — <ul>; если порядок важен — <ol>.

Поле «имя» с подписью: label первой строкой, подсказка — второй

label «Имя» + <small> подсказка «как в паспорте». Практика: аккаунт. Скринридер «свяжет» через for/id + aria-describedby. Не «используйте» placeholder как label. Проверка: фокус — имя озвучено. Доступность «меток».

💡
placeholder «лежит» и исчезает; label — постоянная «идентичность» поля.

Карусель «без» автоплея: пользователь ведёт

Управляемая карусель: кнопки «Вперёд»/«Назад» и «точки». Практика: отзывы. Скринридер «прочтёт» слайд «по» фокусу; автоплей «мешает». Не «автопрокручивайте» контент — «дороже» чтение. Проверка: пауза при фокусе. Доступность «контроля».

💡
aria-roledescription="карусель" — скринридер «назовёт» виджет «правильно».

Сводка «и» деталь: <details> вокруг «таблицы»-спойлера

<details><summary>Смотреть данные</summary><table>… — «раскрываемая» детализация. Практика: отчёт. Скринридер «прочтёт» summary, «развернёт» по Enter. Не «прячьте» «нужное» за свёрнутым. Проверка: таблица доступна. Доступность «скрытых данных».

💡
Для «таблиц» <details> — «удобно»: сводка + <table> в «детали».

Загрузка «файлов»: <input type=file> с большим label-дивом

<label class="drop"><input type="file" class="visually-hidden">…</label> — «зона перетаскивания». Практика: загрузка аватара. Скринридер «найдёт» контрол по label. Не «делайте» «кликабельным» div без роли. Проверка: фокус + Enter открывает диалог. Доступность «загрузки».

💡
aria-describedby «Перетащите или выберите» — подсказка «в» имени контрола.

Видео «постер»: poster + <img> запасной для старого

<video poster="intro.jpg"> — обложка до воспроизведения. Практика: лекция. Скринридер «не» о постере, но «кадр» — «информация». Не «полагайтесь» на видео «для» смысла — транскрипт. Проверка: текст описания рядом. Доступность «медиа».

💡
aria-label «на» video «не» всегда; лучше <figcaption> или текст-описание рядом.

Чекбокс «согласия»: label + ссылка на оферту «внутри»

label «Я принимаю »+<a href>условия</a>. Практика: регистрация. Скринридер «прочтёт» чекбокс и «ссылку». Не «разрывайте» связанный текст. Проверка: фокус идёт по обоим. Доступность «юридического» согласия.

💡
<label><input type=checkbox> Я принимаю <a>оферту</a></label> — один «кусок» текста, обе «цели».

Ссылка-метка «этапа»: <a> «в» аккордеоне-степе

Прогресс-степы: <ol><li><a href="#step1">Корзина</a>… — «якорная» навигация мастера. Практика: оформление заказа. Скринридер «прочтёт» этапы как ссылки. Не «делайте» «неактивный» шаг «недоступным». Проверка: aria-current «на» текущем. Доступность «мастера».

💡
Неактивный шаг: <span> с aria-disabled + текущий — <span aria-current="step">.

Отображение «кода»: <pre><code> с aria-label на копию

<pre><code>блок кода</code></pre> + кнопка «Копировать». Практика: примеры в уроке. Скринридер «не» «прочтёт» CSS «красиво», но «код» — текст. Не «прячьте» «копию» без имени. Проверка: label у кнопки. Доступность «сниппетов».

💡
<button aria-label="Копировать код"> — иконка + понятное «действие» для скринридера.

Кнопка внутри <form> «сверху»: поиск сразу «у» шапки

Форма поиска в шапке: <form role="search"> + поле + кнопка «Найти». Практика: сайт курсов. Скринридер «найдёт» ориентир search; Enter — отправка. Не «забывайте» label (aria-label="Поиск"). Проверка: фокус лёгкий. Доступность «быстрого поиска».

💡
role="search" — «ориентир»: скринридер «перепрыгнет» к поиску сразу.

Тумблер «бургера»: aria-expanded на кнопке меню

<button aria-expanded="false">Меню</button> — телmenu-«переключатель». Практика: мобильная навигация. Скринридер «озвучит» состояние меню. Не «меняйте» имя кнопки «без» смысла. Проверка: aria-expanded истинному состоянию. Доступность «раскрытия».

💡
aria-controls="nav-list" — «связь» кнопки «с» открываемым списком.

Иконка «звука» у ошибки: role="img" aria-label с ясным текстом

Иконка-«восклицание» перед сообщением: <span role="img" aria-label="Ошибка">. Практика: поле с ошибкой. Скринридер «прочтёт» «Ошибка, затем текст». Не «дублируйте» aria-label «если» текст рядом. Проверка: сообщение озвучено. Доступность «алертов».

💡
aria-live="polite" на контейнере ошибок — скринридер «сообщит» о появлении.

Пагинация «широкая»: lots of <a aria-label> для дальних страниц

<nav aria-label="Пагинация">: страницы + «Вперёд». Практика: каталог из 100 курсов. Скринридер «прочтёт» ссылки по именам. Не «оголяйте» «край». Проверка: aria-current="page" на текущей. Доступность «перемещения по каталогу».

💡
<a aria-current="page">2</a> — текущая «отмечена» для скринридера, «не» только стилем.

Заголовок «таблицы данных»: caption или aria — две стратегии

caption «видимая» подпись таблицы «в» DOM. Практика: отчёт. Скринридер «прочтёт» caption перед данными. Не «используйте» и caption, и aria-labelledby «разом» (дубль). Проверка: выбрать одно. Доступность «подписи данных».

💡
caption «всегда» «лучше» для «видимой» подписи; aria-labelledby — «когда» имя «вне».

Ярлык «выбранного» фильтра: aria-pressed на чипсах

Чипс-фильтр «Активный»: <button aria-pressed="true">. Практика: фильтр категорий товара. Скринридер «прочтёт» состояние нажатия. Не «плодите» «вкл/выкл» без aria. Проверка: Tab — цикл; Space — переключение. Доступность «чипсов».

💡
aria-pressed честно «говорит»: «нажата/не нажата» — «для» скринридера «состояние».

Подсказка «под» полем: <small> + aria-describedby на пару значимых

<small id="hint">Только буквы</small> + aria-describedby. Практика: имя. Скринридер «прочтёт» поле, затем подсказку. Не «навешивайте» «всё» подсказки — шум. Проверка: одна осмысленная помощь. Доступность «контекста ввода».

💡
aria-describedby «вторично» к label: «имя» у label, «разъяснение» — у describedby.

Ссылка «целой» карточки: <a> stretched + фокус на весь блок

<article class="card"><a class="stretched" href="#">Заголовок</a></article> — «клик» по всей карточке. Практика: новости. Скринридер «прочтёт» ссылку «Заголовок»; клик — вся зона. Не «плодите» ссылки «внутри» stretched. Проверка: фокус виден. Доступность «карточек».

💡
.card { position:relative } + .stretched::after { content:""; position:absolute; inset:0 }.

Заголовок «навыка»: <h3> в списке карточек компетенций

Карточка навыка: <h3>HTML</h3> + описание. Практика: страница «что умею». Скринридер «прочтёт» заголовок в навигации. Не «пропускайте» уровни. Проверка: h2 → h3 линейно. Доступность «иерархии».

💡
Список карточек — это «список» (<ul>) из «карточек» (<li> article).

Числа «с» подписью: data-атрибут — не «смысл» без текста

<td data-label="Цена"> не «видна», а «имя» — атрибут не несёт доступности. Практика: мобильная таблица. Скринридер «не» о data-атрибутах «в» чтении контента. Не «понимайте» их как «скрытую» семантику. Проверка: aria-метки где надо. Доступность «данных».

💡
data-* — для JS/CSS, «не» для скринридера; альтернативная разметка — для «мобильного» чтения.

Поле «поиска» с «лупой»: aria-label и роль search

<form role="search"><input aria-label="Поиск по сайту"> — «лупа»-иконка. Практика: шапка. Скринридер «назовёт» поле «Поиск». Не «прячьте» label в placeholder. Проверка: имя озвучено. Доступность «поиска с иконкой».

💡
Иконка-лупа — aria-hidden; имя — в aria-label «на» поле.

Списки «компетенций»: <dl> для свойств-значений навыка

<dl><dt>Уровень</dt><dd>Продвинутый</dd> — «пары» свойство-значение. Практика: CV. Скринридер «прочтёт» пару как определение. Не «используйте» таблицу для «ключ-значение» контента. Проверка: dt/dd связаны. Доступность «характеристик».

💡
dl «подходит» для «ключ: значение», а не «когда» это «данные-сетка» — тогда таблица.

Активная «вкладка»: role=tablist + aria-selected правильно

Вкладки: <div role="tablist"> + кнопки role="tab" aria-selected. Практика: «Описание»/«Отзывы». Скринридер «прочтёт» состояние выбора. Не «используйте» role=tab без JS-поведения (стрелки). Проверка: фокус на активном. Доступность «вкладок».

💡
Ручные вкладки требуют js: ←/→ переключает; «не» «делайте» их «простыми» без скрипта.

Ошибка «поле»: aria-invalid связь с сообщением

<input aria-invalid="true" aria-describedby="err">+<span id="err">. Практика: валидация формы. Скринридер «прочтёт» «ошибка» при фокусе. Не «красный» один — текст обязателен. Проверка: сообщение осмысленно. Доступность «обратной связи».

💡
aria-invalid честно «помечает»; «текст» ошибки — через aria-describedby.

Заголовок «статьи» за кадром: <h1> скрытый, но «в» DOM

<h1 class="visually-hidden"> — имя страницы «для» навигации. Практика: лендинг с «epic» hero без видимого заголовка. Скринридер «найдёт» h1 в списке. Не «убирайте» заголовок «совсем». Проверка: aria-навигация по заголовкам. Доступность «имени страницы».

💡
visually-hidden «важно»: «скрыть» визуально, «не» «из» DOM — скринридер «прочтёт».

Чекбокс «не все согласны»: роль-смешанное состояние

Чекбокс «выбрать все» с aria-checked="mixed" — «частичное» состояние. Практика: таблица с чекбоксами. Скринридер «прочтёт» «смешанное». Не «подделывайте» indeterminate без aria. Проверка: состояние честное. Доступность «группового выбора».

💡
<input type=checkbox aria-checked="mixed"> — «некоторые выбраны» — правда состояния.

Ссылка «звук»: <audio> с controls и aria-label на плей

<audio controls aria-label="Урок 3: аудио"> — плеер. Практика: подкаст. Скринридер «прочтёт» имя плеера. Не «прячьте» controls (без них «не» воспроизвести). Проверка: кнопки доступны. Доступность «аудио».

💡
<track kind="captions"> в <video> — субтитры «для» глухих; в <audio> — транскрипт.

Мини-форма «в» карточке: <form> на «одно» действие

Карточка товара со «в» корзину: <form><label>Кол-во</label><input type=number><button>. Практика: store. Скринридер «прочтёт» форму. Не «плодите» «вложенные» формы. Проверка: один form на действие. Доступность «мини-контролов».

💡
<form aria-label="Купить товар"> — имя «действия» для скринридера.

Инлайн-секция «факта»: <blockquote> с cite для источника

<blockquote cite="…"> цитата + <cite> автор. Практика: отзыв клиента. Скринридер «прочтёт» собственно цитату и «автора». Не «путайте» blockquote с «отбивкой текста». Проверка: cite-ссылка валидна. Доступность «цитирования».

💡
<cite> «обязателен» «для» именования источника цитаты.

Подпись «фото»: figure + figcaption «как» легато-метка

<figure><img alt="…"><figcaption>Подпись</figcaption> — «пара» картинка-подпись. Практика: галерея. Скринридер «прочтёт» alt и подпись. Не «дублируйте» alt в figcaption (шум). Проверка: подпись осмысленна. Доступность «подписей».

💡
Не «повторяйте» в figcaption то, что уже «в» alt — дважды читается.

Поле «купон» на кассе: скидка «в» форме — <input>+кнопка «Применить»

<label>Промокод</label> + поле + «Применить». Практика: корзина. Скринридер «прочтёт» пару. Не «пишите» «скидка» мёртвым «текстом» без обновления. Проверка: статус «применён/неверен». Доступность «промо».

💡
<div aria-live="polite"></div> — «промокод принят: −15%» озвучено.

«Ломаный» рейтинг «в» звёздах: role="img" aria-label="4 из 5"

★★★★☆ + роль-картинка. Практика: обзоры курсов. Скринридер «прочтёт» «4 из 5 звёзд». Не «дублируйте» «в» текст рядом. Проверка: цвета не «нужны». Доступность «рейтинга».

💡
Декоративные звёзды aria-hidden + aria-label на span role=img.

Переключатель «светлой» темы: prefers-color-scheme + кнопка «в» DOM

Кнопка «Тёмная тема» + aria-pressed. Практика: переключение. Скринридер «прочтёт» состояние. Не «программируйте» «сложность» без пользы. Проверка: сохранение выбора. Доступность «тем».

💡
localStorage + data-theme на html — «живая» тема без «разлома» контраста.

Ссылка «на» почту с «темой»: mailto: + subject «через» URL-encoding

<a href="mailto:help@site.ru?subject=Вопрос%20по%20уроку"> — тема «заранее». Практика: форма «написать». Скринридер «прочтёт» имя ссылки «Написать в поддержку». Не «добавляйте» «и» тело «гигантское». Проверка: почта открывается. Доступность «контакта».

💡
Если «это» целая форма — <form action=mailto> «хуже»; лучше данными через бэкенд.

Заголовок-повторение? «Нет»: у артикула «в» карточке «не» надо h каждый раз

Карточка: <h3> названия «достаточно»; «артикул», «цена» — «p/span». Практика: витрина. Скринридер «прочтёт» h3 и детали «в» потоке. Не «лепите» «заголовок» на «каждую» строчку. Проверка: h-уровни «не» «скачут». Доступность «плотности».

💡
<h3>Nike Air 42</h3><p>Арт. 8841</p> — «порядок» важного «в» карточке.

Кнопка «поделиться» в соцсети: ссылка-«шаре» с осмысленным aria-label

💡Кнопка-шеринг (VK, Telegram) — ссылки (без формы) + aria-label="Поделиться в ВК". Практика: статья. Скринридер «прочтёт» целевое соц-имя. Не «делайте» иконку «без» слов. Проверка: имя ссылки ясно. Доступность «шаринга».

💡
aria-label «конкретный»: «Поделиться ссылкой во ВКонтакте» — не «Кнопка».

Псевдо-логика «в» CSS: :checked «выглядит» выбранным в кастом-чекбоксе

Стилизация радио/чекбокса: appearance:none + :checked → «фон+галка». Практика: набор фильтров. Скринридер «не» о визуале, но «состояние» DOM честное (aria-checked). Не «прячьте» «нативный» вид без «полного» кастома. Проверка: фокус виден. Доступность «кастом-галки».

💡
input:focus-visible + стиль-кольцо — кастом «не» «съел» индикатор фокуса.

Иконка «уведомления» в шапке: кнопка + aria-label

<button aria-label="Уведомления, 3 новых">🔔</button> — счётчик внутри. Практика: лента. Скринридер «прочтёт». Обновите aria-label при изменении. Проверка: число в имени. Доступность «колокольчика».

💡
Если «иконка» декоративна — aria-hidden, имя — в aria-label кнопки.

Таблица «с» длинным контентом: <th> scope + <caption> «как» имя

Результаты спринта: <caption>Итоговые баллы</caption>. Практика: таблица успеваемости. Скринридер «прочтёт» подпись «перед» данными. Проверка: scope у заголовков. Доступность «данных».

💡
<td data-label="Балл"> — псевдо-адаптив; «лучше» мало колонок.

Ссылка «на следующий урок» в конце: <nav aria-label="Уроки"> rel="next"

<a href="lesson-3.html" rel="next" aria-label="Следующий урок: Флексы"> →. Практика: линейный курс. Скринридер «прочтёт» имя. Проверка: rel=next у «вперёд». Доступность «цепочки».

💡
Полное имя: «Следующий урок: Урок 4 — доступная типографика», не просто «Вперёд».

Поле «Год» и <select> «дискретность»: aria-метка как число

<label for="year">Год рождения</label> + select с 1990–2010. Практика: регистрация. Скринридер «именует» селект. Проверка: порядок «возрастает». Доступность «года».

💡
Опции «1990»… «2010» — «просто» числа, «не» «магической» разметкой.

Карусель «review»: <figure> + <figcaption> автор «в» слайде

<figure><blockquote>«Отличный курс»</blockquote><figcaption>— Анна, дизайнер</figcaption>. Практика: лендинг-отзывы. Скринридер «свяжет» цитату с автором. Проверка: порядок тело-подпись. Доступность «отзыва».

💡
<blockquote cite="…"> при «внешнем» источнике — «не» для своих учеников.

Слайдер «цены»: <input type=range> + <output> с aria-live

Диапазон фильтра цены: <input type=range min=0 max=50000 step=500> + <output aria-live="polite">. Практика: каталог. Скринридер «озвучит» значение. Доступность «диапазона».

💡
aria-valuetext="до 25 000 ₽" — «отформатированное» число для озвучки.

Поле «поиска» + datalist: «города» с подсказками

<input list="cities"> + <datalist id="cities">. Практика: форма доставки. Скринридер «прочтёт» поле, подскажет список. Не «дублируйте» label. Проверка: выбор «свободен». Доступность «локаций».

💡
<datalist> «не» заменяет select «полноценно»: это «подсказка», а «не» «строгий» список.

Заголовок «свет»-секции и aria-labelledby к ней

Секция «Наши преподаватели» <section aria-labelledby="teachers"> + <h2 id="teachers">. Практика: команда. Скринридер «назовёт» регион. Проверка: id уникален. Доступность «секции».

💡
«Одна» секция — «одно» имя; не «дублируйте» aria-label «и» lablledby.

Мини-навигация «прогресса»: <progress> + aria-label

<progress max="100" value="40" aria-label="Прогресс курса">. Практика: панель пользователя. Скринридер «прочтёт» значение в процентах. Проверка: max не забыт. Доступность «прогресса».

💡
aria-valuetext="40 процентов завершено" — «осмысленнее», чем голое «40».

Аккордеон FAQ <details>: summary «имя» вопроса

<details><summary>Что входит в курс?</summary><p>…</p>. Практика: FAQ. Скринридер «прочтёт» вопрос и развернётся. Проверка: Tab — фокус на summary. Доступность «аккордеона».

💡
Вложенные заголовки «в» summary «не» плодите — «вопрос» и есть имя.

Колонки «признаков»: <ul> двумерный — не надо, «лучше» grid-section

<section><ul class="features"> в grid на «десктопе». Практика: преимущества. Скринридер «прочтёт» список «как» список «в» DOM-порядке. Проверка: flex-колонки «не» ломают «поток». Доступность «сеток».

💡
grid «визуальная» раскладка, ul — «семантика»: «разводите» их «по» ролям.

Псевдокласс :focus-within для «карточки с кнопкой»

.card:focus-within { outline:… } — фокус «в» любом элементе карточки виден на «внешней» рамке. Практика: карточки-товара. Скринридер «не» о визуале, но «глаз» «найдёт». Проверка: фокус на кнопке — граница карточки «осветилась». Доступность «зоны».

💡
Комбо: <button> в карточке + :focus-within ‘рамка’ — кнопке «не» обязателен «отдельный» кольцо.

Ссылка «на детальную статью» из preview: карточка-статья с h3

<article><h3><a href="#">…</a></h3><p>анонс</p><p><a href="#">Читать→</a></p>. Практика: блог. Скринридер «прочтёт» заголовок-ссылку. Проверка: «Читать» не «пустой». Доступность «превью».

💡
«Читать далее» + aria-label="Читать далее: Заголовок статьи" — «полное» имя ссылки.

Поле «Тема» со «списком» datalist: «раздел» обращения

<input list="topics"> «Причина обращения: Оплата» — подсказка. Практика: обратная связь. Скринридер «прочтёт». Проверка: label ясен. Доступность «тикет-тем».

💡
Если «вариантов» 5–8 — <select> честнее, чем «свободное» datalist.

Компетенции «с» прогрессом «в» профиле: <ul> + role="meter" «через» <progress>

<li><label>HTML</label><progress max=100 value=85 aria-label="HTML: 85%">. Практика: профиль студента. Скринридер «прочтёт». Проверка: имя у каждого. Доступность «навыков».

💡
«Не» перегружайте aria-valuenow — progress сам «скажет».

Кнопка «у» поля «пароля» в группе «глаз»: aria-pressed + aria-controls на input

<div><input type=password id=pw><button aria-pressed=false aria-controls=pw>👁</button></div> — aria-controls «связь». Практика: вход. Скринридер «прочтёт». Проверка: фокус на кнопке. Доступность «пароля».

💡
«Меняйте» type — «не» прячьте «глаз» «в» label.

Строка-«промо» у формы: <p> c aria-hidden «и» SVG «баннер»

Баннер-«скидка» над формой: <p class="promo">🔥 −15%</p>. Практика: ранняя пташка. Скринридер «прочтёт» текст «−15%»; «огонёк» — deco. Проверка: имя ясно. Доступность «акций».

💡
Эмодзи «огонёк» — «как» текст; «если» «не» «нужен» — aria-hidden на обёртке.

Чекбокс «вариант» в карточке: <label> стоит «рядом» с <span> текстом

Карточка-«выбор тарифа» с чекбоксом: <label><input type=checkbox><span>…«10 уроков»</span></label>. Практика: бандл. Скринридер «свяжет». Проверка: фокус. Доступность «бандла».

💡
label «инлайн» содержит «всю» карточку-опцию: клик «по» любой части выбирает.

Счётчик «символов» в textarea: <output> + aria-live polite на остаток

<textarea maxlength=200> + <output aria-live="polite">200 осталось. Практика: отзыв. Скринридер «услышит» обновление. Не «кричите» assertive. Проверка: остаток корректен. Доступность «лимита».

💡
«Достижение» 0 — «тогда» assertive «Лимит исчерпан».

Лайк-кнопка «сердце»: aria-pressed + «счётчик» в aria-label

<button aria-pressed=false aria-label="Лайк: 12">♥</button>. Практика: урок-галерея. Скринридер «прочтёт» состояние «и» число. Проверка: имя осмысленно. Доступность «лайка».

💡
«Сердце»-декор — aria-hidden; «счётчик» — «в» aria-label.

Прогресс «скролла статьи»: <progress> + aria-live только «на» вехе

Прогресс-бар «чтения»: обновляется «в» JS; aria-live polite «на» «Прочитано 50%». Практика: лонгрид. Скринридер «услышит» вехи. Не «сканьте» на каждый пиксель. Проверка: «сообщение» осмысленно. Доступность «чтения».

💡
<progress aria-label="Прогресс чтения"> + aria-valuetext="Половина".

Меню «в» шапке на мобильном: details+summary «скрывает» пункты

<details><summary>Меню</summary><nav>…</nav></details> — бургер «без» JS. Практика: адаптив. Скринридер «раскроет» summary. Проверка: фокус «внутри». Доступность «бургера».

💡
Активное меню-ориентир «в» details «не» «виден» «до» раскрытия? — «да»: aria-expanded у summary.

Псевдо-сайт-«визитки»: binding «имя и должность» через dl в footer

<dl><dt>Автор</dt><dd>Иван</dd>… в <footer>. Практика: подвал. Скринридер «прочтёт» пары. Проверка: «семантика» пары. Доступность «контактов».

💡
Не «разбивайте» одного автора «на» много dt-смыслов без нужды.

Энтузиазм «заголовка» лонгрида: <h1> + <time> рядом «не» в h1

<h1>Полное руководство</h1><time>…</time> — дата «вне» заголовка. Практика: статья. Скринридер «сначала» h1, «потом» дату. Проверка: порядок. Доступность «публикации».

💡
<time> «не» «заголовок»: «не» «включайте» «в» h1 «слово»-дату.

Карточка «урока» как <li>: серия с «порядковым» тайтлом

<ul><li><article><h3>Урок 1: Основы</h3>… или «Урок 1» + <time> м. Практика: курс-ветка. Скринридер «прочтёт» «порядок». Проверка: «номера» честные. Доступность «серии».

💡
aria-posinset не нужен — <ol> считает сам.

Инлайн-«смайл» как метка состояния в таблице: aria-label

<td aria-label="Доставлено">✅</td> — «эмодзи» - декорация. Практика: статус заказа. Скринридер «прочтёт» aria-label, «не» «смайл». Проверка: имя у ячейки. Доступность «статусов».

💡
Если «текст» статуса «в» ячейке «есть» — «просто» текст; aria-label «не» нужен.

Заголовок «урока» «в» продающем списке: <ol> + «у» каждого <li> своё h3 «для» имени темы

<ol> «нумерует» уроки; внутри <li> — <h3> «Тема 1: Основы»</h3>. Практика: программа курса. Скринридер «прочтёт» «урок 1, тема…». Проверка: порядок честный. Доступность «программы».

💡
«Не» «оборачивайте» «каждый» урок «в» span-«счётчик» — «хватит» <ol>.

Кнопка «снять выделение» у «цвета» фильтра: aria-pressed на «пипетке»

<button aria-pressed="true">🎨 Цвет: синий ✓</button>. Практика: каталог обоев. Скринридер «прочтёт» состояние. Проверка: имя = цвет. Доступность «пипетки».

💡
aria-pressed «и» aria-label: «Синий — выбран, снять» — полное «имя+состояние».

Число «книг» «в» читалке: <ol> + aria-label на «главу» самые «без» «плодить» h

Читалка: <ol aria-label="Оглавление"><li><a href="#ch2">Глава 2: flexbox</a>. Практика: учебник. Скринридер «прочтёт» как навигацию. Проверка: aria-label осмыслен. Доступность «оглавления».

💡
<a href> «в» li — «глава» кликается; aria-label «не» «нужен» «если» текст ясен.

Мини-форма «вопрос-голосование»: fieldset+legend «+» радио «с» aria-pressed не надо (radio — сам)

Голосование в уроке: «Понравился ли урок?» — fieldset+legend+radio «Да/Нет». Скринридер «свяжет» группу. Проверка: «отправка» по кнопке. Доступность «голосования».

💡
legend «имя» опроса; radio «не» «нужен» aria-pressed (это «не» тумблер).

Ссылка «на» котнракт: <a href="oferta.pdf" aria-label="Оферта — PDF, 310 КБ">

Карточка-заказ: ссылка на оферту. Скринридер «прочтёт» имя «с» форматом. Не «прячьте» формат. Проверка: «скачивание» понятно. Доступность «документов».

💡
aria-label «не» заменяет «видимый» текст — дублируйте «в» тексте тоже.

Заголовок «в» диалоге «подтверждения»: <h2> внутри <dialog> «+» aria-labelledby

<dialog aria-labelledby="dlg-title"><h2 id="dlg-title">Удалить курс?</h2>… Скринридер «прочтёт» заголовок как имя. Проверка: фокус «в» диалоге. Доступность «подтверждения».

💡
aria-description? — «нет»; aria-labelledby «достаточно» для имени.

Ссылка «на» подкомментарий «в» ветке: <a aria-label="Ответить на комментарий Ивана">

Ветка обсуждения: <a href="#" aria-label="Ответить на комментарий Ивана">Ответить. Скринридер «цель» поймёт. Проверка: имя уникально. Доступность «ветки».

💡
«Не» «просто» «Ответить» — «в» ветке «неясно» кому.

Чекбокс «Понятно, не показывать»: label — клик «по» слову

<label><input type="checkbox"> Больше не показывать подсказку. Скринридер «прочтёт» label. Проверка: клик точен. Доступность «настроек».

💡
<small> подсказка с id + aria-describedby — «почему» «это» поле.

Тег <time> «с» диапазоном «—»: datetime="2025-11-20" у ноябрьской недели курса

<time datetime="2025-11-17">Неделя 3: 17–23 ноября — можно двумя time. Практика: расписание. Скринридер «прочтёт». Проверка: обе даты. Доступность «недели».

💡
<time>17</time>–<time>23</time>? — «лучше» одна «неделя»: datetime = конец.

Поле «в» карточке-опроснике: radio с осмысленным legend «и» подсказкой

Практика: «Сколько лет учите?» — fieldset legend + radio «<1, 1-3, 3+». Скринридер «прочтёт» группу. Проверка: «одно» значение. Доступность «стажа».

💡
<span> подсказка «Округлите год» — «в» legend контекст.

Декоративный «разделитель-звезда» между пунктами: <li> + ::after aria-hidden? — проще «без»

Таймлайн: пункты-«звёзды»: <ul role="list"> + li-карточки. Скринридер «прочтёт» пункты. «Звезда» — aria-hidden/decar. Проверка: поток цел. Доступность «таймлайна».

💡
Семантика таймлайна — «просто» список + заголовки дат.

Строка-«шаг» с <details> «как» раскрывающийся совет в инструкции

<details><summary>Совет: как ускорить</summary>… «в» конце шагов. Практика: монтаж. Скринридер «раскроет». Проверка: «важно» не «спрятано». Доступность «премудростей».

💡
Ошибки/критично — «просто» текст, «не» «в» details.

Название «вкладки» «Каталог» с <nav aria-label="Каталог товаров">: не путать с заголовком

<nav aria-label="Каталог товаров"> — ориентир «по» категориям. Практика: магазин. Скринридер «найдёт». Проверка: «заголовок» — отдельно. Доступность «каталога».

💡
aria-label «на» nav «не» «заголовок» — «имя» ориентира.

Заголовок-«аккордеона» FAQ с «вопросом»: <h3> <span> маленькая «часть» «всё» «в» details

<details><summary><h3>Как вернуть?</h3></summary>. Скринридер «прочтёт» вопрос. Проверка: «не» «плодить» h2. Доступность «FAQ».

💡
«Если» вопрос — «заголовок» пункта, «medium» — «да»; «иначе» — просто summary.

Кнопка-«стрелка» «Назад» у микросайта: aria-label + rel="prev"

<a href="lesson.html" aria-label="Назад к списку уроков" rel="prev">←. Практика: курс. Скринридер «прочтёт». Проверка: «не» пустая. Доступность «возврата».

💡
rel="prev/next" — «для» поисковиков; aria-label — «для» людей.

Кнопка «поделиться» «с» иконкой соцсети: aria-label «ВКонтакте»

<button aria-label="Поделиться в ВКонтакте">[svg]</button>. Скринридер «прочтёт». Проверка: «имя» цель. Доступность «шеринга».

💡
Декоративная иконка — aria-hidden.

Поле «выбора» месяца «и» года: <input type="month"> нативный

<input type="month"> — «месяц+год» одним полем. Практика: подписка. Скринридер «прочтёт» значение. Проверка: формат. Доступность «месяца».

💡
min/max «для» диапазона «действия» карты.

Форма «возврат» товара: label+textarea «почему» аргумент

<label>Причина возврата</label><textarea>. Скринридер «свяжет». Проверка: имя. Доступность «возврата».

💡
required? — «опционально»; скажите «поле необязательно».

Селект «Город» «с» optgroup по области: группировка «плоская»

<optgroup label="Московская область">… Практика: доставка. Скринридер «услышит» группу. Проверка: метки. Доступность «группы».

💡
optgroup «для» логических групп; «летать» long-list — «не» «дублировать».

Заголовок «из» данных ячейки: <th> текст + <abbr> «для» правды

<th><abbr title="Домашнее задание">ДЗ</abbr></th>. Скринридер «расшифрует». Проверка: «не» «шум». Доступность «аббревиатур».

💡
«Не» abbr «для» «всего» — «только» значимое первое.

Карусель «без» брата-дубля: aria-roledescription «карусель» + пауза

role="region" aria-roledescription="карусель" aria-label="Отзывы" — «одно» имя. Скринридер «прочтёт». Проверка: «пауза». Доступность «карусели».

💡
Автоплей — stop после фокуса; иначе — «упустишь».

Ссылка «в» меню «урока»: aria-current="true" с якорем секции

<a href="#part2" aria-current="true">Часть 2. Скринридер «прочтёт» «текущая». Проверка: «новая позиция» — убрать. Доступность «меню».

💡
aria-current «снимается», когда «пользователь» «дошёл» #part3.

Инлайн «кода» в статье: <code> + <span aria-label> не надо — «читается» текст

<p>Используйте <code>display:flex</code>. Скринридер «прочтёт» как код. Проверка: «не» «прятать» семантику. Доступность «кода».

💡
<kbd> — «клавиши»; <samp> — «вывод»; <var> — «переменные».

Метка «диапазона» «До X ₽»: <output> + aria-live у суммы слайдера

<output aria-live="polite">до 15000 ₽</output>. Скринридер «услышит». Проверка: «плавно». Доступность «фильтра».

💡
«Не» «кричать» — polite «вместо» assertive «здесь».

Кнопка-«звезда» — «избранное»: aria-pressed с невидимым label-текстом

<button aria-pressed="false" aria-label="В избранное: Сликер 3">☆</button>. Скринридер. Проверка: имя. Доступность «избранного».

💡
aria-pressed «переключает» состояние «желания».

Заголовок «обратного» списка-рейтинга: <ol reversed> «топ»-5 наоборот

<ol reversed> — «5,4,3…» нисходящий. Практика: рейтинг. Скринридер «прочтёт» номера. Проверка: честен. Доступность «рейтинга».

💡
reversed «не» «нужен» CSS-счётчик.

Поле «даты»: <input type="date"> «плюс» label + aria-describedby формата

<label>Дата занятия</label><input type="date">. Скринридер «услышит». Проверка: пример «ДД.ММ.ГГГГ» — describedby. Доступность «даты».

💡
aria-describedby «прочтёт» подсказку формата.

Ссылка «на» описание курса: <a> h2 карточки «О курсе»

Карточка курса: <h3><a href="#detail">Подробнее о курсе</a>. Скринридер «прочтёт». Проверка: имя. Доступность «детали».

💡
«Не» «учебный» заголовок — «ссылка» там «важна» имя-действие.

Мини-аккордеон «Вопросы и ответы»: <details> + <summary> — без JS

FAQ: 8 details. Скринридер «раскроет». Проверка: стрелки. Доступность «аккордеона».

💡
<summary> «имя» вопроса — «не» «плодить» aria.

Заголовок «логина» «без» визуального: aria-label на поле с «иконкой человечка» в форме входа

<input aria-label="Логин или email">. Скринридер «прочтёт». Проверка: имя. Доступность «входа».

💡
«Не» placeholder-«вместо» label.

Пункт «меню-хлеб» активного «сада»: <a aria-current="page">Плоды</a> «в» крошках «—» по-русски

Крошки: «Главная / Каталог / <a aria-current="page">Плоды</a>. Практика: витрина. Скринридер «прочтёт» «текущее». Проверка: текущее «не» ссылка? — «может» «и» span. Доступность «крошек».

💡
<nav aria-label="Хлебные крошки"> — «с» aria-current «на» последнем.

Поле «е-mail» «с» авто-капитализацией «выкл»: autocapitalize="off" для изгороди

<input type="email" autocapitalize="off"> — «мобильная» клавиатура «не» «заглавных». Практика: подписка. Скринридер «не» о, но «ввод» ровнее. Проверка: адрес корректен. Доступность «ввода».

💡
autocorrect="off" — «чтобы» «не» «правили» email.

Шрифт «у» пунктов статистики «в» карточке: system-ui + «и» цифры «через» <output> aria-live

«1500+ учеников» — <span aria-hidden>1500+</span>? — «нет»: «это» текст. Практика: лендинг. Скринридер «прочтёт». Проверка: число. Доступность «статистики».

💡
«Живой» счётчик — aria-live polite; статика — «просто» текст.

Мини-тест «правда/ложь»: radio «с» фразой «в» legend «весь» вопрос

<fieldset><legend>«CSS «может» «переставлять» «порядок» «чтения»?»</legend> + radio «Да/Нет». Скринридер. Проверка. Доступность «теста».

💡
«Вопрос» «в» legend — «имя» группы «и» «формулировка».

Карточка «кура»-«со» «скидкой»: div style + <p class="old-price"> «зачёркнутая» старая

«Старая» цена «зачёркнута»: <del>3900 ₽</del> + <ins>2900 ₽</ins>. Скринридер «прочтёт» обе. Проверка: «не» aria-label'ом. Доступность «цены».

💡
<del>/<ins> — «семантика» «изменения» цены «на» странице.