Invoker Commands
Я уже делал обзор предложения Invokers API от OpenUI. В конце прошлой недели в блоге Chrome For Developers представили
Invoker Commands — это декларативный механизм вызова команд у указанного элемента при нажатии кнопки. Сейчас для этого нужно написать обработчик, найти целевой элемент и вызывать нужный метод. Предлагается способ делать это в HTML двумя атрибутами:
В предложении OpenUI было больше встроенных команд. Принято решение разделить предложение на две части и внедрять его постепенно. В первую часть попали команды для работы с
В рамках первой части также будут реализованы пользовательские команды. Используя синтаксис именования пользовательских свойств CSS можно объявлять собственные команды и реагировать на них через JS. При нажатии кнопки на целевом элементе возникает событие
Внешний
Invoker Commands вот-вот должны появиться в Chrome. Другие браузеры смотрят на предложение позитивно и тоже займутся внедрением. Затем стоит ожидать дальнейшего развития и добавления новых команд.
#html #web_api
Я уже делал обзор предложения Invokers API от OpenUI. В конце прошлой недели в блоге Chrome For Developers представили
command и commandfor. Invokers API теперь называется Invoker Commands, предложение разделили на две части, первую уже готовят к релизу в Chrome. Пришло время взглянуть на это всё ещё раз.Invoker Commands — это декларативный механизм вызова команд у указанного элемента при нажатии кнопки. Сейчас для этого нужно написать обработчик, найти целевой элемент и вызывать нужный метод. Предлагается способ делать это в HTML двумя атрибутами:
command (ранее invokeraction) и commandfor (ранее invokertarget). Первый принимает название команды, второй — id элемента, у которого нужно вызвать команду.<button
type="button"
commandfor="hello-dialog"
command="show-modal"
>
Open Dialog
</button>
<dialog id="hello-dialog">
Hello world
<button
type="button"
commandfor="dialog"
command="close"
>
Close
</button>
</dialog>
show-modal и close — это встроенные команды. Если comandfor ссылается на <dialog>, то они соответствуют вызовам методов showModal() и close(). Команды toggle-popover, hide-popover и show-popover соответствуют методам togglePopover(), hidePopover() и showPopover() у элемента с атрибутом popover. Новые атрибуты работают аналогично существующим атрибутам popovertarget и popovertargetaction из Popover API. Планируется, что новые атрибуты со временем заменят их.В предложении OpenUI было больше встроенных команд. Принято решение разделить предложение на две части и внедрять его постепенно. В первую часть попали команды для работы с
<dialog> и popover. Над второй частью ещё работают, но стоит ожидать команд для переключения <details>, раскрытия системных пикеров у <input> и <select>, управления воспроизведением <audio> и <video>, копирования текста, переключения полноэкранного режима и так далее.В рамках первой части также будут реализованы пользовательские команды. Используя синтаксис именования пользовательских свойств CSS можно объявлять собственные команды и реагировать на них через JS. При нажатии кнопки на целевом элементе возникает событие
command, а в объекте события можно обратиться к свойству command, в котором будет имя команды.<button
type="button"
commandfor="custom"
command="--do-something"
>
Do something
</button>
<div id="custom"></div>
<noscript>
const custom = document.querySelector('#custom');
custom.addEventListener('command', (event) => {
if (event.command === '--do-something') {
// ...
}
});
</noscript>
Внешний
commandfor не найдёт целевой элемент внутри Shadow DOM так же, как <label for> “не видит” элемент с нужным id внутри Shadow DOM. В будущем эти проблемы будут решены за счёт предложения Reference Target. А пока можно напрямую указать целевой элемент, используя свойство commandForElement.<my-element>
<template shadowrootmode="open">
<button
type="button"
command="show-popover"
>
Show popover
</button>
<slot></slot>
</template>
<div popover></div>
</my-element>
<noscript>
class MyElement extends HTMLElement {
connectedCallback() {
const popover = this.querySelector('[popover]');
const button = this.shadowRoot.querySelector('button');
button.commandForElement = popover;
}
}
customElements.define('my-element', MyElement);
</noscript>
Invoker Commands вот-вот должны появиться в Chrome. Другие браузеры смотрят на предложение позитивно и тоже займутся внедрением. Затем стоит ожидать дальнейшего развития и добавления новых команд.
#html #web_api
Chrome for Developers
Introducing command and commandfor | Blog | Chrome for Developers
Learn about the new capabilities for declarative behavior on buttons.
🔥14👍3❤2
Стилизация системных элементов форм
Джен Симмонс поделилась в соцсетях, что её команда работает над давней проблемой веб-разработки — стилизацией системных элементов форм.
Проблема постепенно решается. За последние годы появились точечные свойств, такие как
Команда Джен работает над спецификацией CSS Form Control Styling. Идея в том, чтобы значительно расширить возможности стилизации элементов форм и предоставить доступ к их внутренним частям.
Предлагается новое значение свойства
В режиме базовой отрисовки появится доступ к внутренним частям элементов. Сейчас к некоторым из них можно обратиться через специальные псевдо-элементы с префиксами —
Спецификация стандартизирует псевдо-элементы для доступа ко внутренним частям элементов форм. На данный момент их 15:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
В спецификации есть несколько псевдо-классов для стилизации состояний элемента
-
-
-
Есть в предложении интересная функция —
Черновик стандарта находится в стадии разработки и многое ещё не продумано. Есть много issue, которые затрагивают важные вопросы. Выглядит амбициозно и на самом деле давно пора уже это сделать. Теперь нужно дождаться, когда это примут в CSSWG и начнутся первые эксперименты в браузерах.
#css
Дисклеймер: это обзор раннего предложения новых функций. Синтаксис может измениться в будущем или от функций могут отказаться.
Джен Симмонс поделилась в соцсетях, что её команда работает над давней проблемой веб-разработки — стилизацией системных элементов форм.
Проблема постепенно решается. За последние годы появились точечные свойств, такие как
accent-color, field-sizing и appearance, которые где-то упрощают стилизацию. В Safari реализовали переключатель <input type="checkbox" switch>, а в Chrome вот-вот добьют главного босса — <select>. Стоит отдать должное инициативе OpenUI.Команда Джен работает над спецификацией CSS Form Control Styling. Идея в том, чтобы значительно расширить возможности стилизации элементов форм и предоставить доступ к их внутренним частям.
Предлагается новое значение свойства
appearance: base. Оно отключает отрисовку элементов форм, специфичных для браузера и операционной системы. Вместо этого будут рисоваться элементы с простыми базовыми стилями, одинаковыми для всех браузеров. Эти стили можно переопределить и дополнить.В режиме базовой отрисовки появится доступ к внутренним частям элементов. Сейчас к некоторым из них можно обратиться через специальные псевдо-элементы с префиксами —
::-webkit-slider-thumb или ::-moz-range-thumb. Но они не стандартизированы, работают только в конкретных браузерах и отличаются набором поддерживаемых свойств.Спецификация стандартизирует псевдо-элементы для доступа ко внутренним частям элементов форм. На данный момент их 15:
-
::picker() — выпадающий пикер у <select> и других элементов-
::picker-icon — индикатор пикера-
::file-selector-button — кнопка выбора файла у <input type="file">-
::checkmark — индикатор выбранного состояния у <input type="checkbox">, <input type="radio"> и <option>-
::thumb — ползунок у <input type="range">-
::track — направляющая у <input type="range">, <input type="checkbox" switch>, <meter> и <progress>-
::fill — заполненная часть у <input type="range">, <input type="checkbox" switch>, <meter> и <progress>-
::field-text — редактируемая часть полей ввода-
::clear-icon — кнопка очистки у <input type="search">-
::step-control — блок с кнопками у <input type="number">-
::step-up — кнопка увеличения значения у <input type="number">-
::step-down — кнопка уменьшения значения у <input type="number">-
::field-component — блок с компонентом даты/времени у соответствующих полей-
::field-separator — разделитель компонентов даты/времени у соответствующих полей-
::color-swatch — блок с выбранным цветом у <input type="color">В спецификации есть несколько псевдо-классов для стилизации состояний элемента
<meter>.-
:low-value — значение ниже оптимального-
:high-value — значение выше оптимального-
:optimal-value — значение в оптимально диапазонеЕсть в предложении интересная функция —
control-value(). Она позволяет получать текущее значение элемента формы и выводить его в свойство content псевдо-элементов ::before/::after и других. Можно вывести значение и привязать его к ползунку <input type="range">. Черновик стандарта находится в стадии разработки и многое ещё не продумано. Есть много issue, которые затрагивают важные вопросы. Выглядит амбициозно и на самом деле давно пора уже это сделать. Теперь нужно дождаться, когда это примут в CSSWG и начнутся первые эксперименты в браузерах.
#css
🔥21👍8❤2
You don't know HTML: способы расширения HTML
Стандарт HTML на данный момент предлагает 114 элементов для выражения контента, его структуры, взаимосвязей и семантики. Кажется, что это много. Но на реальных сайтах обычно не используется и половина из них. С другой стороны, постоянно чего-то не хватает.
Авторы стандартов и разработчики браузеров внедряют новые элементы (
Поэтому HTML разработан как расширяемый язык. Спецификация определяет следующие способы безопасного расширения HTML:
- атрибут
- атрибуты
- элемент
- атрибут
- элемент
- модель прототипов JS для расширения существующих API, что делают разные библиотеки. Хотя расширение прототипов считается плохой практикой в мире JS, такой вариант расширения возможен. Главное использовать это с умом, не перезаписывать стандартные свойства и методы, использовать наследование и
- атрибуты
- пользовательские элементы для расширения словаря HTML собственными элементами. В сочетании с другими стандартами веб-компонентов можно создавать полнофункциональные элементы с изолированной разметкой, стилями и поведением.
Стоит отметить ещё некоторые возможности, которые не затронуты стандартом, но также позволяют расширять HTML:
- язык MathML для разметки сложных математических формул
- язык SVG для встраивания векторной графики
- стандарт ARIA для добавления, расширения и переопределения семантики с целью улучшения доступности при использовании вспомогательных технологий
#ydkhtml
Стандарт HTML на данный момент предлагает 114 элементов для выражения контента, его структуры, взаимосвязей и семантики. Кажется, что это много. Но на реальных сайтах обычно не используется и половина из них. С другой стороны, постоянно чего-то не хватает.
Авторы стандартов и разработчики браузеров внедряют новые элементы (
<details>, <dialog>) и улучшают существующие. Но как бы они ни старались, невозможно покрыть всё многообразие контента, сайтов, приложений и способов использования HTML.Поэтому HTML разработан как расширяемый язык. Спецификация определяет следующие способы безопасного расширения HTML:
- атрибут
class для создания собственных элементов на базе наиболее подходящих существующих. Это то, чем постоянно пользуются разработчики. Также class можно использовать для хранения мета-информации или для добавления дополнительной семантики, что используется микро-форматами.- атрибуты
data-* для данных, которые игнорируются браузером и ни на что не влияют, но могут использоваться для стилизации и передачи данных для скриптов. Довольно часто используются для хранения настроек и мета-данных, которые считываются и используются в JS.- элемент
<meta name="…" content="…"> для добавления мета-данных на уровне страницы. Чаще всего используется для добавления социальной разметки (Open Graph, Twitter Card) для поисковых систем и социальных сетей. Также можно добавлять произвольные данные в виде ключ-значение, а сам элемент <meta> можно размещать не только в <head>.- атрибут
rel для аннотации ссылок. Стандарт определяет набор базовых значений, которые используют браузеры. Но можно зарегистрировать собственные значения, чем пользуются разные организации.- элемент
<noscript type="…"> для добавления произвольных данных в текстовом виде. Значение атрибута type, отличное от text/javanoscript, module и importmap делает <noscript> блоком данных. В нём можно разместить любые данные в текстовом формате, которые затем можно считывать и обрабатывать в скриптах. Это используется микро-форматом JSON-LD.- модель прототипов JS для расширения существующих API, что делают разные библиотеки. Хотя расширение прототипов считается плохой практикой в мире JS, такой вариант расширения возможен. Главное использовать это с умом, не перезаписывать стандартные свойства и методы, использовать наследование и
Symbol.- атрибуты
itemtype, itemscope и itemprop для добавления микро-данных, которые используются другими сайтами и приложениями. Наиболее распространён словарь Schema.org, на который ориентируются поисковые системы и различные парсеры.- пользовательские элементы для расширения словаря HTML собственными элементами. В сочетании с другими стандартами веб-компонентов можно создавать полнофункциональные элементы с изолированной разметкой, стилями и поведением.
Стоит отметить ещё некоторые возможности, которые не затронуты стандартом, но также позволяют расширять HTML:
- язык MathML для разметки сложных математических формул
- язык SVG для встраивания векторной графики
- стандарт ARIA для добавления, расширения и переопределения семантики с целью улучшения доступности при использовании вспомогательных технологий
#ydkhtml
👍15❤2
Список описаний на практике
Я как-то делал пост, в котором рассмотрел вариант разметки контента с помощью списка описаний. Там я подробно описал, что такое список описаний, как он работает с точки зрения семантики и где его можно применить. На практике этот элемент встречается редко, хотя я нахожу много сценариев, в которых его можно применить.
Хочу поделиться двумя статьями, которые раскрывают эту тему и предлагают разные примеры использования списка описаний.
Первая статья на HTMHell от Дэвида Лура “The underrated <dl> element”. Дэвид приводит три примера. Первый — классический список терминов и определений, для чего
Вторая статья от Бэна Мейерса “On the <dl>”. Бэн приводит четыре примера. Первый — характеристики продукта, в примере это книга. Второй пример — карточка пользователя с контактами и адресом. Третий пример — блок со свойствами в стиле Википедии. Четвёртый пример самый интересный: Бэн показывает большую карточку с информацией о персонаже из Dungeons & Dragons, которая практически полностью состоит из списков описаний.
Списки описаний полезны для группировки связанного контента, который можно представить в виде пар ключ-значение. Если взглянуть на интерфейсы, такие списки можно встретить чаще, чем кажется. Хотя полезность
#html
Я как-то делал пост, в котором рассмотрел вариант разметки контента с помощью списка описаний. Там я подробно описал, что такое список описаний, как он работает с точки зрения семантики и где его можно применить. На практике этот элемент встречается редко, хотя я нахожу много сценариев, в которых его можно применить.
Хочу поделиться двумя статьями, которые раскрывают эту тему и предлагают разные примеры использования списка описаний.
Первая статья на HTMHell от Дэвида Лура “The underrated <dl> element”. Дэвид приводит три примера. Первый — классический список терминов и определений, для чего
<dl> изначально предназначался. Второй пример — блок с цифрами и подписями, такие часто встречаются на сайтах, чтобы подсветить достижения в виде цифр. Последний, третий пример — список характеристик велосипеда в каталоге.Вторая статья от Бэна Мейерса “On the <dl>”. Бэн приводит четыре примера. Первый — характеристики продукта, в примере это книга. Второй пример — карточка пользователя с контактами и адресом. Третий пример — блок со свойствами в стиле Википедии. Четвёртый пример самый интересный: Бэн показывает большую карточку с информацией о персонаже из Dungeons & Dragons, которая практически полностью состоит из списков описаний.
Списки описаний полезны для группировки связанного контента, который можно представить в виде пар ключ-значение. Если взглянуть на интерфейсы, такие списки можно встретить чаще, чем кажется. Хотя полезность
<dl> с точки зрения семантики слегка сомнительная, всё же стоит его использовать для группировки подходящего контента.#html
Ben Myers
On the ‹dl›
The semantics you didn’t know you needed.
🔥18🌚2❤1
MinskJS #12
Присоединяйтесь к прямой трансляции MinskJS Meetup #12 сегодня в 19:00 по Минску!
Я буду выступать с докладом "Да кто такие эти ваши веб-компоненты?!". С другими докладами можно ознакомиться в программе.
Присоединяйтесь к прямой трансляции MinskJS Meetup #12 сегодня в 19:00 по Минску!
Я буду выступать с докладом "Да кто такие эти ваши веб-компоненты?!". С другими докладами можно ознакомиться в программе.
YouTube
MinskJS Meetup #12
Серия митапов #MinskJS посвящена самому популярному языку программирования в вебе — JavaScript. Основная тематика — программирование, взаимодействие с бэкендом, различные оптимизации и вопросы безопасности.
12-й митап состоится 19 марта 2025 года в 19:00…
12-й митап состоится 19 марта 2025 года в 19:00…
👍9❤7
Автодополнение номера телефона
Я уже писал про автодополнение в браузерах. Работая над улучшением доступности одного проекта, я настраивал автодополнение в рамках критерия WCAG 1.3.5 Identify Input Purpose. Не углубляясь в детали, критерий требует установки соответствующих цели ввода значений атрибутов
На вид все хорошо: лейбл есть,
Значение
Закралось ощущение, что это не баг, а я чего не знаю. В исходом коде теста из расширения есть дополнительная проверка на тип поля. Для
И да, в спецификации HTML есть абзац о том, что значения атрибута
Поле с
Исправленный вариант:
Как было изначально, сохранилось значение
#html #a11y
Я уже писал про автодополнение в браузерах. Работая над улучшением доступности одного проекта, я настраивал автодополнение в рамках критерия WCAG 1.3.5 Identify Input Purpose. Не углубляясь в детали, критерий требует установки соответствующих цели ввода значений атрибутов
type и autocomplete у полей. Одно из них я настроил примерно так:<label for="ContactPhone">
Phone Number
</label>
<input
type="tel"
name="contact[phone]"
id="ContactPhone"
autocomplete="tel-national"
placeholder="123-555-1212"
>
На вид все хорошо: лейбл есть,
type="tel" для ввода телефона есть, autocomplete="tel-national" для телефона в национальном формате (без кода страны, для него рядом другое поле) есть. Проверив этот код через расширение для анализа доступности, я получил ошибку:The autocomplete attribute has an incorrect value
Значение
tel-national описано в спецификации HTML и предназначено для ввода телефона в национальной форме. Баг расширения, подумал я, и завёл issue. Спустя какое-то время я проверил код с этой формой в валидаторе HTML и получил примерно такую же ошибку:Bad value tel-national for attribute autocomplete on element input: The autofill field name tel-national is not allowed in this context.
Закралось ощущение, что это не баг, а я чего не знаю. В исходом коде теста из расширения есть дополнительная проверка на тип поля. Для
type="tel" единственным допустимым значением считается autocomplete="tel", поэтому и возникла ошибка. Но тест же на чём-то основан?И да, в спецификации HTML есть абзац о том, что значения атрибута
autocomplete должны применяться у допустимых типов полей, что отмечено в таблице со значениями. Это логично: спецификация защищает от ситуаций, когда для поля с type="email" устанавливается значение autocomplete="given-name", что не имеет смысла.Поле с
type="tel" предназначено для ввода полного номера телефона, с кодом страны и региона. У такого поля можно установить только autocomplete="tel". Если поле предназначено для ввода части номера (без кода страны и региона), то нужно использовать другое значение autocomplete, а type="tel" для такого уже не подойдёт.Исправленный вариант:
<label for="ContactPhone">
Phone Number
</label>
<input
type="text"
inputmode="tel"
name="contact[phone]"
id="ContactPhone"
autocomplete="tel-national"
placeholder="123-555-1212"
>
Как было изначально, сохранилось значение
autocomplete="tel-national" для номера без кода страны. type="tel" заменён на универсальный type="text" для ввода произвольных данных, который совместим со значением tel-national. И добавлен атрибут inputmode="tel" для возврата виртуальной клавиатуры, удобной для ввода номеров.#html #a11y
Telegram
<divelopers>
You don't know HTML: autocomplete
Атрибут autocomplete устанавливается у элементов <form>, текстовых полей ввода <input> и <textarea>. Это подсказка для браузера, какие данные ожидаются в полях. Это именно подсказка, полного контроля нет. Браузер сам принимает…
Атрибут autocomplete устанавливается у элементов <form>, текстовых полей ввода <input> и <textarea>. Это подсказка для браузера, какие данные ожидаются в полях. Это именно подсказка, полного контроля нет. Браузер сам принимает…
👍29🔥4❤2🤯2
CSS-карусели
Прошло всего несколько месяцев с анонса, а Адам Аргайл поделился новостью, что в 135 версии Chrome появятся встроенные карусели. Их можно будет создавать средствами CSS, с кнопками вперёд/назад, маркерами, плавной прокруткой, перетаскиванием и доступностью.
CSS карусели базируются на Scroll Snap. То есть это блок с горизонтальной и/или вертикальной прокруткой, элементы которого привязываются к заданным точкам. Это поддерживается в браузерах уже достаточно давно.
Есть реализации с дополнительным JS, который добавляет кнопки, маркеры, перетаскивание и прочие возможности. Теперь всё это можно будет добавить с помощью нескольких новых псевдо-элементов:
Кнопки, маркеры и контейнер реализованы с учётом доступности. Вся соответствующая семантика, свойства и состояния передаются в дерево доступности. Элементы могут быть сфокусированы и нажаты с помощью клавиатуры. У меня есть вопросы по реализации доступности, но это лучше, чем у большинства каруселей.
Текущая реализация не включает некоторые возможности, но позволит закрыть 80% задач с каруселями. В дальнейшем планируется расширить функциональность: зацикленная прокрутка, передача собственных элементов в качестве кнопок и маркеров, JS-методы для программной прокрутки и так далее.
Стоит отметить, что расширенные функции прокрутки выходят за рамки каруселей и позволяют реализовать другие интерфейсные паттерны. Есть страница с примерами на базе нового API и запись лайва с обзором разных паттернов от Адама Аргайла. Я в целом против каруселей, но они крайне распространены. Если их можно будет делать без тяжёлых библиотек и со встроенной доступностью — это отлично.
#css
Прошло всего несколько месяцев с анонса, а Адам Аргайл поделился новостью, что в 135 версии Chrome появятся встроенные карусели. Их можно будет создавать средствами CSS, с кнопками вперёд/назад, маркерами, плавной прокруткой, перетаскиванием и доступностью.
CSS карусели базируются на Scroll Snap. То есть это блок с горизонтальной и/или вертикальной прокруткой, элементы которого привязываются к заданным точкам. Это поддерживается в браузерах уже достаточно давно.
Есть реализации с дополнительным JS, который добавляет кнопки, маркеры, перетаскивание и прочие возможности. Теперь всё это можно будет добавить с помощью нескольких новых псевдо-элементов:
::scroll-button(), ::scroll-marker и ::scroll-marker-group.::scroll-button() создаёт кнопки в зависимости от параметра в скобках (top, right, bottom, left и логических аналогов block-start, block-end, inline-start и inline-end). Это полноценные кнопки, которые можно нажимать, фокусировать и стилизовать как нужно.::scroll-marker создаёт маркеры. Они также интерактивные и стилизуемые. Браузер учитывает количество маркеров относительно количества слайдов и шагов прокрутки. Маркеры синхронизируются с прокруткой, а текущий маркер может быть стилизован через псевдо-класс :target-current.::scroll-marker-group соответствует контейнеру с маркерами. Его можно размещать в нужной части карусели, ограничить по ширине или ещё как-либо стилизовать. Контейнер предлагает навигацию по маркерам при помощи стрелок.Кнопки, маркеры и контейнер реализованы с учётом доступности. Вся соответствующая семантика, свойства и состояния передаются в дерево доступности. Элементы могут быть сфокусированы и нажаты с помощью клавиатуры. У меня есть вопросы по реализации доступности, но это лучше, чем у большинства каруселей.
Текущая реализация не включает некоторые возможности, но позволит закрыть 80% задач с каруселями. В дальнейшем планируется расширить функциональность: зацикленная прокрутка, передача собственных элементов в качестве кнопок и маркеров, JS-методы для программной прокрутки и так далее.
Стоит отметить, что расширенные функции прокрутки выходят за рамки каруселей и позволяют реализовать другие интерфейсные паттерны. Есть страница с примерами на базе нового API и запись лайва с обзором разных паттернов от Адама Аргайла. Я в целом против каруселей, но они крайне распространены. Если их можно будет делать без тяжёлых библиотек и со встроенной доступностью — это отлично.
#css
Chrome for Developers
Carousels with CSS | Blog | Chrome for Developers
Support scroll experiences with navigation buttons and markers with just a few lines of CSS.
🔥16🤯8❤6
Заголовки и outline
На старте карьеры я запомнил правило: на странице должен быть один
Ноги растут из алгоритма outline, который был описан в спецификации HTML. Это алгоритм определения структуры документа, выделения содержания (table of content) и навигации горячими клавишами.
Ещё при разработке XHTML 2.0 вместо заголовков разных уровней предлагалось использовать новый элемент
XHTML 2.0 не удался, но некоторые идеи перекочевали в HTML5. В том числе идея использовать
Что давало такую структуру:
Из-за этих примеров стала распространяться информация, что можно использовать только
Но алгоритм outline никогда не был реализован ни в одном из браузеров из-за сложностей. Единственное, что реализовано — стили для
То есть уровень заголовков не вычисляется на основе вложенности, не учитываются элементы секционирования, содержание не генерируется и не предлагаются горячие клавиши для навигации по заголовкам и разделам.
От алгоритма outline решено отказаться. Его следы оставались в спецификации до 1 июля 2022 года. Тогда раздел переписали, удалив описание алгоритма и примеры с
На практике на уровни заголовков полагаются многие инструменты. Программы чтения с экрана строят дерево заголовков и предлагают горячие клавиши для быстрой навигации. Есть расширения, которые строят интерактивное содержание. Поисковым системам и LLM будет проще понять страницу с правильной структурой заголовков. И так далее.
Поэтому старое правило «один
#html
На старте карьеры я запомнил правило: на странице должен быть один
<h1>. Остальные заголовки нужно размечать элементами <h2>-<h6> согласно иерархии. Периодически попадаются статьи и комментарии, которые предлагают использовать <h1> для всех заголовков на странице, потому что браузеры и поисковики умеют с этим работать.Ноги растут из алгоритма outline, который был описан в спецификации HTML. Это алгоритм определения структуры документа, выделения содержания (table of content) и навигации горячими клавишами.
Ещё при разработке XHTML 2.0 вместо заголовков разных уровней предлагалось использовать новый элемент
<h>. Уровень должен был автоматически определяться на основе вложенности в элементы секционирования (<section>, <article>, <aside>, <nav>, и другие). XHTML 2.0 не удался, но некоторые идеи перекочевали в HTML5. В том числе идея использовать
<h1> в сочетании с элементами секционирования для разметки заголовков. Долгое время в спецификации можно было найти подобные примеры кода:<body>
<h1>Основной заголовок</h1>
<section>
<h1>Заголовок раздела 1</h1>
<article>
<h1>Заголовок элемента 1</h1>
</article>
<article>
<h1>Заголовок элемента 2</h1>
</article>
</section>
<aside>
<h1>Заголовок раздела 2</h1>
</aside>
</body>
Что давало такую структуру:
Основной заголовок (ур. 1)
├── Заголовок раздела 1 (ур. 2)
│ ├── Заголовок элемента 1 (ур. 3)
│ └── Заголовок элемента 2 (ур. 3)
└── Заголовок раздела 2 (ур. 2)
Из-за этих примеров стала распространяться информация, что можно использовать только
<h1> и не использовать <h2>-<h6>. Из этого следовало, что на странице можно использовать несколько <h1>. Поисковые системы тоже это учитывают. Индексирующему алгоритму, в общем-то, всё равно как размечены заголовки.Но алгоритм outline никогда не был реализован ни в одном из браузеров из-за сложностей. Единственное, что реализовано — стили для
<h1>, которые зависят от вложенности. Недавно был поднят вопрос о том, чтобы эти стили удалить из спецификации и браузеров.То есть уровень заголовков не вычисляется на основе вложенности, не учитываются элементы секционирования, содержание не генерируется и не предлагаются горячие клавиши для навигации по заголовкам и разделам.
От алгоритма outline решено отказаться. Его следы оставались в спецификации до 1 июля 2022 года. Тогда раздел переписали, удалив описание алгоритма и примеры с
<h1>. Термин outline остался в спецификации, но теперь обозначает дерево всех заголовков страницы, которое формируется на основе уровней. В актуальной версии все ещё есть один пример с несколькими <h1> и подписью “A document can contain multiple top-level heading”.На практике на уровни заголовков полагаются многие инструменты. Программы чтения с экрана строят дерево заголовков и предлагают горячие клавиши для быстрой навигации. Есть расширения, которые строят интерактивное содержание. Поисковым системам и LLM будет проще понять страницу с правильной структурой заголовков. И так далее.
Поэтому старое правило «один
<h1> на страницу» и соблюдение иерархии заголовков всё ещё актуально. Не стоит экспериментировать, меняя все заголовки на <h1>. Даже если поисковые системы это поддерживают и кто-то говорит, что так можно.#html
YouTube
My site's template has multiple H1 tags
My site's template has multiple H1 tags. Is this a problem? -- asks Markus from Germany
Some sites have multiple H1 headings. Is that bad?
We'll answer that question and give some general tips in this video.
Have a question? Ask it in our Webmaster Help…
Some sites have multiple H1 headings. Is that bad?
We'll answer that question and give some general tips in this video.
Have a question? Ask it in our Webmaster Help…
22🔥21🤗3👍2❤1
Миксины в CSS
У Chrome, похоже, марафон анонсов новых функций. Адам Аргайл у себя на сайте поделился, что CSS-миксины уже доступны в Chrome Canary за флагом и готовы к экспериментам. К посту приложен вот такой фрагмент кода:
Миксин объявляется через новую директиву
Миксины и функции основаны на предложении CSS Mixins & Functions от Мириам Сюзанн, которая много работает над развитием CSS и Sass. Предложение приняли в рабочей группе CSS и на его основе пишется спецификация CSS Functions and Mixins.
Лично для меня миксины — последняя часть пазла, чтобы полностью отказаться от препроцессоров. Я пробовал использовать в проектах чистый CSS и именно миксинов мне не хватало. Где-то это решалось через custom properties и сокращенные свойства, где-то дополнительным классом в HTML.
Первое время понадобится постобработка стилей, чтобы подставить вместо встроенных миксинов их значения, как это делается с развёрткой встроенной вложенности. Со временем этот этап постобработки можно будет отключить.
#css
Дисклеймер: это обзор раннего предложения новых функций. Синтаксис может измениться в будущем или от функций могут отказаться.
У Chrome, похоже, марафон анонсов новых функций. Адам Аргайл у себя на сайте поделился, что CSS-миксины уже доступны в Chrome Canary за флагом и готовы к экспериментам. К посту приложен вот такой фрагмент кода:
@mixin --box {
aspect-ratio: 1;
inline-size: 100px;
block-size: 100px;
}
.box {
@apply --box;
}Миксин объявляется через новую директиву
@mixin. Затем следует название в синтаксисе <dashed-ident>, как у custom properties. Далее описывается блок с правилами. Применяется миксин через ещё одну новую директиву — @apply. Почти как в SCSS.Миксины и функции основаны на предложении CSS Mixins & Functions от Мириам Сюзанн, которая много работает над развитием CSS и Sass. Предложение приняли в рабочей группе CSS и на его основе пишется спецификация CSS Functions and Mixins.
Лично для меня миксины — последняя часть пазла, чтобы полностью отказаться от препроцессоров. Я пробовал использовать в проектах чистый CSS и именно миксинов мне не хватало. Где-то это решалось через custom properties и сокращенные свойства, где-то дополнительным классом в HTML.
Первое время понадобится постобработка стилей, чтобы подставить вместо встроенных миксинов их значения, как это делается с развёрткой встроенной вложенности. Со временем этот этап постобработки можно будет отключить.
#css
nerdy.dev
CSS Mixins are ready for experimentation!
Here's how to get started with CSS @mixin in Chrome Canary.
🔥17❤5🥰2💋1
Полностью стилизуемый <select>
Марафон новинок продолжается. В Chrome 135 доступен полностью стилизуемый
Стилизация включается специальным значением свойства
Установка свойства меняет режим отрисовки. Вместо системного виджета генерируется новый виджет c HTML-разметкой и базовыми стилями в Shadow DOM. Это позволяет стилизовать внутренние части
- выпадающий список опций не сможет заходить за пределы окна браузера;
- на мобильных устройствах не будет открываться системный интерфейс выбора опций;
- ширина
Вместе с этим меняется режим парсера. До этого в
-
-
-
-
-
Можно стилизовать
Если браузер не поддерживает новые свойства, то
#css
Марафон новинок продолжается. В Chrome 135 доступен полностью стилизуемый
<select>. Последние несколько лет прорабатывались разные варианты как это сделать. После тестовой реализации и обратной связи от разработчиков, финальный вариант был принят и теперь доступен в Chrome. У нас наконец-то появилась возможность стилизовать стандартный <select>!Стилизация включается специальным значением свойства
appearance:select, select::picker(select) {
appearance: base-select;
}Установка свойства меняет режим отрисовки. Вместо системного виджета генерируется новый виджет c HTML-разметкой и базовыми стилями в Shadow DOM. Это позволяет стилизовать внутренние части
<select>, но убирает некоторые функции:- выпадающий список опций не сможет заходить за пределы окна браузера;
- на мобильных устройствах не будет открываться системный интерфейс выбора опций;
- ширина
<select> не будет соответствовать самой длинной опции.Вместе с этим меняется режим парсера. До этого в
<select> можно было помещать <option>, <optgroup>, а с недавних пор <hr>. Все остальные элементы игнорируются. В новом режиме внутрь <option> можно поместить изображения, SVG и дополнительную разметку. В сам <select> можно добавить <button> и новый элемент <selectedcontent> для отображения выбранной опции.<select>
<button>
<selectedcontent></selectedcontent>
</button>
<option>
<noscript aria-hidden>…</noscript>
<span>HTML</span>
</option>
<option>
<noscript aria-hidden>…</noscript>
<span>CSS</span>
</option>
<option>
<noscript aria-hidden>…</noscript>
<span>JavaScript</span>
</option>
</select>
<button>, <selectedcontent>, <option> и его внутренние элементы можно свободно стилизовать как обычные дочерние элементы. Вместе с ними можно стилизовать внутренние части в Shadow DOM, доступ к которым осуществляется через специальные псевдо-элементы и псевдо-классы:-
select:open — <select> в открытом состоянии;-
select::picker(select) — выпадающий список опций, реализован как Popover с привязкой через Anchor Positioning;-
select::picker-icon — иконка со стрелкой, которая меняется при открытии, в content можно подставить свой символ или изображение;-
option::checkmark — иконка в виде галочки у выбранной опции, в content можно подставить свой символ или изображение;-
option:checked — выбранная опция.Можно стилизовать
<optgroup>, но не его подпись, создаваемую через атрибут label. К счастью, можно вложить <span> с подписью, стилизовать как нужно и привязать к <optgroup> через ARIA. Доступны псевдо-классы :hover, :focus и :focus-visible, поэтому можно изменять внешний вид <option> при наведении и фокусе. Можно применять flexbox и grid для расположения элементов.Если браузер не поддерживает новые свойства, то
<select> будет работать по-старому. Все недопустимые внутри элементы будут игнорироваться и отображаться в виде обычного текста. Также интересно, что можно отключить appearance: base-select по медиа-запросу, чтобы вернуть на мобильных устройствах системный интерфейс выбора опций.#css
Chrome for Developers
The <select> element can now be customized with CSS | Blog | Chrome for Developers
All the same interfaces, sweet new looks.
🔥24🎉6
You don't know HTML: loading
На странице может быть много изображений, которые пользователь не увидит. Потому что они находятся в карусели за пределами видимой области, ниже первого экрана или спрятаны в раскрываемых виджетах. Это хорошие кандидаты для применения шаблона отложенной загрузки.
Для этого применяются специальные библиотеки. Они полагаются на Intersection Observer, чтобы отслеживать пересечения изображений с областью просмотра. Затем ссылки из атрибутов
С библиотеками есть проблемы:
- Зависимость от JS и дополнительные накладные расходы. Ленивая загрузка не будет работать, пока не выполнится код библиотеки. Поэтому ссылки помещают в
- Поисковые роботы могут не извлекать ссылки из
- Атрибут
- Для предотвращения проблем с JS и SEO нужно добавлять дополнительный элемент
Проблемы решает встроенный в HTML атрибут
Атрибут доступен у элементов
При приближении элемента к области просмотра начнётся загрузка. Когда именно это произойдёт — браузер решает сам. Учитывается текущее состояние устройства: тип соединения, загруженность процессора, заряд батареи и прочее.
Можно было бы сказать, что для всех
Изображение может стать кандидатом на LCP (Largest Contentful Paint). В таком случае его важно показать как можно скорее.
#ydkhtml #performance
На странице может быть много изображений, которые пользователь не увидит. Потому что они находятся в карусели за пределами видимой области, ниже первого экрана или спрятаны в раскрываемых виджетах. Это хорошие кандидаты для применения шаблона отложенной загрузки.
Для этого применяются специальные библиотеки. Они полагаются на Intersection Observer, чтобы отслеживать пересечения изображений с областью просмотра. Затем ссылки из атрибутов
data-src и data-srcset подставляются в src и srcset, что инициирует загрузку.С библиотеками есть проблемы:
- Зависимость от JS и дополнительные накладные расходы. Ленивая загрузка не будет работать, пока не выполнится код библиотеки. Поэтому ссылки помещают в
data-* атрибуты;- Поисковые роботы могут не извлекать ссылки из
data-src/data-srcset или игнорировать изображения без src. Таким образом можно навредить SEO;- Атрибут
src обязательный, поэтому многие инструменты анализа разметки (валидаторы, линтеры, сканеры доступности) будут игнорировать такие изображения или помечать как невалидные;- Для предотвращения проблем с JS и SEO нужно добавлять дополнительный элемент
<img> и вкладывать его в <nonoscript>.Проблемы решает встроенный в HTML атрибут
loading. Значение eager сообщает, что ресурс нужно обрабатывать как обычно. Это значение по умолчанию, поэтому атрибут можно не указывать. Значение lazy включает встроенную в браузеры ленивую загрузку.Атрибут доступен у элементов
<img> и <iframe>, также есть предложение добавить его для <video>. При установке loading="lazy" ресурс не будет скачиваться на этапе обработки HTML, сканер предварительной загрузки не добавит ссылку в очередь.При приближении элемента к области просмотра начнётся загрузка. Когда именно это произойдёт — браузер решает сам. Учитывается текущее состояние устройства: тип соединения, загруженность процессора, заряд батареи и прочее.
Можно было бы сказать, что для всех
<img> и <iframe> нужно установить loading="lazy", но это не так. Не стоит устанавливать loading="lazy" для элементов в верхней части страницы, которые видны сразу.Изображение может стать кандидатом на LCP (Largest Contentful Paint). В таком случае его важно показать как можно скорее.
loading="lazy" помешает заранее начать загрузку. В результате пользователи получат изображение позже и значение метрики LCP возрастёт.#ydkhtml #performance
web.dev
Browser-level image lazy loading for the web | Articles | web.dev
This post covers the loading attribute and how it can be used to control the loading of images.
👍18❤3💯3👌2
Slick slider
Помните библиотеку для создания каруселей Slick? На старте карьеры я её использовал, потом перешёл на Swiper, а сейчас предпочитаю лёгкие веб-компоненты или решения на основе Scroll Snap. Работая над улучшением доступности проекта, я вновь столкнулся с Slick.
На сайте заявляется:
В настройках есть параметр
Пришлось потратить какое-то время на доработку с применением костылей. Потому что API Slick не позволяет управлять многими процессами, которые происходят при переключении слайдов. Работы по проекту уже завершены, но недавно я нашёл форк Slick, в котором исправлена доступность.
Сколько бы времени мне это сэкономило, если бы я знал… Это полноценная замена Slick версии 1.8.1, в ядре которой исправлены проблемы с доступностью. API и функциональность остались прежними. Если на проекте используется Slick 1.8.1 и нужно сделать его доступным, можно просто заменить файл скрипта на форк.
А вообще удалите Slick и больше никогда его не используйте. Последняя версия вышла 7 лет назад, на GitHub 1210 открытых issue, проект больше не поддерживается и не развивается. Ещё и jQuery требует для работы. Это касается и React-обёрток над Slick, так в пакетах оказывается jQuery.
Поговорите с дизайнером, действительно ли вам нужны карусели. Если нужны, отдавайте предпочтение лёгким и современным вариантам. Swiper предлагает веб-компонент, совместимый с любым стеком. Для простых случаев рассмотрите sl-carousel, Splide, Scroll Snap Slider, Snap Slider. Со временем можно будет перейти на CSS-карусели.
#js #a11y
Помните библиотеку для создания каруселей Slick? На старте карьеры я её использовал, потом перешёл на Swiper, а сейчас предпочитаю лёгкие веб-компоненты или решения на основе Scroll Snap. Работая над улучшением доступности проекта, я вновь столкнулся с Slick.
На сайте заявляется:
Fully accessible with arrow key navigation
В настройках есть параметр
accessibility со значением true по умолчанию. Slick кое что делает для улучшения доступности: устанавливает атрибуты, добавляет управление стрелками. К сожалению, этого не достаточно, при аудите я обнаружил много проблем. В целом Slick не соответствует рекомендациям от WAI ARIA.Пришлось потратить какое-то время на доработку с применением костылей. Потому что API Slick не позволяет управлять многими процессами, которые происходят при переключении слайдов. Работы по проекту уже завершены, но недавно я нашёл форк Slick, в котором исправлена доступность.
A fully accessible, WCAG 2.0 / 2.1 compliant, drop-in replacement for Slick Slider (1.8.1) intended to make life easier for real-world dev teams who need to pass accessibility audits.
Сколько бы времени мне это сэкономило, если бы я знал… Это полноценная замена Slick версии 1.8.1, в ядре которой исправлены проблемы с доступностью. API и функциональность остались прежними. Если на проекте используется Slick 1.8.1 и нужно сделать его доступным, можно просто заменить файл скрипта на форк.
А вообще удалите Slick и больше никогда его не используйте. Последняя версия вышла 7 лет назад, на GitHub 1210 открытых issue, проект больше не поддерживается и не развивается. Ещё и jQuery требует для работы. Это касается и React-обёрток над Slick, так в пакетах оказывается jQuery.
Поговорите с дизайнером, действительно ли вам нужны карусели. Если нужны, отдавайте предпочтение лёгким и современным вариантам. Swiper предлагает веб-компонент, совместимый с любым стеком. Для простых случаев рассмотрите sl-carousel, Splide, Scroll Snap Slider, Snap Slider. Со временем можно будет перейти на CSS-карусели.
#js #a11y
🔥23👍9👎1
Видео с MinskJS #12
Опубликован плейлист докладов с MinskJS #12. Запись прямой трансляции разрезана на отдельные видео для удобного просмотра.
Я выступал с темой «да кто такие эти ваши веб-компоненты?!» и попытался объяснить, что такое веб-компоненты, сравнил их с фреймворками и показал, где они могут пригодиться.
Глеб Казачинский рассказал, как можно создавать веб-компоненты с помощью одного фреймворка, использовать их в другом и показал это на примере виджета чата.
Настя Котова объяснила, что такое node-gyp, зачем он нужен, почему при установке зависимостей и запуске сборки может упасть ошибка
Если вы хотите выступить с докладом на тему фронтенда, но сомневаетесь — смело подавайте заявку на MinskCSS или MinskJS. Организаторы рады новым спикерам и помогут с подготовкой доклада.
#js
Опубликован плейлист докладов с MinskJS #12. Запись прямой трансляции разрезана на отдельные видео для удобного просмотра.
Я выступал с темой «да кто такие эти ваши веб-компоненты?!» и попытался объяснить, что такое веб-компоненты, сравнил их с фреймворками и показал, где они могут пригодиться.
Глеб Казачинский рассказал, как можно создавать веб-компоненты с помощью одного фреймворка, использовать их в другом и показал это на примере виджета чата.
Настя Котова объяснила, что такое node-gyp, зачем он нужен, почему при установке зависимостей и запуске сборки может упасть ошибка
gyp ERR! и как это можно исправить.Если вы хотите выступить с докладом на тему фронтенда, но сомневаетесь — смело подавайте заявку на MinskCSS или MinskJS. Организаторы рады новым спикерам и помогут с подготовкой доклада.
#js
YouTube
MinskJS Meetup #12 — 19 марта 2025
Share your videos with friends, family, and the world
👍6❤3🔥3
Разметка бокового фильтра
Представьте классическую страницу каталога товаров в интернет-магазине. В центре список карточек в виде плитки, а сбоку фильтр для уточнения параметров. Я задумался, как лучше разметить структуру такой страницы. В частности, как разметить блок с фильтром. Хочется, чтобы это был чётко выделенный участок страницы с возможностью быстрого перехода и понятной семантикой. На ум пришло 6 вариантов.
<aside>
Если опустить мантру «
<nav>
<search>
<form>
Хороший фильтр дополняет адрес страницы параметрами. Самый простой способ добиться этого — использовать
<section>
Можно не вдаваться в нюансы семантики
<div role="group"> или <fieldset> + <legend>
Последний вариант — просто сгруппировать элементы фильтра без добавления дополнительной семантики и создания ориентира. Для этого можно использовать
#html #a11y
Представьте классическую страницу каталога товаров в интернет-магазине. В центре список карточек в виде плитки, а сбоку фильтр для уточнения параметров. Я задумался, как лучше разметить структуру такой страницы. В частности, как разметить блок с фильтром. Хочется, чтобы это был чётко выделенный участок страницы с возможностью быстрого перехода и понятной семантикой. На ум пришло 6 вариантов.
<aside>
Если опустить мантру «
<aside> для боковых колонок», то этот элемент предназначен для контента, который косвенно связан с окружающим его контентом и который можно удалить без потери смысла. Боковой фильтр подходит под эти критерии. К тому же <aside> — ориентир и к нему можно быстро перейти.<nav>
<nav> группирует элементы, которые используются для навигации. Фильтр можно рассматривать как своего рода навигацию по каталогу товаров. Хороший фильтр дополняет адрес страницы параметрами, чтобы можно было сохранить ссылку или поделиться с кем-то. <nav> тоже ориентир и позволяет быстро перейти к фильтру.<search>
<search> предназначен для разметки набора элементов, которые предоставляют функции поиска или фильтрации. Уже по описанию элемент подходит. Как и другие элементы, <search> — ориентир с возможностью быстрого доступа. Остаётся только вопрос с браузерной поддержкой, так как элемент относительно новый.<form>
Хороший фильтр дополняет адрес страницы параметрами. Самый простой способ добиться этого — использовать
<form>. Если элементы фильтра находятся внутри <form> этого может быть достаточно и другие семантические обёртки не нужны. <form> не ориентир, но быстрый доступ возможен через список форм. Или можно связать заголовок и форму, тогда получится ориентир.<section>
Можно не вдаваться в нюансы семантики
<aside>, <search>, <nav> и <form>, а просто использовать <section>. Элемент группирует связанные части страницы без дополнительной смысловой нагрузки. Сам по себе <section> ничего не даёт, но с привязанным заголовком он становится ориентиром. С помощью aria-roledenoscription можно задать произвольное название роли.<div role="group"> или <fieldset> + <legend>
Последний вариант — просто сгруппировать элементы фильтра без добавления дополнительной семантики и создания ориентира. Для этого можно использовать
<div role="group">. Или, следуя первому правилу ARIA — завернуть всё в <fieldset> + <legend>, что создаст группу с заголовком. Для большей понятности можно добавить описание роли.#html #a11y
😱6🔥3❤1👍1
Какой вариант разметки области с боковым фильтром вы выберите?
Final Results
54%
<aside>
6%
<nav>
7%
<search>
6%
<form>
2%
<section>
1%
<div role="group">
3%
<fieldset> + <legend>
4%
<div class="filter">
16%
Смотреть ответы
🌚12
Проверка email
Бывает необходимость проверить email на валидность. Я видел всякое: простую проверку на наличие
Во всех браузерах есть
В спецификации HTML описано, как происходит валидация email-адресов. Там учитываются международные стандарты RFC 5322 и RFC 1034. Ещё одна особенность встроенной валидации в том, что можно проверять одновременно несколько email. Строка будет считаться валидной, если все email, разделённые запятой, будут валидны. Для этого у
Можно доработать функцию, чтобы она работала с массивом email-адресов вместо строки, обрезала лишние пробелы и склеивала всё в строку с запятыми.
#js
Бывает необходимость проверить email на валидность. Я видел всякое: простую проверку на наличие
@ в строке, самописные и взятые со StackOverflow регулярные выражения и даже специализированные библиотеки. Есть более простой способ:function isEmailValid(email) {
const input = document.createElement('input');
input.type = 'email';
input.value = email;
return input.checkValidity();
}
console.log(isEmailValid('foo@bar.baz'));
// true
console.log(isEmailValid('invalid'));
// falseВо всех браузерах есть
<input type="email">. У него под капотом встроена валидация. Зная этот факт, можно создать <input>, установить значение и вызвать метод checkValidity(). Он вернёт true, если email валидный и false в противном случае. При этом не обязательно добавлять поле на страницу.В спецификации HTML описано, как происходит валидация email-адресов. Там учитываются международные стандарты RFC 5322 и RFC 1034. Ещё одна особенность встроенной валидации в том, что можно проверять одновременно несколько email. Строка будет считаться валидной, если все email, разделённые запятой, будут валидны. Для этого у
<input> должен быть установлен атрибут или свойство multiple. function isEmailValid(email) {
const input = document.createElement('input');
input.type = 'email';
input.multiple = true;
input.value = email;
return input.checkValidity();
}
console.log(isEmailValid('foo@bar.baz'));
// true
console.log(isEmailValid('foo@bar.baz,baz@foo.bar'));
// true
console.log(isEmailValid('foo@bar.baz,invalid'));
// falseМожно доработать функцию, чтобы она работала с массивом email-адресов вместо строки, обрезала лишние пробелы и склеивала всё в строку с запятыми.
#js
🔥59👍4🤔3🤝2
16 ошибок производительности (часть 1)
Пол Кальвано выступил с докладом на конференции performance.now(), в котором рассказал о 16-ти распространённых ошибках производительности. На основе базы данных HTTP Archive (~16 миллионов сайтов) он показал, как часто встречаются эти ошибки. Хороший доклад, рекомендую посмотреть. А я поделюсь списком ошибок с кратким описанием. Пост будет разбит на 3 части, чтобы не делать его слишком большим.
1. Ленивая загрузка LCP-изображения
Как исправить: удалить атрибут
2. Ленивая загрузка приоритетных изображений
Атрибут
Как исправить: удалить атрибут
Асинхронное декодирование приоритетных изображений
Изначально изображения с атрибутами
3. Предварительное соединение без crossorigin
Как исправить: добавить к
4. Предварительная загрузка нескольких изображений
Как исправить: использовать
5. Предварительная загрузка большого объёма данных
Загрузка тяжёлых ресурсов требует времени.
Как исправить: удалить
#html #performance
Пол Кальвано выступил с докладом на конференции performance.now(), в котором рассказал о 16-ти распространённых ошибках производительности. На основе базы данных HTTP Archive (~16 миллионов сайтов) он показал, как часто встречаются эти ошибки. Хороший доклад, рекомендую посмотреть. А я поделюсь списком ошибок с кратким описанием. Пост будет разбит на 3 части, чтобы не делать его слишком большим.
1. Ленивая загрузка LCP-изображения
<img src="hero.jpg" loading="lazy" alt="">
loading="lazy" скрывает изображение от сканера предварительной загрузки. Браузеру нужно знать, находится ли изображение в области просмотра, для чего нужно дождаться загрузки всех стилей. LCP-изображение должно загрузиться как можно быстрее. Ленивая загрузка препятствует этому.Как исправить: удалить атрибут
loading у LCP-изображения или установить значение loading="eager".2. Ленивая загрузка приоритетных изображений
<img src="hero.jpg" loading="lazy" fetchpriority="high" alt="">
Атрибут
fetchpriority="high" увеличивает приоритет загрузки, что нужно для важных изображений. loading="lazy", напротив, откладывает загрузку изображений. В итоге изображения будут загружаться с высоким приоритетом, но только после загрузки всех стилей и при попадании в область просмотра.Как исправить: удалить атрибут
loading у изображений с атрибутом fetchpriority="high" или установить значение eager или удалить атрибут fetchpriority, если изображение должно загружаться лениво.Асинхронное декодирование приоритетных изображений
Изначально изображения с атрибутами
fetchpriority="high" и decoding="async" в докладе Пола были отмечены как ошибка. Позже слайды были исправлены, в целом это не ошибка.3. Предварительное соединение без crossorigin
<link rel="preconnect" href="https://example.com">
<link rel="preconnect"> заранее устанавливает соединение с указанным доменом. Без атрибута crossorigin запросы к сторонним доменам блокируются CORS-политикой браузера. Время на инициализацию соединения будет потрачено зря, а соединение в итоге будет прервано.Как исправить: добавить к
<link rel="preconnect"> атрибут crossorigin.4. Предварительная загрузка нескольких изображений
<link rel="preload" href="image@x1.jpg" as="image">
<link rel="preload" href="image@x2.jpg" as="image">
<link rel="preload"> предварительно загружает указанный ресурс с высоким приоритетом. Если указать несколько изображений разных размеров, браузер скачает их, но не все из них будут использованы.Как исправить: использовать
<link rel="preload"> с атрибутами imagesrcset и imagesizes, убедиться, что значения соответствуют атрибутам srcset и sizes у изображений.5. Предварительная загрузка большого объёма данных
<link rel="preload" href="heavy_noscript.js" as="noscript">
Загрузка тяжёлых ресурсов требует времени.
<link rel="preload"> перемещает ресурсы в начало очереди загрузки, из-за чего другие важные ресурсы опускаются ниже. Тяжёлый ресурс займёт соединение на какое-то время, а важные ресурсы от этого загрузятся позже.Как исправить: удалить
<link rel="preload"> с тяжёлыми ресурсами и отложить их загрузку, если они не критичны для страницы.#html #performance
👍16🔥5❤2
16 ошибок производительности (часть 2)
Продолжение предыдущего поста с обзором ошибок производительности из доклада Пола Кальвано.
6. Предварительная загрузка видео
Видео, как правило, очень тяжёлые. Предварительная загрузка видео займёт соединение и отложит более важные ресурсы. Видео лучше загружать по мере необходимости.
Как исправить: удалить
7. Предварительная загрузка неиспользуемых ресурсов
Предварительная загрузка предназначена для важных ресурсов, которые понадобятся на странице в течении первых нескольких секунд. При предварительной загрузке неиспользуемых ресурсов браузер просто так скачает их с высоким приоритетом.
Как исправить: проверить страницы с
8. Предварительная загрузка без as
Атрибут
Как исправить: добавить атрибут
9. Предварительная загрузка разных форматов шрифтов
Браузер использует наиболее подходящий формат шрифта, который он умеет обрабатывать. Все современные браузеры понимают
Как исправить: удалить
10. font-display: swap для предварительно загруженных шрифтов
Как исправить: использовать или
#html #performance
Продолжение предыдущего поста с обзором ошибок производительности из доклада Пола Кальвано.
6. Предварительная загрузка видео
<link rel="preload" href="video.mp4" as="video">
Видео, как правило, очень тяжёлые. Предварительная загрузка видео займёт соединение и отложит более важные ресурсы. Видео лучше загружать по мере необходимости.
Как исправить: удалить
<link rel="preload" href="..." as="video">.7. Предварительная загрузка неиспользуемых ресурсов
Предварительная загрузка предназначена для важных ресурсов, которые понадобятся на странице в течении первых нескольких секунд. При предварительной загрузке неиспользуемых ресурсов браузер просто так скачает их с высоким приоритетом.
Как исправить: проверить страницы с
<link rel="preload"> на предмет ошибки в консоли, которая сообщает о предварительной загрузке неиспользуемых ресурсов, удалить указанные там <link rel="preload">.8. Предварительная загрузка без as
<link rel="preload" href="font.woff2">
Атрибут
as сообщает браузеру тип ресурса, чтобы он понимал, что дальше с ним делать. Без атрибута as предварительная загрузка ресурса не инициируется.Как исправить: добавить атрибут
as к <link rel="preload"> с соответствующим типом ресурса.9. Предварительная загрузка разных форматов шрифтов
<link rel="preload" href="font.ttf" as="font">
<link rel="preload" href="font.eot" as="font">
<link rel="preload" href="font.noscript" as="font">
<link rel="preload" href="font.woff" as="font">
<link rel="preload" href="font.woff2" as="font">
Браузер использует наиболее подходящий формат шрифта, который он умеет обрабатывать. Все современные браузеры понимают
woff2. Остальные шрифты загрузятся просто так и не будут использованы.Как исправить: удалить
<link rel="preload"> для всех форматов шрифтов, кроме woff2.10. font-display: swap для предварительно загруженных шрифтов
<link rel="preload" href="font.woff2" as="font">
<style>
@font-face {
/* ... */
src: url("font.woff2") format("woff2");
font-display: swap;
}
</style>
font-display: swap говорит браузеру, что можно сразу отображать текст системным шрифтом, пока пользовательский не загрузился. Когда загрузка шрифта завершится, нужно заменить системный шрифт на пользовательский. Это что-то вроде ленивой загрузки. <link rel="preload"> загружает шрифт заранее с высоким приоритетом. Возникает конфликт приоритетов.Как исправить: использовать или
<link rel="preload">, или font-display: swap.#html #performance
👍10🔥2😱2🌚1
16 ошибок производительности (часть 3)
Заключительная часть обзора ошибок производительности из доклада Пола Кальвано.
11. Нет сжатия Gzip или Brotli
Сжатие уменьшает размер текстовых ресурсов (HTML, CSS, JS, SVG, JSON и других), что сокращает время их загрузки. Эта оптимизация отсутствует на 10% сайтов по данным HTTP Archive.
Как исправить: включить на сервере gzip, brotli или ztstandard и настроить соответствующие параметры.
12. Низкий уровень сжатия
У сжатия бывают разные уровни. На серверах nginx уровень сжатия (
Как исправить: проверить настройки сжатия и установить более высокие значения в зависимости от возможностей сервера.
13. Сторонние ресурсы не сжаты
Ресурсы сторонних поставщиков (виджеты, маркетинговые скрипты, аналитика, реклама, чат-боты) могут поставляться без сжатия.
Как исправить: связаться с поставщиком и попросить настроить сжатие или разместить ресурсы на собственных серверах/прокси с настроенным сжатием.
14. Обход оптимизации изображений
Иногда для оптимизации изображений используются специальные сервисы и CDN. В URL передаются настройки, которые применяет сервис и возвращает обработанное изображение. Неверные настройки могут привести к загрузке неоптимальных изображений.
Как исправить: проверить URL изображений и убедиться, что переданы нужные настройки.
15. Использование S3 для доставки изображений
S3 от Amazon — это популярное хранилище статических ресурсов. Оно не работает как CDN и не применяет оптимизации к изображениям.
Как исправить: перенести изображения на специализированный для этого CDN.
16. Использование gzip для сжатия изображений
Сжатие gzip подходит для текстовых файлов, так как работает на основе повторяющихся фрагментов текста. Изображения — бинарный формат, они сжимаются иначе. Текстовое сжатие не даст преимуществ в размере бинарных файлов, но потребует время на кодирование/декодирование.
Как исправить: отключить текстовое сжатие для растровых форматов изображений и бинарных файлов.
#performance
Заключительная часть обзора ошибок производительности из доклада Пола Кальвано.
11. Нет сжатия Gzip или Brotli
Сжатие уменьшает размер текстовых ресурсов (HTML, CSS, JS, SVG, JSON и других), что сокращает время их загрузки. Эта оптимизация отсутствует на 10% сайтов по данным HTTP Archive.
Как исправить: включить на сервере gzip, brotli или ztstandard и настроить соответствующие параметры.
12. Низкий уровень сжатия
У сжатия бывают разные уровни. На серверах nginx уровень сжатия (
gzip_comp_level) по умолчанию 1, если не указано иное. При таком уровне размер сжатого файла может быть больше исходного.Как исправить: проверить настройки сжатия и установить более высокие значения в зависимости от возможностей сервера.
13. Сторонние ресурсы не сжаты
Ресурсы сторонних поставщиков (виджеты, маркетинговые скрипты, аналитика, реклама, чат-боты) могут поставляться без сжатия.
Как исправить: связаться с поставщиком и попросить настроить сжатие или разместить ресурсы на собственных серверах/прокси с настроенным сжатием.
14. Обход оптимизации изображений
Иногда для оптимизации изображений используются специальные сервисы и CDN. В URL передаются настройки, которые применяет сервис и возвращает обработанное изображение. Неверные настройки могут привести к загрузке неоптимальных изображений.
Как исправить: проверить URL изображений и убедиться, что переданы нужные настройки.
15. Использование S3 для доставки изображений
S3 от Amazon — это популярное хранилище статических ресурсов. Оно не работает как CDN и не применяет оптимизации к изображениям.
Как исправить: перенести изображения на специализированный для этого CDN.
16. Использование gzip для сжатия изображений
Сжатие gzip подходит для текстовых файлов, так как работает на основе повторяющихся фрагментов текста. Изображения — бинарный формат, они сжимаются иначе. Текстовое сжатие не даст преимуществ в размере бинарных файлов, но потребует время на кодирование/декодирование.
Как исправить: отключить текстовое сжатие для растровых форматов изображений и бинарных файлов.
#performance
🔥8👍3❤2
Ориентиры
Ориентиры (landmarks) — это термин из спецификации ARIA, который описывает важные области страницы с возможностью быстрого доступа. Такие области содержат контент и элементы управления, которые служат определённой цели. Использование ориентиров делает навигацию по странице понятной, предсказуемой и помогает удовлетворить критерии WCAG.
Пользователи программ чтения с экрана могут открыть список всех ориентиров и ознакомиться со структурой страницы без необходимости изучать её целиком. Выбор ориентира из списка или нажатие комбинации клавиш приведёт к переносу фокуса к области, откуда пользователи могут двигаться дальше.
К ориентирам относятся следующие роли ARIA:
-
-
-
-
-
-
-
-
Ориентиры полезны не только для пользователей программ чтения с экрана. Поисковые системы лучше распознают структуру страницы с ориентирами. Браузеры предлагают открыть страницу в режиме чтения без лишних элементов, с возможностью настройки текста и фона. Расширения браузера помогают быстрее навигировать по областям страницы с помощью клавиатуры.
#html #aria #a11y
Ориентиры (landmarks) — это термин из спецификации ARIA, который описывает важные области страницы с возможностью быстрого доступа. Такие области содержат контент и элементы управления, которые служат определённой цели. Использование ориентиров делает навигацию по странице понятной, предсказуемой и помогает удовлетворить критерии WCAG.
Пользователи программ чтения с экрана могут открыть список всех ориентиров и ознакомиться со структурой страницы без необходимости изучать её целиком. Выбор ориентира из списка или нажатие комбинации клавиш приведёт к переносу фокуса к области, откуда пользователи могут двигаться дальше.
К ориентирам относятся следующие роли ARIA:
-
banner, аналог в HTML — <header>, который не вложен в <article>, <aside>, <main>, <nav>, <section> или элементы с соответствующими ролями. Область страницы, которая повторяется на других страницах и содержит навигацию, логотип, поиск, переключатель языка, форму входа в личный кабинет и так далее:-
complementary, аналог в HTML — <aside>. Область страницы с вспомогательным контентом, который дополняет окружающий контент и который можно убрать без потери смысла основного контента;-
contentinfo, аналог в HTML — <footer>, который не вложен в <article>, <aside>, <main>, <nav>, <section> или элементы с соответствующими ролями. Область страницы, которая повторяется на других страницах и содержит дополнительную навигацию, контактные данные, авторские права, юридическую и справочную информацию;-
form, аналог в HTML — <form> с заданным именем. Область страницы с набором полей для ввода данных и отправки их на сервер для дальнейшей обработки;-
main, аналог в HTML — первый <main> на странице. Область страницы с основным контентом, ради которого пришёл пользователь;-
navigation, аналог в HTML — <nav>. Область страницы с навигационными элементами как по всему сайту, так и по текущей странице;-
search, аналог в HTML — <search>. Область страницы, которая предлагает элементы управления для поиска или фильтрации результатов;-
region, аналог в HTML — <section> с заданным именем. Область страницы с произвольным контентом, который не попадает под другие ориентиры и который считается важным, чтобы быть в списке ориентиров;Ориентиры полезны не только для пользователей программ чтения с экрана. Поисковые системы лучше распознают структуру страницы с ориентирами. Браузеры предлагают открыть страницу в режиме чтения без лишних элементов, с возможностью настройки текста и фона. Расширения браузера помогают быстрее навигировать по областям страницы с помощью клавиатуры.
#html #aria #a11y
www.w3.org
Accessible Rich Internet Applications (WAI-ARIA) 1.3
Accessibility of web content requires semantic information about widgets, structures, and behaviors, in order to allow assistive technologies to convey appropriate information to persons with disabilities. This specification provides an ontology of roles…
👍6🔥2❤1
You don't know HTML: hidden
В HTML есть глобальный атрибут
Элементы скрываются по простой причине: атрибут
Если в стилях разработчика для элемента не указано свойство
Есть два способа предотвратить неожиданное поведение «атрибут есть, а элемент не скрыт». Первый — добавить в глобальные стили правило с
Второй способ — добавить правило для
Атрибут
На мой взгляд это удобнее, чем работать с инлайн-стилями или делать специальный атомарный класс.
Из-за того, что у браузеров есть стили для
Ещё одна особенность
Но если указать в качестве значения
Эта особенность полезна при разработке компонентов, которые скрывают контент, например раскрывающиеся блоки или аккордеоны. Есть статья с примером реализации аккордеонов и использованием
#ydkhtml
В HTML есть глобальный атрибут
hidden. Он скрывает элемент, у которого указан. Можно задать hidden без значения или продублировать название атрибута в его значении по правилам булевых атрибутов:<div hidden>
<!-- ... -->
</div>
<div hidden="hidden">
<!-- ... -->
</div>
Элементы скрываются по простой причине: атрибут
hidden реализован в браузерах как презентационный и к нему применяется соответствующее правило.[hidden] {
display: none;
}Если в стилях разработчика для элемента не указано свойство
display, то при добавлении атрибута hidden он будет скрыт. Если значение display в стилях разработчика указано, то оно переопределяет правило атрибута hidden, так как по каскаду стили разработчика переопределяют стили браузера.Есть два способа предотвратить неожиданное поведение «атрибут есть, а элемент не скрыт». Первый — добавить в глобальные стили правило с
!important. Это тот редкий случай, когда использование !important уместно:[hidden] {
display: none !important;
}Второй способ — добавить правило для
[hidden] у элементов, где планируется использовать атрибут. Можно сочетать с :where() для сохранения специфичности:.element {
display: flex;
}
.element:where([hidden]) {
display: none;
}Атрибут
hidden отражается в свойство hidden и наоборот. Поэтому в JS можно удобно переключать состояние видимости и не беспокоиться о значении свойства display.// управление через атрибут
element.setAttribute('hidden', '');
element.removeAttribute('hidden');
// управление через свойство
element.hidden = true;
element.hidden = false;
// проверка, скрыт ли элемент
if (element.hidden) {
//...
}
// переключение
element.hidden = !element.hidden;
На мой взгляд это удобнее, чем работать с инлайн-стилями или делать специальный атомарный класс.
// управление через свойство display
element.style.display = 'none';
// нужно помнить предыдущее состояние
element.style.display = 'flex';
// управление через атрибут style
element.setAttribute('style', 'display: none');
// можно удалить другие нужные инлайн-стили
element.removeAttribute('style');
// управление через утилитарный класс
element.classList.add('is-hidden');
element.classList.remove('is-hidden');
Из-за того, что у браузеров есть стили для
hidden, элемент можно скрыть изначально на уровне разметки. Не нужно ждать загрузки, обработки и применения стилей. Поэтому hidden может стать решением в тех ситуациях, когда при загрузке страницы скрытый контент на доли секунд виден из-за того, что стили не догрузились.Ещё одна особенность
hidden в том, что атрибут может принимать значения. То есть это одновременно булев и перечисляемый атрибут. Так сложилось исторически из-за обратной совместимости. Атрибут без значения, со значением в виде пустой строки, строки hidden и строки с не валидным значением трактуется браузером как если у элемента установлен атрибут без значения.<!-- варианты эквивалентны -->
<div hidden></div>
<div hidden=""></div>
<div hidden="hidden"></div>
<div hidden="invalid"></div>
Но если указать в качестве значения
until-found, поведение меняется. hidden="until-found" делает элемент скрытым до тех пор пока пользователь не воспользуется функцией поиска по странице. Если внутри элемента есть контент, попадающий под условия поиска, то элемент будет показан. Также отличается значение в стилях браузера.[hidden="until-found"] {
content-visibility: hidden;
}Эта особенность полезна при разработке компонентов, которые скрывают контент, например раскрывающиеся блоки или аккордеоны. Есть статья с примером реализации аккордеонов и использованием
hidden="until-found".#ydkhtml
Chrome for Developers
Making collapsed content accessible with hidden=until-found | CSS and UI | Chrome for Developers
How this new attribute value can ensure that content within accordion sections can be found and linked to.
❤18👍7🔥6