
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 последнего проекта и прошёл по списку зависимостей с одним вопросом: а платформа это сама умеет?

Комментарии