Несколько советов и приёмов вёрстки
1. Ссылки — атрибут 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>
2. Доступность элементов 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-анимации следует отображать только в том случае, если пользователь не предпочитает обратного. В случае, если данная настройка активирована, следует с помощью тега и опции 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 Card validator
Facebook Sharing Debugger
LinkedIn Post Inspector
Pinterest Rich Pins Validator
Social Media Preview
Meta Tags
В моём случае все они работали корректно, но в обсуждении в 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;
}
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. Это сбило меня с толку. Спустя некоторое время, потраченное на тестирование и изучение, я узнал, что для определения предназначения поля ввода браузеры учитывают самые разные данные. В данном случае это сработало, так как подпись в элементе содержала слова "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 выберет первый элемент , потому что поддерживает формат AVIF, браузер Edge пропустит первую строку и будет использовать изображение WebP со второй строки, а IE пропустит оба варианта и будет использовать JPG-изображение, из элемента .
Браузерная поддержка 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.
png - исходный: 936KB
png - сжатый: 438KB
webp: 132KB
avif: 71KB
Существует несколько инструментов сжатия, работающих с AVIF. Я точно могу рекомендовать приложение Squoosh .
8. Элемент "section"
Используйте элемент для разметки набора логически связанного контента, обычно имеющего заголовок.
<h1>Welcome to GOV.UK</h1>
<section>
<h2>Services and information</h2>
…
</section>
<section>
<h2>Departments and policy</h2>
…
</section>
…
Не всегда понятно, уместно ли в конкретной ситуации использовать элемент <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. Таблицы стилей для печати (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" есть кнопка с иконкой в виде принтера.