HTMHeaven — 19 советов и приёмов вёрстки +15



Представляю вашему вниманию перевод заметок с сайта HTMHell (раздела Heaven) — коллекции полезных советов и примеров HTML-кода

Ранее была опубликована коллекция заметок с плохими примерами кода с того же ресурса в статье HTMHell — адовая разметка (25 плохих примеров)

Темы статьи:

  1. Доступность элементов iframe

  2. Gif-анимации и "reduce-motion"

  3. Метатеги с описанием страницы

  4. Текущая страница в панели навигации

  5. Автозаполнение полей с паролями

  6. Структура заголовков страницы

  7. Формат изображений AVIF

  8. Элемент "section"

  9. Составление адреса в "mailto:"

  10. Последовательность упорядоченных списков

  11. Таблицы стилей для печати (print)

  12. Зачёркнутый текст

  13. Семантическая вёрстка: ol или ul или div

  14. Автоматические заглавные буквы

  15. Проверка орфографии

  16. Доступные ориентиры

  17. Элемент субтитров "track"

  18. Отладка HTML: Доступность

  19. Отладка HTML: Линтинг

0. Ссылки — атрибут download

С помощью HTML-атрибута download можно превратить обычную ссылку в ссылку для скачивания. Вместо перехода к документу, браузер предложит пользователю сохранить файл на диск.

<a href="myfile_hash5474n.pdf" download>
  Annual Report (666 KB) 
</a>

Значение атрибута задаёт название файла при скачивании

<a href="myfile_hash5474n.pdf" download="report.pdf">
  Annual Report (666 KB)
</a>

1. Доступность элементов iframe

Если iframes содержат значимое содержимое, они должны иметь название. Определить такое доступное название можно с помощью атрибута title. Если он отсутствует, вместо него скринридеры могут озвучить значение атрибута name или src, что усложнит пользователям понимание его предназначения.

<iframe title="Bob Dylan - Visions Of Johanna (Live 1966) YouTube" width="560" height="315" src="https://www.youtube.com/embed/uW9_2r3raHE"></iframe>

Если подключается iframe, который не виден пользователям, потому что не предназначен для взаимодействия, его стоит скрыть от скринридеров, а также сделать недоступным для выбора с клавиатуры

<iframe title="Intentionally hidden" aria-hidden="true" tabindex="-1" src="https://www.mythirdpartyscriptxy.com"></iframe>

2. Gif-анимации и "reduce-motion"

Gif-анимации следует отображать только в том случае, если пользователь не предпочитает обратного. В случае, если данная настройка активирована, следует с помощью тега <picture> и опции prefers-reduced-motion вместо анимации подставлять картинку.

<picture>
  <source srcset="pooh666.gif" media="(prefers-reduced-motion: no-preference)">
  <img src="pooh666.jpg" alt="Pooh Bear doing knee bends in front of a mirror. Instead of the mirror glass, there’s an illustration of Satan. It looks like Pooh is worshipping the devil.">
</picture>

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

В большинстве операционных системы у пользователей есть возможность уменьшить количество движений в браузерах и приложениях, а разработчики могут определить это с помощью prefers-reduced-motion.

По умолчанию мы используем jpg на случай, если операционная система пользователя не предоставляет такой возможности.

<picture>
  <img src="pooh666.jpg" alt="Pooh Bear doing knee bends in front of a mirror. Instead of the mirror glass, there’s an illustration of Satan. It looks like Pooh is worshipping the devil.">
</picture>

Для пользователей, которые не предпочитают уменьшение движений, мы заменяем картинку на анимацию

<picture>
  <source srcset="pooh666.gif" media="(prefers-reduced-motion: no-preference)">
  <img src="pooh666.jpg" alt="Pooh Bear doing knee bends in front of a mirror. Instead of the mirror glass, there’s an illustration of Satan. It looks like Pooh is worshipping the devil.">
</picture>

В итоге, в зависимости от предпочтений, пользователь должен увидеть либо jpg-картинку, либо gif-анимацию

Настройка анимирования в разных ОС:

  • macOS: System Preferences - Accessibility - Display - Reduce Motion

  • iOS: Settings - General - Accessibility - Reduce Motion

  • Android: Settings - Accessibility features - Accessibility - Advanced Visual Effects

  • Windows 10: Settings - Ease of Access - Display - Show animations in Windows.

3. Метатеги с описанием страницы

С помощью метатегов можно описывать страницы. Это важно для сторонних инструментов, сайтов и приложений. Поисковые движки могут использовать описания страниц в списке результатов, а соцсети — в предварительном просмотре, когда пользователь публикует ссылку.

<meta name="description" content="HTML, accessibility, performance, and SEO tips, tricks, and best practices.">
<meta property="og:description" content="HTML, accessibility, performance, and SEO tips, tricks, and best practices.">

Общее

Описания страниц действительно полезны, поскольку помогают пользователю понять, о чём эта страница перед тем, как перейти на неё. При описании страницы следует учесть несколько моментов:

  • Убедитесь, что описание краткое и информативное

  • Напишите уникальное описание для каждой страницы и попытайтесь вставить уникальные ключевые слова

  • Длина описания должна быть от 50 до 155 символов (после этого Google обрезает текст в результатах поисковой выдачи)

  • Обычно поисковые движки берут описание из тегов meta, но могут и не сделать это, если посчитают другое содержимое более подходящим

  • Описание не учитывается алгоритмом ранжирования поисковиков, но может увеличить количество кликов, что может положительно повлиять на позиции страницы в поисковой выдаче

Соцсети

Рекомендуется использовать метатеги description и og:description. Если метатег og:description отсутствует, Facebook, Pinterest и LinkedIn используют в предварительном просмотре содержимое description. Twitter воспринимает только og:description и игнорирует description , но предлагает специальный вариант twitter:description.

<!-- Search engines + Fallback for Facebook, Pinterest and LinkedIn -->
<meta name="description" content="HTML, accessibility, performance, and SEO tips, tricks, and best practices.">
<!-- Social media sites Like Twitter, Pinterest, Facebook or LinkedIn -->
<meta property="og:description" content="HTML, accessibility, performance, and SEO tips, tricks, and best practices.">
<!-- Specific Twitter description -->
<meta property="twitter:description" content="HTML, accessibility, performance, and SEO tips, tricks, and best practices.">

Данные парсеры позволяют проверить, как описания вашей страницы будут выглядеть в разных соцсетях:

В моём случае все они работали корректно, но в обсуждении в Twitter Kilian Valkhof отметил, что официальные парсеры устарели, так что результат стоит перепроверять

#nav-current

4. Текущая страница в панели навигации

Используйте атрибут aria-current, чтобы выделить текущую страницу на панели навигации как визуально, так и семантически.

<nav>
  <ul>
    <li><a href="/home">Home</a></li>
    <li><a href="/about-us" aria-current="page">About us</a></li>
    <li><a href="/contact">Contact</a></li>
  </ul>
</nav>

Если для выделения текущей страницы среди набора ссылок вы используете класс вроде .active, это будет работать только для зрячих пользователей.

<nav>
  <ul>
    <li><a href="/home">Home</a></li>
    <li><a href="/about-us" class="active">About us</a></li>
    <li><a href="/contact">Contact</a></li>
  </ul>
</nav>
.active {
  font-weight: bold;
}

Вместо этого CSS-класса можно использовать aria-current со значением page. Данный атрибут сообщит скринридерам, какая страница является текущей, а также позволит выбрать нужный элемент с помощью селектора атрибута в CSS

<nav>
  <ul>
    <li><a href="/home">Home</a></li>
    <li><a href="/about-us" aria-current="page">About us</a></li>
    <li><a href="/contact">Contact</a></li>
  </ul>
</nav>
[aria-current="page"] {
  font-weight: bold;
}

Вот небольшая демонстрация в TalkBack на Android (Внимание! Громкость записи звука достаточно большая. Извините)

5. Автозаполнение полей с паролями

Вы можете помочь менеджерам паролей и браузерам автоматически заполнять поля с паролями, если будете использовать атрибут autocomplete, и делать кое-что ещё.

<label for="new-password">New Password</label>
<input type="password" autocomplete="new-password" id="new-password" name="new-password" />

Давайте начнём с того самого "кое-чего ещё". Когда я готовил пример для данной публикации, я хотел продемонстрировать, что с атрибутом autocomplete="new-password" браузер предложит сгенерировать пароль.

Браузер Firefox распознаёт это поле как поле для ввода нового пароля и предлагает сгенерировать его
Браузер Firefox распознаёт это поле как поле для ввода нового пароля и предлагает сгенерировать его

Для начала я создал простую форму.

<form>
  <div>
    <label for="username">Username</label>
    <input type="text" id="username" name="username" />
  </div>  

  <div>
    <label for="np">New Password</label>
    <input type="password" id="np" name="np" />
  </div>
</form>

К моему удивлению, браузер Firefox предложил сгенерировать новый пароль даже без атрибута autocomplete. Это сбило меня с толку. Спустя некоторое время, потраченное на тестирование и изучение, я узнал, что для определения предназначения поля ввода браузеры учитывают самые разные данные. В данном случае это сработало, так как подпись в элементе <label> содержала слова "new password". Просто взрыв мозга!

Если, например, перевести подписи на немецкий, Firefox больше не предлагает подставить пароль. Мне понадобилось всего лишь 2 часа, чтобы понять это.

<div>
  <label for="username">Benutzername</label>
  <input type="text" id="username" name="username" />
</div>  

<div>
  <label for="np">Neues Passwort</label>
  <input type="password" id="np" name="np" />
</div>

Вы можете обратить внимание, что атрибуты id и name короткие и трудночитаемые. Это сделано из-за того, что браузеры также используют их для определения предназначения поля ввода. Если значением этих атрибутов будет new-password или даже new-pwd, браузер Firefox снова будет предлагать сгенерировать пароль.

<p>
  <label for="new-pwd">Neues Passwort</label>
  <input type="password" id="new-pwd" name="np" />
</p>

Убедиться в срабатывании можно в этом примере, где атрибуту id задано значение new-pwd, и в этом примере, где атрибуту name задано значение new-password.

Конечно, это очень упрощённый тестовый пример. Обычно всё намного сложнее. Я тестировал это поведение только в одном браузере и только с одним встроенным менеджером паролей. Мы должны не полагаться на алгоритмы браузеров, а помогать им понять предназначение полей ввода.

Мы должны:

  • Использовать стандартные элементы и атрибуты HTML-форм (form, label, input, и т.д.)

  • Используйте правильный атрибут type для каждого поля ввода

  • Убедитесь, что значения атрибутов id и name не генерируются произвольно

  • Для атрибутов name и id задавайте значения, являющиеся одинаковыми или похожими на значение атрибута autocomplete

  • Добавьте атрибут autocomplete и используйте значение current-password для поля с текущим паролем и значение new-password для поля с новым паролем

  • В формах "регистрации" и "авторизации/входа" используйте разные значения для атрибутов name и id как у самого элемента формы, так и у элементов input, select, textarea

  • Тщательно тестируйте в браузерах с разными менеджерами паролей

<form>
  <p>
    <label for="username">Username</label>
    <input type="text" id="username" name="username" autocomplete="username" />
  </p>  

  <p>
    <label for="new-password">New Password</label>
    <input type="password" id="new-password" name="new-password" autocomplete="new-password" />
  </p>
</form>

6. Структура заголовков страницы

С мая по июнь 2021 года компания WebAIM проводила опрос предпочтений пользователей скринридеров и результаты показали, что большинство участников считают полезной правильную структуру заголовков.

Правильная структура заголовков важна по нескольким причинам:

  • Заголовки формируют структуру документа, которую могут учитывать сторонние инструменты. При проверке страницы в валидаторе в блоке "More options" можно выбрать опцию "Show outline", чтобы проверить и отобразить структуру заголовков проверяемой страницы.

  • Заголовки отражают организацию содержимого страницы. Заголовок <h1> сообщает пользователям, о чём эта страница. Заголовок <h2> разделяет страницу на большие разделы, <h3> - <h6> ещё больше структурируют эти большие разделы.

  • Правильное использование заголовков помогает поисковым движкам понимать, о чём ваша страница и как она структурирована.

  • Пользователи скринридеров получают ознакомиться с содержимым страницы, пройдя только по заголовкам. Программы обычно озвучивают содержимое заголовка вместе с его уровнем (1 - 6). Вот почему настолько важным является сохранение чёткой структуры документа. Пропуск заголовков может сбивать с толку, поэтому его следует избегать где это возможно.

VoiceOver на MacOS перечисляет все заголовки страницы с их уровнем и позволяет пользователям перейти к выбранному заголовку
VoiceOver на MacOS перечисляет все заголовки страницы с их уровнем и позволяет пользователям перейти к выбранному заголовку
  • Скринридеры позволяют осуществлять навигацию по заголовкам. Это значит, что вы можете не только просматривать заголовки страницы, но и выбирать их, переходя непосредственно к позиции выбранного заголовка в DOM, чтобы продолжить чтение страницы с этого места.

  • Также заголовки позволяют зрячим людям понимать, как структурирована страница и как разные разделы страницы соотносятся друг с другом

Совет, который я хотел бы дать моим студентам, — представить, что они пишут доклад для университета, когда создают план веб-страницы. У доклада всегда должен быть заголовок, и заголовок должен быть только один (<h1>). Также обычно присутствует несколько основных глав (<h2>), а иногда есть и подглавы (<h3> - <h6>).

Лучше представлять, как логически должна быть структурирована страница, чем делать это исходя из её дизайна. Иногда есть смысл добавлять на страницу заголовки, которые визуально не отображаются, чтобы задать структуру для тех, кто работает со страницей, прослушивая её содержимое, а не глядя на неё. Вы можете доступно скрыть эти заголовки, с помощью набора свойств.

<h2 class="sr-only">I'm visually hidden</h2>
.sr-only {
  position: absolute;
  white-space: nowrap;
  width: 1px;
  height: 1px;
  overflow: hidden;
  border: 0;
  padding: 0;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  margin: -1px;
}

7. Формат изображений AVIF

AVIF — это достаточно новый формат изображений и для меня это одно из самых захватывающих из недавних нововведений в сфере веб-разработки. Почему? На одном из вебсайтов, который я недавно разрабатывал, была возможность сократить размер изображений с 1.72МБ до 172КБ просто сконвертировав их в формат AVIF.

Звучит слишком хорошо, чтобы быть правдой, поэтому давайте начнём с плохой новости: немногочисленными браузерами, поддерживающими данный формат, являются Chrome, Chrome for Android, Opera, Opera Mobile и Samsung Internet. Firefox поддерживает за флагом (на момент перевода — уже по умолчанию).

Прогрессивное улучшение

Хорошая новость заключается в том, что мы можем использовать встроенный механизм прогрессивного улучшения HTML, чтобы обеспечить фолбэк для тех, чей браузер ещё не поддерживает формат AVIF.

Расположенный выше скриншот имеет следующую разметку (на сайте оригинала статьи):

<picture>
  <source srcset="/images/7_avif-support.avif" type="image/avif">
  <source srcset="/images/7_avif-support.webp" type="image/webp">
  <img src="/images/7_avif-support.jpg" width="740" height="251" alt="caniuse.com browser support chart for the avif image format" loading="lazy">
</picture>

Мы предоставляем браузерам набор изображений. Браузеры, в свою очередь, просматривают эти данные и останавливаются на первом же формате, который смогут распознать. Chrome выберет первый элемент <source> , потому что поддерживает формат AVIF, браузер Edge пропустит первую строку и будет использовать изображение WebP со второй строки, а IE пропустит оба варианта и будет использовать JPG-изображение, из элемента <img>.

Браузерная поддержка WebP

Вы даже можете полностью отказаться от варианта с JPG-изображением, потому что в наши дни формат WebP уже довольно хорошо поддерживается всеми современными браузерами, включая Safari.

<picture>
  <source srcset="/images/7_avif-support.avif" type="image/avif">
  <img src="/images/7_avif-support.webp" width="740" height="251" alt="caniuse.com browser support chart for the avif image format" loading="lazy">
</picture>

Сравнение размера изображений

Я сделал скриншот сайта HTMHell, сжал его с помощью OxiPNG и сконвертировал в форматы WebP и AVIF.

Существует несколько инструментов сжатия, работающих с AVIF. Я точно могу рекомендовать приложение Squoosh.

8. Элемент "section"

Используйте элемент <section> для разметки набора логически связанного контента, обычно имеющего заголовок.

<h1>Welcome to GOV.UK</h1>
<section>
  <h2>Services and information</h2>
  …
</section>
<section>
  <h2>Departments and policy</h2>
  …
</section>
…

Выдержка из кода страницы gov.uk

Не всегда понятно, уместно ли в конкретной ситуации использовать элемент <section> и как сделать это правильно. Я собрал рекомендации, которые должны помочь принять решение.

Неявный регион и открытая роль

По умолчанию для вспомогательных технологий в плане семантики нет разницы между элементами <section> и <div>. Для скринридера следующие два фрагмента кода практически одинаковы:

<section>
  <h2>Services and information</h2>
  …
</section>
<div>
  <h2>Services and information</h2>
  …
</div>

Но несмотря на отсутствие разницы для вспомогательных технологий, по-прежнему есть смысл отдавать предпочтение именно <section>, когда уместно. Данный семантический элемент позволяет структурировать страницу и, возможно, стилизовать её соответствующим образом с помощью селектора тега section.

DIV или SECTION

Элемент <section> не является заменой для <div> и не должен использоваться, если нужно просто стилизовать часть содержимого.

Имена классов в коде ниже, а также то, что элементы вложены в <header>, указывают на то, что они предназначены лишь для визуального разделения хедера на 4 колонки. Они не являются важными разделами страницы. Конечно, поиск и блок навигации важны, но <nav> сам по себе уже является является ориентиром (landmark), а форму поиска можно превратить в ориентир, добавив атрибут role="search".

<header class="site-header">
  <section class="site-header__title"></section>
  <section class="site-header__logo"></section>
  <section class="site-header__search"></section>
  <section class="site-header__nav"></section>
</header>

В HTML-спецификации есть полезное практическое правило:

Основное правило заключается в том, что элемент <section> уместно использовать в том случае, если его содержимое может быть явно представлено в структуре разделов документа

Ориентир "region"

Роль и предназначение раздела <section> меняются, когда вы помечаете его с помощью атрибута aria-label, aria-labelledby или title. Именование с помощью этих атрибутов превращает его в ориентир, который задаёт ему ARIA-роль "region" и даёт пользователям скринридеров более простой доступ к разделу.

<section aria-labelledby="results">
  <h2 id="results">Results</h2>
  …
</section>

Другие ориентиры (например, <header>, <main> или <footer>) также являются важными разделами страницы. С учётом сказанного, делайте <section> ориентиром только в том случае, если он играет важную роль на текущей странице.

Структура документа

Если на странице содержится несколько заголовков <h1> и некоторые из них вложены в <section>, семантически у вас по-прежнему несколько заголовков 1 уровня. Размер шрифта элемента <h1>, вложенного в <section>, может уменьшиться, но уровень, сообщаемый вспомогательным технологиям, всё ещё 1.

Заключение

Если вы не уверены, следует ли использовать элемент <section>, возможно не стоит сильно заморачиваться на этот счёт. Гораздо важнее создать правильную структуру документа.

9. Составление адреса в "mailto:"

При нажатии на ссылку, содержащую email-адрес, можно предварительно заполнять поля заголовка и тела создаваемого сообщения.

<a href="mailto:manuel@matuzo.at?subject=HTMHell%20issue%209%20was%20fantastic&body=Thanks,%0D%0AManuel!">
  manuel@matuzo.at
</a>

При нажатии на составленную таким образом ссылку, стандартный email-браузер должен открыть окно с новым email-сообщением, содержащим тему "HTMHell issue 9 was fantastic" и сообщение "Thanks, Manuel".

При создании ссылки на email-адрес вы можете предоставить тему, получателя и тело сообщения. Делается это путём написания email-адреса, за которым следует символ "?", название заголовка, символ "=", и значение заголовка.

Получатели

При создании таких ссылок есть возможность не указывать ни одного адреса, указать только один или несколько адресов

mailto: без значения

Если вы не укажете значение для mailto, стандартный email-клиент откроет новое окно с сообщением, все поля которого будут пустыми.

Результат

<a href="mailto:">
  mailto: only
</a>

mailto: с одним адресом

Добавит в поле "Кому" указанный адрес

Результат

<a href="mailto:manuel@matuzo.at">
  mailto: only, with single address
</a>

mailto: с несколькими адресами

Если адресов несколько, их необходимо разделить запятой без пробелов

Результат

<a href="mailto:manuel@matuzo.at,manuel@webclerks.at">
  mailto: only, with multiple addresses
</a>

mailto + параметр "to"

Задать нескольких получателей также можно с помощью параметра to

Результат

<a href="mailto:manuel@matuzo.at?to=manuel@webclerks.at,info@webclerks.at">
  mailto: + to parameter
</a>

mailto + параметр "cc"

Поля "Кому" (To) и "Копия" (Cc) заполнятся разными адресами

Результат

<p>
  <a href="mailto:manuel@matuzo.at?cc=manuel@webclerks.at">
    mailto: + cc parameter
  </a>
</p>

mailto + параметр "bcc"

Поля "Кому" и "Скрытая копия" заполнятся разными адресами

Результат

<p>
  <a href="mailto:manuel@matuzo.at?bcc=manuel@webclerks.at">
    mailto: + bcc parameter
  </a>
</p>

Тема

Вы можете определить тему с помощью параметра subject. Специальные символы и пробелы должны быть закодированы

Результат

<p>
  <a href="mailto:manuel@matuzo.at?subject=This%20is%20a%20subject">
    mailto: + subject parameter
  </a>
</p>

Тело сообщения

Определить тело сообщения можно с помощью параметра body

Результат

<p>
  <a href="mailto:manuel@matuzo.at?body=Hi!">
    mailto: + body parameter
  </a>
</p>

Специальные символы, переносы строк и пробелы должны быть закодированы

Результат

<p>
  <a href="mailto:manuel@matuzo.at?body=Hi!%0D%0A%0D%0ATom%20%26%20Jerry%20are%20the%20best!">
    mailto: + body parameter
  </a>
</p>

В тело сообщения нельзя добавлять HTML, только простой текст

Дополнительные уточнения

  • Пробелы должны быть закодированы как %20

  • Переносы строк должны быть закодированы как %0D%0A

  • Чтобы избежать проблем с совместимостью, не следует несколько раз указывать один и тот же заголовок. Например, ?to=a@b.at?to=b@c.at

10. Последовательность упорядоченных списков

Изменить последовательность упорядоченных списков можно с помощью атрибута reversed.

<ol reversed>
  <li>Curly Sue (1991)</li>
  <li>Uncle Buck (1989)</li>
  <li>She's Having a Baby (1988)</li>
  <li>Planes, Trains & Automobiles (1987)</li>
  <li>Ferris Bueller's Day Off (1986)</li>
  <li>Weird Science (1985)</li>
  <li>The Breakfast Club (1985)</li>
  <li>Sixteen Candles (1984)</li>
</ol>

Упорядоченные списки

8.Curly Sue (1991)
7. Uncle Buck (1989)
6. She's Having a Baby (1988)
5. Planes, Trains & Automobiles (1987)
4. Ferris Bueller's Day Off (1986)
3. Weird Science (1985)
2. The Breakfast Club (1985)
1. Sixteen Candles (1984)

При использовании атрибута reversed порядок элементов списка остаётся прежним, меняется только нумерация от большего к меньшему.

Можно комбинировать атрибуты reversed и start для изменения числа, с которого начинается отсчёт.

<ol reversed start="5">
  <li>Fifth</li>
  <li>Fourth</li>
  <li>Third</li>
</ol>

5.Fifth
4.Fourth
3 Third

Неупорядоченные списки

На неупорядоченные списки (<ul>) данный атрибут не влияет, даже если с помощью атрибута попытаться изменить тип списка

<ul reversed type="1">
  <li>Third</li>
  <li>Second</li>
  <li>First</li>
</ul>
  • Third

  • Second

  • First

11. Таблицы стилей для печати (print)

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

<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="print.css" media="print">

Одним из самых захватывающих и сложных в веб-разработке является то, что мы не можем заранее предсказать, как пользователи будут использовать содержимое страницы. Существует большое количество различных устройств, браузеров, способов ввода и вывода содержимого. Некоторые люди предпочитают использование навигации, другие — поиска. Одним нравится минималистичный режим чтения, другим — распечатанная версия.

Да, я знаю, сейчас 2021 год, но у людей по-прежнему есть принтеры и они всё ещё время от времени что-то распечатывают. Печатная версия страниц не должна быть идеальной, но мы должны быть уверены, что главное содержимое страницы хотя бы доступно пользователю после печати на бумаге. Мы можем сделать это, предоставив соответствующие стили и включив их в HTML.

<link rel="stylesheet" href="print.css" media="print">

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

Отладка стилей для печати

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

Firefox

В панели разработчика браузера в блоке "Rules" есть специальная кнопка с иконкой, похожей на документ или распечатанную страницу.

Chrome/Edge

В панели разработчика браузеров Chrome/Edge можно нажать на кнопку с тремя точками, перейти в "More tools", далее "Rendering" и наконец в разделе "Emulate CSS media type" выбрать "print".

Safari

На панели инструментов браузера в разделе "Elements" есть кнопка с иконкой в виде принтера.

12. Зачёркнутый текст

HTML предоставляет нам 2 способа определения зачёркнутого текста: элементы <s> и <del>. С точки зрения семантики "зачёркнутый текст" может отображаться перечёркнутым, хотя это и не обязательно.

<h3>Vietnamese Rose Wood Nose Flute</h3>
<p>
  The nose flute is the <del>mots</del> <ins>most</ins> beautiful instrument in the world.
</p>
<p>
  <s>Original price: € 19.99</s>
</p>
<p>
  <strong>Special offer: € 9.99!</strong>
</p>

Элемент < s >

Используйте элемент <s>, чтобы выделить содержимое, которое больше не актуально. Типичным примером является старая и новая цена товара.

<p><s>Original price: € 19.99</s></p>
<p><strong>Special offer: € 9.99!</strong></p>

Важным моментом здесь является то, чтобы отображать не только саму цену, но и её текстовое описание, потому что не все скринридеры озвучивает содержимое элемента <s> нужным образом.

<p><s>€ 19.99</s></p>
<p><strong>€ 9.99!</strong></p>

Данный фрагмент кода без описательного текста скринридеры могут озвучить как "19 евро точка 99 9 евро точка 99", что может запутать пользователя.

Если нужно отобразить только цену, описательный текст можно скрыть визуально

.sr-only {
  position: absolute;
  white-space: nowrap;
  width: 1px;
  height: 1px;
  overflow: hidden;
  border: 0;
  padding: 0;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  margin: -1px;
}
<p>
  <span class="sr-only">Original price: </span><s>€ 19.99</s>
</p>
<p>
  <span class="sr-only">Special offer</span><strong>€ 9.99!</strong>
</p>

Данная разметка будет и корректно озвучиваться скринридерами и корректно отображаться визуально

Steve Faulkner и Adrian Roselli представили способы использования псевдоэлементов, чтобы сделать элемент <s> доступным в статьях Short note on making your mark (more accessible) и Tweaking Text Level Styles.

Элемент del

Используйте элемент <del>, чтобы выделить содержимое, которое было удалено из документа. С помощью необязательного атрибута cite можно указать ссылку на дополнительную информацию про редактирование, а с помощью атрибута datetime указать дату и время редактирования.

<p>
  The nose flute is the <del datetime="2021-09-03T17:42:30">mots</del> <ins datetime="2021-09-03T17:42:36">most</ins> beautiful instrument in the world.
</p>

Talkback в операционной системе Android озвучит предложение как "The nose flute is the mots deletion most insertion beautiful instrument in the world", но VoiceOver и другие скринридеры озвучат как "The nose flute is the mots most beautiful instrument in the world". Чтобы заставить скринридеры озвучивать факт редактирования, Адриан и Стив рекомендуют использовать псевдоэлементы.

del::before, del::after, 
ins::before, ins::after {
  clip-path: inset(100%);
  clip: rect(1px, 1px, 1px, 1px);
  height: 1px;
  width: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
}

del::before {
  content: " [deletion start] ";
}

del::after {
  content: " [deletion end] ";
}

ins::before {
  content: " [insertion start] ";
}

ins::after {
  content: " [insertion end] ";
}

С использованием этой техники VoiceOver озвучит "The nose flute is the deletion start mots deletion end insertion start most insertion end beautiful instrument in the world".

13. Семантическая вёрстка: ol или ul или div

Разница между использованием <ol>, <ul> и <div> для списка элементов.

<!-- Упорядоченный список -->
<ol>
  <li>Clerks (1994)</li>
  <li>Mallrats (1995)</li>
  <li>Jay and Silent Bob Strike Back (2001)</li>
</ol>

<!-- Просто текст -->
<div>
  <div>Clerks (1994)</div>
  <div>Mallrats (1995)</div>
  <div>Jay and Silent Bob Strike Back (2001)</div>
</div>

Недавно в Twitter кто-то спросил: "Действительно ли семантический HTML так полезен?". Это хороший вопрос, поскольку немало людей знают, КАК писать HTML, но не знают, ПОЧЕМУ. В данной статье я постараюсь объяснить, в чём польза семантического HTML. Начнём с элементов <ol> и <ul>.

Чем полезны элементы ul/ol ?

  • Скринридеры могут озвучить их как список элементов

  • Скринридеры могут озвучить количество элементов в списке

  • Скринридеры могут озвучить номер каждого элемента

  • Пользователи скринридеров могут с помощью сочетания клавиш быстро переходить от списка к списку элементов страницы

  • Данные элементы группируют содержимое визуально

  • Данные элементы предоставляют селектор для стилизации (не)упорядоченных списков в CSS

Неупорядоченные списки

Элемент неупорядоченного списка <ul> мы используем для группировки некоторого количества логически связанных элементов, перечисленных в произвольном порядке.

<h3>Some of my favorite movies directed by Kevin Smith</h3>
<ul>
  <li>Jay and Silent Bob Strike Back</li>
  <li>Mallrats</li>
  <li>Clerks</li>
</ul>

NVDA в браузере Firefox озвучит данную разметку следующим образом: "List with 3 items, Bullet Jay and Silent Bob Strike Back, Bullet Mallrats, Bullet Clerks, Out of list"

Упорядоченные списки

Элемент упорядоченного списка <ol> мы используем для группировки некоторого количества логически связанных элементов, перечисленных в определённом порядке.

<h3>Movies directed by Kevin Smith sorted by release date</h3>
<ol>
  <li>Clerks (1994)</li>
  <li>Mallrats (1995)</li>
  <li>Jay and Silent Bob Strike Back (2001)</li>
</ol>

NVDA в браузере Firefox озвучит данную разметку следующим образом: "List with 3 items, 1 Clerks, 2 Mallrats, 3 Jay and Silent Bob Strike Back, Out of list"

Элементы DIV

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

<h3>Movies directed by Kevin Smith sorted by release date</h3>
<div>
  <div>Clerks (1994)</div>
  <div>Mallrats (1995)</div>
  <div>Jay and Silent Bob Strike Back (2001)</div>
</div>

NVDA в браузере Firefox озвучит данную разметку следующим образом: "Clerks, Mallrats, Jay and Silent Bob Strike Back"

14. Автоматические заглавные буквы

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

<label for="words">Words</label>
<input type="text" id="words" autocapitalize="words">

Представим, у вас есть поле ввода, в которое пользователи должны вводить текст с использованием только строчных букв. Но если поставить фокус на текстовое поле, по умолчанию первая введённая буква будет заглавной. Работу с полями можно сделать удобнее, если управлять этой логикой ввода заглавных букв. Если установить атрибут autocapitalize в значение none, автоматического ввода заглавных буквы больше не будет.

<label for="none">All lowercase</label>
<input type="text" id="none" autocapitalize="none">

Данный атрибут поддерживается браузером Safari на iOS и браузерами Chrome, Edge и Samsung Internet на Android. Firefox поддерживает его только за флагом. Также нужно уточнить, что он никак не влияет на ввод с помощью физической клавиатуры.

Помимо none есть ещё три значения:

  • none — переключение на ввод заглавных букв должно быть отключено

  • characters — все буквы должны быть заглавными

  • sentences — первая буква каждого предложения по умолчанию заглавная; все остальные буквы вводятся строчными

  • words — первая буква каждого слова должна быть заглавной; все остальные буквы вводятся строчными

15. Проверка орфографии

Порой бывает полезной возможность отключить проверку орфографии в полях ввода разных типов: input, textarea, contenteditable

<label for="msg">Message</label>
<textarea spellcheck="false" id="msg">HTML is amazzing!</textarea>

Необходимость проверки определяется атрибутом spellcheck, для которого необходимо задать значение true или false. Если атрибут не представлен, браузер сам решает, нужно ли выполнять проверку в конкретном поле.

<div>
  <label for="msg">Message</label>
  <input value="HTML is amazzing!" id="msg">
</div>

<div>
  <label for="msg2">Message</label>
  <textarea id="msg2">HTML is amazzing!</textarea>
</div>

<div>
  <strong>Message:</strong>
  <div contenteditable>HTML is amazzing!</div>
</div>

Если, например, в браузере Firefox нажать на одно из полей приведённой выше разметки, вы увидите волнистую красную линию, указывающую на неправильное написание. Обратите внимание, что в зависимости от браузера и выбранного языка поведение может отличаться. Факт присутствия атрибута является больше подсказкой и не принуждает браузера выполнять проверку.

<div>
  <label for="msg2">Message</label>
  <textarea spellcheck="false" id="msg2">HTML is amazzing!</textarea>
</div>

А в этом поле подчёркивания быть не должно, поскольку атрибут spellcheck установлен в значение false

16. Доступные ориентиры

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

<body>
  <!-- banner landmark -->
  <header>
    <!-- navigation landmark -->
    <nav>
    </nav>
  </header>
  <!-- main landmark -->
  <main>
    <!-- search landmark -->
    <form role="search">
    </form>
  </main>
  <!-- contentinfo landmark -->
  <footer>
  </footer>
</body>

Ориентиры в HTML

Элемент

ARIA-роль

Условия

header

banner

Только в контексте элемента body, а не когда является потомком элементов <article>, <aside>, <main>, <nav> или <section>

nav

navigation

main

main

section

region

Когда имеет доступное название, заданное с помощью aria-labelledby, aria-label или title

form

form

Когда имеет доступное название, заданное с помощью aria-labelledby, aria-label или title

form

search

Когда задан атрибут role="search"

aside

complementary

footer

contentinfo

Только в контексте элемента body, а не когда является потомком элементов <article>, <aside>, <main>, <nav> или <section>

Советы

<body>
  <header>
    <nav aria-label="Primary"></nav>
  </header>
  <footer>
    <nav aria-label="Secondary"></nav>
  </footer>
</body>

Пример списка ориентиров без названий в VoiceOver на macOS

Пример списка ориентиров с названиями в VoiceOver на macOS

  • Избегайте повторов при названии навигационных блоков. Название вроде "primary navigation" может быть озвучена как "primary navigation navigation". Вместо этого назовите её просто "primary"

  • Если на странице присутствует два ориентира панели навигации с одинаковым набором ссылок, то и назвать их стоит одинаково

  • Не добавляйте на страницу слишком много ориентиров. Это может усложнить навигацию

  • Ориентир banner может быть только один

  • Ориентир contentinfo может быть только один

  • Документ не должен содержать более одного элемента <main> без атрибута hidden

  • Если на странице несколько ориентиров complementary, у каждого должно быть уникальное название (aria-label или aria-labelledby)

  • Если на странице несколько ориентиров form, у каждого должно быть уникальное название (aria-label или aria-labelledby)

  • Обычно нет необходимости явно задавать ориентиры. Например, <header></header> вполне достаточно, нет необходимости указывать `<header role="banner"></header>`

  • В большинстве своём ориентиры хорошо поддерживаются, но поддержка может отличаться, особенно для ориентира form. Рекомендуется тестировать ориентиры на разных скринридерах и браузерах. Некоторые из них могут намеренно не озвучивать определённые ориентиры.

17. Элемент субтитров "track"

Элемент <track> позволяет указывать для медиа-элементов синхронизированный текст (например, подписи или субтитры).

<video src="workshop_promo.mp4" controls>
  <track default kind="captions" srclang="en" src="workshop_promo.vtt" label="English">
  <track kind="subtitles" srclang="de" src="workshop_promo_de.vtt" label="Deutsch">
  Sorry, your browser doesn't support embedded videos.
</video>

Например, если для элемента <video> необходимо подставить в определённый момент подписи или субтитры, это можно сделать с помощью встроенного элемента.

Получившийся результат можно посмотреть на странице оригинальной статьи.

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

С элементом <track> можно использовать некоторые атрибуты:

  • src — указывает путь к файлу .vtt, содержащему текст

  • kind — определяет тип добавляемой текстовой дорожки

    • subtitles — для перевода произносимой на видео речи

    • captions — для расшифровки аудио и важной невербальной информации

    • descriptions — для текстового описания содержимого видео

    • chapters и metadata — треки, предназначенные для использования в скриптах (не отображаются пользователю)

  • label — заголовок текстовой дорожки, отображаемый в интерфейсе видео-элемента

  • srclang — позволяет указать язык текстовой дорожки

  • default — определяет текстовую дорожку по умолчанию

CSS

Данные подписи и субтитры можно стилизовать с помощью CSS. Список поддерживаемых свойств можно посмотреть на MDN.

::cue {
  background-color: red;
}

JavaScript

Имеется событие JavaScript, которое вызывается при смене отображаемого в данный момент фрагмента текста.

let textTrackElem = document.querySelectorAll("track")[0];

textTrackElem.addEventListener("cuechange", (event) => {
  let cues = event.target.track.activeCues;
  console.log(cues)
});

Файл субтитров .vtt

Содержимое файла с текстовой дорожкой имеет следующий формат

00:00:00.240 --> 00:00:07.040
Hi! My name is Manuel and if you know me, you know that I like to complain about HTML others have

00:00:07.040 --> 00:00:14.880
written. I post about it on Twitter, I even have a website called htmhell.dev where I post bad code,

18. Отладка HTML: Доступность

Панель разработчика современных браузеров позволяет отлаживать функции доступности HTML-элементов.

Например, ваши пользователи сообщают о том, что с помощью клавиатуры не могут взаимодействовать с бургер-кнопкой для вызова меню в панели навигации. Или что при использовании скринридера название кнопки очень странное и сбивает с толку. Есть несколько способов исправить это с помощью панели разработчиков.

Для начала давайте определимся с требованиями для правильной работы кнопки:

  • Подпись — наличие видимого текста или текстовой альтернативы

  • Семантика — корректная ARIA-роль кнопки

  • Фокус — на кнопку можно поставить фокус с помощью кнопки Tab клавиатуры

  • События — можно нажать на кнопку с помощью нажатия Пробел или Enter

Chrome Dev Tools — панель доступности

В браузере Chrome или Edge откройте панель разработчика, нажмите на вкладку "Elements", выберите элемент, который нужно проверить и перейдите на вкладку "Accessibility". Панель доступности показывает, как элемент представлен в дереве доступности, какие ARIA-атрибуты имеет и вычисленные значения его свойств.

Кнопка 1

Если проинспектировать следующую кнопку, мы узнаем:

<button aria-label="Navigation" class="btn">
  <svg viewBox="0 0 20 20" aria-hidden="true">
    <rect y="3" width="20" height="2"></rect>
    <rect y="9" width="20" height="2"></rect>
    <rect y="15" width="20" height="2"></rect>
  </svg>
</button>
  • Подпись — кнопка берёт название "Navigation" из атрибута aria-label(Name: "Navigation")

  • Семантика — у кнопки правильная роль ( `Role: button` )

  • Фокус — кнопка способна получать фокус (`Focusable: true`)

Кнопка 2

Рассмотрим ещё один пример. Данная кнопка выглядит также, но менее доступна

<div type="button" class="btn">
  <svg class="hamburger" viewBox="0 0 20 20">
    <g>
      <rect y="3" width="20" height="2"></rect>
      <rect y="9" width="20" height="2"></rect>
      <rect y="15" width="20" height="2"></rect>
    </g>
  </svg>
</div>
  • Подпись — у кнопки её попросту нет (Name: " ")

  • Семантика — роль "generic", что значит, что у неё нет никакого семантического значения (Role: generic)

  • Фокус — кнопка не может получать фокус (свойство Focusable пропущено)

19. Отладка HTML: Линтинг

Браузер Edge выделяет потенциальные проблемы в разметке вашего документа прямо в панели разработчика на вкладке Elements и показывается подробное описание на вкладке "Issues".

Одна из моих любимых функций панели разработчика в браузере Edge — встроенный линтер, которые выделяет потенциальные проблемы в вашем HTML, помечая проблемные элементы волнистой линией.

Если на открывающий тег навести курсор мыши, появится всплывающее окошко с описанием проблемы. Нажав Shift+ЛКМ или ПКМ и выбрав "View Issues", можно посмотреть подробное описание данной проблемы или напрямую открыть эту панель "Issues" (CMD/Ctrl + Shift + P -> Show Issues), чтобы посмотреть список все проблем найденных на странице. Данные берутся из axe, @mdn/browser-compat-data, and webhint.




Комментарии (3):

  1. gruzoveek
    /#23776903

    AVIF уже поддерживается в современных версиях Firefix

  2. BlackStar1991
    /#23791979

    Спасибо за перевод. Узнал парочку новых особенностей для себя

  3. sergeytolkachyov
    /#23793305

    Очередная статья, вносящая путаницу с заголовками. Согласно спецификации HTML5 на странице может быть сколько угодно заголовков h1, а логические уровни выстраиваются с помощью section, в каждом из которых может быть свой header и footer. Это при условии, если данный контент неотделим от содержимого страницы. Если отделим, то используется article. Если контент, не относящийся к главному содержимому страницы (рекламные блоки, ссылки на всякие ресурсы "для галочки"), то должен быть aside.