Представляю вашему вниманию перевод заметок с сайта HTMHell (раздела Heaven) — коллекции полезных советов и примеров HTML-кода
Ранее была опубликована коллекция заметок с плохими примерами кода с того же ресурса в статье HTMHell — адовая разметка (25 плохих примеров)
Темы статьи:
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-current4. Текущая страница в панели навигации
Используйте атрибут 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"
браузер предложит сгенерировать пароль.
Для начала я создал простую форму.
<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). Вот почему настолько важным является сохранение чёткой структуры документа. Пропуск заголовков может сбивать с толку, поэтому его следует избегать где это возможно.
Скринридеры позволяют осуществлять навигацию по заголовкам. Это значит, что вы можете не только просматривать заголовки страницы, но и выбирать их, переходя непосредственно к позиции выбранного заголовка в 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-роль | Условия |
| banner | Только в контексте элемента body, а не когда является потомком элементов |
| navigation | |
| main | |
| region | Когда имеет доступное название, заданное с помощью |
| form | Когда имеет доступное название, заданное с помощью |
| search | Когда задан атрибут |
| complementary | |
| contentinfo | Только в контексте элемента body, а не когда является потомком элементов |
Советы
<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 have00: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.