Popover API 🎈 В прошлой части был
<dialog> для модалок. Но половина случаев — это не модалка, а поповер: меню, тултип, дропдаун. И вот тут раньше начинался цирк с z-index, кликом «мимо» и Escape. Теперь два атрибута:<button popovertarget="menu">Меню</button>
<div id="menu" popover>
<a href="#">Профиль</a>
<a href="#">Выйти</a>
</div>
Клик вне — закрывает. Escape — закрывает. Всё в top layer, никаких войн со z-index. Ноль JS.
:has() — родительский селектор 🎯 Двадцать лет фронтенд просил «а можно стилизовать родителя от ребёнка?». Наконец можно:
/* карточка, внутри которой есть картинка */
.card:has(img) { padding-top: 0; }
/* форма с невалидным полем */
form:has(:invalid) button { opacity: .5; }
Раньше это был
querySelectorAll + навешивание классов вручную. Теперь — одна строка CSS.crypto.randomUUID() 🆔 Классика, которая до сих пор живёт в проде:
const id = Date.now() + Math.random();
Нормальный UUID v4 встроен:
const id = crypto.randomUUID();
// "6f4d2c1e-8a3b-4f9c-b2e1-7d0a5c3e1f92"
Криптографически стойкий, коллизий можно не бояться. Работает во всех современных браузерах (нужен https или localhost).
Object.groupBy() 🗂 Сколько раз вы писали reduce, чтобы сгруппировать массив? Теперь это одна функция:
const users = [
{ name: "Аня", role: "dev" },
{ name: "Петя", role: "qa" },
{ name: "Ира", role: "dev" },
];
const byRole = Object.groupBy(users, u => u.role);
// { dev: [Аня, Ира], qa: [Петя] }
Есть и
Map.groupBy(), если ключом должен быть объект. lodash можно не звать.Intl.RelativeTimeFormat ⏳ «2 часа назад», «через 3 дня» — ради этого до сих пор ставят moment/dayjs. А это уже в браузере, да ещё и с локализацией:
const rtf = new Intl.RelativeTimeFormat("ru", { numeric: "auto" });
rtf.format(-2, "hour"); // "2 часа назад"
rtf.format(1, "day"); // "завтра"Сам знает про склонения и «вчера/завтра». Бесплатно, без килобайтов зависимостей.
Иммутабельные методы массива 🔄
sort() и reverse() десятилетиями мутировали исходный массив — и ловили баги в React, где стейт менять нельзя. Теперь есть неразрушающие версии:const sorted = list.toSorted((a, b) => a - b);
const reversed = list.toReversed();
const patched = list.with(2, "new"); // заменить по индексу
Оригинал цел, возвращается копия. Больше никаких
[...arr].sort().View Transitions API 🎬 Плавные переходы между состояниями UI раньше означали Framer Motion или ручные костыли. Теперь браузер сам анимирует разницу до/после:
document.startViewTransition(() => {
updateTheDOM();
});Меняете DOM внутри колбэка — браузер делает кроссфейд между старым и новым кадром. Для SPA-переходов и списков выглядит дорого почти бесплатно. Пока стабильно в Chromium, для остальных — прогрессивное улучшение.
inert 🚫 Открыли модалку, а Tab всё равно уводит фокус на кнопки под ней. Классическая дырка в доступности. Один атрибут — и целое поддерево выключено: ни фокуса, ни кликов, ни скринридера.
<main inert>...весь фон...</main>
<dialog open>...</dialog>
Раньше это был ручной обход всех focusable-элементов и
tabindex="-1". Теперь — inert.text-wrap: balance ⚖️ Заголовок, у которого одно слово одиноко висит на второй строке — вечная боль. Раньше лечили
вручную. Теперь браузер сам балансирует строки:h1 { text-wrap: balance; }
p { text-wrap: pretty; }balance — ровные строки для заголовков, pretty — убирает висячие слова в абзацах. Красиво и без ручной вёрстки.Перед тем как поставить очередную библиотеку, всё чаще стоит проверить — а вдруг платформа это уже умеет? 😄
Что из этого уже используете?
#web #frontend #javascript #css #webdev #dev