Repository navigation
Description
Related antd issues (this was found while working on antd's Listy):
- [Bug] Listy 虚拟模式下自绘滚动条盖住列表项的右内距,与非虚拟模式间距不一致 ant-design/ant-design#59641 — spacing between the content and the virtual scrollbar
- [Bug] Listy 虚拟滚动条颜色过深(未覆写 --rc-virtual-list-scrollbar-bg) ant-design/ant-design#59633 — the virtual scrollbar thumb colour falling back to
rgba(0, 0, 0, 0.5)
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: 0of 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:
- reserve a gutter for it (e.g.
scrollbar-gutter: stable, or add the scrollbar width to the content box), or - 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
Treeaddspadding-rightto 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