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

Автор: @alexnozer
Download Telegram
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
Badging API

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

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

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

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

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

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

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


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

#js #web_api
🔥20👍92
Eleventy v3

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

Eleventy is a simpler static site generator


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Элемент с классом container или wrapper часто используется в вёрстке, чтобы ограничить ширину контента на десктопных экранах. Стили классического резинового контейнера выглядят примерно так:

.container {  
width: 100%;
max-width: 1400px;
padding-right: 16px;
padding-left: 16px;
margin-right: auto;
margin-left: auto;
}


width: 100% не обязательно указывать, но это нужно для случаев, когда контейнер находится внутри flex-контекста. max-width ограничивает ширину контейнера. padding задаёт отступы, чтобы контент не прилипал к краям на узких экранах, а margin со значением auto используется для горизонтального центрирования. Обычно контейнер применяется так:

<section class="section">
<div class="container">
<!-- Контент первой секции -->
</div>
</section>
<section class="section">
<div class="container">
<!-- Контент второй секции -->
</div>
</section>


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

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

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

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

:root {
--container-max-width: 1400px;
--container-min-field: 16px;
--container-field: calc(
(var(--vw) - var(--container-max-width)) / 2
);
}

.container {
width: 100%;
padding-inline: max(
var(--container-field),
var(--container-min-field)
);
}


В :root объявлены пользовательские свойства:

- --container-max-width — максимальная ширина контейнера.
- --container-min-field — минимальный размер одного поля, чтобы контент не прилипал к краям экрана.
- --container-field — динамический размер одного поля, который вычисляется так: от ширины вьюпорта отнимается максимальная ширина контейнера и делится на 2. В итоге получается тот размер, который обычно достигается через margin со значением auto.

Ширина области просмотра в формуле представлена пользовательским свойством --vw, чтобы учесть ширину прокрутки. Про это есть отдельный пост.

Далее используется функция max(), чтобы выбрать максимальное значение из динамического и минимального поля. На десктопе поле будет занимать всё свободное пространство от края экрана до контентной области контейнера, а на узких экранах поле будет минимальной ширины.

Работает это аналогично классическому контейнеру. Благодаря полям вместо отступов на одном узле можно и фон использовать, и ширину контента ограничить. То есть смиксовать секцию и контейнер, говоря терминами БЭМ.

<section class="container section">
<!-- Контент -->
</section>

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

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

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

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

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

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

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

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

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

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

#html #web_api
👍10🔥5🫡21