Во-первых, это может давать неочевидные бенефиты от того, что проявляется «магия нетворкинга и слабых связей», то есть собрание в одном месте незнакомых разносторонних людей может непредсказуемым образом вовремя дать какую-то нужную инфу, совет или предложение, которые существенно повлияют на мою жизнь.
Во-вторых, ведением канала у автора (то есть меня) создаётся некий образ, который может помогать. То есть незнакомые люди из пункта 1, хоть мы и не знакомы, но сразу на старте общения уже представляют меня в определённом виде (честно, был удивлён этому, хотя вроде очевидно). Наверное это называется «репутация».
И ещё добавлю пункт, который понял ещё раньше. Что блог — норм такая площадка для прокачки внутренней силы, помимо внешней из пунктов 1 и 2. То есть страдаешь, изучаешь что-то, формулируешь, пишешь пост
То есть, хоть блог и не приносит прямых материальных доходов и при этом забирает временнЫе ресурсы, он добавляет 1) рандомные бонусы от общества и 2) повышает репутацию. И ещё это приятно в итоге, хоть и бывает трудно в процессе.
В общем, это я к чему. Зря забросил канал, надо продолжать вести, буду вкатываться обратно потихонечку. Вот, кстати, и новый пост.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16❤9🔥9
Проблемы во фронтовых проектах и их решения
Подсобрал список типовых проблем и их решений, с которыми неизбежно сталкиваются разросшиеся фронтовые монорепы или семейства проектов, если заранее не позаботиться о решениях.
⚠️ Неудобная локальная разработка, зоопарк технологий/подходов, сложно вкатываться в существующие проекты
Решения:
0️⃣ Переход на единую типовую сборку (Vite, Webpack…) с общим конфигом, общий инструментарий для монореп (workspaces, turbo, lerna…).
1️⃣ Создание единого шаблона для старта нового приложения.
2️⃣ Выработка единообразного стека:
🔡 система: node/bun, TS, фреймворк (React, Vue, Next…)
🔡 зависимости:
🔵 пакетный менеджер (yarn/npm/pnpm, для консистентности лок-файлов)
🔵 стейт-менеджер (или его отсутствие => Tanstack Query)
🔵 роутер
🔵 инструменты для работы с формами (хуки, валидация, маски для инпутов…) и валидатор схем
🔵 стилизация (CSS Modules, SASS, Styled-подобные, ваниль), использование единообразного UI-kit (Storybook)
🔵 утилиты (работа с датами, общие либы типа lodash)
3️⃣ Процесс регулярных обновлений зависимостей.
4️⃣ Единообразная структура проекта/проектов, приведение к единому виду (FSD…).
5️⃣ Архитектурные соглашения по выносу шаренных кусочков в микрофронты, связь между «отдельными кусочками» приложений в рамках общих приложений.
6️⃣ Общий/стандартизированный CI/CD.
7️⃣ Общий подход к проксированию запросов на бэк, к развёртыванию проекта локально.
8️⃣ Общий код-стайл и линтинг (ESLint, Prettier, Biome…).
9️⃣ Общий подход к логированию аналитики.
⚠️ Долгая разработка, необходимость частых ресерчей
Решения:
0️⃣ Централизованное внедрение AI-инструментов для помощи в написании кода (выбор единых агентов, генерация вспомогательных артефактов типа
1️⃣ Создание бойлерплейта для документации и внедрение AI-генерации документации. Хранение документации в самих проектах и регулярная перегенерация.
⚠️ Дублирования кода, велосипеды
Решение:
Вынос повторяющихся пакетов в шареное хранилище переиспользуемых решений, хостинг в приватном npm-реестре, поддержка.
⚠️ Большое количество багов, уязвимостей и неконтролируемый/плохой перф и UX
Решения:
0️⃣ Внедрение мониторинга ошибок (Sentry), настройка алертов в чаты/дашбордов на ошибки (Grafana).
1️⃣ Подключение измерения метрик перфа в рантайме у пользователей (Vitals, TTVC и подобные), а также на CI/CD («бюджет» на перф в пулреквестах).
2️⃣ Интеграция в CI/CD инструментов для поиска уязвимостей в коде.
3️⃣ Процесс регулярного пополнения и разгребания бэклога техдолга (баги, уязвимости, оптимизации).
4️⃣ Внедрение среды для тестов/автотестов, написание/генерация юнит-тестов и e2e-тестов, регрессионное тестирование, запуск на CI/CD.
5️⃣ Выработка плавил работы со статикой (картинки на CDN, автоматизированное сжатие, ленивая загрузка).
⚠️ Несогласованная командная работа
Решения:
0️⃣ Использование контрактов (Open API) на бэке/фронте, кодогенерации типов и API-клиентов, клиент-серверных фреймворков (trpc).
1️⃣ Внедрение автоматизации в код-ревью (автоназначение ревьюеров, выработка правил мерджа в master, AI-генерация описаний пулреквестов и ревью).
2️⃣ Договорённости в единообразном именовании коммитов (conventional commits), веток, пулреквестов; единые подходы к организации дев/стейдж/релиз-веток; семантическое версионирование релизов.
#Лаборатория_веб_платформы
@web_platform | Поддержать платформу💕
Подсобрал список типовых проблем и их решений, с которыми неизбежно сталкиваются разросшиеся фронтовые монорепы или семейства проектов, если заранее не позаботиться о решениях.
⚠️ Неудобная локальная разработка, зоопарк технологий/подходов, сложно вкатываться в существующие проекты
Решения:
⚠️ Долгая разработка, необходимость частых ресерчей
Решения:
claude.md)⚠️ Дублирования кода, велосипеды
Решение:
Вынос повторяющихся пакетов в шареное хранилище переиспользуемых решений, хостинг в приватном npm-реестре, поддержка.
⚠️ Большое количество багов, уязвимостей и неконтролируемый/плохой перф и UX
Решения:
⚠️ Несогласованная командная работа
Решения:
#Лаборатория_веб_платформы
@web_platform | Поддержать платформу
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12🤮8❤3🥱2👎1👌1
Про микрофронты на коленке. Iframe
Мне в работе попадалась реализация микрофронтов на основе
Как это работает: по определённому адресу хостится мини-приложение, например, по урлу
Важный момент: чтобы не возникало проблем с CORS, при таком подходе приложения должны находиться на одном домене, а также полностью должны совпадать порт и протокол.
Редко когда такое мини-приложение живёт само по себе. Обычно нужно, чтобы оно как-то общалось со внешним миром. Для этого есть система двунаправленных событий, которые отправляются с помощью метода
Получаются отправленные события подпиской на
parent.html:
child.html:
Эту идею можно раскрутить и дальше. Например, если одни и те же данные с сервера нужны в нескольких микрофронтах, то это может привести к дублированию запросов. Один из выходов — перенести этот дублирующийся запрос в хост-приложение и шарить его с фреймами. Ещё один вариант — сделать отдельный
Также помимо общения через
Broadcast Channel обеспечивает широковещательную связь между всеми контекстами одного origin, а Channel Messaging создает секьюрный двусторонний канал связи между двумя конкретными контекстами.
То есть этот самый «стор с шаренными данными» при получении данных может в зависимости от цели пулять данные или на всех, или точечно в конкретное место. Работают оба способа тоже на похожих
В случае с Broadcast Channel надо как-то пошарить между потребителями созданный объект самого канала:
В случае с Channel Messaging параметры для связи двух контекстов передаются в доп параметрах
Бонус: у
И ещё: в хромиум-браузерах все
#Лаборатория_веб_платформы
@web_platform | Поддержать платформу🌟
Мне в работе попадалась реализация микрофронтов на основе
iframe. В целом, это вариант, когда надо «дёшево и сердито». Обычно такое используется, когда хост-приложение не одно и в каждом внутри своеобразная сборка, то есть федерацией модулей или монорепой всех проблем не решить.Как это работает: по определённому адресу хостится мини-приложение, например, по урлу
example.com/header грузится общее приложение хедера для всех возможных хост-сред. Во все хост-приложения хедер встраивается через iframe:
<iframe src="/header"></iframe>
Важный момент: чтобы не возникало проблем с CORS, при таком подходе приложения должны находиться на одном домене, а также полностью должны совпадать порт и протокол.
Редко когда такое мини-приложение живёт само по себе. Обычно нужно, чтобы оно как-то общалось со внешним миром. Для этого есть система двунаправленных событий, которые отправляются с помощью метода
postMessage:window.parent.postMessage — отправляет сообщение изнутри фрейма наружу, в родителя.iframe.contentWindow.postMessage — отправляет сообщение снаружи, от родителя, внутрь фрейма.Получаются отправленные события подпиской на
message (как изнутри iframe, так и в родительском приложении снаружи):parent.html:
<iframe id="myFrame" src="/child"></iframe>
<script>
const iframe = document.getElementById('myFrame');
// Отправка сообщения во фрейм
iframe.addEventListener('load', () => {
iframe.contentWindow.postMessage({ type: 'fromParent', payload: 'Привет!' }, '*');
});
// Получение сообщения от фрейма
window.addEventListener('message', (event) => {
if (event.data.type === 'fromChild') {
console.log('Получено от фрейма:', event.data.payload);
}
});
</script>
child.html:
<script>
// Отправка сообщения родителю
window.parent.postMessage({ type: 'fromChild', payload: 'Ответ!' }, '*');
// Получение сообщения от родителя
window.addEventListener('message', (event) => {
if (event.data.type === 'fromParent') {
console.log('Получено от родителя:', event.data.payload);
}
});
</script>
Эту идею можно раскрутить и дальше. Например, если одни и те же данные с сервера нужны в нескольких микрофронтах, то это может привести к дублированию запросов. Один из выходов — перенести этот дублирующийся запрос в хост-приложение и шарить его с фреймами. Ещё один вариант — сделать отдельный
iframe, в котором визуально ничего нет (его можно даже скрыть), но есть данные. Данные этот стор-iframe может шарить наружу всем желающим, тоже через postMessage.Также помимо общения через
postMessage шаринг данных между контекстами можно построить на Broadcast Channel API или Channel Messaging API.Broadcast Channel обеспечивает широковещательную связь между всеми контекстами одного origin, а Channel Messaging создает секьюрный двусторонний канал связи между двумя конкретными контекстами.
То есть этот самый «стор с шаренными данными» при получении данных может в зависимости от цели пулять данные или на всех, или точечно в конкретное место. Работают оба способа тоже на похожих
postMessage.В случае с Broadcast Channel надо как-то пошарить между потребителями созданный объект самого канала:
const bc = new BroadcastChannel("test_channel");
bc.postMessage("This is a test message.");
bc.onmessage = (event) => {
console.log(event);
};
В случае с Channel Messaging параметры для связи двух контекстов передаются в доп параметрах
postMessage:
const channel = new MessageChannel();
const port1 = channel.port1;
port1.onmessage = onMessage;
port1.postMessage("value");
Бонус: у
iframe есть атрибут loading=lazy, чтобы не подгружать те фреймы, которые находятся вне вьюпорта.И ещё: в хромиум-браузерах все
iframe на одном домене будут обрабатываться в одном потоке, то есть могут потенциально блокировать друг друга, даже если не блокируют родителя (лайфхак: если нет нужды держать их на одном домене, то выносом на другой домен можно «распараллелить потоки»).#Лаборатория_веб_платформы
@web_platform | Поддержать платформу
Please open Telegram to view this post
VIEW IN TELEGRAM
❤15👍8🔥6
А вы заметили, что со всеми этими ии-движухами как-то морально проще стало экспериментировать с новым софтом и подходами в работе. Раньше ну узнаешь, что какая-то там либа вышла, ну подумаешь, что надо когда-нибудь попробовать, и всё на том. Ну максимум на свежем проекте или новом компе что-то свежее ставишь, пробуешь.
А теперь как-то реально попроще со всем этим стало: узнаёшь, сразу пробуешь, профит (наверное это можно назвать таблеткой от FOMO, правда хоть это и купирует приступы, но провоцирует повторные 😁).
Возможно на такой волне стоит присмотреться и к остальной экосистеме. Например, вот в pnpm подвели итоги прошлого года. В v10 много всего интересного.
Самое важное — pnpm перестал доверять всем пакетам. Раньше
В v10 lifecycle scripts блокируются по умолчанию. Никакого preinstall/postinstall без явного разрешения.
Ещё одна фишка pnpm — content-addressable store (дедупликация файлов). В v10.12 сделали ещё шаг дальше — глобальное виртуальное хранилище.
Раньше каждый проект имел свой
1. Экономия диска — одинаковые графы зависимостей шарятся между проектами.
2. Быстрые установки — если 10 проектов используют
Для монорепозиториев и сложных настроек есть конфиги зависимостей. Это позволяет шарить конфиги pnpm (хуки, патчи, разрешения на билды) между проектами.
Конфиги зависимостей устанавливаются в
Это помогает шарить
Кроме того, pnpm давно умеет переключать версию Node.js для рантайма (не нужен nvm). В 2025 добавили Deno и Bun.
Pnpm сам скачает и будет использовать указанную версию для скриптов.
Поддержка JSR — тоже прикольно, но пока что не особо актуально, как я вижу.
Кроме того, ещё от себя добавлю, что в pnpm есть
#Пульс_веб_платформы
@web_platform | Поддержать платформу🌀
А теперь как-то реально попроще со всем этим стало: узнаёшь, сразу пробуешь, профит (наверное это можно назвать таблеткой от FOMO, правда хоть это и купирует приступы, но провоцирует повторные 😁).
Возможно на такой волне стоит присмотреться и к остальной экосистеме. Например, вот в pnpm подвели итоги прошлого года. В v10 много всего интересного.
Самое важное — pnpm перестал доверять всем пакетам. Раньше
pnpm install давал любому пакету в дереве зависимостей запускать произвольный код (preinstall, postinstall). Это само собой создавало вектор атак через supply chain.В v10 lifecycle scripts блокируются по умолчанию. Никакого preinstall/postinstall без явного разрешения.
allowBuilds:
esbuild: true
nx@21.6.4: true
Ещё одна фишка pnpm — content-addressable store (дедупликация файлов). В v10.12 сделали ещё шаг дальше — глобальное виртуальное хранилище.
pnpm-workspace.yaml
enableGlobalVirtualStore: true
Раньше каждый проект имел свой
node_modules. Теперь общие зависимости линкуются один раз глобально. А это:1. Экономия диска — одинаковые графы зависимостей шарятся между проектами.
2. Быстрые установки — если 10 проектов используют
react@19, pnpm линкует его один раз из глобального хранилища на машине.Для монорепозиториев и сложных настроек есть конфиги зависимостей. Это позволяет шарить конфиги pnpm (хуки, патчи, разрешения на билды) между проектами.
Конфиги зависимостей устанавливаются в
node_modules/.pnpm-config до основного графа зависимостей.
# pnpm-workspace.yaml
configDependencies:
pnpm-plugin-my-company: "1.0.0+sha512-..."
Это помогает шарить
.pnpmfile.cjs хуки между репозиториями, централизовать патчи для patchedDependencies, поддерживать список пакетов, которым разрешено запускать build скрипты.Кроме того, pnpm давно умеет переключать версию Node.js для рантайма (не нужен nvm). В 2025 добавили Deno и Bun.
// package.json
{
"devEngines": {
"runtime": {
"name": "node",
"version": "24.6.0"
}
}
}
Pnpm сам скачает и будет использовать указанную версию для скриптов.
Поддержка JSR — тоже прикольно, но пока что не особо актуально, как я вижу.
Кроме того, ещё от себя добавлю, что в pnpm есть
catalogs-алиасы (чтобы один раз в конфиге объявить набор зависимостей), workspaces (для организации монореп), возможность патчить зависимости (pnpm patch) и проверять их на актуальность (pnpm outdated)#Пульс_веб_платформы
@web_platform | Поддержать платформу
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18🔥7❤🔥1💯1
react-best-practices
Тут Vercel подогналимозгов для ваших frontend-агентов набор бестпрактисов и антипаттернов по React, можно установить в виде скиллов в ваш любимый агент или просто почитать. Темы, рассматриваемые внутри: устранение вотерфоллов, оптимизация размера бандла, SSR, дата-фетчинг на клиенте (жаль, что без tanstack, а только swr), оптимизация ререндеров, перфоманс рендеринга и JS, а также ещё немного мелочей.
#Пульс_веб_платформы
@web_platform | Поддержать платформу🌀
Тут Vercel подогнали
#Пульс_веб_платформы
@web_platform | Поддержать платформу
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥27🤔2❤1💩1🍌1💅1🗿1
Что нового Baseline Widely Available, часть 1
В прошлом году (+ в январе 2026) довольно много платформенных фич достигли статуса Baseline Widely Available — это значит, что они поддерживаются во всех основных браузерах уже 30 месяцев! А это поводбескомпромиссно тащить их в прод проверить аналитику по пользовательским устройствам и браузерам в вашем проекте (скорее всего ваши пользователи уже обзавелись браузером, которые эти фичи поддерживают).
Собрал списком все фичи с кратким описанием и моими комментами к их использованию.
CSS
Container Queries — применение стилей на основе размеров родительского контейнера, а не viewport. Позволяет создавать адаптивные компоненты, которые реагируют на свой контейнер.
Мнение: 5 из 5, но требует перестроить майндсет в целом при работе с UI (то есть не только вам как разработчику, но ещё из продукту и дизайнеру, часто думающим "мобилками"). Хорошо ложится на UI-киты и разработку изолированных компонентов.
—
Individual Transform Properties — отдельные свойства
Мнение: 3 из 5, приятный сахар, помогает сократить повторящийся код при написании трансформов, легко внедрить.
—
Motion Path — анимация элементов вдоль заданного пути с помощью
Мнение: 2 из 5, специфичная, но прикольная фича, но если появится подходящая задача и вовремя вспомнить, можно избежать использования JS для анимации движения элемента по траектории.
—
Grid Animation — анимация свойств
Мнение: 2 из 5, в целом прикольная фича для разъезжающихся динамических лейаутов, но применимость нечастая.
—
animation-composition — выбор способа комбинирования анимаций, влияющих на одно свойство.
Мнение: 2 из 5, если делаете руками CSS-анимации, может пригодиться, иначе не будет полезно.
—
color-mix() — смешивание двух цветов в заданном цветовом пространстве. Полезно для создания светлых/тёмных оттенков.
Мнение: 4 из 5, чрезвычайно мощная штука, если настраиваете собственную дизайн-систему, стайлгайд или UI-кит в проекте.
—
color() — выбор цвета из указанного цветового пространства. Поддерживает wide-gamut пространства вроде
Мнение: 4 из 5, аналогично мнению про
—
Lab и LCH — цветовые пространства с перцептивной равномерностью. Lab использует прямоугольные координаты, LCH — полярные.
Oklab и OkLCh — цветовые пространства, разработанные для лучшего соответствия человеческому восприятию цвета. OkLCh — полярный вариант Oklab.
Мнение: 4 из 5, формат
—
Small, Large, Dynamic Viewport Units — единицы
Мнение: 4 из 5, если вы когда-нибудь боролись с fullscreen-высотой в вёрстке на мобилках, то в следующий раз присмотритесь к этим значениям.
продолжение следует...
#Пульс_веб_платформы
@web_platform | Поддержать платформу⭐️
В прошлом году (+ в январе 2026) довольно много платформенных фич достигли статуса Baseline Widely Available — это значит, что они поддерживаются во всех основных браузерах уже 30 месяцев! А это повод
Собрал списком все фичи с кратким описанием и моими комментами к их использованию.
CSS
Container Queries — применение стилей на основе размеров родительского контейнера, а не viewport. Позволяет создавать адаптивные компоненты, которые реагируют на свой контейнер.
.card {
container-type: inline-size;
}
@container (min-width: 300px) {
.card { background-color: lightblue; }
}
Мнение: 5 из 5, но требует перестроить майндсет в целом при работе с UI (то есть не только вам как разработчику, но ещё из продукту и дизайнеру, часто думающим "мобилками"). Хорошо ложится на UI-киты и разработку изолированных компонентов.
—
Individual Transform Properties — отдельные свойства
translate, scale, rotate вместо одного transform. Упрощает анимации и улучшает читаемость кода.
.element {
translate: 20px 10px;
scale: 0.8;
rotate: 90deg;
}
Мнение: 3 из 5, приятный сахар, помогает сократить повторящийся код при написании трансформов, легко внедрить.
—
Motion Path — анимация элементов вдоль заданного пути с помощью
offset-path.
.element {
offset-path: path('M10 10 L 100 100');
animation: move 2s linear infinite;
}
Мнение: 2 из 5, специфичная, но прикольная фича, но если появится подходящая задача и вовремя вспомнить, можно избежать использования JS для анимации движения элемента по траектории.
—
Grid Animation — анимация свойств
grid-template-columns и grid-template-rows.
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
transition: grid-template-columns 0.3s;
}
.grid:hover {
grid-template-columns: 1fr 2fr;
}
Мнение: 2 из 5, в целом прикольная фича для разъезжающихся динамических лейаутов, но применимость нечастая.
—
animation-composition — выбор способа комбинирования анимаций, влияющих на одно свойство.
.element {
animation-composition: add; /* replace, add, accumulate */
}
Мнение: 2 из 5, если делаете руками CSS-анимации, может пригодиться, иначе не будет полезно.
—
color-mix() — смешивание двух цветов в заданном цветовом пространстве. Полезно для создания светлых/тёмных оттенков.
.element {
background: color-mix(in srgb, red 50%, white 50%);
}
Мнение: 4 из 5, чрезвычайно мощная штука, если настраиваете собственную дизайн-систему, стайлгайд или UI-кит в проекте.
—
color() — выбор цвета из указанного цветового пространства. Поддерживает wide-gamut пространства вроде
display-p3 для более насыщенных цветов.
.element {
color: color(display-p3 0.5 0.2 0.8);
}
Мнение: 4 из 5, аналогично мнению про
color-mix().—
Lab и LCH — цветовые пространства с перцептивной равномерностью. Lab использует прямоугольные координаты, LCH — полярные.
Oklab и OkLCh — цветовые пространства, разработанные для лучшего соответствия человеческому восприятию цвета. OkLCh — полярный вариант Oklab.
.element {
color: lab(50% 20 -30);
background: lch(60% 50 150);
color: oklab(0.5 0.2 -0.1);
background: oklch(0.6 0.5 120);
}
Мнение: 4 из 5, формат
oklch может также пригодиться для гибкой настройки цветовой палитры и её производных в проекте.—
Small, Large, Dynamic Viewport Units — единицы
dvh, svh, lvh для учёта UI мобильных браузеров. sv* — минимальный размер, lv* — максимальный, dv* — динамический.
.element {
height: 100svh; /* 100% минимального viewport height */
width: 100dvw; /* 100% динамического viewport width */
}
Мнение: 4 из 5, если вы когда-нибудь боролись с fullscreen-высотой в вёрстке на мобилках, то в следующий раз присмотритесь к этим значениям.
продолжение следует...
#Пульс_веб_платформы
@web_platform | Поддержать платформу
Please open Telegram to view this post
VIEW IN TELEGRAM
👍20🔥11🎉3❤1💯1
Что нового Baseline Widely Available, часть 2
...продолжение
CSS
Two-value display property — свойство
Мнение: 2 из 5, любопытно, но не более.
—
overflow: clip — обрезка содержимого без создания скроллов, с возможностью обрезки только по одной стороне
Мнение: 4 из 5, полезно, если нужно включить скролл по одной стороне + также это значение overflow, в котором не создаётся новый formatting context.
—
overscroll-behavior — управление поведением при прокрутке за границы.
Мнение: 3 из 5, очень узкоспециализированная фича, но может быть крайне полезна для управлением мульти-скроллом.
—
outline — свойства для стилизации линии вокруг элемента вне border, не влияющие на layout.
Мнение: 3 из 5, фича-то старая, но был баг в Safari, который починили в 03.2023.
—
:nth-child() of <selector> — более точный выбор элементов с фильтром по селектору.
Мнение: 4 из 5, прикольная штука для условной стилизации, полезно вспомнить перед решением реализовать такое на JS.
—
Media Query Range Syntax — новый синтаксис для медиавыражений с диапазонами.
Мнение: 4 из 5, синтаксический сахар, нравится, что наконец не нужно каждый раз вспоминать и перебирать варианты min/max-width.
—
Resolution Media Query — медиавыражение для применения стилей на основе плотности пикселей устройства.
Мнение: 2 из 5, узкоспециализированная фича, но может пригодиться для детальной стилизации для разных мобильных устройств.
—
Trigonometric Functions —
Мнение: 2 из 5, любопытно, но слишком хардкорно, малоприменимо в реальной жизни.
—
calc() Keywords — математические константы
Мнение: 1 из 5, не могу придумать, где это применить.
—
font-palette — выбор цветовой палитры из шрифта, с возможностью переопределения отдельных цветов через
Мнение: 1 из 5, малополезная фича, применимая только для "цветных" шрифтов, не стоит внимания
—
Style Containment и Inline-size Containment — изоляция стилей элемента.
Мнение: 3 из 5, мелкие малополезные фичи,
—
HTML / DOM
inert — атрибут для отключения элементов от взаимодействия и accessibility tree. Элементы не получают фокус и не реагируют на клики.
Мнение: 4 из 5, мощная и простая фича, хороший юзкейс для ограничения пределов фокуса при показе модальных окон: модалка работает, body под ней за inert-ом.
продолжение следует...
#Пульс_веб_платформы
@web_platform | Поддержать платформу⭐️
...продолжение
CSS
Two-value display property — свойство
display теперь принимает два значения для явного указания внешнего и внутреннего режима layouts. Например, inline flex или block flow.
.card {
display: inline flex;
}
.container {
display: block flow;
}
Мнение: 2 из 5, любопытно, но не более.
—
overflow: clip — обрезка содержимого без создания скроллов, с возможностью обрезки только по одной стороне
.nav-bar {
overflow-x: clip;
overflow-y: visible;
}
Мнение: 4 из 5, полезно, если нужно включить скролл по одной стороне + также это значение overflow, в котором не создаётся новый formatting context.
—
overscroll-behavior — управление поведением при прокрутке за границы.
.container {
overscroll-behavior: contain;
}
Мнение: 3 из 5, очень узкоспециализированная фича, но может быть крайне полезна для управлением мульти-скроллом.
—
outline — свойства для стилизации линии вокруг элемента вне border, не влияющие на layout.
.element {
outline: 2px dashed red;
outline-offset: 5px;
}
Мнение: 3 из 5, фича-то старая, но был баг в Safari, который починили в 03.2023.
—
:nth-child() of <selector> — более точный выбор элементов с фильтром по селектору.
li:nth-child(odd of .active) {
background: yellow;
}
Мнение: 4 из 5, прикольная штука для условной стилизации, полезно вспомнить перед решением реализовать такое на JS.
—
Media Query Range Syntax — новый синтаксис для медиавыражений с диапазонами.
@media (400px <= width <= 800px) {
/* стили */
}
Мнение: 4 из 5, синтаксический сахар, нравится, что наконец не нужно каждый раз вспоминать и перебирать варианты min/max-width.
—
Resolution Media Query — медиавыражение для применения стилей на основе плотности пикселей устройства.
@media (min-resolution: 2dppx) {
/* стили для high-DPI экранов */
}
Мнение: 2 из 5, узкоспециализированная фича, но может пригодиться для детальной стилизации для разных мобильных устройств.
—
Trigonometric Functions —
sin(), cos(), tan() в CSS для математических вычислений.
.element {
transform: rotate(calc(sin(45deg) * 1turn));
}
Мнение: 2 из 5, любопытно, но слишком хардкорно, малоприменимо в реальной жизни.
—
calc() Keywords — математические константы
e, pi, infinity, NaN в CSS math функциях.
.element {
font-size: calc(var(--base-size) * pi);
height: calc(100vh * sin(pi / 2));
}
Мнение: 1 из 5, не могу придумать, где это применить.
—
font-palette — выбор цветовой палитры из шрифта, с возможностью переопределения отдельных цветов через
@font-palette-values.
.element {
font-palette: alternate;
}
Мнение: 1 из 5, малополезная фича, применимая только для "цветных" шрифтов, не стоит внимания
—
Style Containment и Inline-size Containment — изоляция стилей элемента.
style ограничивает расчёт счётчиков и кавычек внутри элемента, а inline-size предотвращает установку inline-размеров элемента его содержимым, ускоряя рендеринг.
.widget {
contain: style;
}
.container {
contain: inline-size;
}
Мнение: 3 из 5, мелкие малополезные фичи,
style может пригодиться разве что в нестандартных нумерованных списках; inline-size, могу только в теории представить, что может быть полезно в разного рода wysiwyg-ах (за исключением контейнерных запросов, которые без этой шутки не заработают).—
HTML / DOM
inert — атрибут для отключения элементов от взаимодействия и accessibility tree. Элементы не получают фокус и не реагируют на клики.
<div inert>
<button>Неактивная кнопка</button>
</div>
Мнение: 4 из 5, мощная и простая фича, хороший юзкейс для ограничения пределов фокуса при показе модальных окон: модалка работает, body под ней за inert-ом.
продолжение следует...
#Пульс_веб_платформы
@web_platform | Поддержать платформу
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥20❤1
playwright-cli
Тут вышел https://github.com/microsoft/playwright-cli, более эргономичный, чем MCP, способ запускать ботом безголового playwright для выполнения действий в браузере.
Да, собственно, не обязательно ботом, можно просто в рамках CLI-API написать скрипт для автоматизации действия в браузере:
Вот эти вот e21 и e35 — это наименования DOM-нод в открытом сайте. Командой
По необходимости можно вызывать playwright "с головой":
Для ботов прописаны доки для выполнения рутинных задач:
- Request mocking — перехват и мок запросов
- Running Playwright code — выполнение playwright-скриптов
- Browser session management — управление браузерными сессиями
- Storage state (cookies, localStorage) — запись и чтение из браузерного хранилища
- Test generation — генерация тестов после взаимодействия с сайтом
- Tracing — запись трейсов
- Video recording — запись видео
Тут вышел https://github.com/microsoft/playwright-cli, более эргономичный, чем MCP, способ запускать ботом безголового playwright для выполнения действий в браузере.
Да, собственно, не обязательно ботом, можно просто в рамках CLI-API написать скрипт для автоматизации действия в браузере:
playwright-cli open https://demo.playwright.dev/todomvc/ --headed
playwright-cli type "Buy groceries"
playwright-cli press Enter
playwright-cli type "Water flowers"
playwright-cli press Enter
playwright-cli check e21
playwright-cli check e35
playwright-cli screenshot
Вот эти вот e21 и e35 — это наименования DOM-нод в открытом сайте. Командой
open сайт открывается и записывается его снепшот в виде yaml-файла. Каждая нода помечается своим айдишником.По необходимости можно вызывать playwright "с головой":
playwright-cli open https://playwright.dev --headed
Для ботов прописаны доки для выполнения рутинных задач:
- Request mocking — перехват и мок запросов
- Running Playwright code — выполнение playwright-скриптов
- Browser session management — управление браузерными сессиями
- Storage state (cookies, localStorage) — запись и чтение из браузерного хранилища
- Test generation — генерация тестов после взаимодействия с сайтом
- Tracing — запись трейсов
- Video recording — запись видео
🔥14❤2
Что нового Baseline Widely Available, часть 3
...продолжение
JavaScript
Array findLast() и findLastIndex() — поиск в массиве в обратном порядке.
Мнение: 3 из 5, хорошо, когда удаётся утилиты заменять на встроенные методы, так что мелочь, а приятно.
—
Array by Copy — методы
Мнение: 3 из 5, это вот из разряда "теперь надо запомнить, что так делать можно", а то по привычке спредишь массив перед любой мутацией
—
Import Maps — декларативное управление импортами через JSON.
Мнение: 4 из 5, вот это, кстати, довольно важная непроходная фича, по сути позволяющая на ES-модулях построить проект без особой сборки, чтобы само собиралось в рантайме (но пригодно, на мой взгляд, в небольших проектах).
—
JavaScript Modules in Workers — использование ES-модулей в Web Workers с
Мнение: 2 из 5, с одной стороны, да, появилась возможность импортировать ES-модули, с другой — в этом режиме они импортируются под CORS-ом, а в классических воркерах доступен и `no-cors`-режим; в общем годная тема, ещё больше можно вынести в воркер.
—
requestAnimationFrame() in Workers — анимации в Web Workers.
Мнение: 2 из 5, тоже плюсом к предыдущему пункту, если работаете с воркерами, то можно более плавно встраивать результат вычисления обратно в основной поток.
—
Compression Streams — API для сжатия/распаковки данных в формате gzip или deflate.
Мнение: 3 из 5, может быть полезно для сжатия пользовательских файлов или распаковки полученных сжатых файлов, приятно, что заехало прям в платформу, можно не использовать доп либы.
—
Constructed Stylesheets — программное создание стилей через
Мнение: 2 из 5, более навороченный способ добавлять динамически новые стили, хз, может будет полезно авторам runtime-стилизационных библиотек.
продолжение следует...
#Пульс_веб_платформы
@web_platform | Поддержать платформу⭐️
...продолжение
JavaScript
Array findLast() и findLastIndex() — поиск в массиве в обратном порядке.
const array = [5, 12, 8, 130, 44];
const isLargeNumber = (element) => element > 100;
console.log(array.findLast(isLargeNumber)); // 130
console.log(array.findLastIndex(isLargeNumber)); // 3
Мнение: 3 из 5, хорошо, когда удаётся утилиты заменять на встроенные методы, так что мелочь, а приятно.
—
Array by Copy — методы
toSorted(), toReversed(), toSpliced(), возвращающие новый массив без изменения оригинала.
const arr = [1, 2, 3];
const sorted = arr.toSorted();
const reversed = arr.toReversed();
Мнение: 3 из 5, это вот из разряда "теперь надо запомнить, что так делать можно", а то по привычке спредишь массив перед любой мутацией
—
Import Maps — декларативное управление импортами через JSON.
<script type="importmap">
{
"imports": {
"lodash": "/node_modules/lodash-es/lodash.js",
"utils": "/src/utils.js"
}
}
</script>
Мнение: 4 из 5, вот это, кстати, довольно важная непроходная фича, по сути позволяющая на ES-модулях построить проект без особой сборки, чтобы само собиралось в рантайме (но пригодно, на мой взгляд, в небольших проектах).
—
JavaScript Modules in Workers — использование ES-модулей в Web Workers с
{ type: "module" }.
// worker.js
import { processData } from './utils.js';
self.onmessage = (e) => {
const result = processData(e.data);
self.postMessage(result);
};
Мнение: 2 из 5, с одной стороны, да, появилась возможность импортировать ES-модули, с другой — в этом режиме они импортируются под CORS-ом, а в классических воркерах доступен и `no-cors`-режим; в общем годная тема, ещё больше можно вынести в воркер.
—
requestAnimationFrame() in Workers — анимации в Web Workers.
// worker.js
function animate() {
// логика анимации
requestAnimationFrame(animate);
}
animate();
Мнение: 2 из 5, тоже плюсом к предыдущему пункту, если работаете с воркерами, то можно более плавно встраивать результат вычисления обратно в основной поток.
—
Compression Streams — API для сжатия/распаковки данных в формате gzip или deflate.
const compressed = readableStream
.pipeThrough(new CompressionStream('gzip'));
Мнение: 3 из 5, может быть полезно для сжатия пользовательских файлов или распаковки полученных сжатых файлов, приятно, что заехало прям в платформу, можно не использовать доп либы.
—
Constructed Stylesheets — программное создание стилей через
CSSStyleSheet и их совместное использование через adoptedStyleSheets.
const sheet = new CSSStyleSheet();
sheet.replaceSync('.my-class { color: blue; }');
document.adoptedStyleSheets = [sheet];
Мнение: 2 из 5, более навороченный способ добавлять динамически новые стили, хз, может будет полезно авторам runtime-стилизационных библиотек.
продолжение следует...
#Пульс_веб_платформы
@web_platform | Поддержать платформу
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10🔥6❤4
Что нового Baseline Widely Available, часть 4
...продолжение
Web APIs
Offscreen Canvas — рендеринг canvas в отдельном потоке без зависимостей от DOM.
Мнение: 4 из 5, по идее довольно мощная возможность для оптимизации, опять же в связке с web worker-ом, куда можно кинуть "внеэкранную канву" для рендера чего-либо тяжёлого.
—
Screen Orientation — API для управления ориентацией экрана.
Мнение: 4 из 5, мастхэв для работы с мобилками, пригождалось.
—
Server Timing — API для получения информации о таймингах запросов с сервера.
Мнение: 4 из 5, это мощное дополнение к Web Performance API, правда требует настройки и бэка/сервера, чтобы отправлялись нужные заголовки.
—
Push Messages — API для push-уведомлений.
Мнение: 5 из 5, о, думаю, появлению этого API на iOS для PWA очень порадовались банки и другие выпиленные из сторов компании; всему, что убирает монополию вендоров в пользу веб-платформы ставим пятюню.
—
MathML — разметка математических формул без картинок и JS-библиотек.
Мнение: 4 из 5, вот это очень хоть и узкоспециализировано, но на мой взгляд тоже круто, так как позволяет latex-у и подобным инструментам, избавиться от доп кода для рендера формул.
#Пульс_веб_платформы
@web_platform | Поддержать платформу⭐️
...продолжение
Web APIs
Offscreen Canvas — рендеринг canvas в отдельном потоке без зависимостей от DOM.
const offscreen = canvas.transferControlToOffscreen();
worker.postMessage({ canvas: offscreen }, [offscreen]);
Мнение: 4 из 5, по идее довольно мощная возможность для оптимизации, опять же в связке с web worker-ом, куда можно кинуть "внеэкранную канву" для рендера чего-либо тяжёлого.
—
Screen Orientation — API для управления ориентацией экрана.
await screen.orientation.lock('landscape');
Мнение: 4 из 5, мастхэв для работы с мобилками, пригождалось.
—
Server Timing — API для получения информации о таймингах запросов с сервера.
const entries = performance.getEntriesByType('navigation');
const serverTiming = entries[0].serverTiming;
Мнение: 4 из 5, это мощное дополнение к Web Performance API, правда требует настройки и бэка/сервера, чтобы отправлялись нужные заголовки.
—
Push Messages — API для push-уведомлений.
const subscription = await pushManager.subscribe();
Мнение: 5 из 5, о, думаю, появлению этого API на iOS для PWA очень порадовались банки и другие выпиленные из сторов компании; всему, что убирает монополию вендоров в пользу веб-платформы ставим пятюню.
—
MathML — разметка математических формул без картинок и JS-библиотек.
<p>
The fraction
<math>
<mfrac>
<mn>1</mn>
<mn>3</mn>
</mfrac>
</math>
is not a decimal number.
</p>
Мнение: 4 из 5, вот это очень хоть и узкоспециализировано, но на мой взгляд тоже круто, так как позволяет latex-у и подобным инструментам, избавиться от доп кода для рендера формул.
#Пульс_веб_платформы
@web_platform | Поддержать платформу
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11🔥8❤7
npm lifecycle scripts
TIL, что в npm есть lifecycle scripts. Перед и после стандартных команд типа
Добавляете в
Сначала
Полезно, когда перед запуском надо что-то подготовить — собрать ассеты, проверить типы, создать папки.
Что можно с этим сделать:
-
-
-
Дока
#Лаборатория_веб_платформы
@web_platform | Поддержать платформу💕
TIL, что в npm есть lifecycle scripts. Перед и после стандартных команд типа
dev, build, test есть "хуки", на которые можно подцепить нужные дополнительные команды.
npm run dev | predev → dev → postdev
npm run build | prebuild → build → postbuild
npm test | pretest → test → posttest
Добавляете в
package.json скрипт с префиксом pre или post — npm запустит их с соответствующем порядке:
{
"scripts": {
"build:client": "node esbuild.client.mjs",
"predev": "npm run build:client"
"dev": "tsx src/server/index.ts",
}
}
Сначала
predev соберёт клиент, dev запустит сервер.Полезно, когда перед запуском надо что-то подготовить — собрать ассеты, проверить типы, создать папки.
Что можно с этим сделать:
-
prepublishOnly — проверки перед публикацией пакета в npm-
prebuild — генерация кода или очистка папки dist-
postinstall — автоматические действия после npm installДока
#Лаборатория_веб_платформы
@web_platform | Поддержать платформу
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤6🤓3
End-to-end разработчик
Деление на фронтенд и бэкенд никогда не было про технику. Это было больше про бизнес-процессы и найм: в индустрии удобнее искать заменяемых специалистов на узкий скоуп. Фронтендер имплементит "клиентскую" логику, бэкендер — API и "серверную" логику. Но по сути это единое целое и деление, если посмотреть свежим взглядом, весьма условное. Я прекрасно помню времена, когда фреймворки не затачивались конкретно на фронт/бэк, а были про веб-разработку проекта целиком.
И сейчас это деление начинает терять смысл. ИИшки привносят собой инфляцию ценности навыков (и стоимости строк кода). Уметь писать код стоит меньше, чем год назад. И будет стоить ещё меньше через год. Причём стоит меньше и в деньгах, и в самой ценности навыка: знания становятся все более и более общедоступны и легкодоступны, и само понятие конкретного навыка — это теперь не что-то труднодостигаемое и "выстрадываемое" потом и кровью.
Приходит эпоха полнофункционального разработчика (олды тут? привет, вебмастера). Это как бы «фуллстек» в старом понимании (например, в связке React + Node) + одновременно, который понимает систему целиком от и до, может и обозревать верхнеуровнево, и "зумиться" до детальной имплементации, настраивает инфраструктуру, валидирует, что и как система делает, а главное — следит, как система соотносится с бизнесом, насколько качественно решает его проблемы. End-to-end-разработчик.
Карьерный путь становится короче и интенсивнее. "Junior" в привычном смысле исчезает — вместо него "junior senior": человек приходит уже с наработанным опытом через пет-проекты, симуляционные окружения. С образованием вообще всё просто небывало круто. Как никогда раньше обучение может быть безостановочным процессом и не привязанным к конкретному обучающему вендору — селф-обучение выходит на первый план, а бизнесы напрямую предоставляют свою инфру для специализации в нужном контексте.
Реагировать на происходящее можно по-разному. Отрицать, игнорировать, плыть по течению или против. Но если хочется сохранить привычные заработки и скорость их роста, в обозримом будущем все пути ведут к тому, чтобы брать на себя бо́льшую ответственность. Для разработчика это или уходить в пипл-менеджмент, или же расширять и углублять технику. Я за свою карьеру два раза проходит цикл "разраб -> тимлид -> разраб" и понял, что не хочу копать в менеджмент дальше. Поэтому сейчас отзываясь на вызовы нового времени выбираю второе.
Так что буду уделять больше своего времени частям веб-разработки и платформы, которые выходят за пределы интерфейса в браузере: инфраструктуре, "серверу", инструментам сборки и доставки, проектированию систем. И вникать в зарождающуюся роль e2e-разработчика — теперь больше "пчеловода", чем "собирателя".
Думаю, это также отразится и на содержании блога.
Всем послушных пчёл!👁️
#Фикшн_веб_платформы
@web_platform | Поддержать платформу
Деление на фронтенд и бэкенд никогда не было про технику. Это было больше про бизнес-процессы и найм: в индустрии удобнее искать заменяемых специалистов на узкий скоуп. Фронтендер имплементит "клиентскую" логику, бэкендер — API и "серверную" логику. Но по сути это единое целое и деление, если посмотреть свежим взглядом, весьма условное. Я прекрасно помню времена, когда фреймворки не затачивались конкретно на фронт/бэк, а были про веб-разработку проекта целиком.
И сейчас это деление начинает терять смысл. ИИшки привносят собой инфляцию ценности навыков (и стоимости строк кода). Уметь писать код стоит меньше, чем год назад. И будет стоить ещё меньше через год. Причём стоит меньше и в деньгах, и в самой ценности навыка: знания становятся все более и более общедоступны и легкодоступны, и само понятие конкретного навыка — это теперь не что-то труднодостигаемое и "выстрадываемое" потом и кровью.
Приходит эпоха полнофункционального разработчика (олды тут? привет, вебмастера). Это как бы «фуллстек» в старом понимании (например, в связке React + Node) + одновременно, который понимает систему целиком от и до, может и обозревать верхнеуровнево, и "зумиться" до детальной имплементации, настраивает инфраструктуру, валидирует, что и как система делает, а главное — следит, как система соотносится с бизнесом, насколько качественно решает его проблемы. End-to-end-разработчик.
Карьерный путь становится короче и интенсивнее. "Junior" в привычном смысле исчезает — вместо него "junior senior": человек приходит уже с наработанным опытом через пет-проекты, симуляционные окружения. С образованием вообще всё просто небывало круто. Как никогда раньше обучение может быть безостановочным процессом и не привязанным к конкретному обучающему вендору — селф-обучение выходит на первый план, а бизнесы напрямую предоставляют свою инфру для специализации в нужном контексте.
Реагировать на происходящее можно по-разному. Отрицать, игнорировать, плыть по течению или против. Но если хочется сохранить привычные заработки и скорость их роста, в обозримом будущем все пути ведут к тому, чтобы брать на себя бо́льшую ответственность. Для разработчика это или уходить в пипл-менеджмент, или же расширять и углублять технику. Я за свою карьеру два раза проходит цикл "разраб -> тимлид -> разраб" и понял, что не хочу копать в менеджмент дальше. Поэтому сейчас отзываясь на вызовы нового времени выбираю второе.
Так что буду уделять больше своего времени частям веб-разработки и платформы, которые выходят за пределы интерфейса в браузере: инфраструктуре, "серверу", инструментам сборки и доставки, проектированию систем. И вникать в зарождающуюся роль e2e-разработчика — теперь больше "пчеловода", чем "собирателя".
Думаю, это также отразится и на содержании блога.
Всем послушных пчёл!
#Фикшн_веб_платформы
@web_platform | Поддержать платформу
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21🔥9👎4❤3⚡2💊2
Скриншот-тестирование компонентов в Docker
В нескольких проектах, где я работал, пробовали внедрить скриншот-тестирование компонентов. Принцип работы такой:
1) меняем компонент
2) делаем эталонный скриншот, сохраняем в репе
3) повторяем на всех компонентах
4) при повторных изменениях запускаем тесты, в которых сравниваются эталонные скриншоты и текущее отображение компонентов
5) если все расхождения запланированные, меняем эталонные скрины; если есть незапланированные расхождения, поздравляю, тесты нашли регресс!
Такие тесты хорошо подходят для теста вёрстки элементов ui-kit-а (многочисленные состояниях ховера, фокуса, разные режимы компонента) или же каких-то критичных компонентов (форма логина, чекаут), которые никак нельзя случайно поломать.
Снепшотные тесты, юниты, e2e тут не помогут, нужно именно сравнение скринов.
Звучит хорошо, но есть одно НО. Во всех случаях на моей практике дело упиралось в то, что скриншоты снимаются разными разработчиками на разных машинах с разными ОС. И в зависимости от этого скриншоты начинают расходиться. Шрифты другие. Субпиксельный рендеринг другой. Антиалиасинг другой. Также отличаются элементы форм. И тест падает, хотя визуально всё ок. Вместо помощи тесты начинают мешать и бесить.
Логично было бы предположить делать скриншоты на одной машине, например, вынести этот процесс на CI/CD или на отдельный сервер, но обычно с этим в компаниях сложно, нужно всё согласовывать, заводить, что есть заморочь.
И тут изучая Docker открыл для себя идеально подходящую под кейс фичу! Через Docker можно связать файловую систему хоста (ваша машина) с файловой системой запущенного контейнера. То есть храним скрины по прежнему в репо (развёрнутом у каждого разработчика), монтируем папку с ними внутрь Docker-контейнера, снимаем скриншоты, сравниваем, при необходимости обновляем. Внутри контейнера один и тот же образ с фиксированной ОС, фиксированной версией браузера, фиксированными шрифтами. И соотвественно таким же макаром запускается в CI.
Собрал пруф оф концепт.
Что там вкратце, происходит: Storybook изолирует компоненты, Playwright делает скриншоты, Docker обеспечивает одинаковый рендеринг.
Оркестрация через
Docker-образ с Playwright официальный от Microsoft уже содержит браузеры и все зависимости.
Каждый визуальный тест — это три действия: открыть стори, дождаться рендера, сравнить скриншот. Storybook рендерит в статическом режиме каждый отдельный компонент по адресу
Пример теста кнопки:
Особенности настройки Playwright: можно оставить небольшой задел на случайные расхождения, выключить анимации, спрятать каретку в инпутах.
После прогона тестов формируется HTML-репорт: интерактивный diff, где можно посмотреть, что именно изменилось (приложил скрин, как это выглядит в комментах).
#Лаборатория_веб_платформы
@web_platform | Поддержать платформу⭐️
В нескольких проектах, где я работал, пробовали внедрить скриншот-тестирование компонентов. Принцип работы такой:
1) меняем компонент
2) делаем эталонный скриншот, сохраняем в репе
3) повторяем на всех компонентах
4) при повторных изменениях запускаем тесты, в которых сравниваются эталонные скриншоты и текущее отображение компонентов
5) если все расхождения запланированные, меняем эталонные скрины; если есть незапланированные расхождения, поздравляю, тесты нашли регресс!
Такие тесты хорошо подходят для теста вёрстки элементов ui-kit-а (многочисленные состояниях ховера, фокуса, разные режимы компонента) или же каких-то критичных компонентов (форма логина, чекаут), которые никак нельзя случайно поломать.
Снепшотные тесты, юниты, e2e тут не помогут, нужно именно сравнение скринов.
Звучит хорошо, но есть одно НО. Во всех случаях на моей практике дело упиралось в то, что скриншоты снимаются разными разработчиками на разных машинах с разными ОС. И в зависимости от этого скриншоты начинают расходиться. Шрифты другие. Субпиксельный рендеринг другой. Антиалиасинг другой. Также отличаются элементы форм. И тест падает, хотя визуально всё ок. Вместо помощи тесты начинают мешать и бесить.
Логично было бы предположить делать скриншоты на одной машине, например, вынести этот процесс на CI/CD или на отдельный сервер, но обычно с этим в компаниях сложно, нужно всё согласовывать, заводить, что есть заморочь.
И тут изучая Docker открыл для себя идеально подходящую под кейс фичу! Через Docker можно связать файловую систему хоста (ваша машина) с файловой системой запущенного контейнера. То есть храним скрины по прежнему в репо (развёрнутом у каждого разработчика), монтируем папку с ними внутрь Docker-контейнера, снимаем скриншоты, сравниваем, при необходимости обновляем. Внутри контейнера один и тот же образ с фиксированной ОС, фиксированной версией браузера, фиксированными шрифтами. И соотвественно таким же макаром запускается в CI.
Собрал пруф оф концепт.
Что там вкратце, происходит: Storybook изолирует компоненты, Playwright делает скриншоты, Docker обеспечивает одинаковый рендеринг.
Оркестрация через
docker-compose.yml (именно там указывается, какие локальные папки монтируются в контейнер):
services:
visual-tests:
build: .
volumes:
# Persist screenshots - accessible from host
- ./__screenshots__:/app/__screenshots__
# HTML report for visual diff
- ./html-report:/app/html-report
# Test results (traces, screenshots on failure)
- ./test-results:/app/test-results
# Source files for quick iteration (read-only)
- ./src:/app/src:ro
environment:
- CI=true
command: sh -c "npm run storybook -- --host 0.0.0.0 & sleep 15 && npx playwright test --project=visual"
Docker-образ с Playwright официальный от Microsoft уже содержит браузеры и все зависимости.
FROM mcr.microsoft.com/playwright:v1.48.0-noble
Каждый визуальный тест — это три действия: открыть стори, дождаться рендера, сравнить скриншот. Storybook рендерит в статическом режиме каждый отдельный компонент по адресу
/iframe.html?id=${storyId}&viewMode=story, ждём пока загрузятся картинки и шрифты, можно снимать скриншот Playwright-ом и сравнивать тоже им.Пример теста кнопки:
test.describe('Button', () => {
test('hover', async ({ page }) => {
const component = await gotoStory(page, 'design-system-button--primary');
const button = component.locator('button.button').first();
await button.hover();
await expect(component).toHaveScreenshot();
});
});
Особенности настройки Playwright: можно оставить небольшой задел на случайные расхождения, выключить анимации, спрятать каретку в инпутах.
toHaveScreenshot: {
maxDiffPixels: 100,
animations: "disabled",
caret: "hide",
},
После прогона тестов формируется HTML-репорт: интерактивный diff, где можно посмотреть, что именно изменилось (приложил скрин, как это выглядит в комментах).
#Лаборатория_веб_платформы
@web_platform | Поддержать платформу
Please open Telegram to view this post
VIEW IN TELEGRAM
👍20🔥9❤2🥰1
Не теряемся
Раньше я составлял и публиковал дайжесты #Пульс_веб_платформы. Дело это было довольно муторное и меня хватило на выпуск где-то примерно 80 еженедельных дайджестов. В процессе я каждый четверг пропускал через себя несколько других дайджестов, около сотни тг-каналов и примерно столько же отдельных RSS-блогов.
С момент последнего выпуска прошло около 10 месяцев, одно время я совсем бросил что-то читать, но потом интерес вернулся. Попробовал собрать несколько дайджестов, но мозг сразу сказал, что заниматься написанием дайджестов больше не будет. Но читать дайджесты при этом хочется!
И я решил построить автоматизированный пайплайн сборки дайджестов (прежде всего для себя, но может быть и вам зайдёт).
В общем,дождался блокировки телеги и вынес дайджест в отдельного бота @web_platform_pulse_bot.
В нём ежедневно утром бот публикует примерно 10 ссылок с самым интересным, что произошло в индустрии за прошлые сутки. Аннотации пишу уже не я, а модель, поэтому в душевности дайджест, конечно, потерял. Но в информативности, пожалуй, даже прибавил, так как посты стали ежедневными вместо еженедельных, и покрывают большее число материалов. Если хочется за пару минут понять, что там произошло в целом, достаточно краткой сводки утром по диагонали вместо чтения объёмных рассылок.
Я умышленно отделил авторский контент блога от генерёжки дайджеста, поэтому он будет именно в отдельном боте @web_platform_pulse_bot.
Помимо бота дайджест публикуется на сайт https://juwain.github.io/web-platform/pulse/ и в RSS https://juwain.github.io/web-platform/pulse/rss.xml.
Основной веблог тоже перевёз на gh pages https://juwain.github.io/web-platform/blog/ и в RSS https://juwain.github.io/web-platform/rss.xml (на очередную оплату домена webplatform.tech блог, увы, не заработал).
Отдельно настроил публикацию в зеркало на статику яндексовского sourcecraft. Там ресурсы доступны даже на парковке, так как Яндекс в белых списках.
Вот зеркало дайджеста https://juwain.sourcecraft.site/web-platform/pulse/ и его RSS https://juwain.sourcecraft.site/web-platform/pulse/rss.xml.
А также зеркало основного блога https://juwain.sourcecraft.site/web-platform/blog/ и его RSS https://juwain.sourcecraft.site/web-platform/rss.xml.
Если интересны технические детали реализации, поставьте реакций, я тогда позже напишу отдельный пост, как эта фиговина работает в более-менее автономном режиме и в лучшем случае требует от меня поревьюить подготовленные тексты через интерфейс TG.
В комментах приложу пару примеров дайджестов за прошлые дни.
#Лаборатория_веб_платформы
@web_platform | Поддержать платформу🌟
Раньше я составлял и публиковал дайжесты #Пульс_веб_платформы. Дело это было довольно муторное и меня хватило на выпуск где-то примерно 80 еженедельных дайджестов. В процессе я каждый четверг пропускал через себя несколько других дайджестов, около сотни тг-каналов и примерно столько же отдельных RSS-блогов.
С момент последнего выпуска прошло около 10 месяцев, одно время я совсем бросил что-то читать, но потом интерес вернулся. Попробовал собрать несколько дайджестов, но мозг сразу сказал, что заниматься написанием дайджестов больше не будет. Но читать дайджесты при этом хочется!
И я решил построить автоматизированный пайплайн сборки дайджестов (прежде всего для себя, но может быть и вам зайдёт).
В общем,
В нём ежедневно утром бот публикует примерно 10 ссылок с самым интересным, что произошло в индустрии за прошлые сутки. Аннотации пишу уже не я, а модель, поэтому в душевности дайджест, конечно, потерял. Но в информативности, пожалуй, даже прибавил, так как посты стали ежедневными вместо еженедельных, и покрывают большее число материалов. Если хочется за пару минут понять, что там произошло в целом, достаточно краткой сводки утром по диагонали вместо чтения объёмных рассылок.
Я умышленно отделил авторский контент блога от генерёжки дайджеста, поэтому он будет именно в отдельном боте @web_platform_pulse_bot.
Помимо бота дайджест публикуется на сайт https://juwain.github.io/web-platform/pulse/ и в RSS https://juwain.github.io/web-platform/pulse/rss.xml.
Основной веблог тоже перевёз на gh pages https://juwain.github.io/web-platform/blog/ и в RSS https://juwain.github.io/web-platform/rss.xml (на очередную оплату домена webplatform.tech блог, увы, не заработал).
Отдельно настроил публикацию в зеркало на статику яндексовского sourcecraft. Там ресурсы доступны даже на парковке, так как Яндекс в белых списках.
Вот зеркало дайджеста https://juwain.sourcecraft.site/web-platform/pulse/ и его RSS https://juwain.sourcecraft.site/web-platform/pulse/rss.xml.
А также зеркало основного блога https://juwain.sourcecraft.site/web-platform/blog/ и его RSS https://juwain.sourcecraft.site/web-platform/rss.xml.
Если интересны технические детали реализации, поставьте реакций, я тогда позже напишу отдельный пост, как эта фиговина работает в более-менее автономном режиме и в лучшем случае требует от меня поревьюить подготовленные тексты через интерфейс TG.
В комментах приложу пару примеров дайджестов за прошлые дни.
#Лаборатория_веб_платформы
@web_platform | Поддержать платформу
Please open Telegram to view this post
VIEW IN TELEGRAM
👍20❤16🔥5🤔2
Как superset.sh привёл меня к собственной теме для Zed
В последнее время моя ежедневная работа всё больше смещается в терминал. Когда параллельно открыто несколько проектов и у каждого по несколько вкладок в терминале, обычный плоский список табов терминала (у меня iTerm) слишком скукоживается.
Не хватает ещё одного измерения: горизонтально — вкладки внутри одного проекта, вертикально — сами проекты.
И тут узнал про superset.sh, и он мне зашёл. Получается такой 2D-терминал: слева список проектов, внутри каждого свои табы.
Параллельно с увеличением количества времени проводимого в терминале меняется и роль редактора кода. Если раньше IDE была главным рабочим местом, то в агентоцентричной разработке "редактор" больше нужен для просмотра diff, точечных правок и удобной навигации по файлам.
У меня эту роль уже около года выполняет Zed: быстрый, легковесный и менее прожорливый по ресурсам, чем VS Code.
Теперь про смену контекста между режимом "терминала" и "редактора".
В superset-е мне очень понравилась тёмная тема по умолчанию (такая тёплая ламповая), а при переходе в Zed была другая тёмная тема, о которую спотыкается глаз, когда много раз за день прыгаешь между "менеджером терминалов" и "редактором для проверки изменений".
В общем, я собрал тему для Zed на основе темы superset.
В целом, нужно было экспортнуть тему из superset и сопоставить с токенами темы в Zed. Так как это всё таки немного разные контексты, пришлось подтюнить тему в течение пары недель использования, пока окончательно не привык.
В СПб сейчас начинаются белые ночи, поэтому больше счас сижу в светлой теме днём (на основной работе) и в тёмной после заката (на пет-проектах).
Тему для Zed я опубликовал здесь https://github.com/juwain/superset-zed-theme.
Скрины в комментах.
#Лаборатория_веб_платформы
@web_platform | Поддержать блог💙
В последнее время моя ежедневная работа всё больше смещается в терминал. Когда параллельно открыто несколько проектов и у каждого по несколько вкладок в терминале, обычный плоский список табов терминала (у меня iTerm) слишком скукоживается.
Не хватает ещё одного измерения: горизонтально — вкладки внутри одного проекта, вертикально — сами проекты.
И тут узнал про superset.sh, и он мне зашёл. Получается такой 2D-терминал: слева список проектов, внутри каждого свои табы.
Параллельно с увеличением количества времени проводимого в терминале меняется и роль редактора кода. Если раньше IDE была главным рабочим местом, то в агентоцентричной разработке "редактор" больше нужен для просмотра diff, точечных правок и удобной навигации по файлам.
У меня эту роль уже около года выполняет Zed: быстрый, легковесный и менее прожорливый по ресурсам, чем VS Code.
Теперь про смену контекста между режимом "терминала" и "редактора".
В superset-е мне очень понравилась тёмная тема по умолчанию (такая тёплая ламповая), а при переходе в Zed была другая тёмная тема, о которую спотыкается глаз, когда много раз за день прыгаешь между "менеджером терминалов" и "редактором для проверки изменений".
В общем, я собрал тему для Zed на основе темы superset.
В целом, нужно было экспортнуть тему из superset и сопоставить с токенами темы в Zed. Так как это всё таки немного разные контексты, пришлось подтюнить тему в течение пары недель использования, пока окончательно не привык.
В СПб сейчас начинаются белые ночи, поэтому больше счас сижу в светлой теме днём (на основной работе) и в тёмной после заката (на пет-проектах).
Тему для Zed я опубликовал здесь https://github.com/juwain/superset-zed-theme.
Скрины в комментах.
#Лаборатория_веб_платформы
@web_platform | Поддержать блог
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9🤣1😇1
Виталий и Платформа
#Лаборатория_веб_платформы Look Ma, это я в 2025 пишу фича-флаги прямо в CSS! /* обычные стили */ /* ... */ @container style(--wow-such-a-feature: true) { /* стили за фича-флагом */ /* ... */ } @container style(--wow-such-an-another…
Ну ошибся на годик, с кем не бывает.
Там в FF 151, наконец, завезли 🥳
Там в FF 151, наконец, завезли 🥳
The @container CSS at-rule supports style() queries
🔥9👍3😁2
Как пошарить системное видео/аудио в расширение браузера
Недавно увидел в одном расширении в Chrome интересное решение, расковырял исходники, решил поделиться.
Функциональность расширения — запись звука, сохранение в файл и дальнейшая отправка его в сервис транскрибации.
Так вот интересный момент как именно расширение получает аудио. Допустим вы открываете во браузере Google Meet, десктопный Zoom или любой другой апп для созвонов. Начинаете созвон, приложение/браузер стримит туда-сюда звук и видео. И этот поток направляется в браузерное расширение. В Chrome при этом надо дать явное разрешение и затем появляется уведомление, что происходит шаринг вкладки или всей системы в расширение (аналогичным образом вы обычно шарите свой экран в веб-приложение, а тут веб-приложением выступает браузерное расширение).
Как это работает под капотом. Там Manifest V3 приложение. Микрофон получается через
Потом два независимых потока, микрофон и собеседник, нужно свести в один. И делается это Web Audio API:
То есть по сути Web Audio используется как микшер. В процессе там заодно можно что-то дополнительно обработать, например, усилить микрофон, а на выходе получить единый поток.
Ок, поток получили, теперь задача его записать. Для этого используется MediaStream Recording API и интерфейс MediaRecorder. Пишется не одним куском в конце, а порциями:
То есть кратенько: Media Capture and Streams и Screen Capture добывают
И ещё один интересный момент уже про само браузерное расширение. Вообще оно работает в service worker, у которого нет доступа к DOM и всем веб-API. И чтоб это обойти используется Chrome-специфичное chrome.offscreen, которое создаёт полноценную "невидимую" страницу, где и выполняется код.
#Лаборатория_веб_платформы
@web_platform | Поддержать блог💙
Недавно увидел в одном расширении в Chrome интересное решение, расковырял исходники, решил поделиться.
Функциональность расширения — запись звука, сохранение в файл и дальнейшая отправка его в сервис транскрибации.
Так вот интересный момент как именно расширение получает аудио. Допустим вы открываете во браузере Google Meet, десктопный Zoom или любой другой апп для созвонов. Начинаете созвон, приложение/браузер стримит туда-сюда звук и видео. И этот поток направляется в браузерное расширение. В Chrome при этом надо дать явное разрешение и затем появляется уведомление, что происходит шаринг вкладки или всей системы в расширение (аналогичным образом вы обычно шарите свой экран в веб-приложение, а тут веб-приложением выступает браузерное расширение).
Как это работает под капотом. Там Manifest V3 приложение. Микрофон получается через
getUserMedia из (Media Capture and Streams API). А вот звук стрима берётся через getDisplayMedia — это уже Screen Capture API, в рамках которой браузер показывает системный диалог "чем поделиться". Таким образом и получается что угодно шарить в браузерное расширение.Потом два независимых потока, микрофон и собеседник, нужно свести в один. И делается это Web Audio API:
const ctx = new AudioContext();
const mix = ctx.createMediaStreamDestination();
ctx.createMediaStreamSource(micStream).connect(micGain).connect(mix);
ctx.createMediaStreamSource(tabStream).connect(tabGain).connect(mix);
const combined = mix.stream; // снова обычный MediaStream, его и пишем
То есть по сути Web Audio используется как микшер. В процессе там заодно можно что-то дополнительно обработать, например, усилить микрофон, а на выходе получить единый поток.
Ок, поток получили, теперь задача его записать. Для этого используется MediaStream Recording API и интерфейс MediaRecorder. Пишется не одним куском в конце, а порциями:
start(timeslice) отдаёт данные кусками по ходу записи, и если что-то пойдёт не так на втором часу разговора, то всё не будет потеряно. У WebM при этом есть давняя болячка — MediaRecorder не проставляет длительность в заголовок, и готовый файл выглядит «бесконечным» для плееров и сервера. Лечится это пост-обработкой блоба, дописыванием правильных метаданных уже после остановки.То есть кратенько: Media Capture and Streams и Screen Capture добывают
MediaStream (с устройств или с экрана), Web Audio умеет этот поток обрабатывать и микшировать, а MediaStream Recording берёт готовый MediaStream на вход и сериализует его в сжатые байты — Blob в webm/mp4/ogg, которые уже можно сохранить на устройство или отправить в какой-то внешний сервис дальше.И ещё один интересный момент уже про само браузерное расширение. Вообще оно работает в service worker, у которого нет доступа к DOM и всем веб-API. И чтоб это обойти используется Chrome-специфичное chrome.offscreen, которое создаёт полноценную "невидимую" страницу, где и выполняется код.
#Лаборатория_веб_платформы
@web_platform | Поддержать блог
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥13👍2❤1
Виталий и Платформа
Как superset.sh привёл меня к собственной теме для Zed В последнее время моя ежедневная работа всё больше смещается в терминал. Когда параллельно открыто несколько проектов и у каждого по несколько вкладок в терминале, обычный плоский список табов терминала…
4 мая -> 20 июня, столько провисел мой pr в zed до мерджа
зато теперь тема superset доступна через сам zed https://zed.dev/extensions/superset-theme👍
зато теперь тема superset доступна через сам zed https://zed.dev/extensions/superset-theme
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5🗿2
Виталий и Платформа
4 мая -> 20 июня, столько провисел мой pr в zed до мерджа зато теперь тема superset доступна через сам zed https://zed.dev/extensions/superset-theme 👍
btw там интересная система в репе, используются git submodules: в
SHA коммита в
а в
.gitmodules указывается источник субмодуля (внешний реп):
[submodule "extensions/superset-theme"]
path = extensions/superset-theme
url = https://github.com/juwain/superset-zed-theme.git
SHA коммита в
.gitmodules не хранится, она лежит в самом git-дереве реестра (специальная запись с режимом 160000 (gitlink)). По пути extensions/superset-theme в дереве лежит не папка с файлами, а запись типа commit = aa134f47….а в
extensions.toml указывается, что публиковать:
[superset-theme]
submodule = "extensions/superset-theme"
version = "0.0.1"
👨💻3