[labs/virtualizer] Flow layout: scrollToIndex scrolls to margin edge instead of border edge
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 21.8k
- Forks
- 1.1k
- Avg merge
- 18h 25m
- Merged PRs (30d)
- 2
Description
## Description
When `scrollToIndex` scrolls to an item that was not previously in the rendered range, the virtualizer scrolls to the item's margin edge rather than its border edge. Native `scrollIntoView({block: 'start'})` scrolls to the element's border edge, so the virtualizer's behavior is inconsistent with the platform.
This is most visible when items have large margins — the viewport appears offset from the target item by the item's `margin-block-start` value.
## Reproduction
[Lit Playground repro](https://lit.dev/playground/#gist=1dcc912acfd1956dba0d82f081316106)
Use the "Scroll to index" buttons to jump to items not currently in view (e.g., scroll to index 100, then back to 0). Compare the scroll position of the reference list (left) vs the virtualized list (right).
**Expected:** Both lists scroll to the same visual position — the top of the target item's border box aligned with the viewport edge.
**Actual:** The virtualized list scrolls to a position offset by the item's `margin-block-start`, showing extra margin space above the target item.
## Root cause
In `BaseLayout._calculateScrollIntoViewPosition()`, the scroll target is computed from `_getItemPosition(index).insetBlockStart`. In the Flow layout, `_getItemPosition` returns:
```
insetBlockStart: this._getPosition(idx) - marginOffset
```
where `marginOffset` is the item's `marginBlockStart`. This gives the position of the item's margin box (where the absolutely-positioned element starts in the DOM), not its border box (where the visual content starts). Since CSS margins on absolutely-positioned elements shift the element from the transform origin, the margin is already accounted for visually — but the scroll calculation treats it as the content position.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with BaseLayout._calculateScrollIntoViewPosition() and the Flow layout's _getItemPosition(index), then run the linked Lit Playground reproduction with items that have large margins. Confirm that jumping to an unrendered index aligns the target item's border box with the viewport edge, matching native scrollIntoView({block: 'start'}).
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 74/100