Frontend VK Hub
رفتن به کانال در Telegram
Комьюнити VK для фронтендеров. Кто и что стоит за интерфейсами, которыми пользуются миллионы пользователей, — от Почты Mail до VK Teams — рассказываем здесь 🤘
نمایش بیشتر1 120
مشترکین
اطلاعاتی وجود ندارد24 ساعت
اطلاعاتی وجود ندارد7 روز
-130 روز
در حال بارگذاری دادهها
کانالهای مشابه
هیچ دادهای
مشکلی وجود دارد؟ لطفاً صفحه را تازه کنید یا با مدیر پشتیبانی ما تماس بگیرید.
ابر برچسبها
اشارات ورودی و خروجی
---
---
---
---
---
---
جذب مشترکین
اکتبر '26اکتبر '26
اکتبر '26
+6
در 0 کانالها
سپتامبر '26
+21
در 1 کانالها
Get PRO
اوت '26
+11
در 2 کانالها
Get PRO
ژوئیه '26
+4
در 0 کانالها
Get PRO
ژوئن '26
+6
در 1 کانالها
Get PRO
مه '26
+9
در 1 کانالها
Get PRO
آوریل '26
+14
در 1 کانالها
Get PRO
مارس '26
+9
در 0 کانالها
Get PRO
فوریه '26
+7
در 0 کانالها
Get PRO
ژانویه '26
+26
در 1 کانالها
Get PRO
دسامبر '25
+429
در 19 کانالها
Get PRO
نوامبر '25
+178
در 0 کانالها
Get PRO
اکتبر '25
+836
در 20 کانالها
| تاریخ | رشد مشترکین | اشارات | کانالها | |
| 18 اکتبر | 0 | |||
| 17 اکتبر | 0 | |||
| 16 اکتبر | 0 | |||
| 15 اکتبر | +1 | |||
| 14 اکتبر | 0 | |||
| 13 اکتبر | +1 | |||
| 12 اکتبر | 0 | |||
| 11 اکتبر | +1 | |||
| 10 اکتبر | 0 | |||
| 09 اکتبر | +1 | |||
| 08 اکتبر | +2 | |||
| 07 اکتبر | 0 | |||
| 06 اکتبر | 0 | |||
| 05 اکتبر | 0 | |||
| 04 اکتبر | 0 | |||
| 03 اکتبر | 0 | |||
| 02 اکتبر | 0 | |||
| 01 اکتبر | 0 |
پستهای کانال
| 2 | ES2026: что из нового стандарта можно брать в код уже сейчас
ECMAScript обновляется раз в год, и к моменту утверждения стандарта его новинки обычно уже работают в браузерах. 30 июня 2026 года Ecma International утвердила ES2026, семнадцатую редакцию языка.
Интересного синтаксиса в ней нет, зато закрыты мелкие задачи, ради которых годами тянули зависимости или писали свои утилиты:
🔵Base64 из байтов
🔵точная сумма чисел
🔵Map со значением по умолчанию
🔵большие числа в JSON
Почти всё это уже доступно в свежих версиях браузеров.
#frontendvkhub #ecmascript | 289 |
| 3 | Frame-sizing в Chrome 154: iframe сам подстраивает высоту под контент
Виджет комментариев, форма оплаты или дашборд во фрейме почти всегда упираются в высоту. Хост не знает, сколько места нужно странице внутри, поэтому она меряет себя сама и шлёт размер через postMessage, а родитель ловит сообщение и меняет высоту фрейма. Этот мост каждая команда пишет заново, и пока первое сообщение не пришло, фрейм стоит с фиксированной высотой и прокруткой внутри.
Chrome 154, вышедший 22 сентября, справляется без скриптов. Нужны две вещи: CSS-свойство frame-sizing на стороне хоста и метатег с согласием в документе внутри фрейма.
.embed {
width: 100%;
height: 500px; /* для браузеров без поддержки */
}
@supports (frame-sizing: content-height) {
.embed {
height: auto;
frame-sizing: content-height;
max-height: 80vh;
}
}
Встраиваемая страница должна разрешить это явно, иначе хост её размер не получит:
<meta name="responsive-embedded-sizing"
content="allow-origins=https://publisher.example">
Решение принимает встраиваемая сторона: в allow-origins через пробел перечисляются домены, которым можно сообщать размер, либо ставится *. Схема работает и для фреймов с другого домена и дополняет frame-ancestors из CSP. Тег должен быть в документе с самого начала, добавить его скриптом после загрузки не выйдет.
Кроме content-height, у свойства есть content-width, логические content-inline-size и content-block-size, а также auto, которое оставляет прежнее поведение. Для виджетов, растущих вниз, подходит content-height.
Размер браузер берёт после загрузки и дальше за каждым изменением вёрстки не следит. Если содержимое поменялось, например подгрузились новые комментарии, документ сам просит пересчитать размер:
if ("requestResize" in window) {
window.requestResize();
}
Вызывать его лучше после того, как все изменения внесены, а не на каждом шаге, чтобы не уйти в цикл пересчётов.
За удобство платят сдвигом вёрстки: фрейм вырастает уже после загрузки встроенной страницы и тянет вниз всё, что под ним, поэтому в первом экране такой виджет ухудшит CLS.
Старый мост на postMessage пока выбрасывать рано, потому что в Firefox и Safari поддержки нет. Зато метатег во встраиваемые страницы можно добавлять уже сейчас, а на хосте включать frame-sizing под @supports и оставить прежний код запасным путём.
А вы сейчас подгоняете высоту iframe через postMessage или живёте с фиксированной?
#frontendvkhub #chrome #css | 378 |
| 4 | Vite+ — не новая версия Vite и не его замена. Это отдельная утилита, которая объединяет инструменты вокруг Vite в один CLI: Vite 8, Vitest 5, Oxlint, Oxfmt, Rolldown и tsdown.
28 сентября VoidZero выпустила первый стабильный релиз. Разбираемся вместе с Петром Гилевичем, руководителем веб-разработки контентных сервисов ВКонтакте, как там всё устроено.
В чём идея
Фронтенд-проект требует набор инструментов: сборщик, тесты, линтер, форматтер, сборка npm-пакетов. Каждый обновляется сам по себе, у каждого своя конфигурация, а локальное окружение и CI расходятся — привет, worked on my machine.
Заявленные кратные ускорения касаются линтинга и нового ядра сборки в Vite 8. Версии всех инструментов подобраны и протестированы вместе, обновляются одной командой и гарантированно работают в связке.
Как устроено
Глобальная утилита vp — отдельная программа, ставить через npm не нужно. Она смотрит, какая версия Node.js нужна проекту, скачивает её и переключает при переходе между проектами.
В самом проекте вместо отдельных Vite, Vitest и Oxlint в зависимостях остаётся один пакет vite-plus — версии инструментов зашиты в него. Есть нюанс: если доставить какой-то инструмент отдельно, можно случайно получить вторую его копию и сломать связку — документация предупреждает об этом.
Vite Task в монорепозитории и кеширование сборки
Если вы разрабатываете несколько пакетов в одном репозитории, рутину — прогоны тестов и другие пайплайны — берёт на себя движок Vite Task. Он умеет то, ради чего раньше отдельно ставили Turborepo.
Во-первых, он видит, какой пакет от какого зависит, и запускает сборки в правильном порядке: сперва библиотека, потом зависящее от неё приложение. Внутри одной сборки порядок модулей и так определяет сборщик — а вот очерёдность сборок между пакетами одного репозитория как раз зона ответственности Vite Task.
Во-вторых, он кеширует сборки: vp run build --cache снимает fingerprint зависимостей модуля. Запустите команду снова, не меняя код, — сборка полностью пропустится, и результат мгновенно вернётся из кеша. Пока кеш хранится локально, но у Vite+ в планах удалённый — как у конкурентов, в том числе для использования на CI.
Как Vite+ стал открытым
Изначально Vite+ планировали сделать платным. Но до публичного релиза от платной модели отказались: код открыли под MIT. Почему передумали — в секции MIT License на сайте VoidZero прокомментировали: «Мы устали спорить, какие функции должны быть платными и как их ограничивать». Летом 2026 Cloudflare купила VoidZero — инструменты остаются открытыми.
Что в итоге
Получаем управление всей экосистемой вокруг Vite одной командой: один апгрейд вместо синхронизации пяти инструментов.
Из коробки есть миграция: vp migrate пытается влить все конфигурации — линтера, форматтера, тестов — в один vite.config.ts, переписывает скрипты package.json на vp-команды и перед изменениями показывает план. Миграция рассчитана на свежие Vite 8+ и Vitest 4.1+; прехуки на Husky (утилита для Git-хуков) остаются как были — их подключают к Vite+ вручную.
Отдельный CLI — знак того, что проект растят как целую экосистему для JS-проектов без привязки к пакетному менеджеру и предустановленному Node. Можно предположить, что дальше мы увидим поддержку других сред выполнения и возможность миграции между ними, но обещаний нет.
Старт мощный: 2,4 млн скачиваний в неделю на начало октября, мейнтейнер с мировым именем. Небольшим проектам, которые уже на Vite, пробовать можно. Крупным, вероятно, надо повременить: недавно Vite сменил ядро сборки на Rolldown, и это был очень серьёзный апдейт.
Непонятно, будут ли подобные обновления в ближайшие год-два. Экспериментальный Bundled Dev Mode может быть причиной новых мажорных изменений — тогда мигрировать придётся не один раз.
#frontendvkhub #vite #что_нового | 393 |
| 5 | 🔵STP 252: @supports научился проверять сами условия
11 сентября в Safari Technology Preview 252 добавили CSSConditionRule.supports и named-feature() в @supports — проверку поддержки фич без детектируемого синтаксиса, например anchor positioning. Заодно распрефиксовали user-select — теперь это стандартное свойство без -webkit-.
🔵Safari 27 — новые возможности CSS и HTML
14 сентября вышел Safari 27 с кастомизацией <select>, псевдоклассом :heading, sizes="auto" и расширениями color-mix(). WebKit также обновил загрузчик ES-модулей, улучшив поддержку top-level await.
🔵Node.js 26.9 — быстрые Worker Threads и FFI
16 сентября Node.js ускорил запуск Worker Threads до 2 раз и снизил их потребление памяти. Экспериментальный node:ffi теперь включён по умолчанию — вызывать функции нативных библиотек можно без отдельного модуля C++.
🔵Chrome 154: браузер переходит на двухнедельный релиз-цикл
22 сентября вышел Chrome 154 — первый релиз по новому двухнедельному расписанию вместо прежнего четырёхнедельного. Из фич для вёрстки:
• scroll-marker-group получил режимы links/tabs, которые задают фокус-порядок и ARIA-семантику для ::scroll-marker-group
• iframe теперь можно сделать самоподстраивающимся под контент через frame-sizing: content-height — без ручного измерения высоты и postMessage
🔵Next.js: серия security-апдейтов закрывает критическую уязвимость в апстриме
22 сентября Vercel выпустил внеплановый патч 16.3.6 и 15.5.26 из-за критической уязвимости в сторонней зависимости. А 30 сентября — плановый сентябрьский security-релиз 16.3.8 и 15.5.27, закрывающий ещё 7 уязвимостей: 1 высокой степени критичности, 5 средних и 1 низкую. Если вы ещё не обновили проект на Next.js — не откладывайте это.
🔵Safari Technology Preview 253: sibling-index() добрался до @container
23 сентября в STP 253 sibling-index() и sibling-count() стало можно использовать прямо в условиях @container. То есть раскладка контейнера теперь может зависеть не только от его размера, но и от позиции элемента среди соседей. WebKit также обновил безопасное выравнивание (safe/unsafe) для абсолютно позиционированных боксов, чтобы они не вылетали за пределы исходного содержащего блока.
#frontendvkhub #дайджест | 450 |
| 6 | CSS в 2026 году: где JavaScript уже не обязателен
Открыть поповер, подсветить карточку с выбранным чекбоксом, растянуть textarea под текст — раньше на каждую такую мелочь заводился скрипт, состояние и подписка на события. За два года CSS забрал у JavaScript целый пласт таких задач, и часть фич уже прошла порог Baseline.
Максим Кузнецов из команды веб-версий ВКонтакте разобрал, что из этого можно брать в продакшен прямо сейчас, что подключать осторожно, а за чем пока просто следить.
➡️В карточках — короткая версия с актуальными статусами поддержки. А подробнее читайте на Хабре.
#frontendvkhub #css | 469 |
| 7 | URLPattern: маршруты, которые не нужно переписывать в регулярки
В любом приложении со своей маршрутизацией рано или поздно заводится файл с регулярками. /^\/users\/(\d+)\/posts\/([\w-]+)$/ работает, но читается тяжело, номера групп приходится помнить наизусть, а проверка домена и протокола живёт отдельным условием рядом. Обычный выход состоит в том, чтобы притащить path-to-regexp и получить ещё одну зависимость ради разбора адреса.
Разбирать адреса умеет сама платформа. URLPattern принимает шаблон в знакомом по роутерам синтаксисе и сопоставляет с ним URL целиком, а не только путь:
const route = new URLPattern({ pathname: "/users/:id/posts/:slug" });
route.test("https://app.example.com/users/42/posts/hello"); // true
const match = route.exec("https://app.example.com/users/42/posts/hello");
match.pathname.groups; // { id: "42", slug: "hello" }
Группы именованные, так что возиться с их порядком не нужно. Кроме :name, доступны звёздочка для любого остатка, модификаторы ? и +, фигурные скобки для необязательных кусков пути и собственные регулярки прямо внутри шаблона: /v:version(\\d+)/ поймает только числовую версию.
Шаблон покрывает все части адреса сразу, и на этом удобно строить таблицу маршрутов в Service Worker или в edge-функции:
const routes = [
[new URLPattern({ pathname: "/api/v:v(\\d+)/*" }), handleApi],
[new URLPattern({ hostname: "cdn.example.com" }), handleAssets],
];
self.addEventListener("fetch", (event) => {
const route = routes.find(([pattern]) => pattern.test(event.request.url));
if (route) event.respondWith(route[1](event.request));
});
Главная разница между двумя формами записи проявляется как раз тут. В объектной форме всё, что не указано, считается звёздочкой: шаблон выше совпадёт с любым протоколом и любой строкой запроса. В строковой форме, new URLPattern("https://example.com/books/:id"), пропущенные части приравниваются к пустой строке, поэтому адрес с параметрами мимо такого шаблона пройдёт, пока не дописать ?*#* вручную.
Дальше идёт мелочёвка. Двоеточие внутри литерала экранируется обратным слешем, иначе парсер примет его за начало группы. Юникод в регулярных группах придётся кодировать процентами самостоятельно: автоматически платформа этого не делает. Регистр по умолчанию учитывается, а выключается опцией ignoreCase.
С сентября 2025, когда поддержку закрыл Safari 26, API считается Baseline и работает в том числе в воркерах. Для старых браузеров есть официальный полифилл urlpattern-polyfill, он же закрывает Node без экспериментальных флагов.
Смысл переезжать есть там, где роутер только сопоставляет адреса и достаёт из них параметры: слой регулярок или path-to-regexp в этом случае удаляется целиком. Если поверх маршрутизации навешаны вложенные layout-компоненты, middleware и ленивые импорты, URLPattern занимает нижний уровень, а остальное остаётся вашим кодом.
#frontendvkhub #javascript #urlpattern | 504 |
| 8 | بدون متن... | 400 |
| 9 | Как поделиться данными с воркером и не копировать их
Воркер получает данные через postMessage, и браузер по умолчанию копирует всё, что вы туда положили. На массиве в 100 МБ копирование само по себе подвешивает главный поток — ровно там, где воркер должен был его разгрузить.
Обойти это можно двумя путями. Первый — отдать буфер воркеру насовсем: делается одной строкой и работает везде. Второй — открыть память, в которую оба потока пишут одновременно.
Второй путь мощнее, но браузер за него просит дорого. Придётся отгородить сайт от чужих скриптов, видео и виджетов — и разбираться, что из этого после отвалится. Подробнее рассказываем в карточках.
#frontendvkhub #воркер | 630 |
| 10 | بدون متن... | 433 |
| 11 | Архитектурные векторы: альтернативы
В предыдущем посте мы рассмотрели мейнстримную линейку: от серверных шаблонов до серверных компонентов. Параллельно с ней живут подходы, которые отвергают само допущение, что веб-приложение обязано быть JS/TS SPA с клиентским состоянием и JSON API. Разбираем в карточках альтернативные архитектурные подходы к фронтенду.
#frontendvkhub #обзор #эволюция_веба | 557 |
| 12 | VK Frontend Meetup SPB — про веб-анимации, AI и автоматизацию
30 сентября в Санкт-Петербурге встречаемся с фронтенд-разработчиками на VK Frontend Meetup SPB. В этот раз разберём три слоя фронтенда: то, что видит пользователь, то, как мы создаём интерфейсы, и то, как масштабируем разработку.
Поговорим о практических задачах, которые возникают в больших продуктах: как стабильно рендерить много анимаций, как научить AI верстать по правилам проекта, а не «примерно похоже», и как автоматизировать типовые задачи, чтобы запускать сотни промо без постоянного участия разработчиков.
В программе:
🔵Артем Стукалов, МАХ — слой интерфейса и производительности. Расскажет, как команда пришла к собственной архитектуре для веб-анимации стикеров. Разберём, почему одного Lottie оказалось недостаточно, что изменилось после перехода на собственное решение и как удалось стабильно рендерить большое количество анимаций без лагов.
🔵 Эмиль Янгиров, Одноклассники — слой создания интерфейсов и AI. Поговорим о том, почему AI пока не умеет по-настоящему верстать production-код. Обсудим, как подготовить Figma и дизайн-систему к работе с AI-агентом, передать ему правила проекта, не дать выдумывать компоненты и автоматически проверять адаптивность, доступность и визуальное соответствие.
🔵 Александр Сарафанников, Почта Mail — слой масштабирования и автоматизации. Покажет, как запускать 300 промо в год за 10 минут. Это история о том, как команда прошла путь от единого места с коммуникациями и ручной работы разработчика до платформенного решения, которое позволяет запускать типовые промо сразу в нескольких проектах — без отдельной задачи для разработчика.
Митап полностью офлайн — записей докладов не будет. Зато после основной программы останемся на фуршет и неформальное общение: можно будет задать вопросы спикерам, обсудить доклады и обменяться опытом с коллегами.
Митап бесплатный, но для участия обязательна регистрация.
Приходите — разберём фронтенд слой за слоем.
#frontendvkhub #митап | 433 |
| 13 | Архитектурные векторы: мейнстрим
Сегодня в продакшене одновременно работают разные архитектурные подходы: серверные шаблоны, jQuery, SPA, SSG, SSR, islands и Server Components. Эволюция веба — это не смена победителей, ведь каждая модель отвечала на свои вызовы. Разбираемся, как происходило развитие архитектур веба.
#frontendvkhub #обзор #эволюция_веба | 415 |
| 14 | 🔵18 августа вышел Firefox 154 — самый заметный для вёрстки релиз месяца.
Появились text-box-trim, text-box-edge и шорткат text-box. Проблема, которую они решают, знакома любому, кто верстал заголовки по макету.
Шрифт несёт с собой пространство над и под строкой, причём у каждой гарнитуры своё: у одной сверху остаётся два пикселя, у другой шесть. Дизайнер в Figma видел текст, посаженный по оптическому краю, а в браузере тот же блок съезжал. Фиксилось это отрицательными марджинами, подобранными вручную, а при смене шрифта подбор начинался заново. Новые свойства срезают лишнее на уровне движка, и блок встаёт туда, где его нарисовали.
Вторая пара — sibling-count() и sibling-index(). Первая возвращает число соседних элементов вместе с самим элементом, вторая — порядковый номер, счёт с единицы, а не с нуля.
Раньше для стилизации по позиции шаблонизатор или скрипт проставлял каждому элементу инлайновый --index, от которого уже считались задержка анимации, толщина, прозрачность — список из полусотни карточек означал полсотни инлайновых стилей в разметке. Теперь это делает сам CSS, без nth-child-арифметики.
🔵Что зреет за флагами
line-clamp заработал без вендорного префикса, правда пока без значения no-ellipsis, то есть обрезка с многоточием работает, а без многоточия ещё нет. Функция progress() считает значение по положению между минимумом и максимумом — заготовка для плавных зависимостей без акробатики с вложенными clamp().
В Nightly отдельно подъехала CSS Typed Object Model: работа со стилями из JS через типизированные объекты вместо разбора строк. До стабильной ветки ей далеко, но направление понятное.
🔵25 августа Chrome 152 ушёл в стейбл.
::backdrop, ::scroll-marker и ::view-transition теперь доступны как полноценные объекты CSSPseudoElement, а не только как селекторы — закрыть диалог по клику на фон можно без ручного сравнения координат. Плюс появился relative alpha colors (alpha() из CSS Color 5) для точечного управления прозрачностью существующего цвета.
Свойство window-drag размечает области установленного веб-приложения, за которые окно двигается как за заголовок. Кастомный тайтлбар в PWA перестал требовать возни с обработчиками мыши и вычислением дельты курсора.
#frontendvkhub #дайджест | 465 |
| 15 | Customizable select: нативный дропдаун, который наконец стилизуется
<select> больше двадцати лет оставался чёрным ящиком. Шрифт поменять можно, иногда — фон, дальше начинается территория операционной системы: своя стрелка, свои отступы, свой список опций, который рисуется вне окна браузера и никакому CSS не подчиняется.
Дизайнер просит фирменный шеврон и скруглённые опции — разработчик идёт за Choices.js или Select2. Библиотека выкидывает нативный элемент и собирает вместо него стопку из div, после чего клавиатурную навигацию, ARIA-атрибуты и работу со скринридерами восстанавливают вручную. Тридцать килобайт и постоянная поддержка — цена за одну стрелку.
Customizable select убирает этот обмен. Команда Safari показала его на WWDC 2026, работает он в Safari 27 и Chrome 135, Firefox прототипирует за флагом.
Включается одним значением, но объявить его нужно дважды — на самом элементе и на выпадающем списке:
.custom-select {
&,
&::picker(select) {
appearance: base-select;
}
}
После этого браузер перестаёт рисовать виджет средствами системы и превращает <select> в обычное дерево элементов. Список опций уезжает в top layer, как поповер, и позиционируется через anchor(), а значит, больше не обрезается родителем с overflow: hidden.
Открывается доступ к внутренностям, которых раньше не существовало. ::picker(select) — сам список, ::picker-icon — стрелка, ::checkmark — галочка у выбранной опции, :open — состояние раскрытого списка, :checked — текущая опция. Обычные селекторы, к которым применяются переходы и анимации.
Отдельно стоит <selectedcontent> — он зеркалит содержимое выбранной опции внутрь кнопки. В той же опции теперь можно класть настоящую разметку: флаг картинкой, иконку, вложенный span с описанием. Раньше <option> принимал только текст, остальное браузер молча выбрасывал.
<select class="custom-select">
<button>
<selectedcontent></selectedcontent>
</button>
<option value="ru">
<img src="/flags/ru.svg" alt=""> Россия
</option>
</select>
Отличие от библиотечного подхода в том, что элемент остаётся нативным. Клавиатурная навигация, отправка в форме, семантика для скринридеров и валидация продолжают работать сами — меняется только внешний вид.
При всём этом в базовом режиме список не рендерится за пределами окна браузера и не вызывает системные компоненты выбора на мобильных — вместо привычного колеса iOS пользователь увидит ваш список. Включение к тому же меняет разбор содержимого <select> парсером, и на старой разметке это может дать неожиданный результат. Chrome держит фичу под управляемым экспериментом на случай быстрого отката.
До Baseline пока далеко, но браузер без поддержки игнорирует незнакомое значение и рисует обычный <select>. Фолбэков писать не нужно, а если хочется явно развести стили — есть проверка через @supports (appearance: base-select).
#frontendvkhub #css #select | 405 |
| 16 | Speculation Rules: как готовить страницу до того, как по ней кликнули
Между кликом по ссылке и появлением новой страницы всегда есть пауза. Сервер может ответить за 30 миллисекунд, но дальше браузеру предстоит разобрать HTML, дотянуть стили и шрифты, выполнить скрипты — и человек всё это время смотрит на старую страницу.
Speculation Rules переносят эту работу на время до клика. Пока пользователь читает текущую страницу, браузер готовит следующую в фоне, а после клика показывает уже готовую.
В карточках — чем prefetch отличается от prerender, как настраивается момент срабатывания, какие лимиты держит Chrome и что делать с аналитикой, которая успевает выстрелить раньше визита.
#frontendvkhub #html #speculation_rules | 495 |
| 17 | Подсветка текста, которая не трогает DOM
Задача звучит просто: подсветить найденные слова на странице. Реализация традиционно упирается в то, что подсвечивать в вебе можно только элемент. Значит, нужно найти текстовый узел, разрезать его, обернуть кусок в <mark>, а при следующем запросе всё это разобрать обратно.
Ломается такой подход быстро. Range.surroundContents() бросает исключение, если диапазон пересекает границы тегов, а найденная фраза запросто начинается в <em> и заканчивается снаружи. Обработчики, повешенные на исходные узлы, теряются вместе с этими узлами.
Виртуализованный список перерисовывается и уносит подсветку. Получается, что мы работаем с DOM ради того, что к структуре документа отношения не имеет.
Custom Highlight API решает это иначе — диапазон описывается, но в разметку не попадает:
const range = new Range();
range.setStart(textNode, 10);
range.setEnd(textNode, 25);
CSS.highlights.set('search', new Highlight(range));
::highlight(search) {
background-color: oklch(90% 0.15 100);
color: black;
}
DOM после этого выглядит ровно так же, как до. Диапазон живёт отдельно от дерева, а браузер рисует поверх текста при отрисовке. Пересечение границ тегов перестаёт быть проблемой: Range спокойно тянется через несколько элементов, потому что ничего оборачивать не требуется.
Одна подсветка может содержать сколько угодно диапазонов. Зарегистрировать можно сколько угодно именованных подсветок — каждая со своим ::highlight(name) и своими стилями. Для совместной работы это то, что нужно: выделение каждого участника получает своё имя и свой цвет, а снимается всё через CSS.highlights.delete('name') или разом через clear().
Набор доступных свойств намеренно узкий: color, background-color, text-decoration, text-shadow и обводка. Геометрию поменять нельзя, background-image игнорируется. Ограничение осмысленное: раскладка от подсветки не зависит, поэтому браузеру не нужно пересчитывать её при каждом изменении диапазонов.
По поддержке история долгая: Chrome и Edge закрыли её ещё в 105-й версии в августе 2022, Safari подключился в 17.2 в декабре 2023, Firefox замкнул круг позже, и с марта 2026 фича считается Baseline. На 2026 год она попала в фокус Interop.
Механизм полностью зависит от JavaScript и молча ничего не делает там, где не поддерживается. Отсюда рабочая схема — оставить в разметке настоящий <mark> со стилями по умолчанию, а поверх включить ::highlight() через @supports selector(::highlight(search)). Читатель без поддержки видит семантическую подсветку, остальные — динамическую, и никто не остаётся с пустой страницей.
А вы у себя чем подсвечиваете поиск по странице? Делитесь в комментах — интересно, кто уже выкинул обёртки в <mark>.
#frontendvkhub #javascript | 463 |
| 18 | Как приоритет слоёв побеждает специфичность в CSS
Каскад выбирает победителя по цепочке шагов: важность, origin, слой, специфичность, порядок объявления. На большой кодовой базе первые шаги обычно не используются, и всё сводится к гонке специфичности: reset с нулевой, дизайн-система со средней, продукт с высокой. Потом приходит правило вида .button.primary.active, и структура рассыпается.
@layer добавляет шаг, который стоит выше специфичности. Слои объявляются один раз, от менее приоритетного к более:
@layer reset, base, components, utils;
Порядок фиксируется этим первым объявлением, дальше правила складываются в слои по имени, а место в очереди не зависит от того, где физически лежит код.
@layer reset {
* { margin: 0; padding: 0; }
}
@layer components {
.button { padding: 12px 20px; background: blue; }
}
@layer utils {
.m-0 { margin: 0; }
}
.m-0 победит любой селектор из components, включая .card .button.primary:hover. Внутри одного слоя специфичность работает как обычно, между слоями она не значит ничего.
Второй частый сценарий — сторонняя библиотека в отдельном слое. Statement @layer разрешён до @import, и ставить его первым надёжнее: порядок задан явно, а не собирается из очерёдности импортов.
@layer reset, vendor, base, components, utils;
@import url("normalize.css") layer(reset);
@import url("bootstrap.css") layer(vendor);
Bootstrap уезжает в vendor, наши компоненты лежат выше и перебивают его без !important. Всё, что написано вне слоёв, живёт в неявном верхнем слое и перебивает любой именованный, поэтому миграция идёт мягко: @layer подключается в новом коде, старый unlayered работает поверх и переезжает по частям.
С !important порядок слоёв инвертируется, important из reset перебьёт important из utils. Менее очевидная часть той же механики: unlayered important оказывается слабее любого слоя. Правка, которая годами держала переопределение поверх всего, после переезда библиотеки в слой молча перестанет работать.
Слои вкладываются друг в друга: @layer components { @layer forms, buttons, cards; }. Приоритет считается по иерархии, а весь блок components занимает одно место в глобальном порядке. Рабочая раскладка обычно укладывается в шесть слоёв: reset, tokens, base, components, utils, overrides.
Поддержка приехала в браузеры ещё в 2022, сейчас @layer в статусе Baseline widely available. Единственное — Safari 15.3 и старше выбрасывает содержимое слоя целиком, без @supports там будет пустая страница.
Порядок объявляется один раз и дальше не требует ни сборки, ни договорённостей внутри команды. А в ваших проектах слои уже разложены или порядок стилей всё ещё держится на очерёдности импортов?
#frontendvkhub #css | 512 |
| 19 | Функции и условия в CSS без препроцессора
Кастомные свойства решили половину задачи: значение можно вынести в переменную и переиспользовать. Но переменная хранит ровно то, что в неё положили, — посчитать что-то на её основе она не умеет. Отсюда Sass, который считает на этапе сборки и потому не видит того, что происходит в браузере.
Теперь CSS умеет это сам. @function описывает функцию с аргументами и типами, if() возвращает разные значения по условию — и то и другое работает во время выполнения, с живыми значениями переменных.
#frontendvkhub #css | 8 035 |
| 20 | CSS @scope — изоляция стилей без Shadow DOM и препроцессоров
Изоляция стилей в CSS долго решалась косвенно: BEM с длинными именами, CSS Modules с хешированием, scoped-атрибут в Vue, Shadow DOM в Web Components. У каждого варианта своя цена. @scope даёт нативный способ ограничить область действия правил. В Chrome 118 с октября 2023, Safari 17.4 с марта 2024, Firefox 143 с сентября 2025 — и в 2026 везде.
Идея простая. Внутри @scope правила применяются только к элементам в поддереве заданного корня:
@scope (.card) {
h2 { color: purple; }
p { line-height: 1.6; }
}
Все h2 и p внутри .card получат эти стили. Классов вроде .card__title не нужно: селектор внутри scope пишется как есть, границу задаёт сам scope.
Верхняя граница задаётся через to:
@scope (.article) to (.comments) {
p { font-size: 1.1rem; }
}
Правило применяется ко всем p внутри .article, но не проникнет в .comments и вложенное. Решает классическую задачу: статья и блоки комментариев стилизуются независимо, но рендерятся внутри одной иерархии.
Второй вариант — inline-scope через <style> внутри HTML. Тег <style> с @scope без селектора привязывается к родителю:
<div class="widget">
<style>
@scope {
h3 { color: teal; }
button { background: navy; }
}
</style>
<h3>Заголовок</h3>
<button>Кнопка</button>
</div>
Ближайший CSS-аналог <style scoped> из Vue: описываем стили прямо в компоненте, они применяются только к его поддереву. Без прекомпилятора и обёрток.
Правила внутри @scope считаются с той же специфичностью, что и снаружи, но при равной выигрывает правило, находящееся ближе к элементу по DOM. Правило из внутреннего @scope перебьёт правило из внешнего.
Практический паттерн — темизация секции:
@scope (.section--dark) {
:scope { background: #1a1a1a; color: #eee; }
a { color: #7cb; }
button { background: #333; color: #eee; }
}
:scope — сам элемент, к которому привязана область (аналог :host в Shadow DOM). Всё внутри .section--dark перекрашивается разом, без каскадных префиксов и без глобальных override.
@scope ограничивает только те правила, что внутри самого @scope. Не блокирует утечку правил снаружи: если во внешнем CSS .card p { color: red }, оно применится к p внутри scope.
Если в проекте BEM используется только ради изоляции, или CSS Modules подключены только для избежания конфликтов имён, @scope часто закрывает задачу без сборочного шага.
#frontendvkhub #css #scope | 551 |
