Хочу рассказать о своей npm‑библиотеке — это мой второй и самый сложный npm‑проект на момент написания статьи.
Библиотека решает ряд проблем и ограничений связанных с дефолтным поведением браузерного элемента скролл.Глубоко в код погружаться не буду — поделюсь историей создания и мотивацией.✨ Читать далее →
Уровень сложностиПростой
Время на прочтение6 мин
Охват и читатели9.1K
Кейс
Хочу рассказать о своей npm‑библиотеке — это мой второй и самый сложный npm‑проект на момент написания статьи. Глубоко в код погружаться не буду — поделюсь историей создания и мотивацией.
ВступлениеПримерно в 2015 году я получил свою первую работу UI/UX‑дизайнером. Рисовал интерфейсы мобильных и десктопных приложений в популярном тогда Sketch, на выделенном мне Mac, и был этому искренне рад. Шло всё неплохо: я соблюдал гайдлайны iOS и Android, а когда идея приложения во мне откликалась, старался уделить дизайну больше внимания — наполнял интерфейс своими иллюстрациями (я художник по образованию) и необычными решениями отдельных элементов. Иногда выходило вполне достойно.
Но был один элемент, изменения которого не принимались никогда, — полоса прокрутки. Разработчики, которым я передавал макет, очень не любили, когда её трогали, и прямо об этом говорили. Скролл, а особенно его бегунок, был тёмным лесом, в который лучше не заходить. И когда я в очередной раз слышал «это сделать нельзя», я начинал чувствовать себя тем самым парнем, который весело рисует облачка на кнопках под звуки поп‑исполнителей, пока разработчик страдает, перенося художественный замысел в код. Именно так макет UI и финальная реализация прощаются друг с другом.
СтановлениеШло время, и постепенно я начал не только рисовать макеты, но и верстать их на React. Так я всё глубже погружался в разработку — и однажды встретился со своим старым врагом: скроллом в браузере. Тогда я и ощутил на себе тот урон, который терпели тысячи разработчиков, разбивая труды дизайнеров о скалы невозможного.
Но что же с ним не так, о каких ограничениях речь? Поговорим о тех самых проклятьях, обрекающих на страдания невинных разработчиков.
Проклятье первое — «Вавилонская башня»Когда‑то давно у всех был один язык, все друг друга понимали, и о кроссплатформенных бедах никто не слышал. Потом разработчики браузеров, при отсутствие единой спецификации и стандартов, начали делать одно и то же, но по‑разному. Как итог один и тот же элемент выглядит немного иначе в зависимости от того, чем вы открыли страницу.
Проклятье второе — «Лишение»Кастомизации этот элемент почти не поддаётся, а то, что есть, изменениями назвать трудно: цвет через CSS да три варианта ширины. Есть ещё ::-webkit-scrollbar, но и это лишь покраска: поставить вместо бегунка свой элемент — с анимацией или реакцией на движение, просто нельзя. Дизайнеру же остаётся только чувство беспомощности. Странно, правда? Элемент, который пользователь видит на каждой странице, почти нельзя оформить.
Вызов брошенИ вот, имея при себе знания JS и React, в 2023 году я решил дать бой этим бедствиям. Я не до конца понимал, какой длинный и тернистый путь меня ждёт, но награда того стоила: где‑то вдалеке виднелся тот самый UI мечты, где вместо скучного серого бегунка можно поставить свой — любой формы и стиля, светящийся при наведении, растущий при нажатии, какой угодно. Да хоть фаербол вместо бегунка — я должен иметь возможность это сделать. Зачем эти ограничения?!
БойЦель ясна, а вот способа её реализации пока не было. Хотелось, чтобы библиотека работала сразу, из коробки. С моими знаниями я мог написать React‑компонент, и это было неплохим решением, но оставался вопрос стилизации: стили по умолчанию должны были приезжать вместе с компонентом. Можно было бы при монтировании рендерить в HTML тег <style>, но это ненадёжно, значит, стили должны жить внутри компонента — и ответ оказался предельно простым: инлайн‑стили. Я не фанат инлайн‑стилей, но здесь они разом решали целый пласт проблем: стилизация сразу оказывалась на месте и именно там, где нужно. Это заставило меня хорошо продумать элементы скролла и попутно ответить на вопросы реализации. Было также ясно, что без TypeScript я не справлюсь: в какой‑то момент просто не смогу поддерживать проект. Ну что ж, стек ясен — делаем.
Раунд первый — «Смешение»В первой версии я пытался собрать странное смешение: браузерный скролл со скрытым бегунком и моим элементом вместо него. Он умел только вертикальную прокрутку и горизонтальную, которая по сути была повёрнутой вертикальной. Очень быстро я упёрся во множество ограничений и понял, что писать придётся всё самому — и обработку событий прокрутки, и анимацию движения. А ещё нужно было учесть прокрутку по горизонтали и вертикали одновременно. Стало ясно, что текущая реализация несерьёзна и больше похожа на первые шаги, чтобы нащупать направление. Что делать дальше, я уже понимал, но немного выгорел и взял паузу, чтобы всё обдумать и вернуться со свежими силами.
Раунд второй — «Всё сам»Сев за вторую версию, я уже лучше понимал, что библиотека должна уметь и что для этого нужно. Я глубже погрузился в разработку и в создание внутренних механик, и у меня начало получаться. Но чем дольше я работал, тем больше идей приходило в голову, и список «что можно сделать ещё» вышел внушительным. Например, я понял, что написанное легко превращает компонент не только в скролл, но и в слайдер, — и добавил такой режим, а с ним стрелки и отдельную анимацию перелистывания. А по опыту работы со списками я знал, что нужны и ленивая отрисовка (объект появляется, когда до него докрутили, и остаётся), и виртуализация (в DOM живёт только то, что видно).
В какой‑то момент самым сложным стало не наличие механик, а то, как они уживаются друг с другом.
Простой пример. На странице есть горизонтальная лента карточек, а сама страница прокручивается вертикально. Пользователь ставит на ленту палец и ведёт — кому достаётся жест? Если палец пошёл вбок — ленте, если вниз — странице, и решать это нужно по первым же пикселям движения, пока человек ничего не заметил. А если он тянет не карточки, а бегунок ленты? Тогда жест принадлежит только ей, и страница не должна сдвинуться ни на пиксель. По отдельности каждое правило простое, но мышь, палец, колесо, клавиатура, вложенные скроллы и слайдеры начинают спорить друг с другом — и больше всего времени уходило именно на их регулировку.
Всё это сводило с ума и требовало огромного количества проверок и тестов. Стоило мне решить, что сделано всё, что можно, как я придумывал что‑то классное, что немедленно хотелось добавить, — и это начинало раздражать. Так библиотека разрослась, а API перестал быть цельным. Я снова выгорел и взял паузу.
Раунд третий — «Порядок»К третьей версии подступиться было трудно: всего накопилось много, но надо было наводить порядок и переделывать API. Тут я подключил к разработке ИИ, чтобы ничего не упустить при переделке и покрыть код тестами — иначе, чиня одно, я неизбежно ломал другое. И вот на горизонте забрезжил финальный API, который меня устраивал, а тесты были готовы. Добавилось и несколько новых фич: плитка из элементов разных размеров, бесконечная прокрутка и список, который начинается справа, — для языков, где читают справа налево.
Добавлю, что название выбрано не случайно: компонент может вести себя совершенно по‑разному и даже вовсе не походить на скролл — всё зависит от вашего воображения.
На момент написания статьи вышла третья версия библиотеки, и API стабилен.
ИтогВсё получилось, но по дороге библиотека заставила меня ответить на множество вопросов о том, как скролл должен себя вести и что уметь.
Читая эту статью, можно задаться вопросом: зачем столько трудов ради одного элемента и кому это нужно? Берите библиотеку, если хотите сделать что‑то необычное, а не то, что даёт браузер по умолчанию. Ещё она пригодится разработчикам игр — там стандартных решений почти не бывает.
От себя добавлю: если вас когда‑то пугали злым драконом, к встрече с которым вы были не готовы из‑за недостаточной квалификации, но который всё время попадается на пути, — попробуйте бросить ему вызов при следующей встрече. Как минимум вы приобретёте новые знания и навыки. Как максимум — сделаете мир немного дружелюбнее или просто интереснее.
Отдельно хочу поблагодарить коллег за терпение, пока я внедрял эту библиотеку на проде.
Посмотреть и почитать документацию, а так же собрать свой скролл можно [на странице morphing-scroll].
Удачи и успехов в разработке!
PS: тот самый [фаербол вместо бегунка]

Если эта публикация вас вдохновила и вы хотите поддержать автора — не стесняйтесь нажать на кнопку
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | [Перевод] 10 браузерных API, заменяющих библиотеки, которые я раньше все время устанавливал | 0 | 9.35 | 28-09-2026 |
| 2 | О том, как я написал свой стейт‑менеджер | 0 | 9.14 | 27-09-2026 |
| 3 | Как писать стили в 2026: CSS Modules, CSS‑in‑JS, Tailwind и zero‑runtime | 0 | 10.84 | 01-10-2026 |
| 4 | Кризис идентичности, или как Vue-разработчик мигрировал enterprise-проект на Angular 18 | 0 | 9.94 | 25-09-2026 |
| 5 | Почему архитектура важнее выбора стейт-менеджера в React | 0 | 8.37 | 01-10-2026 |
| 6 | Продвинутый статический анализ в TypeScript-проектах: выходим за рамки tsc и ESLint | 0 | 14.75 | 01-10-2026 |
| 7 | Удалить что угодно со страницы одним кликом: как устроен «пипеточный» блокировщик на Manifest V3 | 0 | 8.97 | 25-09-2026 |
| 8 | Как я писал сервер и нечаянно пробил 1М RPS | 0 | 10 | 31-08-2026 |
| 9 | Как мы создали Open-Source альтернативу закрытым платёжным системам на React, Node.js и WebNFC | 0 | 8.98 | 26-09-2026 |