HTML это уже умеет — чем заменить популярные npm-библиотеки в 2026
21 августа 2026 г.
5 мин
6

HTML это уже умеет — чем заменить популярные npm-библиотеки в 2026

Почти миллион раз в неделю скачивают полифилл для того, что Safari умеет с марта 2022 года.

Пакет называется smoothscroll-polyfill. Он добавляет плавную прокрутку в браузеры, которые её не поддерживают. Таких браузеров не осталось - последним был Safari до версии 15.4, и это было четыре с половиной года назад. Пакет не обновлялся с 2019-го. Его качают 914 тысяч раз в неделю.

Это не единичный случай. wicg-inert - полифилл атрибута, который стал стандартом в октябре прошлого года - 99 тысяч в неделю. picturefill — полифилл <picture> для Internet Explorer - 22 тысячи в неделю. В 2026 году.

Никто не принимал решения их ставить. Они просто лежат в package.json с позапрошлого проекта, и туда больше никто не смотрел.

Я как-то полез разбираться, что ещё из этой категории у меня в проектах. Получился неприятный список.

🔥 Аккордеон: один атрибут вместо 9 зависимостей

@radix-ui/react-accordion - почти 50 миллионов загрузок в неделю. 7,9 кБ в gzip, 9 транзитивных зависимостей. Через shadcn/ui он приезжает практически в каждый второй React-проект.

Вот что он делает:

<!-- ✗ Было: компонент, 9 зависимостей, 7,9 кБ -->
<Accordion type="single" collapsible>
  <AccordionItem value="1">…</AccordionItem>
</Accordion>
 
<!-- ✓ Стало: атрибут name группирует секции в аккордеон -->
<details name="faq">
  <summary>Как оформить возврат?</summary>
  <p>В течение 14 дней с момента получения.</p>
</details>
 
<details name="faq">
  <summary>Сколько идёт доставка?</summary>
  <p>2–5 рабочих дней.</p>
</details>

Одинаковый name - открытие одной секции закрывает остальные. Ровно то поведение, ради которого ставят библиотеку.

Атрибут появился в стандарте 2 октября 2023 года. Chrome 120, Safari 17.2, Firefox 130 - сейчас это 90% пользователей. В старом Firefox секции просто откроются все сразу. Не поломка, а деградация - сайт остаётся рабочим.

💡 Модалка: <dialog> вместо react-modal

Тут цифры ещё веселее. @radix-ui/react-dialog - 61 миллион загрузок в неделю, 12,3 кБ, 15 зависимостей. @headlessui/react - 61,5 кБ в gzip. Это больше, чем весит React DOM в некоторых сборках.

<dialog id="confirm">
  <form method="dialog">
    <h2>Точно удалить?</h2>
    <button value="cancel" autofocus>Отмена</button>
    <button value="ok">Удалить</button>
  </form>
</dialog>
 
<script>
  document.getElementById('confirm').showModal();
</script>

Что showModal() даёт бесплатно: диалог уходит в top layer - то есть его не перекроет ни один z-index и не обрежет родительский overflow. Остальная страница становится недоступной для фокуса и скринридера. Работает фокус-трап. Esc закрывает. aria-modal проставляется сам. ::backdrop можно стилизовать как обычный элемент.

Элемент <dialog> лежит в спецификации с 11 апреля 2012 года. Firefox и Safari поддержали его в марте 2022. Аргумент «а вдруг в сафари не работает» устарел на четыре года.

Честно про минусы: две вещи придётся дописать руками.

/* скролл фона нативно не блокируется */
html:has(dialog[open]:modal) { overflow: hidden; }

И закрытие кликом по подложке. Для него есть атрибут closedby, но в Safari его нет - так что пока ловим клик и сравниваем event.target с самим диалогом. Это строк пять. Не пятнадцать зависимостей.

🤔 Фокус-трап: атрибут inert

focus-trap - 5,2 миллиона в неделю. react-focus-lock - 2,9 миллиона и 6 зависимостей.

document.getElementById('page').inert = true;

Всё. Поддерево не фокусируется, не кликается, не находится через Ctrl+F и убирается из дерева доступности - скринридер его не увидит.

Атрибут впервые попал в спецификацию тем же коммитом, что и <dialog>, в 2012 году. Потом его убирали, возвращали, финальную редакцию приняли в марте 2022-го. Baseline widely available - с 11 октября 2025.

Отдельно смешное: полифилл wicg-inert продолжают качать 99 тысяч раз в неделю. Через год после того, как фича стала общедоступной.

❤️ Где нативное реально проигрывает

Если бы я на этом остановился, получилась бы агитка. Так что вот обратный пример.

<datalist> - нативный автокомплит. На бумаге заменяет react-select (7,7 млн загрузок, 29 кБ, 9 зависимостей). На практике - почти никогда.

Он не ограничивает ввод: пользователь всё равно напечатает что угодно, список это только подсказки. Нет нечёткого поиска и исправления опечаток. Список нельзя стилизовать вообще - а значит, нельзя адаптировать под высококонтрастный режим. Шрифт подсказок не масштабируется при зуме страницы. NVDA в Firefox не озвучивает их содержимое. А в Safari на iOS список подсказок перекрывает то самое поле, в которое вы печатаете.

Комбобокс с поиском, валидацией по списку и мультивыбором - это библиотека. Без вариантов.

И это общее правило: нативные примитивы закрывают процентов девяносто. Последние десять - анимация высоты до auto, позиционирование, стилизация внутренностей контролов, одинаковый вид во всех браузерах - до сих пор либо не работают везде, либо не существуют. Именно за эти десять процентов и платят библиотекой. Вопрос только в том, платите ли вы за них осознанно.

😤 Почему мы этого не знаем - и при чём тут ИИ

Обычное объяснение - привычка. Не читаем спеки, копируем из прошлого проекта.

Но есть вторая причина, и она новая. Ассистент вас от нативного решения отговорит.

Я это ловлю регулярно: просишь модалку - получаешь div с position: fixed, ручным управлением фокусом и обработчиком Esc. Просишь тултип - получаешь класс js-hidden и функцию dismissAllTooltips().

Механизм объяснили во freeCodeCamp:

«Новые браузерные API появляются за годы до того, как накопится достаточно туториалов, ответов на Stack Overflow и реального кода, чтобы заметно попасть в обучающую выборку. Легаси-паттерн побеждает не потому, что модель не знает - а потому что обучающий сигнал у устаревшего подхода сильнее.»

Это не догадка отдельных людей. Google выпустил под эту проблему отдельный набор инструкций для кодинг-агентов - modern-web-guidance, 102 фичи веб-платформы. В описании сказано прямо: агенты по умолчанию скатываются к старым паттернам, потому что в обучающих данных горы легаси, и генерируют раздутый JavaScript там, где у платформы давно есть нативное решение.

У меня в блоге целая серия про Claude Code - я пользуюсь этим каждый день. И это ровно тот случай, когда инструмент надо перепроверять: не по ответу модели, а по MDN и Baseline.

Что чем заменить — короткая таблица

Ставим Загрузок/нед Вес Что умеет платформа
@radix-ui/react-dialog 61 млн 12,3 кБ <dialog> + showModal()
@radix-ui/react-accordion 50 млн 7,9 кБ <details name>
react-day-picker 40 млн 19,3 кБ <input type="date">
scroll-into-view-if-needed 7,6 млн 1,7 кБ scroll-behavior: smooth
focus-trap 5,2 млн 6,4 кБ атрибут inert
@headlessui/react 4,9 млн 61,5 кБ <dialog> + popover
nprogress 3,2 млн 2,3 кБ <progress>
smoothscroll-polyfill 914 тыс. 1,4 кБ не нужен с 2022 года
wicg-inert 99 тыс. 2,5 кБ не нужен с 2025 года

Что я с этим сделал

Открыл package.json последнего проекта и прошёл по списку зависимостей с одним вопросом: а платформа это сама умеет?