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

Автор: @alexnozer
Download Telegram
Новогодние каникулы

Канал <divelopers> вместе с его автором уходит на новогодние каникулы и вернётся 9 января с новыми силами и постами. Спасибо что читаете!

Если хочется чего-то почитать на каникулах, предлагаю всю подборку постов You don’t know HTML (особенно если подписались недавно и пропустили). В ней я, вдохновившись серией книг Кайла Симпсона “You don’t know JS”, пишу про разные возможности HTML и Web API:

- You don't know HTML: download
- You don't know HTML: <datalist>
- You don't know HTML: input.showPicker()
- You don't know HTML: Media Capture
- You don't know HTML: inert
- You don't know HTML: <search>
- You don't know HTML: кнопки как ссылки
- You don't know HTML: Exclusive Accordion
- You don't know HTML: noscript и блок данных
- You don't know HTML: autocomplete
- You don’t know HTML: слеш в конце элементов

Если хочется послушать, предлагаю 443й выпуск подкаста “Веб-стандарты”, где я в качестве приглашённого гостя час обсуждаю с ведущими веб-компоненты. А если хочется посмотреть, есть запись доклада “What we do in the Shadow (DOM)” с MinskCSS, где я рассказываю как работает изоляция в Shadow DOM.

Всех с наступающими праздниками! Увидимся в Новом Году!
🔥9🎉84👍3🥰1
Возвращение к работе

Новогодние каникулы подошли к концу, а значит пора возвращаться к работе и каналу.

В планах на этот год:

- увеличить частоту и стабильность выхода контента
- запустить несколько новых рубрик
- писать больше о доступности, производительности и веб-компонентах
- попробовать новые форматы постов

А ещё 5 января у канала был день рождения 🥳 Формально, канал создан 16 декабря 2023 года, но запуск произошёл 5 января 2024 года. За год собралось 434 подписчика и опубликовано 173 поста. Скромно, но у меня нет цели гнаться за цифрами. Канал создан для того, чтобы делиться полезным контентом по HTML, CSS, доступности, производительности, UX и веб-стандартам. Это те темы, которые мне интересны, в которых есть экспертиза и желание делиться.

Спасибо всем, что читаете! Буду рад, если поделитесь ссылкой на канал с коллегами и порепостите посты. Если у вас есть идеи, предложения или вопросы, смело пишите мне в личку.
👍16🔥7🎉4🌚2
Встроенный CSS-in-JS

В экосистеме JS-фреймворков распространён подход CSS-in-JS, когда стили пишутся в JS-файлах. В современных браузерах есть встроенный механизм для работы со стилями в JS — Constructable Stylesheets. С его помощью можно создавать таблицы стилей и применять их глобально к документу или Shadow Root.

Таблица стилей создаётся через конструктор CSSStyleSheet. Методы replace и replaceSync формируют стилевые правила из строки. В массиве adoptedStyleSheets у document или Shadow Root хранятся текущие таблицы стилей, туда же можно добавить созданную через API:

const stylesheet = new CSSStyleSheet();

stylesheet.replaceSync(`
.element {
color: #4e4e4e;
text-decoration: underline;
}
`);

/* применение к документу */
document.adoptedStyleSheets.push(stylesheet);

const div = document.createElement('div');
div.attachShadow({ mode: 'open' });

/* применение к Shadow Root */
div.shadowRoot.adoptedStyleSheets.push(stylesheet);


Из объекта CSSStyleSheet можно получить информацию о стилях: список правил и медиа-запросов, родительскую таблицу стилей (при использовании @import) и т.д. Интерес представляет список правил, с которым можно работать:

const firstRule = stylesheet.cssRules[0];

console.log(firstRule.cssText);
// '.element { color: #4e4e4e; text-decoration: underline; }'

console.log(firstRule.selectorText);
// '.element'

console.log(firstRule.style.color);
// '#4e4e4e'

console.log(firstRule.style.textDecoration);
// 'underline'

console.log(firstRule.style.cssText);
// 'color: red; text-decoration: underline;'


А ещё в JS появились Import Attributes. Можно импортировать стили как ES-модуль, указав тип css через оператор with. При импорте таким образом создаётся экземпляр CSSStyleSheet. Поэтому можно писать стили во внешних файлах и импортировать их в JS:

/* styles.css */
.element {
color: #4e4e4e;
text-decoration: underline;
}


import syncStylesheet from '/styles.css' with { type: 'css' };

console.log(syncStylesheet.cssRules[0].cssText);
// '.element { color: #4e4e4e; text-decoration: underline; }'

/* или через динамический импорт */
const dynamicStylesheet = await import('/styles.css', { with: { type: 'css'} });



Написав небольшую обвязку вокруг этих API, можно получить что-то вроде CSS Modules или Styled Components.
12👍5🔥3
You don't know HTML: fetchpriority

Чтобы нарисовать страницу, браузеру нужно получить все необходимые для этого ресурсы. Первым делом браузер получает HTML и начинает его разбирать. В процессе браузер строит DOM и ищет все ресурсы, которые используются на странице: CSS, JS, изображения, видео, SVG и так далее. Параллельно с парсером, используется ещё один механизм — сканер предварительной загрузки. Его задача — искать ресурсы не дожидаясь, пока парсер до них дойдёт.

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

Атрибут fetchpriority можно указывать у элементов, которые загружают ресурсы: <link>, <noscript>, <img> и <iframe>. В качестве значений принимаются high, low и auto. Атрибут предназначен для изменения стандартного приоритета загрузки ресурса. Значение high делает ресурс более приоритетным, а low — менее приоритетным. Значение auto возвращает поведение по умолчанию, предоставляя браузеру выбор приоритета.

В HTML уже есть некоторые возможности для управления приоритетом, например:

- loading у <img> и <iframe>
- preload у <video>
- defer, async и type="module" у <noscript>
- <link rel="preload">

fetchpriority — ещё одна такая возможность. Какие есть варианты использования:

- Загрузить важную картинку, которая влияет на LCP, с высоким приоритетом
- Понизить приоритет картинок в карусели, которые отображаются только при переключении слайда
- Предварительно загружать некритические ресурсы с низким приоритетом
- Понизить приоритет классического блокирующего скрипта в конце <body>
- Повысить приоритет асинхронного скрипта

Приоритет также можно указывать в функции fetch():

const response = await fetch('/resource', { priority: 'low' });


Подробнее о Fetch Priority API можно прочитать в статье на web.dev.

#ydkhtml #html #performance
🔥36👍2🌚1
Доступность контента в социальных сетях

Доступность важно учитывать не только при разработке сайтов, но и при публикации контента в социальных сетях. Делюсь хорошим сайтом accessible-social.com, на котором собраны гайды по созданию доступного контента и использованию функций доступности в социальных сетях.

Также предлагаю запись вебинара про доступный контент от Глафиры Жур, где можно посмотреть, как использовать те или иные функции доступности социальных сетей.

#a11y
👍75🔥5🥰2🌚1
focusgroup

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


В ARIA существуют некоторые виджеты, для которых нужно реализовывать управление фокусом и навигацию с клавиатуры. При перемещении по интерактивным элементам при помощи Tab и Shift+Tab, весь виджет должен быть одним шагом в последовательности табуляции. Навигация по внутренним элементам виджета должна осуществляться через клавиши со стрелками и специальные клавиши Home, End, Page Up, Page Down и другие.

Есть две стратегии реализации:

- виртуальный фокус и атрибут aria-activedescendant
- техника “Roving Tabindex” и манипуляция атрибутом tabindex

Обе техники требуют нетривиальных реализаций на JS с прослушкой множества событий клавиатуры и фокуса. Подводных камней хватает.

Команда Edge в рамках группы OpenUI предложила добавить в браузеры атрибут focusgroup для управления фокусом и клавиатурой в виджетах.

<div
role="toolbar"
focusgroup
aria-label="Text Formatting"
aria-controls="…"
>
<button
type="button"
aria-pressed="false"
value="bold"
tabindex="-1"
>
Bold
</button>
<button
type="button"
aria-pressed="false"
value="italic"
tabindex="-1"
>
Italic
</button>
<!-- ... -->
</div>


При перемещении по странице через клавиатуру, фокус попадёт на первую кнопка в тулбаре, не смотря на tabindex="-1". Браузер находит внутри элемента с focusgroup все интерактивные элементы и те, которые отмечены атрибутом tabindex="-1".

Перемещение между кнопками в тулбаре осуществляется стрелками. “Вверх” и “вправо” — следующая кнопка, “вниз” и “влево” — предыдущая. Кнопка “Home” переносит фокус на первую кнопку, а “End” — на последнюю. Предусмотрен зацикленный переход между первой и последней кнопками при указании focusgroup="wrap".

При выходе фокуса из тулбара, браузер сохранит последнюю сфокусированную кнопку и вновь сфокусирует её при возврате фокуса в тулбар. Это отключается, если указать focusgroup="no-memory".

Значения inline и block настраивают направление. inline — навигация стрелками “влево”/“вправо”. block — навигация стрелками “вверх”/“вниз”. При этом учитывается направление письма.

<div
role="toolbar"
focusgroup="inline wrap no-memory"
aria-label="Text Formatting"
aria-controls="…"
>
<!-- ... -->
</div>


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

Значение grid включает автоматическую табличную навигацию. Этот режим работает со стандартными HTML-таблицами и табличной раскладкой (display: table-*) для автоматического вычисления порядка фокуса при навигации. Дополнительные значения flow, col-wrap, row-wrap, col-flow и row-flow уточняют, как нужно переносить фокус между столбцами и строками таблицы.

Значение manual-grid включает ручную табличную навигацию. В этом режиме для строк и столбцов нужно дополнительно указать focusgroup со значениями grid-row и grid-cell. Это позволяет добавлять навигацию в пользовательские таблицы на основе <div> + flexbox или grid.

Для всех значений атрибута focusgroup предлагаются аналоги в виде CSS-свойств:

.element {
focus-group-type: linear; /* grid | manual-grid |
grid-row | grid-cell */
focus-group-direction: both; /* inline | block */
focus-group-wrap: none; /* wrap | flow | col-wrap |
col-flow | col-none | row-wrap | row-flow | row-none */
focus-group-memory: auto; /* none */
}


Есть экспериментальный пользовательский полифилл с поддержкой базовых функций (без табличной навигации и CSS) и демо-страница, на которой его можно опробовать. Также есть оригинальный полифилл от команды Edge, но он разработан для более раннего варианта синтаксиса, который уже изменился.

Статус функции пока не понятен. По результатам State of HTML. в focusgroup есть заинтересованность со стороны разработчиков. Браузеры, вроде как, тоже относятся положительно. В Chrome были какие-то эксперименты. Работа над предложением продолжается, хотя и не очень активно.

#html #css #a11y
8🔥7🌚2
JSX из прошлого

React популяризировал синтаксис JSX (JavaScript XML). На первый взгляд выглядит как HTML, но пишется в JS как есть, без кавычек и ближе по правилам к XML.

export default function TodoList() {
return (
<>
<h1>Hedy Lamarr's Todos</h1>
<img
src="https://i.imgur.com/yXOvdOSs.jpg"
alt="Hedy Lamarr"
className="photo"
/>
<ul>
<li>Invent new traffic lights</li>
<li>Rehearse a movie scene</li>
<li>Improve the spectrum technology</li>
</ul>
</>
);
}


Но такой код нигде работать не будет. При попытке запустить код в браузере, на первом же символе < возникнет ошибка синтаксиса. JSX — это синтаксическое расширение JS. Чтобы код работал в браузере, его необходимо транспилировать. Для этого используются соответствующие инструменты: babel, esbuild, swc, tsc и другие. В процессе вся HTML-подобная разметка заменяется на вызовы функций. JSX разработан для удобства описания разметки и композиции UI в JS.

Но когда-то похожий код в JS вполне работал. Существует стандарт ECMA-357 (PDF), который описывает расширение синтаксиса JS под названием E4X (ECMAScript for XML). Суть была в том, чтобы добавить в JS поддержку синтаксиса XML и предоставить возможности для работы с ним.

В конце 90-х и 00-x XML был распространённым форматом структурированных данных. Логично, что разработчики хотели удобно работать с ним напрямую в JS. Поддержка E4X была реализована в разных продуктах. Среди них Flash, Thunderbird, Firefox, OpenOffice и другие. Но в Firefox 10 E4X был объявлен устаревшим, а в Firefox 21 поддержка была полностью удалена. Работает ли E4X где-то сегодня, сказать трудно, информации практически нет.

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

Со временем формат JSON вытеснил XML, а DOM API получил развитие и новые возможности. Необходимость в E4X пропала. Если нужно работать с XML, то для этого есть DOMParser и DOM API. Интересно, что со временем JSX стал распространённым синтаксисом и даже высказывались предложения по его стандартизации. При этом JSX не связан с E4X и уходит корнями к PHP и синтаксическому расширению XHP.

#js #xml
🤔12🤓4👍32🔥1
user-select

В CSS есть непримечательное свойство user-select. Оно управляет тем, как выделяется текст. Это свойство используется крайне редко, потому что к механизму выделения текста по умолчанию все привыкли и обычно менять ничего не нужно.

Я иногда использую user-select: none; чтобы отключить выделение у кнопок. Бывает, что при долгом нажатии на тач-устройствах или при быстром многократном нажатии происходит выделение подписи кнопки. На телефонах при выделении всплывает тулбар с кнопками “копировать”, “перевести”, зумится экран или ещё что-то происходит. Обычно для кнопок этого не хочется.

Недавно узнал, что у свойства user-select есть значение all из статьи Стаса Мельникова. Меня это натолкнуло на некоторые мысли, где и как это можно использовать.

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

И тут на сцену выходит user-select: all;. Свойство включает автоматическое выделение всего, что есть в текущем узле DOM и всех его дочерних узлах. Иными словами будет выделен весь текст узла. Это приводит к тому, что одиночное нажатие или тап выделяет всё сразу, включая спецсимволы и текст вложенных узлов.

Где это может пригодиться:

- Блоки с кодом. Как инлайновые <code>, <samp>, <kbd>, <var>, так и потоковые <pre> часто содержат код с кавычками, скобками, точками и прочим. А в <pre> часто вкладываются <span>-ы для реализации подсветки синтаксиса. К тому же сниппеты кода часто копируются для воспроизведения.
- Номера телефона, электронная почта. Если по каким-то причинам для их разметки не подходит <a> со схемой (tel:, email:), то можно обернуть в <span> с user-select: all;, чтобы номер или почта выделялись целиком в один клик, включая спецсимволы.
- Идентификаторы. Это может быть артикул товара, ID пользователя в системе, штриход, промокод, номер плательщика и так далее. Такие идентификаторы иногда содержат спецсимволы. Да и просто быстро выделить весь идентификатор одним кликом удобно.

Думаю можно ещё придумать места, где это применимо. В целом, если пользователи что-то часто копируют, то это может быть хорошим местом для применения user-select: all;. Думаю, это свойство также могут использовать разработчики WYSIWYG и текстовых редакторов.

В каком-то смысле свойство может быть ленивой реализацией кнопки “скопировать”, которую часто делают в блогах про разработку рядом с блоками кода. Нажали на код, выделился весь код в блоке, нажали Ctrl+C (или тапнули “копировать” на тулбаре). Это не заменяет отдельную кнопку “скопировать”, но будет как прогрессивное улучшение и дополнение к ней.

Кстати, у Стаса есть канал CSS isn’t magic. Там узнаю интересные штуки. Рекомендую посмотреть.

#css
🔥17👍84🤬1
Улучшения <dialog>

В ноябре прошлого (2024) года команда Chrome поделилась улучшениями <details>, которые сделают его удобнее с точки зрения DX. Теперь представлены улучшения элемента <dialog>. За последние годы его хорошенько подтянули по функциональности и доступности. Тем не менее, всё ещё есть неудобные моменты. Их и хотят решить новыми функциями.

Декларативное открытие и закрытие

Popover API предлагает декларативное создание кнопки-триггера через атрибуты popovertarget и popovertargetaction. Диалог можно открыть только вызовом методов в JS. Предложение Invokers Command предлагает декларативный способ открытия и закрытия диалогов через атрибуты по аналогии с Popover API. Я делал обзор предложения (со старым синтаксисом invokertarget/invokeraction)

<button type="button" commandfor="dialog" command="show-modal">
Show modal
</button>

<dialog id="dialog">
<p>I am some dialog content...</p>
<button type="button" commandfor="dialog" command="close">
Close dialog
</button>
</dialog>


Атрибут closedby

Это новый атрибут, который добавлен в спецификацию HTML. Он предназначен для управления механизмом закрытия диалога. При значении closerequest диалог закроется кнопкой Esc, жестом «назад», специальными жестами в скринридере, методом close() или командой close в HTML. При значении any, помимо указанных способов, диалог закроется при нажатии за его пределами или по ::backdrop. При значении none закрыть диалог можно будет только программно.

События beforetoggle и toggle

Для диалогов, поповеров и <details> разработаны новые события. beforetoggle происходит перед показом/скрытием элемента, а toggle — после. Оба события предоставляют свойства oldState и newState со значениями "open" или "closed". Так можно определить, элемент показывается или скрывается. preventDefault() в обработчике beforetoggle отменяет показ/скрытие.

scrollbar-gutter: stable

Свойство не новое и уже достаточно давно доступно в браузерах (в Safari недавно тоже завезли). Свойство управляет пространством, которое резервируется для скроллбара. Работает только для скроллбаров, которые занимают место, на плавающие скроллбары не влияет. Свойство решает проблему смещения страницы, когда при открытии диалога прокрутка блокируется через overflow: hidden.

***

Эти нововведения направлены на более удобную работу с элементом <dialog>. Что-то уже реализовано во всех браузерах, что-то частично, а что-то находится за экспериментальными флагами. Постепенно функции появятся во всех браузерах.

#html
👍16🔥2🌚21
You don't know HTML: translate

В браузерах есть встроенные функции перевода страницы. Chrome, например, отображает попап и предлагает перевести страницу, если обнаружит, что язык контента не соответствует языку системы.

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

Избежать этого поможет глобальный атрибут translate, который можно указывать у любого элемента. Он входит в стандарт HTML и принимает два значения: yes и no. Переводчики учитывают этот атрибут и не переводят текст внутри элементов, у которых установлено translate="no".

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

При вёрстке полезно указать translate="no" для блоков с контентом, который не нужно переводить: ФИО, email (если разделён на несколько частей), специальные названия, фрагменты кода, никнеймы. Важно помимо этого указать правильное значение атрибута lang у <html> и фрагментов контента на других языках.

Анастасия Батарей поделилась статьёй с примерами использования атрибута в рамках адвент-календаря HTMHell. Также короткой заметкой с примером поделился Стефан Джудис.

#ydkhtml #html
14👍10🔥3😱3🥰1
Анимация ключевых слов

В прошлом году в Chromium браузерах появилась возможность создавать анимацию между значениями длины и специальными ключевыми словами (intrinsic sizing keywords). Значения длины — это px, rem, % и так далее. Ключевые слова — это auto, min-content, fit-content и другие. Они доступны в качестве значений некоторых свойств, например height.

Проблема в том, что браузеру нужно вычислить значение, соответствующее ключевому слову на основе контента. Поэтому не совсем понятно, как анимировать 0px в auto. А auto это сколько? Теперь есть механизм, который включает интерполяцию. Браузер будет вычислять необходимые промежуточные значения и делать переходы между ними.

Включается такое поведение свойством interpolate-size: allow-keywords. Его можно указать у конкретного элемента, где это нужно, или глобально для всей страницы, так как свойство наследуемое.

:root {
/* Включение интерполяции для всей страницы */
interpolate-size: allow-keywords;
}

.accordion {
/* Или только для конкретного селектора */
interpolate-size: allow-keywords;
}


Классический пример — аккордеон. Нужно, чтобы высота блока с произвольным контентом плавно изменялась от 0 до высоты контента и наоборот. Так как контент произвольный, высота заранее не известна и хотелось бы использовать ключевое слово auto или max-content. Раньше для этого использовался JS, теперь можно сделать на CSS.

.accordion__content {
height: 0px;
overflow: hidden;
transition: height .5s;
interpolate-size: allow-keywords;
}

.accordion__button[aria-expanded="true"] + .accordion__content {
height: max-content;
/* или auto вместо max-content */
}


Другой способ — использовать новую функцию calc-size(). Это как calc(), только умеет работать с ключевыми словами.

.accordion__content {
height: 0px;
overflow: hidden;
transition: height .5s;
}

.accordion__button[aria-expanded="true"] + .accordion__content {
height: calc-size(max-content, size);
/* или auto вместо max-content */
}


Особенность calc-size() в том, что ко второму аргументу можно применять математические операции. Можно сделать так, чтобы аккордеон открылся на высоту контента и ещё дополнительные 20px:

.accordion__button[aria-expanded="true"] + .accordion__content {
height: calc-size(max-content, size + 20px);
/* или auto вместо max-content */
}


В статье на Chrome for Developers более подробно описано как это всё работает с примерами кода и видео (для браузеров, которые ещё это не поддерживают).

#css
🔥20👍5🌚21
Техника фасадов

Использование фасадов — это техника оптимизации загрузки виджетов сторонних поставщиков. Речь о виджетах, которые вставляются на страницу через <iframe> или <noscript> с инициализацией в <div>: карты, плееры YouTube, чат-боты, рейтинги и так далее.

В случае с <iframe> проблема в том, что внутри загружается отдельный документ со своими стилями и скриптами. И это происходит для каждого <iframe>. С <noscript> проблема в том, что код второстепенных виджетов загружает основной поток и конфликтует с более важными скриптами.

Частично эти проблемы решаются ленивой загрузкой (<iframe loading="lazy">) и отложенными скриптами (<noscript defer>). Ещё можно выставить низкий приоритет загрузки. Не смотря на это, ресурсы всё равно загрузятся и обработаются, а пользователь может даже не будет взаимодействовать с виджетом.

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

К заглушке (фасаду) добавляются обработчики событий: нажатие, наведение курсора или появления в области просмотра. Событие инициирует загрузку ресурсов и фасад заменяется на настоящий виджет. В итоге ресурсы загружаются только тогда, когда они действительно нужны.

У Вадима Макеева есть видео с реализацией фасада для встраиваемого плеера YouTube. Ролику уже 6 лет, но он всё ещё актуален и хорошо демонстрирует суть (вместо padding-хака для пропорций сейчас стоит использовать свойство aspect-ratio).

Для плеера YouTube есть готовый веб-компонент с реализацией фасада. В его описании есть ссылки на компоненты для Vimeo и других виджетов. Для Nuxt есть отдельное решение для сторонних скриптов, которое в том числе использует фасады. Lighthouse будет выдавать рекомендацию по использованию фасадов, если обнаружит на странице сторонние виджеты.

#performance
🔥21👍92🌚2
Функция attr()

В CSS давно существует функция attr(). Настолько давно, что она поддерживается в IE8. Её задумка в том, чтобы получать данные из атрибутов и применять их к свойствам в CSS. Если посмотреть синтаксис и параметры attr() на MDN, то это достаточно мощная функция. Нюанс в том, что браузеры поддерживают ограниченный набор возможностей. Сейчас функция работает только со строковыми значениями и только для свойства content.

<style>
a::after {
content: ' (ссылка: ' attr(href) ')';
}
</style>

<p>Подробнее про функцию <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/attr">attr()</a> на MDN.</p>

<!--
Будет отрисовано:

Подробнее про функцию attr() (ссылка: https://developer.mozilla.org/en-US/docs/Web/CSS/attr) на MDN.
-->


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

В Chrome 133 завозят обновления функции attr(), расширяя её возможности до уровня, описанного в спецификации и на MDN. Теперь её можно использовать во всех CSS-свойствах, указывать тип данных, единицу измерения и резервное значение.

Тип данных указывается после имени атрибута через type(). Это нужно, чтобы CSS правильно обработал строковое значение из атрибута и конвертировал его в нужный тип.

<style>
div {
color: attr(data-color type(<color>));
}
</style>

<div data-color="red"></div>


Поддерживаемые типы:

- <angle>: угол (90deg или 0.25turn)
- <color>: цвет (redrgb(255 0 0), #ff0000oklch(62.8% 0.2577 29.24), и т.д.)
- <custom-ident>: пользовательский идентификатор (используется для именованных анимаций View Transition или именованных якорей в Anchor Positioning)
- <integer>: целое число (5, 100)
- <length>: число + единица измерения (10px, 5rem, 40vw)
- <length-percentage>: как <length>, но ешё может быть в процентах
- <number>: число с опциональной плавающей точкой (5, 1.5, .25)
- <percentage>: процент (25%)
- <resolution>: разрешение (96dpi)
- <time>: время (3s или 3000ms)
- <transform-function>: функция трансформации (rotate(180deg))

Вместо типа значения можно указать одну из единиц измерения. На основе этого браузер определит нужный тип.

<style>
div {
width: attr(data-size px); /* 50px */
height: attr(data-size px); /* 50px */
}
</style>

<div data-size="50"></div>


Вторым параметром в attr() можно указать резервное значение. Оно будет использоваться, если атрибут не указан, указан с пустым значением или не удалось конвертировать тип.

<style>
div {
width: attr(data-size px, 50px);
height: attr(data-size px, 50px);
}
</style>

<div data-size="big"></div>


Эти нововведения позволят передавать значения из HTML напрямую в CSS. Это возможно и сейчас с Custom Properties, но с attr() выглядит удобнее.

<p id="p1" style="--font-size: 2rem;"></p>
<p id="p2" data-font-size="2"></p>

<style>
#p1 {
font-size: var(--font-size, 1rem);
}

#p2 {
font-size: attr(data-font-size rem, 1rem);
}
</style>

<noscript>
const newFontSize = 1.5;

p1.style.setProperty('--font-size', `${newFontSize}rem`);
p2.dataset.fontSize = newFontSize;
</noscript>


Я думаю сообществу ещё предстоит открыть кейсы, где это можно применить. Но я уже вижу в этом пользу для контента, который настраивается через CMS. Можно будет записывать настройки из CMS в атрибуты и подтягивать через attr() вместо style с пользовательскими свойствами.

Больше подробностей и примеров в статье Уны Кравец и в блоге Chrome for Developers. Другие браузеры положительно смотрят на это нововведение и, вполне возможно, что тоже займутся его внедрением.

#html #css
🔥19👍75🌚2
RSS

В 459 выпуске Веб-стандартов слушатель задал вопрос:

Насколько актуален RSS и стоит ли его внедрять в сайты и приложения?


Пару недель назад мне попалась заметка I've been advocating for RSS support, and you should too. Автор призывает использовать RSS и приводит ряд преимуществ.

RSS — это набор основанных на XML форматов данных для предоставления обновлений контента. Плагин для CMS, фреймворка или инструмента сборки автоматически генерирует файл с XML-разметкой в специальном формате — фид. Он содержит список с заголовком, датой публикации, кратким анонсом, ссылкой, может содержать полный текст статьи и другие мета-данные. Фид обычно расположен в корне сайта под названием feed.xml или в виде ссылки с оранжевой иконкой, похожей на иконку Wi-Fi.

Разные программы и сервисы способны потреблять фиды, достаточно указать ссылку. Есть специализированные приложения — RSS-ридеры. Они позволяют подписаться на фид и получать обновления. Я, например, использую Feedly. У него более 5 млн скачиваний на Play Market, что говорит о достаточно большой аудитории. Есть и другие популярные аналоги.

В чём преимущество RSS:

- Распространение. Есть сервисы, которые принимают фид в качестве источника данных. Например, все популярные подкаст-платформы принимают фид с описанием, датой и ссылкой на аудио. При выходе нового эпизода платформы подхватят данные и автоматически загрузят эпизод к себе. Фиды распространены в сфере электронной коммерции для дистрибуции товаров.

- Удобство чтения. Можно подписаться на блоги любимых авторов, анонсы брендов, новостные ленты, объявления и так далее. Вместо посещения сайтов можно потреблять контент через RSS-ридер. Никаких алгоритмических лент, рекламы каждые три поста, раздражающих всплывающих чатов, куки-баннеров и странного дизайна.

- Электронные книги. Современные электронные книги оснащены браузером и позволяют выходить в Интернет. Мощности электронных книг недостаточно для посещения многих сайтов, особенно перегруженных скриптами. И чёрно-белые экраны с низкой частотой обновления не предназначены для просмотра динамических сайтов. Но можно читать контент через RSS-ридер, который отображает контент в упрощённом виде.

- API. Фид может выступать в роли публичного API. Если нужно получить список статей, товары в интернет-магазине, новости с новостного портала или объявления с онлайн-доски, можно использовать фид как источник данных. Запросить через fetch(), использовать DOMParser для разбора и методы DOM API для ивлечения нужных данных.

- Архив. Фид можно использовать для генерации страницы архива. Пользователь будет заходить по адресу фида и видеть красивую страницу с архивом контента. Это возможно благодаря таблицам стилей XSLT. Старая технология, которая уже практически не используется, но всё ещё поддерживается всеми браузерами.

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

#xml
12👍6👌5🔥4👨‍💻2🆒1
Про Lighthouse

Периодически вижу, как разработчики делятся скриншотами аудита Lighthouse из DevTools с четырьмя соточками. Хорошо, что разработчики находят время запустить аудит и стремятся улучшить показатели. Однако соточки не говорят о том, что сайт действительно хорошо сделан.

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

Второй нюанс в том, что Lighthouse в DevTools запускается на текущем устройстве. Как правило, у разработчиков достаточно мощные устройства, и цифры получаются выше. Также отсутствуют сетевые издержки. Пользователи посещают сайты с более дешёвых, старых и медленных устройств, и не всегда со стабильным соединением. Chrome позволяет выставить задержку сети и замедлить процессор, но это эмуляция, которая далека от реальности.

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

Lighthouse в DevTools предназначен для получения общей картины производительности во время разработки. Это позволяет на ранней стадии отлавливать распространённые проблемы и регрессии. Можно думать об этом как о линтере. Здесь локальный запуск в DevTools играет на руку: можно делать контрольные замеры в начале работы, вносить изменения, повторно замерять и сравнивать.

Важно отметить, что баллы Lighthouse не влияют на SEO. Если аудит показывает максимальные баллы по всем критериям, для поисковой системы это ничего не значит. Влияют на SEO полевые метрики Core Web Vitals, собранные с реальных пользователей, посещающих сайт через Chrome. Их можно увидеть в отчёте CrUX при достаточном количестве посещений. Но цифры из Lighthouse коррелируют с Core Web Vitals.

В конце пару мыслей:

- Не полагайтесь на Lighthouse, если хотите отслеживать производительность сайта. Это не совсем релевантно, а полученные баллы ни на что, в общем-то, не влияют.
- Используйте Lighthouse для получения общей картины производительности во время разработки или для промежуточных замеров при работе над производительностью.
- Запускайте аудит для каждого ключевого типа страниц сайта. Желательно запускать аудит на максимально тяжёлых вариантах этих страниц.
- Для получения более реалистичной картины используйте PageSpeed Insights или WebPageTest. PageSpeed под капотом использует тот же Lighthouse, но аудит запускается на усреднённых устройствах и учитывает сетевые издержки. При наличии достаточного количества данных, PageSpeed покажет результаты Core Web Vitals из CrUX. WebPageTest также даёт много полезных данных, особенно если научиться правильно их интерпретировать.
- Если вы хотите серьёзно заняться производительностью, то в первую очередь ориентируйтесь на полевые метрики RUM (Real User Monitoring), Core Web Vitals и данные из CrUX. Постепенно можно разработать и внедрить собственные метрики.

#performance
👍20🔥158🌚3
Пользовательские функции в CSS

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


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

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

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

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


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

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

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

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


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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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


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

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

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

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

#css
🤔11👎2🔥2🤯21