Skip to content

[Bug] Virtual scrollbar overlaps the content instead of reserving a gutter #391

Open

Description

@leshalv

Related antd issues (this was found while working on antd's Listy):

What problem does this feature solve?

In virtual mode the custom scrollbar is absolutely positioned at the right edge of the list root and does not reserve any space, so the content underneath it gets covered.

Concrete case — antd Listy:

  • The row breathing room lives on the item itself: .ant-listy-item { padding: 12px 16px } — that 16px right padding is what separates the row content from the list edge.
  • The vertical scrollbar is 8px wide and sits at right: 0 of the list root.
  • Net effect: the scrollbar covers the rightmost 8px of that 16px padding, so the gap between the content and the scrollbar is 8px instead of 16px — and unlike a native overlay scrollbar this one is always visible, so the halved spacing is permanent.

Measured (antd 6.6.7, Chrome 155, macOS 26.7), list width 320px:

mode item right padding content box right edge scrollbar left edge content → scrollbar
virtual 16px 336px 344px 8px
non-virtual 16px 336px 352px 16px

Same component, same items, same width — only the spacing differs because of the virtual switch.

Repro: https://codesandbox.io/p/devbox/k3g67m?file=%2Findex.tsx — two <Listy> side by side, the top one with virtual, the bottom one without.

What does the expected behavior look like?

The scrollbar should not sit on top of the content. Either:

  1. reserve a gutter for it (e.g. scrollbar-gutter: stable, or add the scrollbar width to the content box), or
  2. behave like a native overlay scrollbar (visible on hover / while scrolling only).

At minimum the distance between the content and the scrollbar should not change when toggling virtual.

Where the code lives

src/ScrollBar.tsx:

const containerStyle = {
  position: 'absolute',
  visibility: visible ? null : 'hidden',
};
// vertical
Object.assign(containerStyle, {
  width: 8,
  top: 0,
  bottom: 0,
  [isLTR ? 'right' : 'left']: 0,
});

The scrollbar is rendered as a sibling of the holder and positioned against the list root, so the content box is never inset by the scrollbar width.

Additional notes

  • The horizontal scrollbar has the same problem in the other direction (height: 8; bottom: 0) — it covers the bottom padding of the last visible row.
  • The thumb colour falls back to rgba(0, 0, 0, 0.5) here (var(--rc-virtual-list-scrollbar-bg, rgba(0, 0, 0, 0.5))); consumers currently have to override that variable themselves, e.g. antd does it per component (see [Bug] Listy 虚拟滚动条颜色过深(未覆写 --rc-virtual-list-scrollbar-bg) ant-design/ant-design#59633).
  • Consumers also work around the overlap themselves — e.g. antd's Tree adds padding-right to its list to keep the row highlight clear of the capsule scrollbar. Handling it once here would be better for every consumer.

Environment

  • @rc-component/virtual-list: 1.5.2
  • antd: 6.6.7 (the consumer used for the measurements above)
  • Chrome 155.0.8059.85 / macOS 26.7.1

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions