Scroll

Scroll — контейнер с кастомными скроллбарами поверх OverlayScrollbars. Оборачивает любой контент и подменяет нативные скроллбары на стилизованные — в двух размерах, с управляемыми стратегиями скрытия, автоскролом к краю и опциональным ресайзом.

Демо

Элемент списка 1
Элемент списка 2
Элемент списка 3
Элемент списка 4
Элемент списка 5
Элемент списка 6
Элемент списка 7
Элемент списка 8
Элемент списка 9
Элемент списка 10
Элемент списка 11
Элемент списка 12
Элемент списка 13
Элемент списка 14
Элемент списка 15
Элемент списка 16
Элемент списка 17
Элемент списка 18
Элемент списка 19
Элемент списка 20
Элемент списка 21
Элемент списка 22
Элемент списка 23
Элемент списка 24
Элемент списка 25

Когда использовать

  • Прокручиваемые области внутри интерфейса: боковые панели, чаты, логи, списки, таблицы в карточках.
  • Контент с динамической высотой, где нужно прилипание к низу (чат, live-лог) — через autoscrollTo='bottom'.
  • Контейнер, размер которого пользователь регулирует сам — через resize='both' | 'horizontal' | 'vertical'.

Когда не использовать:

  • На весь <body> — нативный скролл окна лучше для SEO, клавиатуры и системных жестов.
  • Внутри виртуализированных списков — они уже управляют своим скроллом.
  • Для коротких блоков, которые гарантированно помещаются — лишний контейнер и JS без выгоды.

Анатомия

Size

Толщина скролл-бара: s — дефолт для панелей и плотных списков; m — для крупных поверхностей, где нужен более заметный/удобный для мыши бар.

Bar hide strategy

Когда скрывать скролл-бар: never — всегда видим; leave — скрыть при уводе курсора; scroll — видим только во время прокрутки; move — видим при движении курсора над областью.

Resize

Разрешение пользовательского resize через CSS resize: none (дефолт), horizontal, vertical, both.

Autoscroll to

Автоприлипание к краю при появлении нового контента: bottom — для чатов и лент; right — для горизонтальных лент.

Установка

pnpm add @cloud-ru/ds-scroll
import { Scroll } from '@cloud-ru/ds-scroll'

Примеры использования

Базовый скролл

Базовый скроллОборачивает любой контент; родитель задаёт высоту.
Строка контента 1
Строка контента 2
Строка контента 3
Строка контента 4
Строка контента 5
Строка контента 6
Строка контента 7
Строка контента 8
Строка контента 9
Строка контента 10
Строка контента 11
Строка контента 12
Строка контента 13
Строка контента 14
Строка контента 15
Строка контента 16
Строка контента 17
Строка контента 18
Строка контента 19
Строка контента 20
tsx
import { Scroll } from '@cloud-ru/ds-scroll';

export function Basic() {
  return (
    <div style={{ height: 200, width: 320 }}>
      <Scroll>
        <div style={{ padding: 8 }}>
          {Array.from({ length: 20 }, (_, i) => (
            <div key={i}>Строка контента {i + 1}</div>
          ))}
        </div>
      </Scroll>
    </div>
  );
}

Компактный размер s

Компактный размер sДля узких областей и popover-ов.
Пункт 1
Пункт 2
Пункт 3
Пункт 4
Пункт 5
Пункт 6
Пункт 7
Пункт 8
Пункт 9
Пункт 10
Пункт 11
Пункт 12
Пункт 13
Пункт 14
Пункт 15
tsx
import { Scroll } from '@cloud-ru/ds-scroll';

export function SmallSize() {
  return (
    <div style={{ height: 180, width: 280 }}>
      <Scroll size='s'>
        <div style={{ padding: 8 }}>
          {Array.from({ length: 15 }, (_, i) => (
            <div key={i}>Пункт {i + 1}</div>
          ))}
        </div>
      </Scroll>
    </div>
  );
}

Скроллбар появляется при наведении

Скроллбар появляется при наведении`barHideStrategy="leave"` — спокойный, не отвлекающий UI.
Строка 1
Строка 2
Строка 3
Строка 4
Строка 5
Строка 6
Строка 7
Строка 8
Строка 9
Строка 10
Строка 11
Строка 12
Строка 13
Строка 14
Строка 15
Строка 16
Строка 17
Строка 18
Строка 19
Строка 20
tsx
import { Scroll } from '@cloud-ru/ds-scroll';

export function HideOnLeave() {
  return (
    <div style={{ height: 200, width: 320 }}>
      <Scroll barHideStrategy='leave'>
        <div style={{ padding: 8 }}>
          {Array.from({ length: 20 }, (_, i) => (
            <div key={i}>Строка {i + 1}</div>
          ))}
        </div>
      </Scroll>
    </div>
  );
}

Ресайзируемый контейнер

Ресайзируемый контейнер`resize="both"` — пользователь тянет за угол.
Контент, который можно ресайзить — 1
Контент, который можно ресайзить — 2
Контент, который можно ресайзить — 3
Контент, который можно ресайзить — 4
Контент, который можно ресайзить — 5
Контент, который можно ресайзить — 6
Контент, который можно ресайзить — 7
Контент, который можно ресайзить — 8
Контент, который можно ресайзить — 9
Контент, который можно ресайзить — 10
Контент, который можно ресайзить — 11
Контент, который можно ресайзить — 12
Контент, который можно ресайзить — 13
Контент, который можно ресайзить — 14
Контент, который можно ресайзить — 15
Контент, который можно ресайзить — 16
Контент, который можно ресайзить — 17
Контент, который можно ресайзить — 18
Контент, который можно ресайзить — 19
Контент, который можно ресайзить — 20
tsx
import { Scroll } from '@cloud-ru/ds-scroll';

export function Resizable() {
  return (
    <div style={{ height: 200, width: 320 }}>
      <Scroll resize='both'>
        <div style={{ padding: 8 }}>
          {Array.from({ length: 20 }, (_, i) => (
            <div key={i}>Контент, который можно ресайзить — {i + 1}</div>
          ))}
        </div>
      </Scroll>
    </div>
  );
}

Автоскрол вниз

Автоскрол внизДля чатов и live-логов — прилипание к низу при добавлении сообщений.
tsx
import { Scroll } from '@cloud-ru/ds-scroll';

export function AutoscrollBottom() {
  return (
    <div style={{ height: 200, width: 320 }}>
      <Scroll autoscrollTo='bottom'>
        <div style={{ padding: 8 }}>
          {Array.from({ length: 40 }, (_, i) => (
            <div key={i}>Сообщение {i + 1}</div>
          ))}
        </div>
      </Scroll>
    </div>
  );
}

Props

Types

PropsScrollProps
PropTypeDefaultRequiredDescription
autoscrollTo"bottom" | "right"noВключает автоскрол при маунте и изменении размера контента: <br /> - `bottom` - автоскрол вниз, <br /> - `right` - автоскрол вправо,
barHideStrategy"leave" | "move" | "never" | "scroll"leavenoУправление скрытием скролл баров: <br /> - `Never` - показывать всегда <br /> - `Leave` - скрывать когда курсор покидает компонент <br /> - `Scroll` - показывать только когда происходит скроллинг <br /> - `Move` - показывать при движении курсора над компонентом
childrenstring | number | boolean | ReactElement<any, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | null | undefinedno
classNamestringno
clickScrollingbooleantruenoСкролить ли по клику в скроллбар.
data-test-idstringno
onInitialized(() => void)noКоллбэк вызывающийся на инициализацию скролла
onScroll((event?: Event) => void)noКолбек события скрола.
overflow{ x?: "hidden" | "scroll" | "visible" | "visible-hidden" | "visible-scroll"; y?: "hidden" | "scroll" | "visible" | "visible-hidden" | "visible-scroll"; } | undefinednoПоведение overflow по осям. По умолчанию OverlayScrollbars выставляет `scroll` на обе оси; если контента по оси быть не должно — передавай `'hidden'`.
paddingAbsolutebooleannoДолжны ли паддинги быть абсолютными
resize"both" | "horizontal" | "none" | "vertical"nonenoНастройка возможности регулировать Scroll-контейнер: <br /> - `None` - нельзя изменять размер <br /> - `Horizontal` - можно изменять размер только по горизонтали <br /> - `Vertical` - можно изменять размер только по вертикали <br /> - `Both` - можно изменять размер в обеих координатах
size"m" | "s"mnoРазмер скролбаров
untouchableScrollbarsbooleanfalsenoОтключает возможность взаимодействовать со скролбарами мышью.

Unions

Types

ScrollProps

Unions

Storybook

Figma