Рандомные мысли про HTML, CSS, доступность, пользовательские интерфейсы, производительность, браузеры и веб-стандарты.
Автор: @alexnozer
Post #223
965
- 🌚 13
<D @alexnozer_dev
Showing posts older than #224 · Back to latest
<aside> для боковых колонок», то этот элемент предназначен для контента, который косвенно связан с окружающим его контентом и который можно удалить без потери смысла. Боковой фильтр подходит под эти критерии. К тому же <aside> — ориентир и к нему можно быстро перейти.<nav> группирует элементы, которые используются для навигации. Фильтр можно рассматривать как своего рода навигацию по каталогу товаров. Хороший фильтр дополняет адрес страницы параметрами, чтобы можно было сохранить ссылку или поделиться с кем-то. <nav> тоже ориентир и позволяет быстро перейти к фильтру.<search> предназначен для разметки набора элементов, которые предоставляют функции поиска или фильтрации. Уже по описанию элемент подходит. Как и другие элементы, <search> — ориентир с возможностью быстрого доступа. Остаётся только вопрос с браузерной поддержкой, так как элемент относительно новый.<form>. Если элементы фильтра находятся внутри <form> этого может быть достаточно и другие семантические обёртки не нужны. <form> не ориентир, но быстрый доступ возможен через список форм. Или можно связать заголовок и форму, тогда получится ориентир.<aside>, <search>, <nav> и <form>, а просто использовать <section>. Элемент группирует связанные части страницы без дополнительной смысловой нагрузки. Сам по себе <section> ничего не даёт, но с привязанным заголовком он становится ориентиром. С помощью aria-roledescription можно задать произвольное название роли.<div role="group">. Или, следуя первому правилу ARIA — завернуть всё в <fieldset> + <legend>, что создаст группу с заголовком. Для большей понятности можно добавить описание роли.gyp ERR! и как это можно исправить.Fully accessible with arrow key navigation
accessibility со значением true по умолчанию. Slick кое что делает для улучшения доступности: устанавливает атрибуты, добавляет управление стрелками. К сожалению, этого не достаточно, при аудите я обнаружил много проблем. В целом Slick не соответствует рекомендациям от WAI ARIA.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.
data-src и data-srcset подставляются в src и srcset, что инициирует загрузку.data-* атрибуты;data-src/data-srcset или игнорировать изображения без src. Таким образом можно навредить SEO;src обязательный, поэтому многие инструменты анализа разметки (валидаторы, линтеры, сканеры доступности) будут игнорировать такие изображения или помечать как невалидные;<img> и вкладывать его в <noscript>.loading. Значение eager сообщает, что ресурс нужно обрабатывать как обычно. Это значение по умолчанию, поэтому атрибут можно не указывать. Значение lazy включает встроенную в браузеры ленивую загрузку.<img> и <iframe>, также есть предложение добавить его для <video>. При установке loading="lazy" ресурс не будет скачиваться на этапе обработки HTML, сканер предварительной загрузки не добавит ссылку в очередь.<img> и <iframe> нужно установить loading="lazy", но это не так. Не стоит устанавливать loading="lazy" для элементов в верхней части страницы, которые видны сразу.loading="lazy" помешает заранее начать загрузку. В результате пользователи получат изображение позже и значение метрики LCP возрастёт.
<select>. Последние несколько лет прорабатывались разные варианты как это сделать. После тестовой реализации и обратной связи от разработчиков, финальный вариант был принят и теперь доступен в Chrome. У нас наконец-то появилась возможность стилизовать стандартный <select>!appearance:select, select::picker(select) {
appearance: base-select;
}<select>, но убирает некоторые функции:<select> не будет соответствовать самой длинной опции.<select> можно было помещать <option>, <optgroup>, а с недавних пор <hr>. Все остальные элементы игнорируются. В новом режиме внутрь <option> можно поместить изображения, SVG и дополнительную разметку. В сам <select> можно добавить <button> и новый элемент <selectedcontent> для отображения выбранной опции.<select>
<button>
<selectedcontent></selectedcontent>
</button>
<option>
<svg aria-hidden>…</svg>
<span>HTML</span>
</option>
<option>
<svg aria-hidden>…</svg>
<span>CSS</span>
</option>
<option>
<svg aria-hidden>…</svg>
<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 по медиа-запросу, чтобы вернуть на мобильных устройствах системный интерфейс выбора опций.
Дисклеймер: это обзор раннего предложения новых функций. Синтаксис может измениться в будущем или от функций могут отказаться.
@mixin --box {
aspect-ratio: 1;
inline-size: 100px;
block-size: 100px;
}
.box {
@apply --box;
}@mixin. Затем следует название в синтаксисе <dashed-ident>, как у custom properties. Далее описывается блок с правилами. Применяется миксин через ещё одну новую директиву — @apply. Почти как в SCSS.
<h1>. Остальные заголовки нужно размечать элементами <h2>-<h6> согласно иерархии. Периодически попадаются статьи и комментарии, которые предлагают использовать <h1> для всех заголовков на странице, потому что браузеры и поисковики умеют с этим работать.<h>. Уровень должен был автоматически определяться на основе вложенности в элементы секционирования (<section>, <article>, <aside>, <nav>, и другие). <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>. Поисковые системы тоже это учитывают. Индексирующему алгоритму, в общем-то, всё равно как размечены заголовки.<h1>, которые зависят от вложенности. Недавно был поднят вопрос о том, чтобы эти стили удалить из спецификации и браузеров.<h1>. Термин outline остался в спецификации, но теперь обозначает дерево всех заголовков страницы, которое формируется на основе уровней. В актуальной версии все ещё есть один пример с несколькими <h1> и подписью “A document can contain multiple top-level heading”.<h1> на страницу» и соблюдение иерархии заголовков всё ещё актуально. Не стоит экспериментировать, меняя все заголовки на <h1>. Даже если поисковые системы это поддерживают и кто-то говорит, что так можно.::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 соответствует контейнеру с маркерами. Его можно размещать в нужной части карусели, ограничить по ширине или ещё как-либо стилизовать. Контейнер предлагает навигацию по маркерам при помощи стрелок.
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", поэтому и возникла ошибка. Но тест же на чём-то основан?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" для возврата виртуальной клавиатуры, удобной для ввода номеров.<dl> изначально предназначался. Второй пример — блок с цифрами и подписями, такие часто встречаются на сайтах, чтобы подсветить достижения в виде цифр. Последний, третий пример — список характеристик велосипеда в каталоге.<dl> с точки зрения семантики слегка сомнительная, всё же стоит его использовать для группировки подходящего контента.
<details>, <dialog>) и улучшают существующие. Но как бы они ни старались, невозможно покрыть всё многообразие контента, сайтов, приложений и способов использования HTML.class для создания собственных элементов на базе наиболее подходящих существующих. Это то, чем постоянно пользуются разработчики. Также class можно использовать для хранения мета-информации или для добавления дополнительной семантики, что используется микро-форматами.data-* для данных, которые игнорируются браузером и ни на что не влияют, но могут использоваться для стилизации и передачи данных для скриптов. Довольно часто используются для хранения настроек и мета-данных, которые считываются и используются в JS.<meta name="…" content="…"> для добавления мета-данных на уровне страницы. Чаще всего используется для добавления социальной разметки (Open Graph, Twitter Card) для поисковых систем и социальных сетей. Также можно добавлять произвольные данные в виде ключ-значение, а сам элемент <meta> можно размещать не только в <head>.rel для аннотации ссылок. Стандарт определяет набор базовых значений, которые используют браузеры. Но можно зарегистрировать собственные значения, чем пользуются разные организации.<script type="…"> для добавления произвольных данных в текстовом виде. Значение атрибута type, отличное от text/javascript, module и importmap делает <script> блоком данных. В нём можно разместить любые данные в текстовом формате, которые затем можно считывать и обрабатывать в скриптах. Это используется микро-форматом JSON-LD.Symbol.itemtype, itemscope и itemprop для добавления микро-данных, которые используются другими сайтами и приложениями. Наиболее распространён словарь Schema.org, на который ориентируются поисковые системы и различные парсеры.Дисклеймер: это обзор раннего предложения новых функций. Синтаксис может измениться в будущем или от функций могут отказаться.
accent-color, field-sizing и appearance, которые где-то упрощают стилизацию. В Safari реализовали переключатель <input type="checkbox" switch>, а в Chrome вот-вот добьют главного босса — <select>. Стоит отдать должное инициативе OpenUI.appearance: base. Оно отключает отрисовку элементов форм, специфичных для браузера и операционной системы. Вместо этого будут рисоваться элементы с простыми базовыми стилями, одинаковыми для всех браузеров. Эти стили можно переопределить и дополнить.::-webkit-slider-thumb или ::-moz-range-thumb. Но они не стандартизированы, работают только в конкретных браузерах и отличаются набором поддерживаемых свойств.::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">. command и commandfor. Invokers API теперь называется Invoker Commands, предложение разделили на две части, первую уже готовят к релизу в Chrome. Пришло время взглянуть на это всё ещё раз.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. Планируется, что новые атрибуты со временем заменят их.<dialog> и popover. Над второй частью ещё работают, но стоит ожидать команд для переключения <details>, раскрытия системных пикеров у <input> и <select>, управления воспроизведением <audio> и <video>, копирования текста, переключения полноэкранного режима и так далее.command, а в объекте события можно обратиться к свойству command, в котором будет имя команды.<button
type="button"
commandfor="custom"
command="--do-something"
>
Do something
</button>
<div id="custom"></div>
<script>
const custom = document.querySelector('#custom');
custom.addEventListener('command', (event) => {
if (event.command === '--do-something') {
// ...
}
});
</script>
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>
<script>
class MyElement extends HTMLElement {
connectedCallback() {
const popover = this.querySelector('[popover]');
const button = this.shadowRoot.querySelector('button');
button.commandForElement = popover;
}
}
customElements.define('my-element', MyElement);
</script>
<style> для стилей, <script> для скриптов, <body> для контента. Можно даже не выносить в отдельные файлы, если кода мало. Базовая модульность обеспечивается с помощью <link>, @import, <script type="module"> и ESM. Для удобства в современных браузерах есть <script type="importmap">.popover="auto" (или просто popover) на разных элементах, которые должны сосуществовать. Сейчас у элементов с popover="auto" есть поведение light dismiss, когда отображение всплывающего элемента приводит к скрытию другого недочернего всплывающего элемента. Во многих случаях так и должно быть. Но есть отдельный класс элементов, где такое поведение всё ломает.popover="auto" для меню и подсказки приведёт к тому, что отображение подсказки закроет меню. Этого можно избежать за счёт popover="manual", но придётся вручную повторять поведение, которое из коробки даёт Popover API. popover="hint". У всплывающих элементов с этим атрибутом меняется поведение light dismiss по сравнению со значении auto. На словах сложно объяснить разницу, проще ознакомиться с сравнительной таблицей в статье из блога Chrome for Developers. Но приведённый пример с меню и подсказкой будет работать так, как ожидается.interesttarget, который позволит открывать всплывающие элементы не кликом по кнопке, как c атрибутом popovertarget, а при наведении указателя или при фокусе на элемент. Я сделаю отдельный пост с обзором этих новых API.
Forwarded from MinskCSS/MinskJS

container или wrapper часто используется в вёрстке, чтобы ограничить ширину контента на десктопных экранах. Стили классического резинового контейнера выглядят примерно так:.container {
width: 100%;
max-width: 1400px;
padding-right: 16px;
padding-left: 16px;
margin-right: auto;
margin-left: auto;
}width: 100% не обязательно указывать, но это нужно для случаев, когда контейнер находится внутри flex-контекста. max-width ограничивает ширину контейнера. padding задаёт отступы, чтобы контент не прилипал к краям на узких экранах, а margin со значением auto используется для горизонтального центрирования. Обычно контейнер применяется так:<section class="section">
<div class="container">
<!-- Контент первой секции -->
</div>
</section>
<section class="section">
<div class="container">
<!-- Контент второй секции -->
</div>
</section>
:root {
--container-max-width: 1400px;
--container-min-field: 16px;
--container-field: calc(
(var(--vw) - var(--container-max-width)) / 2
);
}
.container {
width: 100%;
padding-inline: max(
var(--container-field),
var(--container-min-field)
);
}:root объявлены пользовательские свойства:--container-max-width — максимальная ширина контейнера.--container-min-field — минимальный размер одного поля, чтобы контент не прилипал к краям экрана.--container-field — динамический размер одного поля, который вычисляется так: от ширины вьюпорта отнимается максимальная ширина контейнера и делится на 2. В итоге получается тот размер, который обычно достигается через margin со значением auto.--vw, чтобы учесть ширину прокрутки. Про это есть отдельный пост.max(), чтобы выбрать максимальное значение из динамического и минимального поля. На десктопе поле будет занимать всё свободное пространство от края экрана до контентной области контейнера, а на узких экранах поле будет минимальной ширины.<section class="container section">
<!-- Контент -->
</section>