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 }} />
|
<div aria-hidden data-testid="dock-spacer" style={{ height: dockReserve }} />
|
||||||
</ConversationContent>
|
</ConversationContent>
|
||||||
{/* The dock: an overlay sibling of the scroller (the
|
{/* The dock: an overlay sibling of the scroller (the
|
||||||
use-stick-to-bottom pattern — like the scroll button). Before
|
use-stick-to-bottom pattern). The scroll-down button lives
|
||||||
the button in DOM order so the button keeps painting above
|
INSIDE the overlay now (owner refinement round, 2026-09-24):
|
||||||
the dock, as it always has. */}
|
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
|
<DockOverlay
|
||||||
pending={pending}
|
pending={pending}
|
||||||
chatId={chatId}
|
chatId={chatId}
|
||||||
@@ -1128,7 +1130,6 @@ export function Chat({
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ConversationScrollButton />
|
|
||||||
</Conversation>
|
</Conversation>
|
||||||
|
|
||||||
{/* The session-scoped focuses (chrome-melt wave, 2026-09-19): mounted
|
{/* The session-scoped focuses (chrome-melt wave, 2026-09-19): mounted
|
||||||
@@ -1504,6 +1505,18 @@ function DockOverlay({
|
|||||||
turns={turns}
|
turns={turns}
|
||||||
dictationSlot={dictationSlot}
|
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>
|
</div>
|
||||||
{/* The plan marginalia (owner, plan-placement round): the bare
|
{/* The plan marginalia (owner, plan-placement round): the bare
|
||||||
diagram in the band's right flank — boundaless, meet-fitted, part
|
diagram in the band's right flank — boundaless, meet-fitted, part
|
||||||
|
|||||||
Reference in New Issue
Block a user