Scroll-down disc rides the dock: anchored above the composer, never over it
The scroll-to-bottom disc (ConversationScrollButton) was the scroller's last child — DOM order painted it ABOVE the dock overlay, so it floated at the scroller's bottom edge, partly covering the composer (owner refinement round 3, 2026-09-24: it must ALWAYS sit above the composer, never over any element, and stay positioned with respect to the composer — which grows a lot taller when a question owns the band). The fix is a DOM move, not a coordinate: the button now renders INSIDE the DockOverlay, as a sibling of the DOCK container within the dock block — `absolute bottom-full mb-2`, anchored to the BLOCK's top edge. Because the anchor is the block itself (not the scroller's bottom): - it sits above the composer shell AND the stats row, in the gradient's transparent band; - it rides every height change LIVE: typing growth, a pending question expanding the band (verified: the disc moves with the 393px question form, 9px clear), the 180ms fold morph to the slim bar (8px clear of the folded bar — no overlap mid-morph either, height animations reflow per frame); - pointer-events-auto (the overlay is none by default); the isAtBottom/scrollToBottom context still reaches it — DockOverlay is a Conversation child. The use-stick-to-bottom pattern comment updated; the shared ConversationScrollButton component itself is untouched (positioning arrives via className, twMerge swaps bottom-4 → bottom-full). Live-verified on :3001 with a tall transcript, all three states: chat mode (15px clear of the composer textarea), expanded question (9px clear of the form top), folded slim bar (8px clear). 902/902.
This commit is contained in:
+17
-4
@@ -1070,9 +1070,11 @@ export function Chat({
|
||||
<div aria-hidden data-testid="dock-spacer" style={{ height: dockReserve }} />
|
||||
</ConversationContent>
|
||||
{/* The dock: an overlay sibling of the scroller (the
|
||||
use-stick-to-bottom pattern — like the scroll button). Before
|
||||
the button in DOM order so the button keeps painting above
|
||||
the dock, as it always has. */}
|
||||
use-stick-to-bottom pattern). The scroll-down button lives
|
||||
INSIDE the overlay now (owner refinement round, 2026-09-24):
|
||||
anchored to the dock block's top edge, so it rides every
|
||||
height morph (typing, a question growing the band, the
|
||||
fold) and NEVER overlaps the composer. */}
|
||||
<DockOverlay
|
||||
pending={pending}
|
||||
chatId={chatId}
|
||||
@@ -1128,7 +1130,6 @@ export function Chat({
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<ConversationScrollButton />
|
||||
</Conversation>
|
||||
|
||||
{/* The session-scoped focuses (chrome-melt wave, 2026-09-19): mounted
|
||||
@@ -1504,6 +1505,18 @@ function DockOverlay({
|
||||
turns={turns}
|
||||
dictationSlot={dictationSlot}
|
||||
/>
|
||||
{/* The scroll-down disc (moved here from the Conversation,
|
||||
owner refinement round 2026-09-24): anchored to the BLOCK's
|
||||
top edge — above the composer shell AND the stats row, in
|
||||
the gradient's transparent band — so it never overlaps
|
||||
any dock element. Because the anchor is the block itself
|
||||
(not the scroller's bottom), it rides every height change
|
||||
LIVE: typing growth, a pending question expanding the
|
||||
band, the 180ms fold morph. pointer-events-auto: the
|
||||
overlay is none by default. The isAtBottom/scrollToBottom
|
||||
context still reaches it — DockOverlay is a Conversation
|
||||
child. */}
|
||||
<ConversationScrollButton className="pointer-events-auto bottom-full mb-2" />
|
||||
</div>
|
||||
{/* The plan marginalia (owner, plan-placement round): the bare
|
||||
diagram in the band's right flank — boundaless, meet-fitted, part
|
||||
|
||||
Reference in New Issue
Block a user