fix(chat): remove phantom scrollbar in ChatLayout self-scroll mode#3575
fix(chat): remove phantom scrollbar in ChatLayout self-scroll mode#3575cixzhang wants to merge 2 commits into
Conversation
The message area was forced to the scroll container's full height (min-height: 100%) while the in-flow sticky dock added its own height on top, so the container always overflowed by the composer height even when messages were short. Make the self-scroll root a flex column and let the message area grow into the remaining space instead. Closes #2573
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PR Analysis Report📚 Storybook PreviewView Storybook for this PR 🧪 Sandbox PreviewView Sandbox for this PR No new or modified components detected. Bundle Size Summary
Accessibility AuditStatus: No accessibility violations detected. Generated by PR Enrichment workflow | Storybook | Sandbox | View full report |
|
Good catch — you're right, and I dug into why. The phantom-scroll fix makes the self-scroll root a So it's a real flex-vs-sticky tension: the flex-column layout fixes the empty-space case but breaks sticky docking, which is the opposite trade from where we started ( I'd rather not push a guess here since the two goals pull against each other. The direction I think is right: keep the block scroll container + sticky dock (which pins correctly on scroll) and instead stop the message area from stacking its full height on top of the in-flow dock — i.e. reserve the dock's height rather than forcing Rather than force something visual, which do you prefer — keep sticky + reserve dock height, or move the dock out of flow? I'll implement whichever you pick and re-verify both the short-content (no scrollbar) and long-content (composer stays docked) cases. |
|
The composer inside the chat layout should be at the bottom of the layout even with short content, so the composer's position remains stable as messages stream in. |
Address review: the flex-column approach fixed the phantom scrollbar but broke the composer's sticky docking (it scrolled away with long content). Use a single-cell grid instead: the message area and the sticky dock share one grid cell, so the dock overlaps the message tail rather than adding flow height. This removes the phantom scrollbar AND keeps the composer pinned to the bottom for both short and long content, so its position stays stable as messages stream in.
|
Fixed in 360a776. Kept the composer docked at the bottom for both short and long content by switching the self-scroll root to a single-cell grid: the message area and the sticky dock now share one grid cell, so the dock overlaps the tail of the messages instead of adding its own flow height. That removes the phantom scrollbar (which was exactly the dock height) while preserving |

Summary
In self-scroll mode (no external
scrollRef),ChatLayoutalways overflowed its scroll container by exactly the composer/dock height, producing a phantom vertical scrollbar even when messages were far shorter than the viewport.Root cause
packages/core/src/Chat/ChatLayout.tsx. The root is the scroll container (rootScrollable→overflowY: auto;root→flex: 1; min-height: 0) with two in-flow children:min-height: 100%(pluspadding-block-end), so it alone is already the full height of the scroll container.position: sticky; bottom: 0. Sticky elements still occupy space in normal flow, so the dock adds its full height on top.Content height =
messageArea (100%)+dock height→ the container overflows by the dock height regardless of message count.Fix
rootScrollable).min-height: 100%withflex-grow: 1; min-height: 0(applied only in self-scroll mode viamessageAreaSelfScroll).The message area now grows into the space left after the dock instead of forcing the container's full height, so short content no longer overflows. External-scroll mode (
scrollRefprovided, dockposition: fixed) is unchanged.Validation
Added tests to
ChatLayout.test.tsxthat assert the exact structural/style mechanism the overflow depends on, using StyleX's dev runtime injection under jsdom (getComputedStyle):min-height: 100%and the self-scroll root wasdisplay: block— the two in-flow children (100% message area + sticky dock) guaranteed overflow.display: flex; flex-direction: column; message area isflex-grow: 1; min-height: 0; the sticky dock andoverflow: autocontainer are preserved.Why this method: jsdom performs no layout, so
scrollHeight/clientHeightcan't be measured directly. Asserting the resolved styles pins the precise cause (a full-height in-flow child sitting beside an in-flow sticky dock) rather than a proxy. Full Chat suite: 125 passed. ESLint clean.To eyeball (Storybook / docsite)
Closes #2573