Кампания Build Awesome
Font Awesome запустила кампанию Build Awesome на Kickstarter. Ранее они уже проводили подобное с Web Awesome: собрали денег и выпустили ребрендинг библиотеки Shoelace с улучшениями и новыми PRO функциями.
На этот раз планируется ребрендинг Eleventy — популярного генератора статических сайтов. Его создатель — Зак Лезерман, как и создатель Shoelace — Кори ЛаВиска, перешли работать в Font Awesome вместо со своими проектами.
Учитывая историю с Web Awesome и данные на странице кампании, получается следующее:
- Следующая мажорная версия Eleventy (4.0) выходит под новым названием Build Awesome. Просто новое название, проект по-прежнему останется с открытым исходным кодом, сохранит синтаксис, API и будет совместим с существующими плагинами;
- Появится платформа, которая должна упростить создание и поддержку сайтов. Предположительно это веб-интерфейс с админкой, где можно будет собрать сайт с помощью Eleventy, выкачать статику, опубликовать на CDN и управлять контентом;
- Появятся шаблоны для блога, портфолио и персонального сайта для быстрого старта, а также плагины для использования иконок Font Awesome и веб-компонентов Web Awesome;
- Появятся платные PRO функции: коллаборативное редактирование, платформа для превью и публикации, премиум-шаблоны, DevTools для проверки качества сайтов, инструменты миграции, премиум поддержка и, конечно же, ИИ функции.
Также в зависимости от суммы сборов заявлены дополнительные функции:
- Система темизации
- Плагин для SEO/Favicon/OpenGraph
- Инструменты для новостной рассылки
- Интеграция со Stripe
- Инструменты синдикации для социальных сетей
- Аналитика
- Формы
- Плагин для шрифтов
- PWA с поддержкой local-first/offline-first
- Медиа-библиотека аудио/видео
Чтож, посмотрим, что из этого выйдет. Для любителей Eleventy это, возможно, тревожный звоночек. С другой стороны, это обеспечит проекту стабильность и долговечность, авторам тоже нужно кормить себя и семью.
Ядро Web Awesome и основные компоненты остались доступными в виде проекта с открытым исходным кодом при ребрендинге Shoelace. Аналогично должно быть с Eleventy. PRO функции не обязательны, а ядро сохраняется как есть.
#tools
Font Awesome запустила кампанию Build Awesome на Kickstarter. Ранее они уже проводили подобное с Web Awesome: собрали денег и выпустили ребрендинг библиотеки Shoelace с улучшениями и новыми PRO функциями.
На этот раз планируется ребрендинг Eleventy — популярного генератора статических сайтов. Его создатель — Зак Лезерман, как и создатель Shoelace — Кори ЛаВиска, перешли работать в Font Awesome вместо со своими проектами.
Учитывая историю с Web Awesome и данные на странице кампании, получается следующее:
- Следующая мажорная версия Eleventy (4.0) выходит под новым названием Build Awesome. Просто новое название, проект по-прежнему останется с открытым исходным кодом, сохранит синтаксис, API и будет совместим с существующими плагинами;
- Появится платформа, которая должна упростить создание и поддержку сайтов. Предположительно это веб-интерфейс с админкой, где можно будет собрать сайт с помощью Eleventy, выкачать статику, опубликовать на CDN и управлять контентом;
- Появятся шаблоны для блога, портфолио и персонального сайта для быстрого старта, а также плагины для использования иконок Font Awesome и веб-компонентов Web Awesome;
- Появятся платные PRO функции: коллаборативное редактирование, платформа для превью и публикации, премиум-шаблоны, DevTools для проверки качества сайтов, инструменты миграции, премиум поддержка и, конечно же, ИИ функции.
Также в зависимости от суммы сборов заявлены дополнительные функции:
- Система темизации
- Плагин для SEO/Favicon/OpenGraph
- Инструменты для новостной рассылки
- Интеграция со Stripe
- Инструменты синдикации для социальных сетей
- Аналитика
- Формы
- Плагин для шрифтов
- PWA с поддержкой local-first/offline-first
- Медиа-библиотека аудио/видео
Чтож, посмотрим, что из этого выйдет. Для любителей Eleventy это, возможно, тревожный звоночек. С другой стороны, это обеспечит проекту стабильность и долговечность, авторам тоже нужно кормить себя и семью.
Ядро Web Awesome и основные компоненты остались доступными в виде проекта с открытым исходным кодом при ребрендинге Shoelace. Аналогично должно быть с Eleventy. PRO функции не обязательны, а ядро сохраняется как есть.
#tools
Kickstarter
Build Awesome (Campaign Paused) (Canceled)
We're relaunching soon. Visit our new prelaunch page to get notified.
1🤔2❤1💩1🌚1💯1👨💻1
Применение новых функций веб-платформы с учётом кроссбраузерности
Вот читаете вы этот канал, другие каналы, статьи в блогах, смотрите видео или посещаете конференции, где рассказывают о новых функциях веб-платформы. Всё хорошо, но вы опасаетесь их применять из-за браузерной поддержки.
Знакомо? Это как мем наоборот: пока кто-то живёт в 2026, мы живём в 2018. То есть используем только стабильные функции которые давно и широко поддерживаются. Как тогда использовать все эти классные новые функции?
Прежде всего — проверьте аналитику. Возможно, вы трансформируете код, поставляете полифилы и отказываетесь от современных возможностей ради поддержки версий браузеров, с которых к вам никто не заходит.
Если доля пользователей старых версий существенна, это не повод отказываться от того, что у них не поддерживается. Отсутствие некоторых функций ни на что не повлияет, а для других можно предоставить резервные варианты.
View Transition можно добавить прямо сейчас. Если браузер не умеет, функция будет пропущена (при наличии проверки) и ничего не сломается, просто не будет плавного перехода. Многие функции можно использовать по такому принципу.
В некоторых случаях можно предоставлять упрощённые резервные варианты. Если браузер не поддерживает конический градиент, можно перед ним указать линейный градиент, обычный сплошной цвет или изображение с градиентом.
Директива
Для более сложных функций, как Popover API, Anchor Positioning или Invoker Commands, требуются полифилы. Нужно решить, готовы ли вы на частичное замещение функции у той части аудитории, у которой она не доступна.
Если функция отсутствует у 5-10% пользователей, кажется, что уместно добавить для них полифил и оставить 90-95% пользователей со встроенной функцией. Если соотношение другое, возможно, стоит повременить.
С оценкой зрелости функций поможет Baseline. У каждой функции есть один из трёх статусов:
- Limited Availabity — функция внедрена в один или несколько основных браузеров, но не во все;
- Newly Available — функция внедрена во все основные браузеры относительно недавно, поддержка узкая;
- Widely Available — функция внедрена во все основные браузеры как минимум 2.5 года и широко поддерживается.
Функции в статусе Widely Available можно использовать на проектах и они будут доступны для подавляющего большинства пользователей. 2.5 года это время, которого достаточно для широкого распространения функций.
Современные браузеры обновляются гораздо чаще, чем раньше. Причём делают это автоматически. Если у пользователя не старый iPhone или iPad, для которых не выпускаются обновления, то у него будет самая свежая версия браузера.
Техники прогрессивного улучшения и обнаружения функций всё ещё хорошо работают. Предоставьте хороший и современный опыт большинству и приемлемый опыт пользователям более старых версий браузеров.
#html #css #js #web_api
Вот читаете вы этот канал, другие каналы, статьи в блогах, смотрите видео или посещаете конференции, где рассказывают о новых функциях веб-платформы. Всё хорошо, но вы опасаетесь их применять из-за браузерной поддержки.
Знакомо? Это как мем наоборот: пока кто-то живёт в 2026, мы живём в 2018. То есть используем только стабильные функции которые давно и широко поддерживаются. Как тогда использовать все эти классные новые функции?
Прежде всего — проверьте аналитику. Возможно, вы трансформируете код, поставляете полифилы и отказываетесь от современных возможностей ради поддержки версий браузеров, с которых к вам никто не заходит.
Если доля пользователей старых версий существенна, это не повод отказываться от того, что у них не поддерживается. Отсутствие некоторых функций ни на что не повлияет, а для других можно предоставить резервные варианты.
View Transition можно добавить прямо сейчас. Если браузер не умеет, функция будет пропущена (при наличии проверки) и ничего не сломается, просто не будет плавного перехода. Многие функции можно использовать по такому принципу.
В некоторых случаях можно предоставлять упрощённые резервные варианты. Если браузер не поддерживает конический градиент, можно перед ним указать линейный градиент, обычный сплошной цвет или изображение с градиентом.
.section {
background-image:
url('section-gradient.jpg')
;
background-image:
conic-gradient(/*...*/)
;
}Директива
@supports и JS API CSS.supports() позволяют проверять поддержку новых функций CSS и реализовывать резервные. Аналогично в JS можно проверить наличие того или иного свойства, метода или объекта.Для более сложных функций, как Popover API, Anchor Positioning или Invoker Commands, требуются полифилы. Нужно решить, готовы ли вы на частичное замещение функции у той части аудитории, у которой она не доступна.
Если функция отсутствует у 5-10% пользователей, кажется, что уместно добавить для них полифил и оставить 90-95% пользователей со встроенной функцией. Если соотношение другое, возможно, стоит повременить.
С оценкой зрелости функций поможет Baseline. У каждой функции есть один из трёх статусов:
- Limited Availabity — функция внедрена в один или несколько основных браузеров, но не во все;
- Newly Available — функция внедрена во все основные браузеры относительно недавно, поддержка узкая;
- Widely Available — функция внедрена во все основные браузеры как минимум 2.5 года и широко поддерживается.
Функции в статусе Widely Available можно использовать на проектах и они будут доступны для подавляющего большинства пользователей. 2.5 года это время, которого достаточно для широкого распространения функций.
Современные браузеры обновляются гораздо чаще, чем раньше. Причём делают это автоматически. Если у пользователя не старый iPhone или iPad, для которых не выпускаются обновления, то у него будет самая свежая версия браузера.
Техники прогрессивного улучшения и обнаружения функций всё ещё хорошо работают. Предоставьте хороший и современный опыт большинству и приемлемый опыт пользователям более старых версий браузеров.
#html #css #js #web_api
👍6❤2🌚1🤝1
Бюджеты ресурсов для разработки производительных сайтов на 2026 год
Размер загружаемых на страницы ресурсов из года в год растёт, догоняя рост качества и скорости сети, а также производительности устройств. Лишние килобайты ресурсов точно не способствуют быстроте сайта.
Если цель — разработать сайт, который будет быстро загружаться у реальных пользователей, а не у разработчиков, менеджеров и тестировщиков с топовыми конфигурациями, стоит подумать о бюджетах производительности.
Алекс Рассел анализирует общее состояние производительности в вебе и предлагает бюджеты производительности. Они должны рассчитываться исходя из данных по конкретному проекту, но Алекс предлагает хорошую стартовую точку.
Тестовые устройства, которые отражают 75й перцентиль пользовательского опыта:
Смартфоны:
- Samsung Galaxy A24 4G
- Samsung Galaxy A16 4G
- Samsung Galaxy A07 4G
- Xiaomi Redmi Note 13 Pro 4G
- Samsung Galaxy A51
- Motorola Moto E15
Ноутбуки:
- HP 14 dq3500nr
- Или аналоги в пределах 250$, на процессоре Celeron, c модулем eMMC и работающий на WIndows.
(сравните их характеристики со своим рабочим сетапом)
Тестовые параметры сети:
- Входящая скорость 9 Мбит/с;
- Исходящая скорость 3 Мбит/с;
- Задержка (RTT) 100 мс.
Эти устройства и параметры сети — базовый уровень, на который стоит ориентироваться при анализе производительности. Бюджеты рассчитаны на загрузку страницы за 3 и 5 секунд. Это приемлемые показатели для данных условий.
Из-за разных подходов к разработке выделяются две категории сайтов для формирования бюджетов:
- Сайты с «лёгким JS», где JS составляет 15% от объёма всех ресурсов на критическом пути. Это традиционные MPA с точечным JS для прогрессивного улучшения или подходы с островной архитектурой;
- Сайты с «тяжёлым JS», где JS составляет 50% от объёма всех ресурсов на критическом пути. Это соответствует подходам SPA и SSR с гидратацией на стороне клиента.
Ресурсы критического пути — это те, которые необходимы для первоначальной отрисовки и функционирования страницы. Сюда не входят лениво-загружаемые и отложенные ресурсы. Бюджеты отражают именно ресурсы критического пути.
Разделение ресурсов на JS и всё остальное обусловлено тем, что JS — самый тяжёлый с точки зрения производительности ресурс. 500 кб JS не то же самое, что 500 кб изображения. «Остальное» включает в себя HTML, CSS, изображения и данные.
Теперь, когда все пояснения даны, собственно, сами бюджеты:
Загрузка за 3 секунды сайтов с «лёгким JS»:
- 0.3 мб JS
- 1.7 мб остальные ресурсы
- 2.0 мб всего ресурсов
Загрузка за 3 секунды сайтов с «тяжёлым JS»:
- 0.62 мб JS
- 0.62 мб остальные ресурсы
- 1.2 мб всего ресурсов
Загрузка за 5 секунд сайтов с «лёгким JS»:
- 0.57 мб JS
- 3.2 мб остальные ресурсы
- 3.7 мб всего ресурсов
Загрузка за 5 секунд сайтов с «тяжёлым JS»:
- 1.15 мб JS
- 1.15 мб остальные ресурсы
- 2.3 мб всего ресурсов
Как трактовать эти бюджеты? Если речь о сайте электронной коммерции (отрисовка HTML на сервере, точечный прогрессивный JS для интерактивности, категория «лёгкий JS»), то для загрузки за 3 секунды на целевом устройстве и сети нужно:
- чтобы HTML страницы, блокирующие стили и LCP-изображение в сумме составляли 1.7 мб;
- чтобы весь блокирующий JS (не
- чтобы общий вес этих ресурсов не превышал 2 мб.
Если речь о SPA, то для загрузки за 3 секунды нужно:
- чтобы начальный HTML, стили и LCP-изображение в сумме составляли 0.62 мб;
- чтобы весь JS приложения с данными в сумме составляли 0.62 мб.
- чтобы общий вес ресурсов не превышал 1.2 мб.
Это более чем реально, но требует определённой дисциплины в команде и контроля размера ресурсов и укладывания в бюджеты. С контролем помогут инструменты, а дисциплину и инженерную культуру нужно развивать.
#js #performance
Размер загружаемых на страницы ресурсов из года в год растёт, догоняя рост качества и скорости сети, а также производительности устройств. Лишние килобайты ресурсов точно не способствуют быстроте сайта.
Если цель — разработать сайт, который будет быстро загружаться у реальных пользователей, а не у разработчиков, менеджеров и тестировщиков с топовыми конфигурациями, стоит подумать о бюджетах производительности.
Алекс Рассел анализирует общее состояние производительности в вебе и предлагает бюджеты производительности. Они должны рассчитываться исходя из данных по конкретному проекту, но Алекс предлагает хорошую стартовую точку.
Тестовые устройства, которые отражают 75й перцентиль пользовательского опыта:
Смартфоны:
- Samsung Galaxy A24 4G
- Samsung Galaxy A16 4G
- Samsung Galaxy A07 4G
- Xiaomi Redmi Note 13 Pro 4G
- Samsung Galaxy A51
- Motorola Moto E15
Ноутбуки:
- HP 14 dq3500nr
- Или аналоги в пределах 250$, на процессоре Celeron, c модулем eMMC и работающий на WIndows.
(сравните их характеристики со своим рабочим сетапом)
Тестовые параметры сети:
- Входящая скорость 9 Мбит/с;
- Исходящая скорость 3 Мбит/с;
- Задержка (RTT) 100 мс.
Эти устройства и параметры сети — базовый уровень, на который стоит ориентироваться при анализе производительности. Бюджеты рассчитаны на загрузку страницы за 3 и 5 секунд. Это приемлемые показатели для данных условий.
Из-за разных подходов к разработке выделяются две категории сайтов для формирования бюджетов:
- Сайты с «лёгким JS», где JS составляет 15% от объёма всех ресурсов на критическом пути. Это традиционные MPA с точечным JS для прогрессивного улучшения или подходы с островной архитектурой;
- Сайты с «тяжёлым JS», где JS составляет 50% от объёма всех ресурсов на критическом пути. Это соответствует подходам SPA и SSR с гидратацией на стороне клиента.
Ресурсы критического пути — это те, которые необходимы для первоначальной отрисовки и функционирования страницы. Сюда не входят лениво-загружаемые и отложенные ресурсы. Бюджеты отражают именно ресурсы критического пути.
Разделение ресурсов на JS и всё остальное обусловлено тем, что JS — самый тяжёлый с точки зрения производительности ресурс. 500 кб JS не то же самое, что 500 кб изображения. «Остальное» включает в себя HTML, CSS, изображения и данные.
Теперь, когда все пояснения даны, собственно, сами бюджеты:
Загрузка за 3 секунды сайтов с «лёгким JS»:
- 0.3 мб JS
- 1.7 мб остальные ресурсы
- 2.0 мб всего ресурсов
Загрузка за 3 секунды сайтов с «тяжёлым JS»:
- 0.62 мб JS
- 0.62 мб остальные ресурсы
- 1.2 мб всего ресурсов
Загрузка за 5 секунд сайтов с «лёгким JS»:
- 0.57 мб JS
- 3.2 мб остальные ресурсы
- 3.7 мб всего ресурсов
Загрузка за 5 секунд сайтов с «тяжёлым JS»:
- 1.15 мб JS
- 1.15 мб остальные ресурсы
- 2.3 мб всего ресурсов
Как трактовать эти бюджеты? Если речь о сайте электронной коммерции (отрисовка HTML на сервере, точечный прогрессивный JS для интерактивности, категория «лёгкий JS»), то для загрузки за 3 секунды на целевом устройстве и сети нужно:
- чтобы HTML страницы, блокирующие стили и LCP-изображение в сумме составляли 1.7 мб;
- чтобы весь блокирующий JS (не
defer или async) в сумме составлял 0.3 мб.- чтобы общий вес этих ресурсов не превышал 2 мб.
Если речь о SPA, то для загрузки за 3 секунды нужно:
- чтобы начальный HTML, стили и LCP-изображение в сумме составляли 0.62 мб;
- чтобы весь JS приложения с данными в сумме составляли 0.62 мб.
- чтобы общий вес ресурсов не превышал 1.2 мб.
Это более чем реально, но требует определённой дисциплины в команде и контроля размера ресурсов и укладывания в бюджеты. С контролем помогут инструменты, а дисциплину и инженерную культуру нужно развивать.
#js #performance
1👍5❤3🔥1🥰1🌚1
Abusing Customizable Select
Патрик Броссет экспериментирует с новым стилизуемым <select> и некоторыми другими возможностями CSS. В статье он показывает 3 демо и описывает шаги и методы их реализации:
- Изогнутое меню выбора директории;
- Выбор карты из колоды в виде веера;
- Круговой выбор эмодзи.
Для каждого демо есть видео (на случай если браузер не поддерживает стилизуемый
#html #css #ui
Патрик Броссет экспериментирует с новым стилизуемым <select> и некоторыми другими возможностями CSS. В статье он показывает 3 демо и описывает шаги и методы их реализации:
- Изогнутое меню выбора директории;
- Выбор карты из колоды в виде веера;
- Круговой выбор эмодзи.
Для каждого демо есть видео (на случай если браузер не поддерживает стилизуемый
<select>), а также Codepen с полной реализацией. Все демо по своему интересные и демонстрируют возможности нового API.#html #css #ui
CSS-Tricks
Abusing Customizable Selects | CSS-Tricks
Let’s go over a few demos using the new customizable <select> feature that may be wild, but also give us a great chance to learn new things in CSS.
👍9❤2🔥1🌚1🤗1
Проблемы карточек, обёрнутых в ссылки
Карточки, обёрнутые в
Первая проблема заключается в доступности. Весь текстовый контент внутри ссылки будет использован для генерации имени для вспомогательных технологий. Для карточки из примера выше итоговым именем будет:
Слишком длинное и избыточное имя. Оно будет озвучиваться программами чтения с экрана, отображаться в списке ссылок и на дисплее Брайля, его нужно будет частично или полностью произнести при голосовом управлении интерфейсом.
Быстро исправить проблему можно путём добавления на ссылку атрибута
При использовании
Вторая проблема — дополнительные интерактивные элементы в карточке: тэги, кнопки галереи, переключатели параметров, кнопка добавления в корзину и так далее. Предположим, что в примере бренд — это ссылка на другую страницу:
Тогда получается интерактивный элемент внутри интерактивного элемента, что недопустимо по стандарту. Это приводит к неожиданному для пользователя поведению при нажатии и ухудшает опыт взаимодействия с карточкой.
Исправить первую и вторую проблемы, соблюдая при этом первое правило ARIA, можно перенеся ссылку внутрь заголовка и растянув её псевдо-элемент на всю карточку. Нажатие по псевдо-элементу делегирует нажатие на элемент.
Чтобы псевдо-элемент не перекрывал другие интерактивные элементы карточки, препятствуя взаимодействию с помощью мыши и касаний, их нужно приподнять на слой выше. Также побочный эффект — невозможность выделения текста.
Получается несколько более сложная реализация карточки за счёт псевдо-элемента и слоёв. Зато она более надёжная с точки зрения новых интерактивных элементов и удобная с точки зрения вспомогательных технологий.
Более подробно эту технику описывает Хейдон Пикеринг в онлайн книге Inclusive Components. Также тему проблем с карточками затрагивает Адриан Розелли в своей статье Block Links, Cards, Clickable Regions, Rows, Etc.
В будущем, надеюсь, решать эту задачу можно будет с помощью Link Area Delegation API, разрабатываемого в рамках OpenUI. Это механизм делегации кликов с других элементов на ссылку, навеянный проблемами с карточками.
#html #css #a11y
Карточки, обёрнутые в
<a>, достаточно распространённый шаблон в Интернете. Дизайнеры хотят, чтобы нажатие в любое место карточки вызывало переход на соответствующую страницу. Самый простой способ — обернуть всё в <a>.<a href="...">
<article class="card">
<img
src="..."
class="card__image"
alt="Чёрные беспроводные наушники Sony WH‑1000XM5, вид сбоку."
>
<h2 class="card__title">
Беспроводные наушники Sony WH‑1000XM5 (Чёрные)
</h2>
<p class="card__vendor">
Sony
</p>
<p class="card__price">
299 $
</p>
</article>
</a>
Первая проблема заключается в доступности. Весь текстовый контент внутри ссылки будет использован для генерации имени для вспомогательных технологий. Для карточки из примера выше итоговым именем будет:
Чёрные беспроводные наушники Sony WH‑1000XM5, вид сбоку. Беспроводные наушники Sony WH‑1000XM5 (Чёрные) Sony 299 $
Слишком длинное и избыточное имя. Оно будет озвучиваться программами чтения с экрана, отображаться в списке ссылок и на дисплее Брайля, его нужно будет частично или полностью произнести при голосовом управлении интерфейсом.
Быстро исправить проблему можно путём добавления на ссылку атрибута
aria-label или aria-labelledby с привязкой к заголовку. Оба способа переопределяют имя, генерируемое из тестового контента. Всё содержимое не будет зачитываться.<a
href="..."
aria-label="Беспроводные наушники Sony WH‑1000XM5 (Чёрные)"
>
<!-- ... -->
</a>
<a
href="..."
aria-labelledby="heading"
>
<!-- ... -->
<h2
id="heading"
class="card__title"
>
Беспроводные наушники Sony WH‑1000XM5 (Чёрные)
</h2>
<!-- ... -->
</a>
При использовании
aria-label важно, чтобы текст в атрибуте совпадал с видимыми текстом заголовка согласно критерию 2.5.3 Label in Name. С aria-labelledby это гарантируется за счёт связи с заголовком.Вторая проблема — дополнительные интерактивные элементы в карточке: тэги, кнопки галереи, переключатели параметров, кнопка добавления в корзину и так далее. Предположим, что в примере бренд — это ссылка на другую страницу:
<a href="...">
<!-- ... -->
<a
href="..."
class="card__vendor"
>
Sony
</a>
<!-- ... -->
</a>
Тогда получается интерактивный элемент внутри интерактивного элемента, что недопустимо по стандарту. Это приводит к неожиданному для пользователя поведению при нажатии и ухудшает опыт взаимодействия с карточкой.
Исправить первую и вторую проблемы, соблюдая при этом первое правило ARIA, можно перенеся ссылку внутрь заголовка и растянув её псевдо-элемент на всю карточку. Нажатие по псевдо-элементу делегирует нажатие на элемент.
<article class="card">
<!-- ... -->
<h2 class="card__title">
<a
href="..."
clasd="card__link"
>
Беспроводные наушники Sony WH‑1000XM5 (Чёрные)
</a>
</h2>
<a
href="..."
class="card__vendor"
>
Sony
</a>
<!-- ... -->
</article>
.card {
position: relative;
}
.card__link::before {
content: '';
position: absolute;
inset: 0;
z-index: 1;
}
.card__vendor {
position: relative;
z-index: 2;
}Чтобы псевдо-элемент не перекрывал другие интерактивные элементы карточки, препятствуя взаимодействию с помощью мыши и касаний, их нужно приподнять на слой выше. Также побочный эффект — невозможность выделения текста.
Получается несколько более сложная реализация карточки за счёт псевдо-элемента и слоёв. Зато она более надёжная с точки зрения новых интерактивных элементов и удобная с точки зрения вспомогательных технологий.
Более подробно эту технику описывает Хейдон Пикеринг в онлайн книге Inclusive Components. Также тему проблем с карточками затрагивает Адриан Розелли в своей статье Block Links, Cards, Clickable Regions, Rows, Etc.
В будущем, надеюсь, решать эту задачу можно будет с помощью Link Area Delegation API, разрабатываемого в рамках OpenUI. Это механизм делегации кликов с других элементов на ссылку, навеянный проблемами с карточками.
#html #css #a11y
Inclusive Components
Cards
Some of the components I've explored here have specific standardized requirements in order to work as expected. Tab interfaces, for example, have a prescribed structure and a set of interaction behaviors as mandated by the WAI-ARIA specification. It's at…
2❤22👍2🌚1💯1🆒1
CSS Web Components
Будучи набором браузерных API, веб-компоненты можно использовать самыми разными способами. Некоторые из таких способов применения получили в сообществе собственные названия. Один из них — CSS Web Components.
Суть подхода в том, чтобы взять от Custom Elements API минимум возможностей: никаких Shadow DOM, классов, методов жизненного цикла и JS в целом. Только пользовательские HTML-элементы с атрибутами в разметке и стили.
Любой несуществующий в HTML элемент обрабатывается браузером как
Но
Кроме того, у пользовательских элементов можно задавать произвольные атрибуты без префикса
У пользовательских элементов по умолчанию встроенная роль
Всё это в совокупности можно использовать как альтернативу
Эти элементы стилизуются по имени, или классу, если так привычнее. Атрибуты используются для стилизации различных состояний. С обновлённой функцией
Примеров можно придумать много. Кто-то делится своими идеями и практическими примерами. А кто-то даже свою CSS-методологию построил на основе CSS Web Components. Всё это в целом выглядит непривычно, но интересно.
- You can make up HTML tags
- Replace Divs With Custom Elements For Superior Markup
- Custom Element Examples (Without Javascript)
- Responsive Columns: Build Amazing Layouts With Custom HTML Tags
- TAC: A new CSS methodology
- 3 Examples of the TAC Methodology In Action
Таким образом CSS Web Components — это подход к вёрстке, когда
#html #css
Будучи набором браузерных API, веб-компоненты можно использовать самыми разными способами. Некоторые из таких способов применения получили в сообществе собственные названия. Один из них — CSS Web Components.
Суть подхода в том, чтобы взять от Custom Elements API минимум возможностей: никаких Shadow DOM, классов, методов жизненного цикла и JS в целом. Только пользовательские HTML-элементы с атрибутами в разметке и стили.
Любой несуществующий в HTML элемент обрабатывается браузером как
HTMLUnknownElement. Он отображается, попадает в DOM со всеми дочерними узлами, доступен через DOM API, стилизуется с помощью CSS.Но
HTMLUnknownElement не валиден с точки зрения стандарта. Валидатор покажет ошибку и проигнорирует неизвестный элемент и всё его поддерево. В свою очередь пользовательские элементы — это валидные экземпляры HTMLElement.<!--
HTMLUnknownElement, не валиден
-->
<sidebarpanel>
<!-- ... -->
</sidebarpanel>
<!--
HTMLElement, валиден
-->
<sidebar-panel>
<!-- ... -->
</sidebar-panel>
Кроме того, у пользовательских элементов можно задавать произвольные атрибуты без префикса
data-*, если они не пересекаются со стандартными глобальными атрибутами, например id, class, translate, lang и так далее.У пользовательских элементов по умолчанию встроенная роль
generic, как у <div> или <span>. Поэтому они сами по себе ничего не значат и не ломают семантику. Если нужно, можно указать глобальные aria-* атрибуты и role.Всё это в совокупности можно использовать как альтернативу
<div> и <span> для более осмысленного именования контейнеров, обёрток и компонентов, для которых нет подходящих семантических HTML-элементов.<!--
Контейнер для ограничения
ширины контента и центровки
-->
<page-container width="lg">
<!-- ... -->
</page-container>
<!--
Контейнер для отображения
изображений в виде сетки
-->
<image-grid col="3" gap="sm">
<!-- ... -->
</image-grid>
<!--
Компонент бейджа
-->
<ui-badge variant="primary">
<!-- ... -->
</ui-badge>
Эти элементы стилизуются по имени, или классу, если так привычнее. Атрибуты используются для стилизации различных состояний. С обновлённой функцией
attr() из атрибутов можно извлекать значения и подставлять в свойства.Примеров можно придумать много. Кто-то делится своими идеями и практическими примерами. А кто-то даже свою CSS-методологию построил на основе CSS Web Components. Всё это в целом выглядит непривычно, но интересно.
- You can make up HTML tags
- Replace Divs With Custom Elements For Superior Markup
- Custom Element Examples (Without Javascript)
- Responsive Columns: Build Amazing Layouts With Custom HTML Tags
- TAC: A new CSS methodology
- 3 Examples of the TAC Methodology In Action
Таким образом CSS Web Components — это подход к вёрстке, когда
<div> и <span> заменяются на незарегистрированные (без определения в JS) пользовательские элементы с осмысленными названиями, атрибутами и соответствующими стилями.#html #css
Hawkticehurst
CSS Web Components for marketing sites
The truly No JavaScript web component.
👍6🔥6❤1🤩1🌚1🙈1
Диалог, форма, список выбора и нарушения WCAG
При аудите столкнулся с компонентом переключения страны/валюты в магазине. Он использует кнопку раскрытия диалога, в котором находится форма со списком выбора (listbox), внутри которого опции в виде кнопок отправки формы:
Достаточно маленький компонент, а нарушает сразу несколько критериев WCAG:
- 2.1.1 Keyboard — в списке выбора не реализована соответствующая механика клавиатурного взаимодействия. Фокус должен попадать на
- 2.5.3 Label in Name — имя кнопки раскрытия диалога не совпадает с видимой подписью. Видимая подпись — «United States (USD $)», а программно заданное с помощью атрибута
- 3.2.2 On Input — выбор опции из списка приводит к отправке формы и смене контекста. Из-за реализации опций на основе кнопок с атрибутом
- 3.3.2 Labels or Instructions — нет видимой подписи у кнопки раскрытия диалога и поля выбора. В первом случае подпись задана через
- 4.1.2 Name, Role, Value — у поля выбора нет ассоциированного имени. У
Правильнее было бы не делать внутри формы список выбора и оставить обычный список кнопок отправки формы. Такие кнопки явно дают понять, что ожидается смена контекста. Исправленная версия разметки:
Убран
На мой взгляд такая реализация работает лучше, не требует более сложного виджета списка выбора со своей механикой клавиатурного взаимодействия, более понятна пользователям и не нарушает упомянутые критерии WCAG.
#html #ui #a11y
При аудите столкнулся с компонентом переключения страны/валюты в магазине. Он использует кнопку раскрытия диалога, в котором находится форма со списком выбора (listbox), внутри которого опции в виде кнопок отправки формы:
<button
type="button"
aria-expanded="false"
aria-haspopup="dialog"
aria-controls="popover-id"
aria-label="Change country or currency"
>
United States (USD $)
</button>
<x-popover
role="dialog"
id="popover-id"
>
<form action="...">
<!-- скрытые поля -->
<x-listbox
role="listbox"
aria-activedescendant="option-us"
>
<!-- другие опции -->
<button
type="submit"
id="option-gb"
name="country_code"
value="GB"
role="option"
>
United Kingdom (GBP £)
</button>
<button
type="submit"
id="option-us"
name="country_code"
value="US"
role="option"
aria-selected="true"
>
United States (USD $)
</button>
<!-- другие опции -->
</x-listbox>
</form>
</x-popover>
Достаточно маленький компонент, а нарушает сразу несколько критериев WCAG:
- 2.1.1 Keyboard — в списке выбора не реализована соответствующая механика клавиатурного взаимодействия. Фокус должен попадать на
listbox, опции переключаться стрелками/Home/End с фокусом через aria-activedescendant;- 2.5.3 Label in Name — имя кнопки раскрытия диалога не совпадает с видимой подписью. Видимая подпись — «United States (USD $)», а программно заданное с помощью атрибута
aria-label имя — «Change country or currency»;- 3.2.2 On Input — выбор опции из списка приводит к отправке формы и смене контекста. Из-за реализации опций на основе кнопок с атрибутом
type="submit" при нажатии происходит отправка формы с перезагрузкой страницы;- 3.3.2 Labels or Instructions — нет видимой подписи у кнопки раскрытия диалога и поля выбора. В первом случае подпись задана через
aria-label, а во втором случае не задана ни видимым элементом, ни программно;- 4.1.2 Name, Role, Value — у поля выбора нет ассоциированного имени. У
listbox должно быть имя, которое задано одним из способов. В данном случае имя не задано ни одним из способов.Правильнее было бы не делать внутри формы список выбора и оставить обычный список кнопок отправки формы. Такие кнопки явно дают понять, что ожидается смена контекста. Исправленная версия разметки:
<p id="label-id">
Change country or currency
</p>
<button
type="button"
id="button-id"
aria-expanded="false"
aria-haspopup="dialog"
aria-controls="popover-id"
aria-labelledby="label-id button-id"
>
United States (USD $)
</button>
<x-popover
role="dialog"
id="popover-id"
>
<form action="...">
<!-- скрытые поля -->
<ul>
<!-- другие кнопки -->
<li>
<button
type="submit"
name="country_code"
value="GB"
>
United Kingdom (GBP £)
</button>
</li>
<li>
<button
type="submit"
name="country_code"
value="US"
aria-current="true"
autofocus
>
United States (USD $)
</button>
</li>
<!-- другие кнопки -->
</ul>
</form>
</x-popover>
Убран
listbox, кнопка раскрытия получила видимую подпись и составное имя с помощью aria-labelledby, добавлен обычный список, убрана семантика option и состояние aria-selected, вместо него теперь aria-current, добавлен autofocus для установки фокуса при открытии диалога.На мой взгляд такая реализация работает лучше, не требует более сложного виджета списка выбора со своей механикой клавиатурного взаимодействия, более понятна пользователям и не нарушает упомянутые критерии WCAG.
#html #ui #a11y
❤8👍5🤩2🌚1
Крис Койер про область видимости в CSS
Посмотрел запись доклада Криса Койера про область видимости в CSS с прошедшей в июне 2025 года конференции CSS Day. Другие доклады доступны в плейлисте на YouTube-канале конференции. Теперь пару мыслей о докладе.
Крис начал с того, что написание CSS, по сути, сводится к определению области видимости и заданию стилей для этой области. Селекторы — это область видимости. Вроде базовая мысль, но я с такой точки зрения об этом не думал.
Далее затрагиваются инструменты для автоматического ограничения области видимости через генерацию уникальных классов. Один из таких — CSS Modules. Интересно то, что это набор правил, а не конкретная реализация.
Прозвучала мысль, что если к синтаксису языка добавляется что-то нестандартное, то не стоит использовать расширение
Обычно CSS Modules импортируются с помощью псевдо-импортов, которые не работают в JS и обрабатываются сборщиком. Раз так, то расширение файла могло быть, например,
Вместо этого повсеместно используется конвенция
Далее упоминается подход CSS-in-JS, который помимо прочего обеспечивает область видимости через уникальные классы. По мнению Криса этот подход скорее CSS-in-React, поскольку тот не предлагает решения для стилей.
Это породило множество разных реализаций: styled-components, emotion, jss, и так далее. Они ничего особого не привнесли сообществу в глобальном смысле. Сегодня подход runtime CSS-in-JS пришёл в тупик и изжил себя.
Далее Tailwind, который обеспечивает область видимости иным способом. Не уникальными классами, а набором точечных классов, применяемых к конкретному элементу. Как и для Криса, это не для меня, но имеет место быть.
Отдельного говорится про хардкорные способы области видимости в веб-платформе:
В базовом виде
#css
Посмотрел запись доклада Криса Койера про область видимости в CSS с прошедшей в июне 2025 года конференции CSS Day. Другие доклады доступны в плейлисте на YouTube-канале конференции. Теперь пару мыслей о докладе.
Крис начал с того, что написание CSS, по сути, сводится к определению области видимости и заданию стилей для этой области. Селекторы — это область видимости. Вроде базовая мысль, но я с такой точки зрения об этом не думал.
Далее затрагиваются инструменты для автоматического ограничения области видимости через генерацию уникальных классов. Один из таких — CSS Modules. Интересно то, что это набор правил, а не конкретная реализация.
Прозвучала мысль, что если к синтаксису языка добавляется что-то нестандартное, то не стоит использовать расширение
.css и мимикрировать под обычный CSS. А CSS Modules добавляют нестандартные composes, :local и :global.Обычно CSS Modules импортируются с помощью псевдо-импортов, которые не работают в JS и обрабатываются сборщиком. Раз так, то расширение файла могло быть, например,
.mcss или .cssm, чтобы подчеркнуть отличие от CSS.Вместо этого повсеместно используется конвенция
.module.css в названии файла. Возможно, стандартное расширение .css выбрано ради совместимости с IDE, редакторами, песочницами и плагинами подсветки синтаксиса.Далее упоминается подход CSS-in-JS, который помимо прочего обеспечивает область видимости через уникальные классы. По мнению Криса этот подход скорее CSS-in-React, поскольку тот не предлагает решения для стилей.
Это породило множество разных реализаций: styled-components, emotion, jss, и так далее. Они ничего особого не привнесли сообществу в глобальном смысле. Сегодня подход runtime CSS-in-JS пришёл в тупик и изжил себя.
Далее Tailwind, который обеспечивает область видимости иным способом. Не уникальными классами, а набором точечных классов, применяемых к конкретному элементу. Как и для Криса, это не для меня, но имеет место быть.
Отдельного говорится про хардкорные способы области видимости в веб-платформе:
<iframe> (<object> сюда же) и Shadow DOM. Оба с нюансами и не очень удобные. В конце @scope — новый способ управления областью видимости.В базовом виде
@scope как-будто не имеет особого смысла. Того же эффекта можно добиться сейчас без него. Интересны более продвинутые техники, названные «Donut Scope», «DOM Blasters» и «Proximity». Это уже тема для отдельного поста.#css
YouTube
Scope in CSS - Chris Coyier - CSS Day 2025
I hate to be the one to tell you but writing CSS is half thinking about scope. You don’t always need them, but there are plenty of tools out there that help with scoping one way or another. They are worth considering as they help with a variety of problems…
👍10❤3🌚1😈1
«Donut Scope», «Proximity» и «DOM Blasters»
По горячим следам предыдущего поста про область видимости делаю продолжение. Речь пойдёт о директиве
В базовом виде
Стили
«Donut Scope» позволяет ограничить область видимости «сверху» и «снизу». Так можно создавать «дыры» внутри элементов, в которых селекторы не будут действовать. Полезно для ограничения глобальных стилей в сбросах.
«Proximity» — часть механизма каскада, которая вводит понятие близости элемента к области видимости. При прочих равных применяется тот селектор, который будет ближе к области видимости в DOM-дереве.
С обычной вложенностью ссылка внутри
«DOM Blasters» — так Крис Койер назвал применение
Ссылка внутри первого
Крис в своём докладе предложил смелую идею: а что, если все стили компонентов писать таким образом, используя
Крис провёл замеры на 10000 карточек со стилями в отдельном файле и в
Такой бенчмарк не отражает реальность. 10000 одинаковых карточек действительно хорошо сжимаются, а браузер может применять оптимизации. Нужно проверять на реальном проекте с разными стилями компонентов.
#css
По горячим следам предыдущего поста про область видимости делаю продолжение. Речь пойдёт о директиве
@scope в целом и подходах «Donut Scope», «Proximity» и «DOM Blasters» (названы так в докладе Криса Койера) в частности.@scope — новая директива CSS, доступная на данный момент во всех актуальных версиях основных браузеров (получила статус Baseline Newly Available в декабре 2025). Директива предназначена для управления областью видимости.В базовом виде
@scope создаёт область видимости, за пределы которой селекторы не действуют. Можно писать более простые селекторы и использовать селекторы типа без риска что-то задеть:@scope (.card) {
:scope {
...
}
.title {
...
}
button {
...
}
}Стили
.title и button применятся внутри .card и не повлияют на элементы вне .card. Селектор :scope позволяет стилизовать корневой элемент области видимости, в данном случае .card. В таком виде это не особо полезно и возможно без @scope:/* Вложенность даёт тот же эффект */
.card {
...
.title {
...
}
button {
...
}
}
«Donut Scope» позволяет ограничить область видимости «сверху» и «снизу». Так можно создавать «дыры» внутри элементов, в которых селекторы не будут действовать. Полезно для ограничения глобальных стилей в сбросах.
<style>
@scope (body) to (.content) {
a {
text-decoration: none;
}
}
</style>
<nav>
<a href="...">
Ссылка в меню без подчёркивания
</a>
...
<nav>
<div class="content">
...
<a href="...">
Ссылка в тексте с подчёркиванием
</a>
...
</div>
«Proximity» — часть механизма каскада, которая вводит понятие близости элемента к области видимости. При прочих равных применяется тот селектор, который будет ближе к области видимости в DOM-дереве.
<style>
@scope (.light-theme) {
a {
color: blue;
}
}
@scope (.dark-theme) {
a {
color: lightblue;
}
}
</style>
<div class="dark-theme">
<a href="...">Ссылка</a>
<div class="light-theme">
<a href="...">Ссылка</a>
</div>
</div>
С обычной вложенностью ссылка внутри
light-theme будет светло-синей, потому что применяется последнее по порядку правило. Со @scope ссылка внутри light-theme будет синей, потому что это правило ближе к области видимости light-theme.«DOM Blasters» — так Крис Койер назвал применение
@scope внутри <style> без указания селектора в скобках. Это режим, в котором ближайший родительский для <style> элемент используется как область видимости.<div>
<a href="...">Ссылка</a>
</div>
<div>
<a href="...">Ссылка</a>
<style>
@scope {
a {
color: red;
}
}
</style>
</div>
Ссылка внутри первого
<div> будет отображаться как обычно. Ссылка во втором <div> будет красной. Потому что второй <div> — ближайший родительский элемент для <style>, и в этом <style> используется @scope.Крис в своём докладе предложил смелую идею: а что, если все стили компонентов писать таким образом, используя
<style> и @scope? Пока непонятны преимущества и недостатки такого подхода в реальных условиях, но это идея уровня «что, если».Крис провёл замеры на 10000 карточек со стилями в отдельном файле и в
<style> для каждой карточки. По количеству памяти и времени отрисовки разницы не было. Увеличился размер HTML, но gzip хорошо справился.Такой бенчмарк не отражает реальность. 10000 одинаковых карточек действительно хорошо сжимаются, а браузер может применять оптимизации. Нужно проверять на реальном проекте с разными стилями компонентов.
#css
10🔥7😱3❤1🌚1
Роли presentation, none, generic и aria-hidden
В ARIA есть несколько сущностей, которые могут вызвать путаницу:
- роль
- роль
- роль
- состояние
Роль
Разработчики не должны задавать атрибут
Роль
В примере виджет
Нет смысла использовать
Для элементов с важной семантикой использование
Роль
Итого:
-
-
-
#a11y
В ARIA есть несколько сущностей, которые могут вызвать путаницу:
- роль
generic;- роль
none;- роль
presentation;- состояние
aria-hidden="true". Роль
generic обозначает общие элементы-контейнеры, которые сами по себе ничего не обозначают. В HTML эта роль встроена в такие элементы, как <div>, <span> и некоторые другие. Она не предназначена для использования в коде.Разработчики не должны задавать атрибут
role="generic" каким-либо элементам. Это внутренняя роль для браузера. Вспомогательные технологии не реагируют на generic элементы и работают с их контентом.Роль
presentation обозначает элемент, который выполняет чисто декоративную функцию и не несёт смысла. presentation похожа на generic и делает элемент безымянным контейнером без смысловой нагрузки.presentation можно задавать элементам в отличие от generic. В ряде случаев нужно сбросить семантику, но сохранить элемент с контентом в дереве доступности. Случаи, в основном, связаны со структурой виджетов.<ul role="menubar">
<li role="presentation">
<button
type="button"
role="menuitem"
>
...
</button>
</li>
<li role="presentation">
<button
type="button"
role="menuitem"
>
...
</button>
</li>
...
</ul>
В примере виджет
menubar создан на основе <ul> и <li>. Семантика list переопределена на menubar. Семантика <li> остаётся listitem. Внутри menubar допустимы только элементы menuitem, поэтому listitem сбрасывается.Нет смысла использовать
presentation на элементах, которые по умолчанию не несут смысла: <div>, <span>, <section> без имени, <img alt="">, <a> без href, пользовательских элементах и других со встроенной ролью generic.Для элементов с важной семантикой использование
presentation сбрасывает эту семантику. <h1 role="presentation"> потеряет семантику heading и будет как <span>. Сбрасывать стандартную семантику не рекомендуется.Роль
none — это синоним presentation, то есть они работают одинаково. none была добавлена в ARIA 1.1, чтобы уменьшить путаницу. К тому же none короче по количеству символов и выглядит более понятно.aria-hidden — это состояние, которое можно применять к любым элементам. Значение true исключает элемент и все его дочерние узлы из дерева доступности, как-будто их нет. При этом визуально все элементы сохраняются.presentation и none удаляют семантику, элемент по-прежнему остаётся в дереве доступности как незначимый и семантика всех дочерних элементов сохраняется. aria-hidden="true" полностью исключает элемент из дерева доступности.Итого:
-
generic — незначимый контейнер, системная роль для браузера, нельзя использовать в коде;-
presentation и none — незначимый контейнер, можно использовать в коде, но, скорее всего, не пригодится;-
aria-hidden="true" — элемент и его поддерево скрыто от вспомогательных технологий, но отображается визуально.#a11y
👍14❤4🌚1👀1
Резервное значение font-family
Гарри Робертс написал статью о том, что резервное значение свойства
Обычно базовый шрифт задаётся через
Все заголовки должны отображаться как Playfair Display. Это пользовательский шрифт, который должен загрузиться и обработаться браузером. Это происходит не мгновенно, при отрисовке страницы шрифт может быть ещё не готов.
В этом случае браузер будет использовать резервный шрифт, что зависит от свойства
Кажется, что резервным шрифтом должен быть
В большинстве случаев это шрифт Times New Roman. Чтобы избежать его появления, стоит указать резервные варианты, если шрифт переопределяется. А лучше один раз задать пользовательские свойства и использовать их.
#css #performance
Гарри Робертс написал статью о том, что резервное значение свойства
font-family работает не так, как нам кажется. Поэтому при загрузке страницы на какое-то время текст может отображаться шрифтом Times New Roman, что ухудшает CLS.Обычно базовый шрифт задаётся через
:root, html или body для наследования вглубь дерева. Для заголовков часто шрифт переопределяется на акцентный из шрифтовой пары. Шрифт применяется к заголовкам с использованием селекторов типа или класса: h1, .h1, .heading.:root {
font-family: Inter, system-ui, sans-serif;
}
h1, h2, h3, h4, h5, h6, .heading {
font-family: 'Playfair Display';
}Все заголовки должны отображаться как Playfair Display. Это пользовательский шрифт, который должен загрузиться и обработаться браузером. Это происходит не мгновенно, при отрисовке страницы шрифт может быть ещё не готов.
В этом случае браузер будет использовать резервный шрифт, что зависит от свойства
font-display. Чаще всего используется значение swap, при котором резервный шрифт заменяется пользовательским как только тот загрузится.Кажется, что резервным шрифтом должен быть
system-ui и sans-serif. Но это не так. Резервный шрифт берётся из font-family самого заголовка. Указан только Playfair Display, поэтому резервным будет системный шрифт для этого элемента.В большинстве случаев это шрифт Times New Roman. Чтобы избежать его появления, стоит указать резервные варианты, если шрифт переопределяется. А лучше один раз задать пользовательские свойства и использовать их.
:root {
--font-base: Inter, system-ui, sans-serif;
--font-headings: 'Playfair Display', serif;
font-family: var(--font-base);
}
h1, h2, h3, h4, h5, h6, .heading {
font-family: var(--font-headings);
}#css #performance
Csswizardry
font-family Doesn’t Fall Back the Way You Think – CSS Wizardry
A quick but important reminder that font-family declarations don’t inherit fallback stacks the way many developers assume.
🔥10❤6💯3🌚1😎1
Нужен ли visually-hidden?
Шаблон
Этот шаблон часто применяется для добавления «скрытых» заголовков, подписей к полям и инструкций, которые нужны для улучшения доступности, но не предусмотрены дизайном. Дэвид Бушелл рассказывает историю этого шаблона.
Несмотря на распространённость и удобство, многие специалисты по доступности сходятся во мнении: это хак — следствие проблем при проектировании интерфейсов. Наличие в кодовой базе
Дизайнеры выступают за минимализм, экономию пространства и компактность. В итоге некоторые разделы остаются без заголовков, поля ввода — без подписей и инструкций, текстовые подписи кнопок заменяются иконками.
Опытные разработчики понимают, что это не правильно и добавляют визуально скрытые заголовки, подписи и инструкции, чтобы исправить проблемы без изменения внешнего вида. Хотя правильнее было бы исправить это на уровне дизайна.
В идеальной ситуации те элементы, что скрыты с помощью
#a11y
Шаблон
visually-hidden хорошо знаком специалистам по доступности. Это класс со свойствами для визуального сокрытия элемента с сохранением его в дереве доступности. Во многих библиотеках есть компонент <VisuallyHidden>.Этот шаблон часто применяется для добавления «скрытых» заголовков, подписей к полям и инструкций, которые нужны для улучшения доступности, но не предусмотрены дизайном. Дэвид Бушелл рассказывает историю этого шаблона.
Несмотря на распространённость и удобство, многие специалисты по доступности сходятся во мнении: это хак — следствие проблем при проектировании интерфейсов. Наличие в кодовой базе
visually-hidden считается антипаттерном.Дизайнеры выступают за минимализм, экономию пространства и компактность. В итоге некоторые разделы остаются без заголовков, поля ввода — без подписей и инструкций, текстовые подписи кнопок заменяются иконками.
Опытные разработчики понимают, что это не правильно и добавляют визуально скрытые заголовки, подписи и инструкции, чтобы исправить проблемы без изменения внешнего вида. Хотя правильнее было бы исправить это на уровне дизайна.
В идеальной ситуации те элементы, что скрыты с помощью
visually-hidden, должны отображаться визуально и не должны быть скрыты. Они будут занимать место и загромождать интерфейс, но это уже вопрос удобства против эстетики.#a11y
dbushell.com
Everything you never wanted to know about visually-hidden
The one where I attempt to answer a question
👍7🤔3❤2🌚2👾1
Разница между aria-selected, aria-checked, aria-current и aria-pressed
В стандарте ARIA есть состояния, которые можно применять к элементам. Среди них есть несколько довольно похожих на первый взгляд:
-
-
-
-
-
-
-
Применяется у следующих элементов:
-
-
-
-
-
-
-
-
-
Применяется к следующим элементам:
-
-
-
-
-
-
-
У
-
-
-
-
-
-
-
Назначение
-
-
-
-
Таким образом все четыре атрибута хоть и обозначают некоторое текущее состояние элемента, но отличаются по смыслу и применимости. Краткая сводка:
-
-
-
-
#a11y
В стандарте ARIA есть состояния, которые можно применять к элементам. Среди них есть несколько довольно похожих на первый взгляд:
-
aria-selected;-
aria-checked;-
aria-current;-
aria-pressed.aria-selected обозначает состояние выбора элемента внутри виджетов с одиночным или множественным выбором. Принимает значения:-
true — элемент выбран;-
false — элемент не выбран;-
undefined — элемент не выбираемый.Применяется у следующих элементов:
-
option в listbox;-
treeitem в tree;-
gridcell или row в grid;-
gridcell или row в treegrid;-
tab в tablist.aria-checked обозначает состояние отметки флажка, радио-кнопки, переключателя, отмечаемых пунктов меню или группы, состоящей из отмечаемых элементов со смешанным состоянием. Принимает значения:-
true — элемент отмечен;-
false — элемент не отмечен;-
mixed — элемент в смешанном (indeterminate) состоянии и связан с группой других отмечаемых элементов;-
undefined — элемент не отмечаемый.Применяется к следующим элементам:
-
checkbox;-
radio (кроме mixed);-
switch (кроме mixed);-
option в listbox;-
menuitemcheckbox в menu или menubar;-
menuitemradio в menu или menubar (кроме mixed);-
treeitem в tree.У
option и treeitem можно использовать aria-selected и aria-checked. Подойдёт любой из них. Но рекомендуется для одиночного выбора использовать aria-selected, а в случае множественного выбора использовать aria-checked.aria-current обозначает текущий элемент в контейнере или наборе связанных элементов. Может быть установлен на любой элемент, но принимает в качестве значения один из предустановленных токенов:-
page — элемент визуально выделен как текущая страница из набора страниц, подходит для активной ссылки в меню или хлебных крошках;-
step — элемент визуально выделен как текущий шаг в многошаговом процессе, подходит для визардов и многошаговых форм;-
location — элемент визуально выделен как текущее положение на странице, экране или процессе;-
date — элемент визуально выделен как текущая дата в виджете календаря или другом наборе дат;-
time — элемент визуально выделен как текущее время в таблице времени или другом наборе времени;-
true — элемент визуально выделен как текущий в наборе связанных элементов без дополнительного контекста;-
false — элемент не выделен визуально как текущий в наборе связанных элементов.Назначение
aria-current — программно передать визуальное выделение элемента. При этом только один элемент в наборе может быть отмечен как текущий и это нельзя использовать как альтернативу aria-selected и aria-checked.aria-pressed обозначает состояние кнопки-переключателя и используется только на кнопках и элементах с ролью button. Требует реализации механизма зацикленной смены значений при нажатии. Принимает следующие значения:-
true — кнопка нажата;-
false — кнопка не нажата;-
mixed — кнопка зависит от смешанного состояния нескольких других кнопок;-
undefined — кнопка не работает как переключатель.Таким образом все четыре атрибута хоть и обозначают некоторое текущее состояние элемента, но отличаются по смыслу и применимости. Краткая сводка:
-
aria-selected — состояние выбора для option, treeitem, gridcell, row и tab, рекомендуется для одиночного выбора в случае с option и treeitem;-
aria-checked — состояние отметки для checkbox, radio, switch, option, treeitem, menuitemradio и menuitemcheckbox, рекомендуется для множественного выбора в случае с option и treeitem, может быть в смешанном состоянии mixed у checkbox, option, treeitem и menuitemcheckbox;-
aria-current — визуально выделенный элемент в наборе связанных страниц, шагов, локаций, дат, времени или других элементов, один на весь набор;-
aria-pressed — состояние кнопки-переключателя, применимо только для кнопок, может быть в смешанном состоянии mixed.#a11y
👍12❤3🔥2🌚1👨💻1
Настоящий mobile-first
Mobile-first многим известен как подход к вёрстке, при котором сначала реализуется версия для мобильных и расширяется до десктопа. Это противоположность подходу desktop-first, при котором вёрстка идёт от десктопа к мобильным.
Разница обычно сводится к организации медиа-запросов и использованию
Принципиальной разницы нет. В теории, движение от малых экранов к большим даст в итоге чуть меньше стилей, потому что контент по умолчанию mobile-first, адаптируется под ширину экрана и идёт в потоке сверху вниз.
Однако сам термин mobile-first говорит о том, что подход ориентирован в первую очередь на мобильные устройства. Суть не в том, в какую сторону переопределяются стили, а в том, чтобы учесть ограничения мобильных устройств.
Ограничения исходят из конструктивных особенностей. Мобильные устройства компактные и переносимые, питаются от аккумулятора, работают в беспроводных сетях, управляются касаниями и жестами, вмещают меньше контента на экране.
Размеры и питание от аккумулятора не позволяют использовать более мощное железо и массивные системы охлаждения. Поэтому мобильные устройства уступают десктопам по производительности, что повышает требования к оптимизации.
Точки Wi-Fi и мобильные сети менее стабильны, чем проводные соединения. Скорость приёма/передачи, потери пакетов, задержки и другие характеристики сети на мобильных устройствах, как правило, уступают десктопам.
Не смотря на безлимитные тарифы, всё ещё достаточно распространены тарифы с лимитированным трафиком и скоростью. Покрытие сети вне крупных городов ухудшается. Может быть включен режим экономии трафика или батареи.
Отличаются сценарии взаимодействия. Нет мыши и курсора, виртуальная клавиатура на треть экрана заменяет физическую, управление осуществляется жестами, касаниями и долгими нажатиями вместо кликов и наведения указателя мыши.
Настоящий mobile-first — это разработка сайтов и веб-приложений с учётом всех особенностей и ограничений мобильных устройств. Он затрагивает архитектуру проекта и выбор инструментов разработки, а не только направление медиа-запросов.
В хорошем mobile-first:
- Элементы управления и контент компактно и логично размещены на ограниченном по размеру экране, интерфейс не перегружен;
- Интерактивные элементы достаточно большие для взаимодействия касаниями и расположены так, что с ними удобно работать;
- Ресурсы оптимизированы для минимального размера и быстрой загрузки;
- Видео, анимации, сложные фоновые вычисления и сетевые операции ограничены, если мало заряда батареи или включен энергосберегающий режим;
- Упрощённая версия без лишних ресурсов загружается, если включен режим экономии трафика;
- Контент кэшируется на устройстве и доступен оффлайн при потере соединения;
- Контент адаптируется к изменению ориентации экрана, режиму разделения экрана и другим возможностям устройства;
- Сайт использует аппаратные возможности устройства (камеру, микрофон, Bluetooth, акселерометр и другие), если это уместно и помогает пользователю;
#css #ux #performance
Mobile-first многим известен как подход к вёрстке, при котором сначала реализуется версия для мобильных и расширяется до десктопа. Это противоположность подходу desktop-first, при котором вёрстка идёт от десктопа к мобильным.
Разница обычно сводится к организации медиа-запросов и использованию
min-width/max-width. Сначала применяются мобильные стили и переопределяются на более широких экранах, или десктопные переопределяются на более узких./* Mobile-first */
.product-grid {
--col: 1;
--gap: 12px;
display: grid;
grid-template-columns:
repeat(var(--col), minmax(0, 1fr))
;
gap: var(--gap);
}
@media (min-width: 600px) {
.product-grid {
--col: 2;
--gap: 18px;
}
}
@media (min-width: 900px) {
.product-grid {
--col: 3;
--gap: 24px;
}
}
@media (min-width: 1200px) {
.product-grid {
--col: 4;
}
}
/* Desktop-first */
.product-grid {
--col: 4;
--gap: 24px;
display: grid;
grid-template-columns:
repeat(var(--col), minmax(0, 1fr))
;
gap: var(--gap);
}
@media (max-width: 1200px) {
.product-grid {
--col: 3;
}
}
@media (max-width: 900px) {
.product-grid {
--col: 2;
--gap: 18px;
}
}
@media (max-width: 600px) {
.product-grid {
--col: 1;
--gap: 12px;
}
}
Принципиальной разницы нет. В теории, движение от малых экранов к большим даст в итоге чуть меньше стилей, потому что контент по умолчанию mobile-first, адаптируется под ширину экрана и идёт в потоке сверху вниз.
Однако сам термин mobile-first говорит о том, что подход ориентирован в первую очередь на мобильные устройства. Суть не в том, в какую сторону переопределяются стили, а в том, чтобы учесть ограничения мобильных устройств.
Ограничения исходят из конструктивных особенностей. Мобильные устройства компактные и переносимые, питаются от аккумулятора, работают в беспроводных сетях, управляются касаниями и жестами, вмещают меньше контента на экране.
Размеры и питание от аккумулятора не позволяют использовать более мощное железо и массивные системы охлаждения. Поэтому мобильные устройства уступают десктопам по производительности, что повышает требования к оптимизации.
Точки Wi-Fi и мобильные сети менее стабильны, чем проводные соединения. Скорость приёма/передачи, потери пакетов, задержки и другие характеристики сети на мобильных устройствах, как правило, уступают десктопам.
Не смотря на безлимитные тарифы, всё ещё достаточно распространены тарифы с лимитированным трафиком и скоростью. Покрытие сети вне крупных городов ухудшается. Может быть включен режим экономии трафика или батареи.
Отличаются сценарии взаимодействия. Нет мыши и курсора, виртуальная клавиатура на треть экрана заменяет физическую, управление осуществляется жестами, касаниями и долгими нажатиями вместо кликов и наведения указателя мыши.
Настоящий mobile-first — это разработка сайтов и веб-приложений с учётом всех особенностей и ограничений мобильных устройств. Он затрагивает архитектуру проекта и выбор инструментов разработки, а не только направление медиа-запросов.
В хорошем mobile-first:
- Элементы управления и контент компактно и логично размещены на ограниченном по размеру экране, интерфейс не перегружен;
- Интерактивные элементы достаточно большие для взаимодействия касаниями и расположены так, что с ними удобно работать;
- Ресурсы оптимизированы для минимального размера и быстрой загрузки;
- Видео, анимации, сложные фоновые вычисления и сетевые операции ограничены, если мало заряда батареи или включен энергосберегающий режим;
- Упрощённая версия без лишних ресурсов загружается, если включен режим экономии трафика;
- Контент кэшируется на устройстве и доступен оффлайн при потере соединения;
- Контент адаптируется к изменению ориентации экрана, режиму разделения экрана и другим возможностям устройства;
- Сайт использует аппаратные возможности устройства (камеру, микрофон, Bluetooth, акселерометр и другие), если это уместно и помогает пользователю;
#css #ux #performance
❤8👍3😈2🌚1
Страница на Claude Code и Figma MCP
Недавно посмотрел страницу «Coming Soon» одного проекта, которая создана с помощью Claude Code в сочетании с Figma MCP по отрисованному макету. Её создал человек без опыта разработки за 30 минут. На доработку ему понадобилось 3 промпта.
Сама страница — фоновое изображение, логотип, заголовок, текст, поле для email, кнопка отправки и ещё один логотип. То есть простая страница. С визуальной точки зрения всё неплохо: выглядит как надо, адаптивность есть, форма работает.
Я бы не писал об этом, если бы вновь не пришлось узреть мощь современных технологий. Страница создана на Next и Tailwind. Дело не в самих технологиях, а в том, что их использование не оправдано для такой простой страницы.
Агент выбрал в качестве стэка по умолчанию Next и Tailwind, потому что ему не было указано иное. Уж не знаю, почему они делают такой выбор, но это печально. По всей видимости, статистически самый частый вариант. Получается стрельба из пушки по воробьям.
Интересно то, что Pagespeed выдаёт 100 баллов по производительности и 97 по доступности. То есть всё в порядке, цифры зелёные не смотря на Next. Тут важна трактовка результатов. Было бы странно видеть на такой странице не 100.
Достаточно добавить разделы с формой, каруселью, раскрываемыми FAQ, шапку с выпадающим меню, эффекты и аналитику, и эти 100 баллов стремительно полетят вниз. Весь бюджет JS съеден обвязкой Next уже на самом старте.
На страницу загружается 8 JS-файлов общим размером 160кб (~540кб без сжатия) просто потому что так работает Next. Вся интерактивность на странице — отправка email с формы в сторонний сервис. Для этого не нужен Next.
При загрузке страницы виден прыжок шрифта. Подключается вариативный шрифт размером 1.9мб (4.9мб без сжатия) для отрисовки заголовка и блока текста с одним предложением. Обший размер всех ресурсов — 2.5мб (6мб без сжатия).
Для создания этой простейшей страницы развёрнута целая машинерия. Банальная замена текста — процесс с запросом к Claude, который ждёт сборку и загрузку на Vercel. А ведь можно было просто открыть файл и поправить текст.
С точки зрения использования ИИ это тоже расточительство. Сколько лишней работы, токенов и времени затрачено на генерацию конфигов, создание структуры проекта, установку модулей, создание компонентов и генерацию бойлерплейта.
Эта страница должна быть одним HTML-файлом со встроенными
Это пост не о том, что Claude плохой, а о том, что по умолчанию результат выходит посредственный. Нужна экспертиза и понимание, чтобы задавать правильный вектор: стэк, правила, ограничения и желаемый результат.
А ещё настораживает, что кодогенерацией занимаются специалисты без опыта в разработке и на выходе выдают такие вот «сайты». Наверно, всё же, стоит отдавать эту работу разработчикам, чтобы не вышла «мощь современных технологий».
#performance
Недавно посмотрел страницу «Coming Soon» одного проекта, которая создана с помощью Claude Code в сочетании с Figma MCP по отрисованному макету. Её создал человек без опыта разработки за 30 минут. На доработку ему понадобилось 3 промпта.
Сама страница — фоновое изображение, логотип, заголовок, текст, поле для email, кнопка отправки и ещё один логотип. То есть простая страница. С визуальной точки зрения всё неплохо: выглядит как надо, адаптивность есть, форма работает.
Я бы не писал об этом, если бы вновь не пришлось узреть мощь современных технологий. Страница создана на Next и Tailwind. Дело не в самих технологиях, а в том, что их использование не оправдано для такой простой страницы.
Агент выбрал в качестве стэка по умолчанию Next и Tailwind, потому что ему не было указано иное. Уж не знаю, почему они делают такой выбор, но это печально. По всей видимости, статистически самый частый вариант. Получается стрельба из пушки по воробьям.
Интересно то, что Pagespeed выдаёт 100 баллов по производительности и 97 по доступности. То есть всё в порядке, цифры зелёные не смотря на Next. Тут важна трактовка результатов. Было бы странно видеть на такой странице не 100.
Достаточно добавить разделы с формой, каруселью, раскрываемыми FAQ, шапку с выпадающим меню, эффекты и аналитику, и эти 100 баллов стремительно полетят вниз. Весь бюджет JS съеден обвязкой Next уже на самом старте.
На страницу загружается 8 JS-файлов общим размером 160кб (~540кб без сжатия) просто потому что так работает Next. Вся интерактивность на странице — отправка email с формы в сторонний сервис. Для этого не нужен Next.
При загрузке страницы виден прыжок шрифта. Подключается вариативный шрифт размером 1.9мб (4.9мб без сжатия) для отрисовки заголовка и блока текста с одним предложением. Обший размер всех ресурсов — 2.5мб (6мб без сжатия).
Для создания этой простейшей страницы развёрнута целая машинерия. Банальная замена текста — процесс с запросом к Claude, который ждёт сборку и загрузку на Vercel. А ведь можно было просто открыть файл и поправить текст.
С точки зрения использования ИИ это тоже расточительство. Сколько лишней работы, токенов и времени затрачено на генерацию конфигов, создание структуры проекта, установку модулей, создание компонентов и генерацию бойлерплейта.
Эта страница должна быть одним HTML-файлом со встроенными
<style> и <script>. Без Next, Tailwind, node_modules, Turbopack, 8 чанков JS, вариативного шрифта и так далее. Для отправки формы простой скрипт на чистом JS.Это пост не о том, что Claude плохой, а о том, что по умолчанию результат выходит посредственный. Нужна экспертиза и понимание, чтобы задавать правильный вектор: стэк, правила, ограничения и желаемый результат.
А ещё настораживает, что кодогенерацией занимаются специалисты без опыта в разработке и на выходе выдают такие вот «сайты». Наверно, всё же, стоит отдавать эту работу разработчикам, чтобы не вышла «мощь современных технологий».
#performance
🔥16❤7🤡4👍3🌚1😎1
Оптимизация размера DOM бесполезна?
Я писал про уменьшение размера DOM. На днях смотрел подкаст «Организованное Программирование» про SEO, там гость заявил, что считает совет Lighthouse об оптимизации размера DOM вредным и бесполезным. Так ли это на самом деле?
Загрузка любого сайта начинается с получения HTML-разметки. HTML — это текстовый формат. Больше текста = больше размер файла, который нужно передать по сети. Значит файл будет дольше загружаться, что логично.
Но есть парадокс под названием сжатие. 72% текстовых файлов передаются по сети в сжатом виде. Если вы входите в 28%, то настройте хотя-бы Gzip, а лучше Brotli или Zstd. Сервер сжимает файл при отправке, браузер разжимает при получении.
Более тяжёлый в несжатом виде файл может весить меньше более лёгкого файла, если сравнить их размеры в сжатом виде. Алгоритмы работают на повторяющихся фрагментах текста, поэтому на большом HTML они могут быть эффективнее.
Не всегда рост размера HTML приводит к росту размера файла, передаваемого в итоге по сети. Иногда даже наоборот. Выходит, что оптимизация размера HTML пустая трата времени и бесполезный совет? Отнюдь нет.
Помимо передачи по сети HTML служит основой для построения DOM. HTML-парсер проходится по разметке и создаёт для каждого HTML-элемента, атрибута, строки текста и комментария объект с множеством свойств и связей.
Эти объекты нужны для того, чтобы ими манипулировать через DOM API, что под капотом делают любые JS-фреймворки. Также нужно построить CSSOM — для каждого узла вычислить все CSS-свойства, чтобы знать размеры и положение.
CSSOM нужен для итоговых процессов отрисовки страницы: Render tree, Layout и Paint. Это чтобы первично отрисовать страницу. Далее она прокручивается, меню выпадает, слайды переключаются, кнопки меняют цвет при наведении и так далее.
Всё это приводит к повторным пересчётам. Многие взаимодействия со страницей вызывают пересчёт. Браузеры выполняют огромное количество операций в секунду. За годы существования все эти процессы отлично оптимизированы.
Чтобы страница ощущалась быстрой и плавной, все операции по пересчёту стилей и отрисовке должны укладываться в один кадр отрисовки, то есть ~16мс для экрана с частотой обновления 60 Гц. Желательно не впритык, а с некоторым запасом.
Чем больше DOM-узлов, тем больше всего браузеру нужно рассчитать и подготовить к отрисовке. Осложняют дело «тяжёлые» новинки, такие как селектор
JS-отрисовка добавляет дополнительные накладные расходы. Размеры бандлов растут из-за шаблонов в коде, потребление памяти и процессора растёт, потому что код шаблона нужно выполнить, а с VDOM ещё держать в памяти деревья.
С большим DOM можно выйти за пределы одного кадра отрисовки, тогда начнётся заметная потеря кадров, сопровождаемая «лагами» и «фризами». Особо заметно это на низко- и средне-бюджетных смартфонах, которыми пользуется большинство.
Столкнувшись с «фризами» пользователи уйдут к конкурентам, у которых такого нет. Поисковики эти сигналы считывают и запоминают, ухудшаются поведенческие факторы и сайт теряет позиции в выдаче поиска. Бизнес теряет пользователей.
Это может показаться притянутым за уши. Но проблема точно реальная и с ней борются. Глянуть сайт на предмет просадки кадров точно стоит (в DevTools меню с тремя точками → More tools → Rendering → флажок Frame Rendering States).
Во вкладке Performance можно обратить внимание, сколько времени занимает Parse HTML, Rendering и Painting. Во вкладке Memory можно сделать снэпшот и увидеть, сколько оперативной памяти выделено на разные объекты, в том числе узлы DOM.
Экономия на спичках. Возможно и так, учитывая сложность оптимизации HTML. Если дело дошло до этого, значит всё остальное уже хорошо оптимизировано. Тем не менее, я считаю, что верстать нужно с мыслью об экономии узлов.
Современный CSS с flex и grid, а также немного смекалки позволяют достигать результата с меньшим количеством разметки. Убрали лишние узлы, сжали картинки, взяли встроенное API вместо JS и уже немало сэкономили.
#html #performance
Я писал про уменьшение размера DOM. На днях смотрел подкаст «Организованное Программирование» про SEO, там гость заявил, что считает совет Lighthouse об оптимизации размера DOM вредным и бесполезным. Так ли это на самом деле?
Загрузка любого сайта начинается с получения HTML-разметки. HTML — это текстовый формат. Больше текста = больше размер файла, который нужно передать по сети. Значит файл будет дольше загружаться, что логично.
Но есть парадокс под названием сжатие. 72% текстовых файлов передаются по сети в сжатом виде. Если вы входите в 28%, то настройте хотя-бы Gzip, а лучше Brotli или Zstd. Сервер сжимает файл при отправке, браузер разжимает при получении.
Более тяжёлый в несжатом виде файл может весить меньше более лёгкого файла, если сравнить их размеры в сжатом виде. Алгоритмы работают на повторяющихся фрагментах текста, поэтому на большом HTML они могут быть эффективнее.
Не всегда рост размера HTML приводит к росту размера файла, передаваемого в итоге по сети. Иногда даже наоборот. Выходит, что оптимизация размера HTML пустая трата времени и бесполезный совет? Отнюдь нет.
Помимо передачи по сети HTML служит основой для построения DOM. HTML-парсер проходится по разметке и создаёт для каждого HTML-элемента, атрибута, строки текста и комментария объект с множеством свойств и связей.
Эти объекты нужны для того, чтобы ими манипулировать через DOM API, что под капотом делают любые JS-фреймворки. Также нужно построить CSSOM — для каждого узла вычислить все CSS-свойства, чтобы знать размеры и положение.
CSSOM нужен для итоговых процессов отрисовки страницы: Render tree, Layout и Paint. Это чтобы первично отрисовать страницу. Далее она прокручивается, меню выпадает, слайды переключаются, кнопки меняют цвет при наведении и так далее.
Всё это приводит к повторным пересчётам. Многие взаимодействия со страницей вызывают пересчёт. Браузеры выполняют огромное количество операций в секунду. За годы существования все эти процессы отлично оптимизированы.
Чтобы страница ощущалась быстрой и плавной, все операции по пересчёту стилей и отрисовке должны укладываться в один кадр отрисовки, то есть ~16мс для экрана с частотой обновления 60 Гц. Желательно не впритык, а с некоторым запасом.
Чем больше DOM-узлов, тем больше всего браузеру нужно рассчитать и подготовить к отрисовке. Осложняют дело «тяжёлые» новинки, такие как селектор
:has(), контейнерные и стилевые запросы, @scope, каскадные слои.JS-отрисовка добавляет дополнительные накладные расходы. Размеры бандлов растут из-за шаблонов в коде, потребление памяти и процессора растёт, потому что код шаблона нужно выполнить, а с VDOM ещё держать в памяти деревья.
С большим DOM можно выйти за пределы одного кадра отрисовки, тогда начнётся заметная потеря кадров, сопровождаемая «лагами» и «фризами». Особо заметно это на низко- и средне-бюджетных смартфонах, которыми пользуется большинство.
Столкнувшись с «фризами» пользователи уйдут к конкурентам, у которых такого нет. Поисковики эти сигналы считывают и запоминают, ухудшаются поведенческие факторы и сайт теряет позиции в выдаче поиска. Бизнес теряет пользователей.
Это может показаться притянутым за уши. Но проблема точно реальная и с ней борются. Глянуть сайт на предмет просадки кадров точно стоит (в DevTools меню с тремя точками → More tools → Rendering → флажок Frame Rendering States).
Во вкладке Performance можно обратить внимание, сколько времени занимает Parse HTML, Rendering и Painting. Во вкладке Memory можно сделать снэпшот и увидеть, сколько оперативной памяти выделено на разные объекты, в том числе узлы DOM.
Экономия на спичках. Возможно и так, учитывая сложность оптимизации HTML. Если дело дошло до этого, значит всё остальное уже хорошо оптимизировано. Тем не менее, я считаю, что верстать нужно с мыслью об экономии узлов.
Современный CSS с flex и grid, а также немного смекалки позволяют достигать результата с меньшим количеством разметки. Убрали лишние узлы, сжали картинки, взяли встроенное API вместо JS и уже немало сэкономили.
#html #performance
🔥11👍8❤2🌚1🤝1
Стилизуемый combobox и фильтруемый select
Браузеры совместно с OpenUI продолжают развивать
На конференции BlinkOn 21 контрибьютор движка Chromium поделился прогрессом в этом направлении. Я уже писал о том, как работают атрибуты size и multiple у стандартного
Первое — обёртки
Для состояния, когда не выбрана ни одна опция, а значит
Далее продемонстрирован пример создания виджета выпадающего списка опций с возможностью фильтрации через поле ввода. В качестве образца выступает виджет добавления тегов в GitHub.
Виджет собран из нескольких встроенных API. Кнопка с атрибутами
Прорабатывается вариант для простых случаев, когда есть только опции и поле для их фильтрации, без дополнительных элементов. В таком случае предлагается вкладывать
К фильтрации предусмотрено событие
Показана возможность стилизации уже существующей в браузерах связки
Некоторые из показанных возможностей тестируются и уже доступны в Chrome за флагом экспериментальных технологий. Разработчики собирают обратную связь, обсуждают спорные моменты и дорабатывают API.
#html #css #ui
Браузеры совместно с OpenUI продолжают развивать
<select>, добавляя новые функции и возможности стилизации. В Сhrome уже можно стилизовать стандартный <select> с одиночным выбором, на подходе вариант с множественным выбором и combobox.На конференции BlinkOn 21 контрибьютор движка Chromium поделился прогрессом в этом направлении. Я уже писал о том, как работают атрибуты size и multiple у стандартного
<select> и планах по расширению их функциональности.<select multiple size="1">
<button>
<selectedcontent>
<div>One</div>
<div>Two</div>
</selectedcontent>
<span>0 Selected</span>
</button>
<option selected>One</option>
<option selected>Two</option>
<!-- ... -->
</select>
<style>
selectedcontent:not(:empty) + span {
display: none;
}
</style>
Первое — обёртки
<div> для проецируемых опций в <selectedcontent>. Этот элемент был внедрён как часть стилизуемого <select> для проецирования значения выбранной опции. Теперь проецируются несколько опций и они оборачиваются в <div> для стилизации.Для состояния, когда не выбрана ни одна опция, а значит
<selectedcontent> пуст, предлагается использовать резервный элемент <span> с текстом. Он скрывается через комбинацию псевдо-классов :not(), :empty и комбинатор +.Далее продемонстрирован пример создания виджета выпадающего списка опций с возможностью фильтрации через поле ввода. В качестве образца выступает виджет добавления тегов в GitHub.
<button
commandfor="dialog"
command="showpopover"
>
<selectedcontent id="sc">
</selectedcontent>
</button>
<dialog popover id="dialog">
<input filter="select">
<select
size="4"
id="select"
selectedcontentelement="sc"
>
<!-- опции -->
</select>
<!-- можно добавить другие элементы -->
</dialog>
Виджет собран из нескольких встроенных API. Кнопка с атрибутами
command/commandfor управляет отображением диалога, который реализован с помощью <dialog popover>. Внутри <select>, который связан с <selectedcontent> для проекции выбранной опции.<input> связан с <select> через новый атрибут filter, который позволяет фильтровать опции. Предложенный вариант многословен, но хорош тем, что в <dialog> можно поместить другие элементы (текст, кнопки, иконки) помимо <input> и <select>.Прорабатывается вариант для простых случаев, когда есть только опции и поле для их фильтрации, без дополнительных элементов. В таком случае предлагается вкладывать
<input> внутрь <select> перед опциями. Для этого будет доработан парсер.<select>
<input>
<!-- опции -->
</select>
К фильтрации предусмотрено событие
beforefilter, которое можно перехватить, отменить стандартное поведение через preventDefault(). Это механизм для реализации подгрузки данных из внешнего источника и динамической генерации списка опций.Показана возможность стилизации уже существующей в браузерах связки
<input> и <datalist>. Как и в случае со стилизуемым <select>, API базируется на идее базового внешнего вида и стандартных стилей, задаваемых свойством appearance: base;.<input list="options">
<datalist id="options">
<!-- опции -->
</datalist>
<style>
input[list], datalist {
appearance: base;
}
</style>
Некоторые из показанных возможностей тестируются и уже доступны в Chrome за флагом экспериментальных технологий. Разработчики собирают обратную связь, обсуждают спорные моменты и дорабатывают API.
#html #css #ui
YouTube
Customizable combobox and filterable select [BlinkOn 21]
Enjoy the videos and music you love, upload original content, and share it all with friends, family, and the world on YouTube.
👍6❤1🌚1🤗1
You don't know HTML: <fieldset>
Элемент
Подпись задаётся с помощью элемента
У элемента
Помимо встроенной роли, семантики и привязки имени у
Атрибут
У
Атрибут
У
Таким образом
#ydkhtml
Элемент
<fieldset> в HTML предназначен для группировки элементов формы с опциональной подписью. Явные элементы для группировки — флажки, радио-кнопки, переключатели и данные, которые разбиты на несколько полей, как адрес или телефон.Подпись задаётся с помощью элемента
<legend>. Если он есть, то должен быть первым прямым потоком <fieldset>. Иное размещение или оборачивание в контейнер не валидно и подпись не будет привязана к <fieldset>.У элемента
<fieldset> встроенная роль group. Наличие валидного <legend> задаёт имя для группы. Воспроизвести это можно с помощью ARIA, но зачем, если в платформе есть встроенный элемент с соответствующей семантикой:<div
role="group"
aria-labelledby="legend"
>
<div id="legend">Размер</div>
<!-- переключатели -->
</div>
<!-- встроенный аналог -->
<fieldset>
<legend>Размер</legend>
<!-- переключатели -->
</fieldset>
Помимо встроенной роли, семантики и привязки имени у
<fieldset> есть API. Атрибут disabled отключает вложенные элементы формы, поэтому не нужно вручную каждому проставлять disabled. Полезно для отключения части формы.Атрибут
name задаёт идентификатор группы для доступа к ней через Form API. Это удобно использовать в сочетании со свойством elements, которое хранит все элементы формы или группы. Через него можно получить конкретный элемент.<form name="variants">
<fieldset name="sizes">
<legend>Размер</legend>
<!-- переключатели -->
</fieldset>
<!-- ... -->
</form>
<script>
const variantsForm = document.forms['variants'];
// или document.forms.variants
// или document.forms.namedItem('variants')
const sizeGroup = variantsForm.elements['sizes'];
// или variantsForm.elements.sizes
// или variantsForm.elements.namedItem('sizes')
// отключение группы
sizeGroup.disabled = true;
// элементы группы
const elements = sizeGroup.elements;
</script>
У
<fieldset> можно вызывать методы Constraint Validation API и проверить на валидность только поля из группы, а не всей формы целиком. Также применимы псевдо-классы :valid/:invalid, :user-valid/:user-invalid.Атрибут
form позволяет привязать группу со всеми элементами к указанной форме. Это позволяет вынести группу за пределы формы, сохранив с ней связь. Сбор данных для отправки, валидация и методы работы с элементами будут доступны.У
<fieldset> есть некоторые особенности стилизации. По умолчанию у него блочный контекст форматирования, ширина подстраивается под контент, есть рамка и <legend> размещается поверх рамки. Но элемент полностью стилизуемый.Таким образом
<fieldset> в сочетании с <legend> полезен для группировки полей, передаёт вспомогательным технологиям нужную семантику и имя, обладает API для управления элементами (отключение, вынос из формы, получение, валидация).#ydkhtml
👍11🌚1🤝1
sizes="auto" для изображений
Для адаптивных изображений у элемента
В Firefox 150 и Chrome 124 добавили новое значение
Зачем указывать
Обнаружив изображение, браузер поместит его в очередь загрузки. В моменте нужно решить, какое из изображений ставить в очередь. К этому времени стили могут быть ещё не загружены или не обработаны. Поэтому браузер смотрит значение
С лениво-загружаемыми изображениями ситуация другая. Их загрузка отложена до тех пор, пока изображение не появится в области просмотра. Чтобы это выяснить, браузеру нужно обработать стили и знать размеры и положение на странице.
К моменту загрузки ленивого изображения, даже если оно находится на первом экране, браузер уже обработал стили и знает все размеры. Именно поэтому
Таким образом изображениям с ленивой загрузкой
Синтаксисом предусмотрено совмещение значения
Мэт Маркиз, бывший председатель группы адаптивных изображений (Responsive Image Community Group) написал статью «The end of responsive images», в которой рассказал более чем 10-летнюю историю появления
#html #css
Для адаптивных изображений у элемента
<img> есть два атрибута. srcset задаёт набор изображений с физической шириной или плотностью пикселей. sizes задаёт доступное пространство, которое браузер заполняет подходящим изображением.В Firefox 150 и Chrome 124 добавили новое значение
sizes="auto". Однако не стоит думать, что теперь указывать размеры в sizes не нужно. sizes="auto" работает в паре с loading="lazy". Без него указывать auto нет смысла, работать не будет.Зачем указывать
sizes, разве браузер не знает размеры области для отрисовки изображения? Дело в том, что не знает до загрузки и обработки всех стилей. Сканер предварительной загрузки анализирует HTML и ищет ресурсы.Обнаружив изображение, браузер поместит его в очередь загрузки. В моменте нужно решить, какое из изображений ставить в очередь. К этому времени стили могут быть ещё не загружены или не обработаны. Поэтому браузер смотрит значение
sizes.С лениво-загружаемыми изображениями ситуация другая. Их загрузка отложена до тех пор, пока изображение не появится в области просмотра. Чтобы это выяснить, браузеру нужно обработать стили и знать размеры и положение на странице.
К моменту загрузки ленивого изображения, даже если оно находится на первом экране, браузер уже обработал стили и знает все размеры. Именно поэтому
sizes="auto" будет работать, браузер возьмёт известные ему размеры области.Таким образом изображениям с ленивой загрузкой
loading="lazy" можно задать sizes="auto". Остальным всё ещё нужно указывать размеры. Также остаются браузеры, которые не понимают значение auto, для них тоже нужны размеры.Синтаксисом предусмотрено совмещение значения
auto с размерами. Если браузер не понимает auto или не может получить размеры из стилей, он откатится к размеру, который указан после auto через запятую: sizes="auto, (min-width: 600px) 50vw, 90vw".Мэт Маркиз, бывший председатель группы адаптивных изображений (Responsive Image Community Group) написал статью «The end of responsive images», в которой рассказал более чем 10-летнюю историю появления
sizes="auto".#html #css
Piccalilli
The end of responsive images
Mat Marquis has waited 14 years to write this article. The sizes attribute has been a necessary evil but now, with an auto value capability, it’s completely transformed authoring responsive images on the web.
👍4🔥3🌚1🤝1
ARIA APG: доверяй, но проверяй
Есть ресурс под названием ARIA Authoring Practices Guide, сокращённо APG. На него ссылаются как на источник доступных шаблонов элементов интерфейса. Это неплохой ресурс, но при обращении к нему стоит помнить о некоторых нюансах.
Ресурс выглядит официально, содержит логотип W3C и WAI, размещён на домене w3.org, упоминается на сайте инициативы по веб доступности. Внушает доверие и вызвает ложное ощущение официального стандарта от W3C.
На деле APG — не стандарт W3C, в отличие от ARIA, WCAG, ARIA in HTML и других. APG создан специальной группой APG Task Force из сообщества WAI, которое, в свою очередь, одно из многих сообществ в составе консорциума W3C.
Задача APG — показать, как применять стандарт ARIA для реализации элементов интерфейса. Шаблоны демонстрируют все роли, свойства и состояния в соответствии с правилами стандарта. Это своего рода витрина ARIA.
Если взглянуть на шаблон кнопки, то APG предлагает использовать
Тем временем первое правило ARIA гласит, что не стоит использовать ARIA, если есть встроенный элемент. В HTML кнопка есть —
Аналогично с шаблонами флажков, радио-кнопок, диалогов, раскрываемых блоков, ссылок, ориентиров, шкалы, слайдера, таблиц и так далее. Они показывают, как реализовать с помощью ARIA. Зачастую в этом нет необходимости.
Отдельного внимания заслуживает борьба Адриана Розелли с шаблоном навигации сайта, который долгое время предлагал использовать не подходящие роли меню. Разработчики внедряли этот шаблон на сайты и делали только хуже.
Всё сказанное выше не значит, что на APG не стоит смотреть. Это неплохой источник информации о проектировании доступных интерфейсов. Важно не воспринимать его как официальный источник истины со 100% правильными шаблонами.
Эрик Бэйли считает хорошими частями в APG:
- Названия шаблонов. Хорошо, когда вся команда говорит на одном языке;
- Описания шаблонов. Суть шаблона, для чего нужен, как работает и когда применять;
- Клавиатурная навигация. Клавиши и их сочетания, которые должны работать при взаимодействии;
Остальное вторично. Таблицы поддержки не отражают реальный уровень поддержки, примеры кода не согласованы, написаны в разных стилях, используют устаревшие подходы и не готовы к продакшену, часть шаблонов заменяется встроенным HTML.
Используйте APG как источник знаний о сути того или иного шаблона и навигации с помощью клавиатуры. По возможности используйте встроенные HTML-элементы, обращайтесь к официальному стандарту ARIA и всегда тестируйте.
#ui #a11y
Есть ресурс под названием ARIA Authoring Practices Guide, сокращённо APG. На него ссылаются как на источник доступных шаблонов элементов интерфейса. Это неплохой ресурс, но при обращении к нему стоит помнить о некоторых нюансах.
Ресурс выглядит официально, содержит логотип W3C и WAI, размещён на домене w3.org, упоминается на сайте инициативы по веб доступности. Внушает доверие и вызвает ложное ощущение официального стандарта от W3C.
На деле APG — не стандарт W3C, в отличие от ARIA, WCAG, ARIA in HTML и других. APG создан специальной группой APG Task Force из сообщества WAI, которое, в свою очередь, одно из многих сообществ в составе консорциума W3C.
Задача APG — показать, как применять стандарт ARIA для реализации элементов интерфейса. Шаблоны демонстрируют все роли, свойства и состояния в соответствии с правилами стандарта. Это своего рода витрина ARIA.
Если взглянуть на шаблон кнопки, то APG предлагает использовать
<div>, <a> и <span> с ролью button, tabindex="0" для фокуса и JS для обработки нажатия. APG свою задачу выполнил: показал, как использовать роль button.Тем временем первое правило ARIA гласит, что не стоит использовать ARIA, если есть встроенный элемент. В HTML кнопка есть —
<button>. На практике <div> с ролью кнопки, tabindex и обработчиком нажатий считается антипаттерном.Аналогично с шаблонами флажков, радио-кнопок, диалогов, раскрываемых блоков, ссылок, ориентиров, шкалы, слайдера, таблиц и так далее. Они показывают, как реализовать с помощью ARIA. Зачастую в этом нет необходимости.
Отдельного внимания заслуживает борьба Адриана Розелли с шаблоном навигации сайта, который долгое время предлагал использовать не подходящие роли меню. Разработчики внедряли этот шаблон на сайты и делали только хуже.
Всё сказанное выше не значит, что на APG не стоит смотреть. Это неплохой источник информации о проектировании доступных интерфейсов. Важно не воспринимать его как официальный источник истины со 100% правильными шаблонами.
Эрик Бэйли считает хорошими частями в APG:
- Названия шаблонов. Хорошо, когда вся команда говорит на одном языке;
- Описания шаблонов. Суть шаблона, для чего нужен, как работает и когда применять;
- Клавиатурная навигация. Клавиши и их сочетания, которые должны работать при взаимодействии;
Остальное вторично. Таблицы поддержки не отражают реальный уровень поддержки, примеры кода не согласованы, написаны в разных стилях, используют устаревшие подходы и не готовы к продакшену, часть шаблонов заменяется встроенным HTML.
Используйте APG как источник знаний о сути того или иного шаблона и навигации с помощью клавиатуры. По возможности используйте встроенные HTML-элементы, обращайтесь к официальному стандарту ARIA и всегда тестируйте.
#ui #a11y
1👍5❤4🔥3⚡1🌚1😎1
Переусложнённые радио-кнопки в Shadcn
Пол Геберт в блоге написал о чрезмерно переусложнённых радио-кнопках в Shadcn. Это библиотека готовых компонентов для React, которая построена на базе Radix UI, использует Tailwind и работает по принципу «copy-paste» кода в проект.
Предположим, что в проекте нужны радио-кнопки. В HTML это решается добавлением
Если залезать под капот импортируемого
Радио-кнопка в Shadcn — это кнопка с ARIA-атрибутами для имитации радио-кнопки, SVG-круг, скрытый
Применение
Задумка Shadcn и Radix, по всей видимости, в надёжности стилизации. С
#html #css #ui
Пол Геберт в блоге написал о чрезмерно переусложнённых радио-кнопках в Shadcn. Это библиотека готовых компонентов для React, которая построена на базе Radix UI, использует Tailwind и работает по принципу «copy-paste» кода в проект.
Предположим, что в проекте нужны радио-кнопки. В HTML это решается добавлением
<input type="radio">. В Shadcn для этого нужно 3 импорта, 45 строк кода, сторонняя библиотека иконок для кружка, 30 классов Tailwind.Если залезать под капот импортируемого
RadioGroupPrimitive, там ещё 215 строк кода и 7 импортов. Неужели для радио-кнопок нужно столько кода? Но это ладно. Какой результат получается в браузере при отрисовке этого всего?<button
id="debit"
class="классы Tailwind"
type="button"
role="radio"
aria-checked="true"
data-state="checked"
value="debit"
tabindex="0"
data-radix-collection-item
>
<span
class="классы Tailwind"
data-state="checked"
>
<svg
class="классы Tailwind"
xmlns="..."
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<circle cx="12" cy="12" r="10">
</circle>
</svg>
</span>
</button>
<input
aria-hidden="true"
style="..."
tabindex="-1"
type="radio"
value="debit"
checked
name="credit-or-debit"
>
Радио-кнопка в Shadcn — это кнопка с ARIA-атрибутами для имитации радио-кнопки, SVG-круг, скрытый
<input type="radio">, который нужен для работы форм и классы Tailwind. Всё может быть гораздо проще, если использовать стандартный HTML:<input
type="radio"
name="credit-or-debit"
value="debit"
checked
>
Применение
appearance: none к флажкам и радио-кнопкам сбрасывает их стандартный внешний вид. После этого можно задавать размеры, фон, рамку, тень, закругление и так далее. Маркеры можно сделать при помощи псевдо-элементов.<input
type="radio"
name="credit-or-debit"
value="debit"
checked
>
<style>
input[type="radio"] {
--color: currentColor;
appearance: none;
display: inline-grid;
place-content: center;
width: 24px;
height: 24px;
border: 2px solid var(--color);
border-radius: 50%;
margin: 0;
&::before {
content: '';
width: 12px;
height: 12px;
border-radius: 50%;
background-color: transparent;
transition: background-color .3s;
}
&:checked::before {
background-color: var(--color);
}
@media (forced-colors: active) {
--color: ButtonText;
}
}
</style>
Задумка Shadcn и Radix, по всей видимости, в надёжности стилизации. С
<input> дела обстоят хуже, чем с <button>. Но всё же решение с appearance работает сегодня во всех актуальных браузерах. Тогда к чему весь этот оверинжиниринг?#html #css #ui
Paulmakeswebsites
The Incredible Overcomplexity of the Shadcn Radio Button
Radio buttons are built into web browsers. Why are we using a UI library that wraps another UI library that rebuilds radio buttons from scratch? Why does rendering a radio button require multiple dependencies and several kilobytes of JavaScript? How did we…
🔥11👍5❤3🌚3👨💻1