fix(docs): stop the sidebar drifting when page content resizes (#6301)

* fix(docs): stop the sidebar drifting when page content resizes

The sidebar used fumadocs' `sticky` positioning, and a sticky box is
bottom-limited by its containing block. #nd-docs-layout ends ~660px above the
document bottom because the site footer is a sibling of the layout rather than
a grid child, so across the whole footer the sidebar was pushed progressively
upward. Any content-height change while the reader sat in that zone then moved
it: expanding one FAQ row shifted the sidebar 16.8px at a fixed scroll offset,
and collapsing it shifted it back.

Pin the sidebar and its divider to the viewport instead. A fixed box ignores
both the container's end and the document height, so neither the drift nor the
jump can happen. The grid columns are explicit (`0px 300px 1fr 268px 0px`), so
taking the placeholder out of flow leaves its track intact and the content
column does not move. The footer is already opaque and now out-stacks both, so
it slides over them at the end of the page.

Measured with Playwright before and after: sidebar delta on expand/collapse
16.8px/-16.8px -> 0px/0px, content column left and width unchanged, and the
sidebar holds top:92px at the page bottom on the docs, API-reference, academy
and integrations layouts. Mobile is untouched (the rule is desktop-only).

* refactor(docs): drop dead grid placement, move footer stacking to the component

Review follow-ups. The divider's `grid-row`/`grid-column` stopped doing anything
the moment it became `position: fixed` — a fixed box is out of grid layout
entirely — so they and the comment explaining the grid span were describing
positioning that no longer happens. Verified inert: the divider still computes
to left 300px / width 1px / z-index 21 without them.

The footer's stacking context also belongs on the footer, not in a global rule
matching every desktop `footer` element, so it moves to the component as
`relative z-[22]` with the reason in its TSDoc.
This commit is contained in:
Waleed
2026-08-05 16:26:29 -07:00
committed by GitHub
parent 7193035a72
commit a7d6b96132
2 changed files with 31 additions and 10 deletions
+23 -9
View File
@@ -362,18 +362,32 @@ aside#nd-sidebar [data-radix-scroll-area-viewport] {
min-height: var(--fd-docs-height) !important;
}
/* Sidebar divider line — sticky within the docs layout box, so it ends where
the layout does instead of bleeding into (or past) the footer below it.
#nd-docs-layout is a CSS grid (see fumadocs' Container slot); a sticky
pseudo-element stays in normal flow, so without an explicit grid-area it
gets auto-placed into a real content cell and skews that cell's sizing.
Spanning the full grid keeps it purely decorative/overlaid instead. */
/* Pin the sidebar to the viewport instead of letting fumadocs' `sticky` do it.
A sticky box is bottom-limited by its containing block, and #nd-docs-layout
ends ~660px above the document bottom because the site footer is a sibling
of the layout, not a grid child. So across the whole footer the sidebar gets
pushed upward — and any content-height change while the reader is in that
zone (expanding an FAQ row, say) makes it visibly jump. A fixed box ignores
both the container's end and the document's height, so neither happens.
Safe because the grid columns are explicit (`0px 300px 1fr 268px 0px`), so
removing the placeholder from flow leaves its track intact. `left`/`width`
are restated because a fixed box no longer derives them from its grid cell,
and `top`/`height` already come from fumadocs' own utility classes. */
[data-sidebar-placeholder] {
position: fixed !important;
left: var(--sidebar-offset);
width: var(--fd-sidebar-width);
}
/* Sidebar divider line — pinned for the same reason, and so it stays glued to
the sidebar's right edge. Being fixed takes it out of #nd-docs-layout's grid
entirely, so it needs no grid placement and cannot skew a content cell; its
position comes from `left`/`top` alone. */
#nd-docs-layout::before {
content: "";
display: block;
position: sticky;
grid-row: 1 / -1;
grid-column: 1 / -1;
position: fixed;
top: 92px; /* below navbar */
height: calc(100dvh - 92px);
left: calc(var(--sidebar-offset) + var(--fd-sidebar-width));
+8 -1
View File
@@ -130,9 +130,16 @@ function FooterColumn({ title, items }: { title: string; items: FooterItem[] })
)
}
/**
* Site footer.
*
* `relative z-[22]` stacks it above the docs sidebar (z-20) and that sidebar's
* divider (z-21), both of which are pinned to the viewport, so the footer slides
* over them at the end of the page instead of being drawn through.
*/
export function Footer() {
return (
<footer className='mt-[120px] w-full border-[var(--border)] border-t bg-[var(--bg)] max-sm:mt-16 max-lg:mt-[88px]'>
<footer className='relative z-[22] mt-[120px] w-full border-[var(--border)] border-t bg-[var(--bg)] max-sm:mt-16 max-lg:mt-[88px]'>
<div className='mx-auto w-full max-w-[1460px] px-20 pt-16 pb-16 max-sm:px-5 max-lg:px-8 max-lg:pt-12 max-lg:pb-12'>
<nav
aria-label='Footer navigation'