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
Menu elements
Я рассказывал про меню, его семантику, применение и различия с навигацией. Важно разделять меню команд и навигационное меню. Первое про вызов действий в контексте приложения, его части или выбранного объекта. Второе про набор ссылок для перехода между страницами.
Для реализации навигационного меню в HTML есть элементы
В OpenUI появилось предложение по добавлению в HTML новых элементов для создания меню команд.
Меню-флажок поддерживает псевдо-класс
Эти элементы в сочетании с Popover API, Focusgroup и Anchor Positioning позволят создавать меню команд как в рекомендациях ARIA, но без использования ARIA-атрибутов для передачи семантики и JS для реализации поведения клавиатуры и отображения выпадающих меню. Сами действия, вызываемые элементами меню, само собой, нужно определить в JS.
Предложение находится на ранней стадии. Есть несколько открытых вопросов по синтаксису, набору возможностей, именованию элементов и атрибутов, интеграции с другими существующими и предлагаемыми возможностями. Ожидаем в платформе новый примитив для меню.
#html #ui
Дисклеймер: это обзор раннего предложения новых функций веб-платформы. Синтаксис может измениться в будущем или от функции могут отказаться.
Я рассказывал про меню, его семантику, применение и различия с навигацией. Важно разделять меню команд и навигационное меню. Первое про вызов действий в контексте приложения, его части или выбранного объекта. Второе про набор ссылок для перехода между страницами.
Для реализации навигационного меню в HTML есть элементы
<nav>, <ol>, <ul>, <li> и <a>. Для реализации меню команд в HTML ничего нет, нужно использовать ARIA. Есть элемент <menu>, который по определению предназначен для команд. Но он просто заменяет список <ul> и не добавляет функциональности и нужной семантики.В OpenUI появилось предложение по добавлению в HTML новых элементов для создания меню команд.
<menubar> — контейнер для элементов меню с встроенной ролью menubar. Предназначен для создания основного меню приложения. Обычно меню располагается в верхней части интерфейса, отображается всегда, содержит горизонтальный список опций, которые раскрывают выпадающие подменю. <menulist> — контейнер для элементов меню с встроенной ролью menu. Предназначен для создания выпадающих меню и контекстных меню. Пункты меню, как правило, вызывают действия в приложении или раскрывают дополнительные уровни меню. <menuitem> — элемент меню, аналог кнопки для вызова действий или переключения подменю. Поддерживает атрибут disabled для отключения и псевдо-класс :disabled для стилизации. Может открывать подменю, если указан атрибут menu с идентификатором, как popover. Генерирует событие toggle, если переключает подменю.<menuitemcheckbox> — элемент меню, который работает как флажок. Подразумевает группу из нескольких связанных элементов для выбора дополняющих опций. Например, список отображаемых колонок таблицы или список активных панелей. Для группировки предлагается использовать элемент <fieldset>. Меню-флажок поддерживает псевдо-класс
:checked для стилизации выбранного состояния и псевдо-элемент ::checkmark для стилизации галочки. При нажатии генерирует события change и toggle. Может быть отключён атрибутом disabled и стилизован псевдо-классом :disabled.<menuitemradio> — элемент меню, который работает как радио-кнопка. В основном работает как <menuitemcheckbox>, по предназначен для выбора взаимоисключающих опций. Например, режим сортировки или отображения данных. Поддерживает те же псевдо-классы, псевдо-элементы, атрибуты и события, что и <menuitemcheckbox>.Эти элементы в сочетании с Popover API, Focusgroup и Anchor Positioning позволят создавать меню команд как в рекомендациях ARIA, но без использования ARIA-атрибутов для передачи семантики и JS для реализации поведения клавиатуры и отображения выпадающих меню. Сами действия, вызываемые элементами меню, само собой, нужно определить в JS.
Предложение находится на ранней стадии. Есть несколько открытых вопросов по синтаксису, набору возможностей, именованию элементов и атрибутов, интеграции с другими существующими и предлагаемыми возможностями. Ожидаем в платформе новый примитив для меню.
#html #ui
❤8👍3🔥3
CSS-хаки и здравый смысл
Обозревая новые функции CSS, я часто упоминаю, что они позволяют реализовать тот или иной интерфейс без лишнего JS. Например, карусели, select или аккордеоны. Иногда подход «без лишнего JS» доходит до крайностей, когда придумывают так называемые Pure CSS решения с хаками для замены JS.
В HTML и CSS есть ряд особенностей, которые можно использовать не по прямому назначению и добиваться интересных результатов:
- флажки и радио-кнопоки в сочетании с псевдо-классом
- якорные ссылки в сочетании с псевдо-классом
- псевдо-классы
На основе этого делают вкладки, карусели, аккордеоны, раскрывающиеся меню, диалоги, галереи и прочие компоненты. Есть сайт с примерами.
Я и сам когда-то делал дровер через флажок, многоуровневое меню на ховерах и прочее. Был страшно собой доволен, ведь я сделал работающий компонент без JS, круто! Сейчас я понимаю, что это не правильно.
Нужно различать CSS-хаки и реальные решения для интерфейcов на CSS. Флажок — это элемент формы для выбора опции, а не переключатель. Якорная ссылка — это способ навигации по странице, а не решение для вкладок и диалогов.
CSS-хаки изящны, но портят пользовательский опыт и доступность. Неочевидные возможности, сложные селекторы и хитрые свойства усложняют поддержку проекта. Хаки полагаются на структуру HTML, что делает решения хрупкими. Хорошо эту тему раскрыл Вадим Макеев в старом докладе «Чистый CSS для грязных трюков».
Но есть хорошие примеры использования HTML и CSS для реализации интерфейса. Предлагаю доклад Никиты Дубко и доклад Килиана Валкхофа. Оба про то, как с умом использовать современные возможности HTML и CSS для решения классических задач без JS и вреда пользовательскому опыту.
#html #css #ui
Обозревая новые функции CSS, я часто упоминаю, что они позволяют реализовать тот или иной интерфейс без лишнего JS. Например, карусели, select или аккордеоны. Иногда подход «без лишнего JS» доходит до крайностей, когда придумывают так называемые Pure CSS решения с хаками для замены JS.
В HTML и CSS есть ряд особенностей, которые можно использовать не по прямому назначению и добиваться интересных результатов:
- флажки и радио-кнопоки в сочетании с псевдо-классом
:checked и комбинаторами соседства;- якорные ссылки в сочетании с псевдо-классом
:target и элементами с атрибутом id;- псевдо-классы
:hover и :focus в сочетании с комбинаторами соседства.На основе этого делают вкладки, карусели, аккордеоны, раскрывающиеся меню, диалоги, галереи и прочие компоненты. Есть сайт с примерами.
Я и сам когда-то делал дровер через флажок, многоуровневое меню на ховерах и прочее. Был страшно собой доволен, ведь я сделал работающий компонент без JS, круто! Сейчас я понимаю, что это не правильно.
Нужно различать CSS-хаки и реальные решения для интерфейcов на CSS. Флажок — это элемент формы для выбора опции, а не переключатель. Якорная ссылка — это способ навигации по странице, а не решение для вкладок и диалогов.
CSS-хаки изящны, но портят пользовательский опыт и доступность. Неочевидные возможности, сложные селекторы и хитрые свойства усложняют поддержку проекта. Хаки полагаются на структуру HTML, что делает решения хрупкими. Хорошо эту тему раскрыл Вадим Макеев в старом докладе «Чистый CSS для грязных трюков».
Но есть хорошие примеры использования HTML и CSS для реализации интерфейса. Предлагаю доклад Никиты Дубко и доклад Килиана Валкхофа. Оба про то, как с умом использовать современные возможности HTML и CSS для решения классических задач без JS и вреда пользовательскому опыту.
#html #css #ui
👍17❤3🔥3
Актуальные фавиконки
Самый простой способ добавить фавиконку на сайт — поместить файл
Многие пользуются генераторами фавиконок. Потому что нужны файлы для разных сценариев и проще закинуть генератору исходник и получить готовый результат, чем делать всё руками.
Проблема генераторов в том, что они отдают архив с десятком фавиконок в разных форматах и размерах, а также полотно кода, который нужно вставить в
-
-
-
Если сайт должен устанавливаться и работать как PWA, то к этому нужно добавить ещё четыре файла:
-
-
-
-
В статье от Злых Марсиан подробно объясняется, какие файлы использовать, как их подготовить и подключить к странице. Статья обновляется ежегодно, поэтому рекомендую периодически к ней возвращаться.
Не тащите в проекты гору устаревших фавиконок и не захламляйте
#html
Самый простой способ добавить фавиконку на сайт — поместить файл
favicon.ico в корень. Старый и железобетонный способ, который работает везде.Многие пользуются генераторами фавиконок. Потому что нужны файлы для разных сценариев и проще закинуть генератору исходник и получить готовый результат, чем делать всё руками.
Проблема генераторов в том, что они отдают архив с десятком фавиконок в разных форматах и размерах, а также полотно кода, который нужно вставить в
<head> для подключения. Это избыточно. Сегодня достаточно трёх файлов:-
/favicon.ico-
/icon.noscript-
/apple-touch-icon.pngЕсли сайт должен устанавливаться и работать как PWA, то к этому нужно добавить ещё четыре файла:
-
/manifest.webmanifest-
/icon-192.png-
/icon-mask.png-
/icon-512.pngВ статье от Злых Марсиан подробно объясняется, какие файлы использовать, как их подготовить и подключить к странице. Статья обновляется ежегодно, поэтому рекомендую периодически к ней возвращаться.
Не тащите в проекты гору устаревших фавиконок и не захламляйте
<head> лишними ссылками на эти файлы.#html
evilmartians.com
How to Favicon in 2025: Three files that fit most needs—Martian Chronicles, Evil Martians’ team blog
Prefer SVG over PNG, trust browsers to downscale, drop obscure formats—the ultimate, exhaustive guide to favicons for modern web. Includes steps for static HTML and Webpack.
❤21👍11
Предположения по производительности
Недавно общался с двумя разными разработчиками на тему производительности. Оба работали над улучшением производительности в рамках своих рабочих проектов и оказались в ситуации, когда предпринятые меры не дали значимого результата на цифрах.
То есть разработчики применили ряд распространённых оптимизаций, описанных в руководствах по улучшению производительности, и практически не получили прироста показателей в Pagespeed. Это натолкнуло меня на некоторые мысли.
Одна из проблем — неверные предположения. Разработчики запускают аудит, видят плохие показатели, смотрят на список проблем и выборочно их исправляют. Важно приоритизировать проблемы и делать упор на те, которые оказывают наибольшее влияние.
Нет смысла конвертировать шрифты и изображения в современные форматы, добавлять ленивую загрузку и проставлять атрибуты
Как и нет смысла всё это делать, если приложение загружает несколько мегабайт JS и рисуется на клиенте. В первую очередь нужно смотреть в сторону уменьшения размера бандла, разделения на чанки, удаления лишних зависимостей и оптимизацию доставки. А потом уже смотреть на другие проблемы.
Вторая мысль — оптимизации условно можно разделить на простые и сложные. Простые описаны во многих руководствах, легко гуглятся и просты в реализации. Но часто они направлены на исправление какой-то конкретной маленькой проблемы и не сильно влияют на общую картину.
Сложные оптимизации менее распространены, требуют комплексной работы и затрагивают архитектуру проекта. На это обычно нет времени, так как требуется достаточно длительный рефакторинг. Но именно такие оптимизации дают ощутимые результаты.
Ещё оптимизации могут стрелять в ноги.
Улучшения в одном месте могут привести к ухудшениям в другом. Перед началом работ по улучшению производительности стоит делать контрольные замеры, вносить точечные изменения и делать повторные замеры для сравнения. Для итераций можно использовать Lighthouse в DevTools не смотря на то, что он не показывает реальную картину.
#performance
Недавно общался с двумя разными разработчиками на тему производительности. Оба работали над улучшением производительности в рамках своих рабочих проектов и оказались в ситуации, когда предпринятые меры не дали значимого результата на цифрах.
То есть разработчики применили ряд распространённых оптимизаций, описанных в руководствах по улучшению производительности, и практически не получили прироста показателей в Pagespeed. Это натолкнуло меня на некоторые мысли.
Одна из проблем — неверные предположения. Разработчики запускают аудит, видят плохие показатели, смотрят на список проблем и выборочно их исправляют. Важно приоритизировать проблемы и делать упор на те, которые оказывают наибольшее влияние.
Нет смысла конвертировать шрифты и изображения в современные форматы, добавлять ленивую загрузку и проставлять атрибуты
width и height, если время получения первого байта (TTFB) составляет 1.5 секунды. В первую очередь нужно разбираться с сервером или с сетью.Как и нет смысла всё это делать, если приложение загружает несколько мегабайт JS и рисуется на клиенте. В первую очередь нужно смотреть в сторону уменьшения размера бандла, разделения на чанки, удаления лишних зависимостей и оптимизацию доставки. А потом уже смотреть на другие проблемы.
Вторая мысль — оптимизации условно можно разделить на простые и сложные. Простые описаны во многих руководствах, легко гуглятся и просты в реализации. Но часто они направлены на исправление какой-то конкретной маленькой проблемы и не сильно влияют на общую картину.
Сложные оптимизации менее распространены, требуют комплексной работы и затрагивают архитектуру проекта. На это обычно нет времени, так как требуется достаточно длительный рефакторинг. Но именно такие оптимизации дают ощутимые результаты.
Ещё оптимизации могут стрелять в ноги.
preload ускоряет загрузку критического ресурса, но откладывает другие. Ленивая загрузка полезна, но не в начальной области просмотра. CDN сокращает расстояние до пользователя, но добавляет соединение к стороннему домену.Улучшения в одном месте могут привести к ухудшениям в другом. Перед началом работ по улучшению производительности стоит делать контрольные замеры, вносить точечные изменения и делать повторные замеры для сравнения. Для итераций можно использовать Lighthouse в DevTools не смотря на то, что он не показывает реальную картину.
#performance
👍11🔥2❤1