<divelopers> – Telegram
<divelopers>
1.03K subscribers
21 photos
253 links
Рандомные мысли про HTML, CSS, доступность, пользовательские интерфейсы, производительность, браузеры и веб-стандарты.

Автор: @alexnozer
Download Telegram
Пользовательские функции в CSS

Дисклеймер: это обзор раннего предложения новой функции. Синтаксис может измениться в будущем или от функции могут вовсе отказаться.


Многие возможности препроцессоров уже доступны в CSS, но функций и миксинов всё ещё нет. В прошлом году активно обсуждались идеи их добавления в CSS. А на днях Брамус поделился новостью, что команда Chrome занимается прототипированием пользовательских функций в CSS. Также был опубликован черновик спецификации CSS Functions and Mixins Module. Пока в нём описаны только функции.

В CSS появится новая директива @function для объявления функций. Для названия используется синтаксис <dashed-ident>, аналогично Custom Properties с двумя тире. После названия в скобках указываются аргументы, тоже в синтаксисе <dashed-ident>. Можно задать тип аргумента и значение по умолчанию. После списка аргументов можно указать тип возвращаемого значения через returns. Ключевое слово result внутри функции возвращает значение.

@function --shadow(--shadow-color <color> : inherit) {
result: 2px 2px var(--shadow-color, black);
}

.foo {
--shadow-color: blue;
/* будет синяя тень, так как --shadow-color наследуется */
box-shadow: --shadow();
/* будет красная тень, так как передано значение */
box-shadow: --shadow(red);
}


Внутри функции можно объявлять локальные переменные в виде Custom Properties. Они доступны внутри самой функции и в других функциях, которые вызываются внутри. Это отличается от того, как работают области видимости и замыкания в JS.

@function --outer(--outer-arg) {
--outer-local: 2;
result: --inner();
}

@function --inner() returns <number> {
/* --inner видит --outer-arg и --outer-local, так как вызвана внутри --outer */
result: calc(var(--outer-arg) + var(--outer-local));
}

div {
z-index: --outer(1); /* 3 */
}


Так как в CSS нет условий (пока что), циклов и структур данных, применение пользовательских функций ограничится генерацией параметризованных значений и трансформацией значений встроенными функциями calc(), color(), min() и другими. Но даже это звучит мощно и откроет новые возможности.

Более подробно о функциях в CSS можно узнать из статьи Мириам Сюзанн, соавтора спецификации.

#css
😱14👍94🌚1
Как люди с ограниченными возможностями пользуются Интернетом

На сайте инициативы веб-доступности (WAI) обновился раздел «Как люди с ограниченными возможностями пользуются Интернетом». В нём собраны истории реальных людей с разными ограничениями, барьеры, с которыми они сталкиваются и вспомогательные технологии, которыми пользуются. Также там есть подраздел с инструментами и техниками, применение которых поможет людям с ограничениями пользоваться Интернетом. Раздел пополнился новыми историями пользователей и видео.

В разделе представлены истории девяти человек со следующими ограничениями здоровья:

- Нарушение моторики рук
- Аутизм
- Слепота
- Дальтонизм
- Синдром Дауна
- Глухота
- Глухота и слабое зрение
- СДВГ и дислексия
- Тремор рук и кратковременная потеря памяти

Хороший ресурс для дизайнеров, разработчиков, проектировщиков интерфейса и создателей контента, чтобы узнать, как разные люди пользуются Интернетом и причины создания доступных цифровых продуктов.

#a11y
19🥰3👍1🔥1👌1
You don't know HTML: что такое HTML?

Иногда на собеседованиях на младшие позиции задают вопрос: что такое HTML? Ответ обычно такой:

HTML (HyperText Markup Language) — это язык разметки гипертекста, предназначенный для описания структуры документа с помощью тегов и атрибутов.


В большинстве случаев этого будет достаточно и ответ устроит собеседующего. Но HTML гораздо больше, чем просто язык разметки. Это технология, которая включает в себя язык разметки и другие части веб-платформы, называемые Web API.

В этом можно убедиться, посмотрев на полное содержание спецификации HTML:

1. Введение. Место спецификации среди других стандартов, историческая справка, устройство спецификации и как её читать, быстрое введение в синтаксис HTML, требования к совместимости и соответствию.

2. Общая инфраструктура. Ресурсы, дерево DOM, скрипты, плагины, кодировка символов, механизмы расширения, политики, загрузка ресурсов, типы данных, интерфейсы HTMLAllCollection, HTMLFormCollection, HTMLOptionsCollection и DOMStringList, передача структурированных данных.

3. Семантика, структура и API документов. Объект Document, интерфейс DocumentOrShadowRoot, управление метаданными, механизм блокировки рендеринга, методы доступа к DOM-дереву, семантика, элементы в DOM, глобальные атрибуты, свойства innerText и outerText, алгоритм двунаправленного текста, связь со стандартом ARIA.

4. Элементы HTML. Список элементов, типы ссылок, обработка изображений, альтернативный текст, работа с медиа, связь с MathML и SVG, обработка и отправка форм, Canvas API, Custom Elements API.

5. Микроданные. Назначение и синтаксис, атрибуты itemprop, itemscope и itemtype, примеры микроданных vCard и vEvent, кодировка микроданных в JSON (JSON-LD).

6. Пользовательское взаимодействие. Атрибут hidden, Page Visibility API, атрибут inert, пользовательская активация, ToggleEvent, фокус, атрибуты tabindex и autofocus, атрибут acceskey, редактирование, атрибуты conteneditable, inputmode и enterkeyhint, свойство designMode, поиск по странице, Close Watcher API, Drag-and-Drop API, Popover API.

7. Загрузка веб-страниц. Понятие origin, CORS, Navigation и Session History API, объекты Window, WindowProxy, Location, History, Navigation, контекст просмотра, навигация по ссылкам, жизненный цикл документа.

8. API веб-приложений. Скрипты и их обработка, Event Loop, событийная модель, кодировка Base64, динамическая вставка разметки, DOMParser, свойства innerHTML и outerHTML, методы insertAdjacentHTML() и createContextualFragment(), таймеры, очередь микрозадач, alert(), prompt() и comfirm(), ImageBitmap, анимации фреймов.

9. Коммуникация. MessageEvent, Server-sent Events, общение между документами, Broadcast Channel API.

10. Веб-воркеры. Описание, жизненный цикл, контекст, Worker, Shared Worker, Dedicated Worker, Workers API.

11. Ворклеты. Описание, инфраструктура, Worklet, жизненный цикл.

12. Веб-хранилища. Local Storage API, Session Storage API, приватность, безопасность.

13. Синтаксис HTML. Разбор синтаксиса, парсинг, токенизация, построение DOM-дерева, спекулятивный парсинг, обработка ошибок и крайних случаев, сериализация и обработка фрагментов.

14. Синтаксис XML. Создание документов в XML-синтаксисе, парсинг и обработка XML, парсинг XML-фрагментов.

15. Рендеринг. Связь с CSS, категории элементов, заменяемые элементы, отрисовка виджетов, отрисовка <frame> и <frameset>, интерактивное медиа, печатные медиа, нестилизованные XML-документы.

16. Устаревшие функции. Устаревшие, но совместимые функции, несовместимые функции, требования к <marquee> и <frameset>.

17. IANA. Описание MIME-типов text/html, multipart/x-mixed-replace, application/xhtml+xml, text/ping, application/microdata+json, text/event-stream, схема web+.

Довольно внушительный список. Всё это — HTML, не только теги и атрибуты.

#ydkhtml
🔥17👍3👎3😱32💯2🤝1
Interop 2025

Стартовал Interop 2025, о чём в своих блогах поделились Google, Apple, Bocoup, Igalia, Microsoft и Mozilla. Напомню, что Interop — это инициатива, в рамках которой основные браузеры (Chrome, Edge, Firefox и Safari) договариваются о функциях веб-платформы, над кроссбраузерной совместимостью которых они будут работать в течение года. Инициатива стартовала в 2021 году и с тех пор проводится ежегодно. За последние несколько лет Interop помог сделать множество функций кроссбраузерными.

Список функций Interop 2025 включает:

- Anchor positioning
- backdrop-filter
- Core Web Vitals
- Элемент <details> (улучшения)
- Раскладки (flexbox, grid)
- Модули (Import Attributes, JSON-модули)
- Navigation API
- События указателя и мыши
- Удаление MutationEvent
- @scope
- Событие scrollend
- Storage Access API
- text-decoration
- URLPattern
- View Transition API
- WebAssembly
- Web compat (Performance Timeline, Shadow DOM, Workers)
- WebRTC
- Режимы письма

Дополнительные направления исследований:

- Тестирование доступности
- Тестирование Gamepad API
- Мобильное тестирование
- Тестирование приватности
- WebVTT (субтитры)

Следить за процессом можно на Interop 2025 Dashboard.

#interop
🔥144👍2👏2🤯2
CSS-выражения

Недавно я писал о том, что команда Chrome занимается внедрением пользовательских функций CSS из новой спецификации CSS Functions and Mixins. В старых версиях IE была возможность, которая, кажется, переплюнула бы новые пользовательские функции CSS. Речь о функции expression(), которая была доступна начиная с IE5. Она позволяла запускать JS-выражения в CSS. Это уже какой-то JS-in-CSS получается. Проще один раз увидеть:

#wrapper {
width: expression(documentElement.clientWidth > 1000 ? '1000px' : 'auto');
}


В примере считывалась ширина элемента <html>, а затем использовался тернарный оператор для проверки. expression() возвращала результат JS-выражения. Более того, в функции можно было использовать ключевое слово this, которое ссылалось на элемент, соответствующий селектору. Например, с помощью этой возможности и свойств обхода DOM-дерева можно было получить высоту родительского элемента:

#wrapper {
height: expression(this.parentNode.clientHeight + 'px');
}


Выглядит круто, но на практике с этим было много проблем. Во-первых, это работало только в IE5–7, что делало решение некроссбраузерным. Во-вторых, эта функция не входила ни в один стандарт CSS — Microsoft просто внедрила её в свой браузер. В-третьих, были огромные проблемы с производительностью, баги и вылеты. Ну и, конечно, проблемы с безопасностью. В итоге Microsoft отказалась от expression() и удалила её в IE8.

По сути, это был хак, который использовали из-за того, что IE на тот момент не поддерживал многие возможности CSS 2.1, такие как min-width и max-width. Выражения позволяли хоть как-то их эмулировать.

Глядя на функцию expression() сегодня, кажется, что было бы здорово её вернуть. Тем более, современные браузерные движки стали намного производительнее, вычислительная мощность устройств выросла, а также появились такие возможности, как :has() и Container Queries.

Были попытки внедрить подключаемые JS-ворклеты с пользовательскими функциями раскладки для свойства display в рамках Layout API. Но вряд ли в CSS дадут возможность использовать JS.

#css
🤔11👎2🔥2🤯21
Badging API

У нативных приложений есть возможность отображать бейдж в углу иконки приложения. Это способ показать пользователям, что какие-то события требуют внимания: новые сообщения, незавершённые задачи, уведомления и так далее. Badging API предоставляет возможность установить такой значок.

Есть два типа бейджей:

- бейдж документа, который отображается во вкладке браузера, рядом с фавиконкой
- бейдж приложения, который отображается в углу иконки на рабочем столе или панели приложений

Для установки достаточно вызвать метод setAppBadge() объекта navigator. Опционально можно передать число, которое будет отображаться в бейдже. Операционная система получает команду и решает, когда стоит отобразить бейдж. В некоторых случаях это может не сработать, это стоит иметь ввиду. Метод clearAppBadge() убирает бейдж.

// Установить бейдж в виде точки
navigator.setAppBadge();

// Установить бейдж с числом 5
navigator.setAppBadge(5);

// Удалить бейдж
navigator.clearAppBadge();


API работает для сайтов, которые устанавливаются как отдельные приложения (PWA). Для использования также нужен HTTPS. Бейджи будут полезны для сайтов, где есть чаты или уведомления.

#js #web_api
🔥20👍92
Eleventy v3

В начале октября 2024 года состоялся релиз Eleventy v3, и это отличный повод, чтобы рассказать об этом инструменте.

Eleventy is a simpler static site generator


Eleventy — это простой генератор статических сайтов (SSG) с открытым исходным кодом. Его основная задача — преобразовать папку с шаблонами и данными в статический сайт в виде HTML-файлов. Такие сайты быстрые, безопасные, простые в разработке и поддержке, размещаются на любом хостинге статических файлов или CDN.

Подход со статикой хорошо подходит для разработки контентно-ориентированных сайтов: лендингов, блогов, документации, сайтов компаний, афиш, онлайн-журналов, новостных порталов, каталогов, вики, портфолио, персональных сайтов и так далее.

Eleventy работает с любой структурой папок. Начиная с корня проекта он ищет шаблоны в формате Markdown, Liquid, Nunjucks и JS, затем преобразовывает их в HTML. Дополнительно можно настроить шаблоны на TS, JSX и MDX. Есть официальные плагины для шаблонов Handlebars, Mustache, EJS, Haml и Pug, которые до третьей версии были в составе ядра, но теперь вынесены отдельно. Также существуют плагины от сообщества, например Twig. Выбор на любой вкус, цвет и проект.

Отдельно стоит отметить плагин WebC с набором возможностей для шаблонов в HTML-стиле и серверного рендеринга веб-компонентов в статический HTML или декларативный Shadow DOM для инициализации на клиенте.

Маршрутизация в Eleventy построена на файловой структуре. В результате статической генерации получается набор папок и HTML-файлов, который работает по стандартной для большинства серверов модели. Итоговое расположение файлов можно переопределять. Также страницы можно генерировать на основе данных. В новой версии появились виртуальные шаблоны.

В качестве данных Eleventy предлагает концепцию под названием Data Cascade. Это система из данных в шаблонах (frontmatter), JS и JSON файлах. Все данные каскадируются и объединяются в один объект, который доступен в шаблонах. Можно подключить плагины для поддержки данных в формате YAML или TOML. В JS можно обращаться к внешним API через fetch и преобразовывать данные.

Eleventy предлагает механизм коллекций для организации контента. Элементы коллекций можно получить на любой странице и использовать для генерации контента. По коллекции можно делать пагинацию и генерировать навигационные ссылки.

У Eleventy богатый API. С его помощью можно прикрутить практически что угодно: новые типы шаблонов и данных, препроцессоры CSS, сборщики, валидаторы, обработчики изображений, произвольные функции и фильтры и так далее. Есть официальные и пользовательские плагины для решения разнообразных задач.

Eleventy можно сравнить с набирающим популярность Astro, который тоже задуман для контентных сайтов. По сравнению с ним, Eleventy меньше умеет из коробки, но за счёт плагинов и API более гибкий. Eleventy выделяется высокой скоростью установки и сборки по сравнению с другими решениями. Это актуально, потому что многие CI/CD сервисы тарифицируют время работы.

Я использую Eleventy для разработки контентно-ориентированных проектов, которые ложатся в архитектуру Jamstack. Советую присмотреться к этому инструменту для подобных задач.
👍183
Сканер предварительной загрузки

В современных браузерах существует механизм, известный как сканер предварительной загрузки или спекулятивный HTML парсер, который используется для оптимизации скорости загрузки страниц. В 2021 году этот механизм был определен в стандарте HTML, хотя он применяется уже довольно давно.

Начать следует с основного HTML парсера. Первое, что получает браузер при переходе на любую страницу, это HTML. Как только браузер получает HTML, запускается основной парсер, задача которого — пройти по всему HTML, построить DOM и найти ссылки на все внешние ресурсы для добавления в очередь загрузки.

Некоторые стили блокируют отрисовку, чтобы пользователи не видели нестилизованную страницу. Также синхронные скрипты блокируют и отрисовку, и основной парсер, поскольку неизвестно, как скрипт может изменить страницу в результате выполнения. Основной парсер во время блокировки ждет, пока скрипт выполнится.

Здесь на сцену выходит сканер предварительной загрузки. Это еще один, более легкий парсер, который ищет ссылки на внешние ресурсы, пока основной парсер заблокирован. Обнаруженные таким образом ресурсы передаются сетевому процессу, где они добавляются в очередь загрузки согласно приоритету.

В результате ресурсы могут начать загружаться и даже успеть загрузиться еще до того, как основной парсер их обнаружит. Применение этой оптимизации в свое время ускорило загрузку страниц до 20%. Сегодня эта оптимизация доступна во всех браузерах из коробки, ничего настраивать не нужно, главное — не мешать.

Как не мешать:

- Не подключать важные ресурсы через JS и CSS.
- Не подключать важные ресурсы через сторонние контейнеры (например Tag Manager).
- Не использовать document.write().
- Использовать подсказки ресурсов.
- Использовать loading="lazy" для ленивой загрузки вместо библиотек.
- Использовать статическую генерацию (SSG), рендеринг на стороне сервера (SSR) или гибридные подходы вместо только клиентского рендеринга (CSR).

#html #performance
👍12🔥5🤔2
Переосмысление контейнера

Элемент с классом 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>


У секции может быть фон на всю ширину страницы. А контент внутри должен быть ограничен по ширине и центрирован. Получается вложенность контейнера в секцию.

На одном проекте я решил переосмыслить подход к контейнеру, основываясь на двух идеях:

1. попробовать более новые возможности CSS.
2. избежать лишнего DOM-элемента и вложенности.

И вот что у меня получилось:

: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>

#css
🔥28👍74
Forwarded from MinskCSS/MinskJS
💡Скучали? Мы тоже. Поэтому начинаем новый сезон минских митапов по фронтенду! И стартуем с JavaScript.

MinskJS Meetup #12 пройдет 19 марта 2025 года в 19:00 (по минскому времени) онлайн!

И наш первый спикер — Алексей Назаренко. Он расскажет, что же такое веб-компоненты на самом деле, какие мифы вокруг них существуют и пора ли их уже тащить в свои проекты.

📌 Место проведения: уютная онлайн-трансляция на нашем YouTube канале. Никакой регистрации, просто приходите!
👉 https://youtube.com/live/ILRrXYSthsA

Описание и программа
👉 https://telegra.ph/MinskJS-Meetup-12-03-01
👍4
19 марта буду выступать на MinskJS с докладом про веб-компоненты.
🔥13👍72
popover="hint"

Popover API впервые представлен в Chrome 114 полтора года назад, а в январе этого года получил статус Newly available с выходом Safari iOS 18.3. В сочетании с Anchor Positioning можно решать многие интерфейсные задачи, связанные со всплывающими элементами (собственно, поповерами). Popover API предлагает декларативное решение этих задач с учётом доступности и управления поведением фокуса.

Есть проблема с использованием popover="auto" (или просто popover) на разных элементах, которые должны сосуществовать. Сейчас у элементов с popover="auto" есть поведение light dismiss, когда отображение всплывающего элемента приводит к скрытию другого недочернего всплывающего элемента. Во многих случаях так и должно быть. Но есть отдельный класс элементов, где такое поведение всё ломает.

Например, есть кнопка, при нажатии которой открывается выпадающее меню. Рядом есть кнопка с иконкой, при наведении на которую отображается всплывающая подсказка с пояснением назначения. Использование popover="auto" для меню и подсказки приведёт к тому, что отображение подсказки закроет меню. Этого можно избежать за счёт popover="manual", но придётся вручную повторять поведение, которое из коробки даёт Popover API.

В Chrome 133 добавляют новое значение popover="hint". У всплывающих элементов с этим атрибутом меняется поведение light dismiss по сравнению со значении auto. На словах сложно объяснить разницу, проще ознакомиться с сравнительной таблицей в статье из блога Chrome for Developers. Но приведённый пример с меню и подсказкой будет работать так, как ожидается.

Доработки также направлены на дальнейшую интеграцию Invoker Commands API, разработка которого идёт полным ходом. Конкретно речь идёт об атрибуте interesttarget, который позволит открывать всплывающие элементы не кликом по кнопке, как c атрибутом popovertarget, а при наведении указателя или при фокусе на элемент. Я сделаю отдельный пост с обзором этих новых API.

#html #web_api
👍10🔥5🫡21
Лэндинги и статический HTML

Андрей Ситник в твиттере поднял тему лэндингов и статики. Основная мысль в том, что можно разрабатывать лэндинги как статический HTML без сборки. Меньше подвижных частей — меньше точек отказа, проще разработка и поддержка. Я оказывался в ситуациях, когда для минутных правок на сайте нужно было час сражаться с ошибками запуска dev-режима.

Обычно лэндинг — это одна страница под маркетинговые задачи. Нужно показать товар/услугу, CTA и подвести пользователя к целевому действию. Часто лэндинги одноразовые, сделал и забыл. Некоторые лэндинги живут долго и регулярно меняются. Бывают многостраничные и мультиязычные лэндинги. Но чаще это маленький проект, который нужно сделать быстро и минимальными усилиями.

Лэндинг можно рассматривать как большой однофайловый компонент. В HTML есть <style> для стилей, <noscript> для скриптов, <body> для контента. Можно даже не выносить в отдельные файлы, если кода мало. Базовая модульность обеспечивается с помощью <link>, @import, <noscript type="module"> и ESM. Для удобства в современных браузерах есть <noscript type="importmap">.

Для карусели, анимации, валидации форм и прочего могут понадобиться сторонние библиотеки. Большинство из них поставляются в виде standalone-скриптов. Подключить можно как есть с CDN или положить рядом с проектом. Что-то можно подсобрать с помощью esm.sh или bundlejs, если ну очень нужно.

Для нетривиальной логики с реактивным интерфейсом (калькулятор цен, конфигуратор продукта) есть HTML-first библиотеки: Alpine.js, petite-vue, hypernoscript и десяток других. Они доступны в виде standalone и работают без сборки. В таком же режиме можно использовать Vue.

Чтобы избежать копипасты повторяющихся частей есть веб-компоненты. Google и Bing разворачивают Shadow DOM и видят всё, что внутри, поэтому проблем с SEO быть не должно. В качестве альтернативы трансклюзия фрагментов с помощью sl-include, include-fragment, i-html или даже сниппета htmz. Если сервер позволяет, можно настроить SSI и ESI, главное помнить о нюансах с безопасностью.

Сценарий мультиязычности решается на уровне каталогов и стандартной серверной маршрутизации. Перенаправление на нужную версию делается встроенным в начало страницы скриптом. Или за счёт сервера/прокси, если есть возможность. Переключение языков — обычные ссылки.

Графика оптимизируется один раз руками (squoosh, squish) или через прокси для картинок. Минификация при малом количестве ресурсов не так важна. Сжатие gzip включено на любом сервере и без минификации тоже достаточно эффективно.

Для редактирования контента нашёл интересный инструмент — Textolite. Это админка с WYSIWYG для редактирования статического HTML. После изменений перезаписывает исходный HTML-файл. Работает на PHP, но он есть на любом популярном хостинге. Правда выглядит, что проект заброшен, а аналогов не нашёл.

Звучит всё это как возврат в 00-е, когда так создавали сайты. Отчасти так и есть. Но не всегда старое — значит плохое. Плюс появились новые возможности, которых в то время не было. В итоге такой олдскульный подход вполне может подойти для многих лэндингов. Не нужно усложнять простые задачи инструментами, которые мы по привычке тянем из больших проектов потому что так “принято”.

#html
👍186💯6😴1
Invoker Commands

Я уже делал обзор предложения 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
🔥14👍32
Стилизация системных элементов форм

Дисклеймер: это обзор раннего предложения новых функций. Синтаксис может измениться в будущем или от функций могут отказаться.


Джен Симмонс поделилась в соцсетях, что её команда работает над давней проблемой веб-разработки — стилизацией системных элементов форм.

Проблема постепенно решается. За последние годы появились точечные свойств, такие как 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👍82
You don't know HTML: способы расширения HTML

Стандарт 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
👍152
Список описаний на практике

Я как-то делал пост, в котором рассмотрел вариант разметки контента с помощью списка описаний. Там я подробно описал, что такое список описаний, как он работает с точки зрения семантики и где его можно применить. На практике этот элемент встречается редко, хотя я нахожу много сценариев, в которых его можно применить.

Хочу поделиться двумя статьями, которые раскрывают эту тему и предлагают разные примеры использования списка описаний.

Первая статья на HTMHell от Дэвида Лура “The underrated <dl> element”. Дэвид приводит три примера. Первый — классический список терминов и определений, для чего <dl> изначально предназначался. Второй пример — блок с цифрами и подписями, такие часто встречаются на сайтах, чтобы подсветить достижения в виде цифр. Последний, третий пример — список характеристик велосипеда в каталоге.

Вторая статья от Бэна Мейерса “On the <dl>”. Бэн приводит четыре примера. Первый — характеристики продукта, в примере это книга. Второй пример — карточка пользователя с контактами и адресом. Третий пример — блок со свойствами в стиле Википедии. Четвёртый пример самый интересный: Бэн показывает большую карточку с информацией о персонаже из Dungeons & Dragons, которая практически полностью состоит из списков описаний.

Списки описаний полезны для группировки связанного контента, который можно представить в виде пар ключ-значение. Если взглянуть на интерфейсы, такие списки можно встретить чаще, чем кажется. Хотя полезность <dl> с точки зрения семантики слегка сомнительная, всё же стоит его использовать для группировки подходящего контента.

#html
🔥18🌚21
Автодополнение номера телефона

Я уже писал про автодополнение в браузерах. Работая над улучшением доступности одного проекта, я настраивал автодополнение в рамках критерия 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
👍29🔥42🤯2
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
🔥16🤯86
Заголовки и outline

На старте карьеры я запомнил правило: на странице должен быть один <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
22🔥21🤗3👍21