the docs shell names the row that absorbs the leftover, and the footer reflows on its column

This commit is contained in:
2026-08-23 10:59:31 -07:00
parent 5db09dba92
commit 660342d729
10 changed files with 217 additions and 35 deletions
@@ -197,7 +197,13 @@ export function PageTOCPopoverContent(props: ComponentProps<'div'>) {
<CollapsibleContent
data-toc-popover-content=""
{...props}
className={cn('flex flex-col px-4 max-h-[50vh] md:px-6', props.className)}
className={cn(
// Rows may shrink below their content so the scroller inside takes the
// overflow; the max-h is what makes that reachable. Mirrors
// layouts/docs/page/slots/toc.tsx.
'grid grid-cols-[minmax(0,1fr)] auto-rows-[minmax(0,auto)] px-4 max-h-[50vh] md:px-6',
props.className,
)}
>
<div>{props.children}</div>
</CollapsibleContent>
@@ -254,8 +260,10 @@ export function PageFooter({ items, children, className, ...props }: FooterProps
<>
<div
className={cn(
'@container grid gap-4',
previous && next ? 'grid-cols-2' : 'grid-cols-1',
// See layouts/docs/page/slots/footer.tsx: auto-fit measures the
// column, so the container query, the JS column count and the
// card's col-span override all collapse into this one track.
'grid gap-4 grid-cols-[repeat(auto-fit,minmax(min(100%,16rem),1fr))]',
className,
)}
{...props}
@@ -276,7 +284,9 @@ function FooterItem({ item, index }: { item: Item; index: 0 | 1 }) {
<Link
href={item.url}
className={cn(
'flex flex-col gap-2 rounded-lg border p-4 text-sm transition-colors hover:bg-fd-accent/80 hover:text-fd-accent-foreground @max-lg:col-span-full',
// The column is stated so the track cannot be floored at its content's
// min-content width. See layouts/docs/page/slots/footer.tsx.
'grid grid-cols-[minmax(0,1fr)] gap-2 rounded-lg border p-4 text-sm transition-colors hover:bg-fd-accent/80 hover:text-fd-accent-foreground',
index === 1 && 'text-end',
)}
>
@@ -13,6 +13,22 @@ export function Container(props: ComponentProps<'article'>) {
data-full={full}
{...props}
className={cn(
// STILL FLEX, deliberately — the rest of this layout is grid now.
//
// The shell gives `main` a 1fr row against a min-height of the viewport,
// so on a short page this article is TALLER than its content, and the
// thing that decides where the leftover goes is `flex-1` on the prose
// body (layouts/docs/page/index.tsx) — that is what holds the footer at
// the bottom instead of letting it ride up under the last paragraph.
//
// Under grid that `flex-1` is inert, and the row that should be 1fr
// cannot be named from here: the children are whatever the CONSUMER
// composes into DocsPage (breadcrumb, title, description, body, footer,
// any of them absent), so there is no stable index to put the 1fr on.
// Converting it means moving the growth contract into the public slot
// API, which is a change to what consumers pass, not a change of layout.
//
// Left as it is on purpose. Do not swap the class alone.
'flex flex-col w-full max-w-[900px] mx-auto [grid-area:main] px-4 py-6 gap-4 md:px-6 md:pt-8 xl:px-8 xl:pt-14',
full && 'max-w-[1168px]',
props.className,
@@ -41,8 +41,18 @@ export function Footer({ items, children, className, ...props }: FooterProps) {
<>
<div
className={cn(
'@container grid gap-4',
previous && next ? 'grid-cols-2' : 'grid-cols-1',
// One track statement replaces three rules that all said the same
// thing in different places: a container query on this element, a
// column count picked in JS from how many links exist, and a
// `@max-lg:col-span-full` on the card to undo the count again when
// the container got narrow.
//
// auto-fit measures the COLUMN, so it needs none of them. Two links
// sit side by side wherever two 16rem columns fit and stack where
// they do not; one link collapses the empty track and takes the full
// width on its own. `min(100%,16rem)` keeps the floor from
// overflowing a container narrower than the floor itself.
'grid gap-4 grid-cols-[repeat(auto-fit,minmax(min(100%,16rem),1fr))]',
className,
)}
{...props}
@@ -63,7 +73,15 @@ function FooterItem({ item, index }: { item: Item; index: 0 | 1 }) {
<Link
href={item.url}
className={cn(
'flex flex-col gap-2 rounded-lg border p-4 text-sm transition-colors hover:bg-fd-accent/80 hover:text-fd-accent-foreground @max-lg:col-span-full',
// The column is stated rather than left implicit, so the track cannot be
// floored at the min-content width of whatever a caller puts in here.
//
// It is NOT what saves the truncating description below — measured, a
// bare `grid` ellipsises that line just as well, because `truncate`
// carries `overflow: hidden` and a scroll container contributes zero
// min-content width. The guard is for the siblings that have no
// overflow of their own.
'grid grid-cols-[minmax(0,1fr)] gap-2 rounded-lg border p-4 text-sm transition-colors hover:bg-fd-accent/80 hover:text-fd-accent-foreground',
index === 1 && 'text-end',
)}
>
@@ -61,7 +61,14 @@ export function TOC({ container, header, footer, style = 'normal', list }: TOCPr
id="nd-toc"
{...container}
className={cn(
'sticky top-(--fd-docs-row-1) h-[calc(var(--fd-docs-height)-var(--fd-docs-row-1))] flex flex-col [grid-area:toc] w-(--fd-toc-width) pt-12 pe-4 pb-2 xl:layout:[--fd-toc-width:268px] max-xl:hidden',
// The rail has a definite height, so its rows have to be allowed to be
// SHORTER than their content or the scroller inside never scrolls —
// `auto-rows-[minmax(0,auto)]` is that permission, stated once on the
// container. As a flex column the same thing happened implicitly, via
// every child's default flex-shrink, which is a rule you have to know
// rather than one you can read. The header and footer slots are
// optional, so the rows are auto-placed rather than a fixed template.
'sticky top-(--fd-docs-row-1) h-[calc(var(--fd-docs-height)-var(--fd-docs-row-1))] grid grid-cols-[minmax(0,1fr)] auto-rows-[minmax(0,auto)] [grid-area:toc] w-(--fd-toc-width) pt-12 pe-4 pb-2 xl:layout:[--fd-toc-width:268px] max-xl:hidden',
container?.className,
)}
>
@@ -304,7 +311,12 @@ function ProgressCircle({
function PageTOCPopoverContent(props: ComponentProps<'div'>) {
return (
<CollapsibleContent data-toc-popover-content="" {...props}>
<div className="flex flex-col px-4 max-h-[50vh] md:px-6">{props.children}</div>
{/* Bounded by max-h rather than a definite height, but the row model is
the same as the rail's: rows may shrink below their content so the
scroller inside takes the overflow. */}
<div className="grid grid-cols-[minmax(0,1fr)] auto-rows-[minmax(0,auto)] px-4 max-h-[50vh] md:px-6">
{props.children}
</div>
</CollapsibleContent>
);
}
@@ -66,7 +66,7 @@ export function Sidebar({ footer, banner, collapsible = true, components, ...res
<Base.SidebarViewport>
{/* Marked so a sidebar filter can stand in for the tree while typing,
instead of appearing above it and pushing it off screen. */}
<div className="flex flex-col gap-0.5" data-sidebar-tree>
<div className="grid grid-cols-[minmax(0,1fr)] gap-0.5" data-sidebar-tree>
{menuItems
.filter((v) => v.type !== 'icon')
.map((item, i, list) => (
@@ -83,7 +83,7 @@ export function Sidebar({ footer, banner, collapsible = true, components, ...res
{/* gap-2, not the drawer's gap-3: this rail has a fixed height budget
and the drawer does not, so a point spent here is a point the tree
below never gets. */}
<div className="flex flex-col gap-2 p-4 pb-2">
<div className="grid grid-cols-[minmax(0,1fr)] gap-2 p-4 pb-2">
<div className="flex">
{slots.navTitle && (
<slots.navTitle className="inline-flex text-[0.9375rem] items-center gap-2.5 font-medium me-auto" />
@@ -114,7 +114,7 @@ export function Sidebar({ footer, banner, collapsible = true, components, ...res
</div>
{viewport}
{(slots.languageSelect || iconLinks.length > 0 || slots.themeSwitch || footer) && (
<div className="flex flex-col p-4 pt-2">
<div className="grid grid-cols-[minmax(0,1fr)] p-4 pt-2">
{slots.languageSelect && (
<slots.languageSelect.root
variant="secondary"
@@ -156,7 +156,7 @@ export function Sidebar({ footer, banner, collapsible = true, components, ...res
)}
</SidebarContent>
<SidebarDrawer>
<div className="flex flex-col gap-3 p-4 pb-2">
<div className="grid grid-cols-[minmax(0,1fr)] gap-3 p-4 pb-2">
<div className="flex text-fd-muted-foreground items-center gap-1.5">
<div className="flex flex-1">
{iconLinks.map((item, i) => (
@@ -199,7 +199,9 @@ export function Sidebar({ footer, banner, collapsible = true, components, ...res
{banner}
</div>
{viewport}
<div className="flex flex-col border-t p-4 pt-2 empty:hidden">{footer}</div>
<div className="grid grid-cols-[minmax(0,1fr)] border-t p-4 pt-2 empty:hidden">
{footer}
</div>
</SidebarDrawer>
</>
);
@@ -239,7 +241,36 @@ function SidebarContent({ ref: refProp, className, children, ...props }: Compone
// so the nav fills it. `items-end` used to push the nav to the
// inner edge of a column that grew with the viewport, which is
// what left a widening empty strip inside the rail's own card.
'absolute flex flex-col w-full inset-s-0 inset-y-0 bg-fd-card text-sm border-e duration-250 *:w-(--fd-sidebar-width)',
//
// THREE rows: the banner, the tree, the footer. The rail has a
// definite height (inset-y-0), and the tree is the one region
// that absorbs whatever the other two leave — so it is the only
// row that is 1fr, and minmax(0,…) lets it be shorter than its
// own content, which is what makes its scroller scroll.
//
// As a flex column this was stated on the CHILD instead:
// SidebarViewport carries `min-h-0 flex-1`. That reads as a
// property of the viewport when it is really a fact about this
// rail, and it is invisible from here — the row model says it
// where the rows are. The `flex-1` is inert under grid and stays
// only because SidebarViewport is shared with the notebook and
// flux layouts, which are still flex columns; `min-h-0` is still
// load-bearing there and harmless here.
//
// The footer row is conditional (it renders only when there is a
// language select, icon links, a theme switch or a footer). An
// absent third child leaves row 3 at `auto`, which is zero — the
// first two rows are unaffected either way.
//
// The rows have to be NAMED. Left implicit, all three are `auto`
// and grid's default `align-content: stretch` splits the rail's
// leftover height equally between them — measured at 600px with
// a three-item tree, 193/213/193 instead of 44/512/44, so the
// banner and the footer each stand four times their own content.
// It only misbehaves when the tree is SHORT: at 40 items the two
// spellings are identical, which is how it would have passed a
// casual look and shipped.
'absolute grid grid-cols-[minmax(0,1fr)] grid-rows-[auto_minmax(0,1fr)_auto] w-full inset-s-0 inset-y-0 bg-fd-card text-sm border-e duration-250 *:w-(--fd-sidebar-width)',
collapsed && [
'inset-y-2 rounded-xl transition-transform border w-(--fd-sidebar-width)',
hovered
@@ -297,7 +328,11 @@ function SidebarDrawer({
<Base.SidebarDrawerOverlay className="fixed z-40 inset-0 backdrop-blur-xs data-[state=open]:animate-fd-fade-in data-[state=closed]:animate-fd-fade-out" />
<Base.SidebarDrawerContent
className={cn(
'fixed text-[0.9375rem] flex flex-col shadow-lg border-s inset-e-0 inset-y-0 w-[85%] max-w-[380px] z-40 bg-fd-background data-[state=open]:animate-fd-sidebar-in data-[state=closed]:animate-fd-sidebar-out',
// Same three rows as the desktop rail, and for the same reason: a
// definite height (inset-y-0) with one region — the tree — absorbing
// the leftover. Here all three children always render, so the row
// model is exact.
'fixed text-[0.9375rem] grid grid-cols-[minmax(0,1fr)] grid-rows-[auto_minmax(0,1fr)_auto] shadow-lg border-s inset-e-0 inset-y-0 w-[85%] max-w-[380px] z-40 bg-fd-background data-[state=open]:animate-fd-sidebar-in data-[state=closed]:animate-fd-sidebar-out',
className,
)}
{...props}
@@ -420,7 +455,7 @@ function SidebarFolderContent({
<Base.SidebarFolderContent
className={(state) =>
cn(
'relative flex flex-col gap-0.5 pt-0.5',
'relative grid grid-cols-[minmax(0,1fr)] gap-0.5 pt-0.5',
depth === 1 &&
"before:content-[''] before:absolute before:w-px before:inset-y-1 before:bg-fd-border before:inset-s-2.5",
typeof className === 'function' ? className(state) : className,
@@ -482,7 +517,7 @@ function SidebarTabsDropdown({
<ChevronsUpDown className="shrink-0 ms-auto size-4 text-fd-muted-foreground" />
</PopoverTrigger>
)}
<PopoverContent className="flex flex-col gap-1 w-(--anchor-width) p-1 fd-scroll-container">
<PopoverContent className="grid grid-cols-[minmax(0,1fr)] gap-1 w-(--anchor-width) p-1 fd-scroll-container">
{tabs.map((item) => {
const isActive = selected && item.url === selected.url;
if (!isActive && item.unlisted) return;
@@ -197,7 +197,13 @@ export function PageTOCPopoverContent(props: ComponentProps<'div'>) {
<CollapsibleContent
data-toc-popover-content=""
{...props}
className={cn('flex flex-col px-4 max-h-[50vh] md:px-6', props.className)}
className={cn(
// Rows may shrink below their content so the scroller inside takes the
// overflow; the max-h is what makes that reachable. Mirrors
// layouts/docs/page/slots/toc.tsx.
'grid grid-cols-[minmax(0,1fr)] auto-rows-[minmax(0,auto)] px-4 max-h-[50vh] md:px-6',
props.className,
)}
>
{props.children}
</CollapsibleContent>
@@ -253,8 +259,10 @@ export function PageFooter({ items, children, className, ...props }: FooterProps
<>
<div
className={cn(
'@container grid gap-4',
previous && next ? 'grid-cols-2' : 'grid-cols-1',
// See layouts/docs/page/slots/footer.tsx: auto-fit measures the
// column, so the container query, the JS column count and the
// card's col-span override all collapse into this one track.
'grid gap-4 grid-cols-[repeat(auto-fit,minmax(min(100%,16rem),1fr))]',
className,
)}
{...props}
@@ -275,7 +283,9 @@ function FooterItem({ item, index }: { item: Item; index: 0 | 1 }) {
<Link
href={item.url}
className={cn(
'flex flex-col gap-2 rounded-lg border p-4 text-sm transition-colors hover:bg-fd-accent/80 hover:text-fd-accent-foreground @max-lg:col-span-full',
// The column is stated so the track cannot be floored at its content's
// min-content width. See layouts/docs/page/slots/footer.tsx.
'grid grid-cols-[minmax(0,1fr)] gap-2 rounded-lg border p-4 text-sm transition-colors hover:bg-fd-accent/80 hover:text-fd-accent-foreground',
index === 1 && 'text-end',
)}
>
@@ -13,6 +13,22 @@ export function Container(props: ComponentProps<'article'>) {
data-full={full}
{...props}
className={cn(
// STILL FLEX, deliberately — the rest of this layout is grid now.
//
// The shell gives `main` a 1fr row against a min-height of the viewport,
// so on a short page this article is TALLER than its content, and the
// thing that decides where the leftover goes is `flex-1` on the prose
// body (layouts/docs/page/index.tsx) — that is what holds the footer at
// the bottom instead of letting it ride up under the last paragraph.
//
// Under grid that `flex-1` is inert, and the row that should be 1fr
// cannot be named from here: the children are whatever the CONSUMER
// composes into DocsPage (breadcrumb, title, description, body, footer,
// any of them absent), so there is no stable index to put the 1fr on.
// Converting it means moving the growth contract into the public slot
// API, which is a change to what consumers pass, not a change of layout.
//
// Left as it is on purpose. Do not swap the class alone.
'flex flex-col w-full max-w-[900px] mx-auto [grid-area:main] px-4 py-6 gap-4 md:px-6 md:pt-8 xl:px-8 xl:pt-14',
full && 'max-w-[1168px]',
props.className,
@@ -41,8 +41,18 @@ export function Footer({ items, children, className, ...props }: FooterProps) {
<>
<div
className={cn(
'@container grid gap-4',
previous && next ? 'grid-cols-2' : 'grid-cols-1',
// One track statement replaces three rules that all said the same
// thing in different places: a container query on this element, a
// column count picked in JS from how many links exist, and a
// `@max-lg:col-span-full` on the card to undo the count again when
// the container got narrow.
//
// auto-fit measures the COLUMN, so it needs none of them. Two links
// sit side by side wherever two 16rem columns fit and stack where
// they do not; one link collapses the empty track and takes the full
// width on its own. `min(100%,16rem)` keeps the floor from
// overflowing a container narrower than the floor itself.
'grid gap-4 grid-cols-[repeat(auto-fit,minmax(min(100%,16rem),1fr))]',
className,
)}
{...props}
@@ -63,7 +73,15 @@ function FooterItem({ item, index }: { item: Item; index: 0 | 1 }) {
<Link
href={item.url}
className={cn(
'flex flex-col gap-2 rounded-lg border p-4 text-sm transition-colors hover:bg-fd-accent/80 hover:text-fd-accent-foreground @max-lg:col-span-full',
// The column is stated rather than left implicit, so the track cannot be
// floored at the min-content width of whatever a caller puts in here.
//
// It is NOT what saves the truncating description below — measured, a
// bare `grid` ellipsises that line just as well, because `truncate`
// carries `overflow: hidden` and a scroll container contributes zero
// min-content width. The guard is for the siblings that have no
// overflow of their own.
'grid grid-cols-[minmax(0,1fr)] gap-2 rounded-lg border p-4 text-sm transition-colors hover:bg-fd-accent/80 hover:text-fd-accent-foreground',
index === 1 && 'text-end',
)}
>
@@ -61,7 +61,14 @@ export function TOC({ container, header, footer, style = 'normal', list }: TOCPr
id="nd-toc"
{...container}
className={cn(
'sticky top-(--fd-docs-row-1) h-[calc(var(--fd-docs-height)-var(--fd-docs-row-1))] flex flex-col [grid-area:toc] w-(--fd-toc-width) pt-12 pe-4 pb-2 xl:layout:[--fd-toc-width:268px] max-xl:hidden',
// The rail has a definite height, so its rows have to be allowed to be
// SHORTER than their content or the scroller inside never scrolls —
// `auto-rows-[minmax(0,auto)]` is that permission, stated once on the
// container. As a flex column the same thing happened implicitly, via
// every child's default flex-shrink, which is a rule you have to know
// rather than one you can read. The header and footer slots are
// optional, so the rows are auto-placed rather than a fixed template.
'sticky top-(--fd-docs-row-1) h-[calc(var(--fd-docs-height)-var(--fd-docs-row-1))] grid grid-cols-[minmax(0,1fr)] auto-rows-[minmax(0,auto)] [grid-area:toc] w-(--fd-toc-width) pt-12 pe-4 pb-2 xl:layout:[--fd-toc-width:268px] max-xl:hidden',
container?.className,
)}
>
@@ -304,7 +311,12 @@ function ProgressCircle({
function PageTOCPopoverContent(props: ComponentProps<'div'>) {
return (
<CollapsibleContent data-toc-popover-content="" {...props}>
<div className="flex flex-col px-4 max-h-[50vh] md:px-6">{props.children}</div>
{/* Bounded by max-h rather than a definite height, but the row model is
the same as the rail's: rows may shrink below their content so the
scroller inside takes the overflow. */}
<div className="grid grid-cols-[minmax(0,1fr)] auto-rows-[minmax(0,auto)] px-4 max-h-[50vh] md:px-6">
{props.children}
</div>
</CollapsibleContent>
);
}
@@ -77,7 +77,7 @@ export function Sidebar({ footer, banner, collapsible = true, components, ...res
return (
<>
<SidebarContent {...rest}>
<div className="flex flex-col gap-3 p-4 pb-2">
<div className="grid grid-cols-[minmax(0,1fr)] gap-3 p-4 pb-2">
<div className="flex">
{slots.navTitle && (
<slots.navTitle className="inline-flex text-[0.9375rem] items-center gap-2.5 font-medium me-auto" />
@@ -103,7 +103,7 @@ export function Sidebar({ footer, banner, collapsible = true, components, ...res
</div>
{viewport}
{(slots.languageSelect || iconLinks.length > 0 || slots.themeSwitch || footer) && (
<div className="flex flex-col p-4 pt-2">
<div className="grid grid-cols-[minmax(0,1fr)] p-4 pt-2">
{slots.languageSelect && (
<slots.languageSelect.root
variant="secondary"
@@ -134,7 +134,7 @@ export function Sidebar({ footer, banner, collapsible = true, components, ...res
)}
</SidebarContent>
<SidebarDrawer>
<div className="flex flex-col gap-3 p-4 pb-2">
<div className="grid grid-cols-[minmax(0,1fr)] gap-3 p-4 pb-2">
<div className="flex text-fd-muted-foreground items-center gap-1.5">
<div className="flex flex-1">
{iconLinks.map((item, i) => (
@@ -177,7 +177,9 @@ export function Sidebar({ footer, banner, collapsible = true, components, ...res
{banner}
</div>
{viewport}
<div className="flex flex-col border-t p-4 pt-2 empty:hidden">{footer}</div>
<div className="grid grid-cols-[minmax(0,1fr)] border-t p-4 pt-2 empty:hidden">
{footer}
</div>
</SidebarDrawer>
</>
);
@@ -217,7 +219,36 @@ function SidebarContent({ ref: refProp, className, children, ...props }: Compone
// so the nav fills it. `items-end` used to push the nav to the
// inner edge of a column that grew with the viewport, which is
// what left a widening empty strip inside the rail's own card.
'absolute flex flex-col w-full inset-s-0 inset-y-0 bg-fd-card text-sm border-e duration-250 *:w-(--fd-sidebar-width)',
//
// THREE rows: the banner, the tree, the footer. The rail has a
// definite height (inset-y-0), and the tree is the one region
// that absorbs whatever the other two leave — so it is the only
// row that is 1fr, and minmax(0,…) lets it be shorter than its
// own content, which is what makes its scroller scroll.
//
// As a flex column this was stated on the CHILD instead:
// SidebarViewport carries `min-h-0 flex-1`. That reads as a
// property of the viewport when it is really a fact about this
// rail, and it is invisible from here — the row model says it
// where the rows are. The `flex-1` is inert under grid and stays
// only because SidebarViewport is shared with the notebook and
// flux layouts, which are still flex columns; `min-h-0` is still
// load-bearing there and harmless here.
//
// The footer row is conditional (it renders only when there is a
// language select, icon links, a theme switch or a footer). An
// absent third child leaves row 3 at `auto`, which is zero — the
// first two rows are unaffected either way.
//
// The rows have to be NAMED. Left implicit, all three are `auto`
// and grid's default `align-content: stretch` splits the rail's
// leftover height equally between them — measured at 600px with
// a three-item tree, 193/213/193 instead of 44/512/44, so the
// banner and the footer each stand four times their own content.
// It only misbehaves when the tree is SHORT: at 40 items the two
// spellings are identical, which is how it would have passed a
// casual look and shipped.
'absolute grid grid-cols-[minmax(0,1fr)] grid-rows-[auto_minmax(0,1fr)_auto] w-full inset-s-0 inset-y-0 bg-fd-card text-sm border-e duration-250 *:w-(--fd-sidebar-width)',
collapsed && [
'inset-y-2 rounded-xl transition-transform border w-(--fd-sidebar-width)',
hovered
@@ -271,7 +302,11 @@ function SidebarDrawer({
<Base.SidebarDrawerOverlay className="fixed z-40 inset-0 backdrop-blur-xs data-[state=open]:animate-fd-fade-in data-[state=closed]:animate-fd-fade-out" />
<Base.SidebarDrawerContent
className={cn(
'fixed text-[0.9375rem] flex flex-col shadow-lg border-s inset-e-0 inset-y-0 w-[85%] max-w-[380px] z-40 bg-fd-background data-[state=open]:animate-fd-sidebar-in data-[state=closed]:animate-fd-sidebar-out',
// Same three rows as the desktop rail, and for the same reason: a
// definite height (inset-y-0) with one region — the tree — absorbing
// the leftover. Here all three children always render, so the row
// model is exact.
'fixed text-[0.9375rem] grid grid-cols-[minmax(0,1fr)] grid-rows-[auto_minmax(0,1fr)_auto] shadow-lg border-s inset-e-0 inset-y-0 w-[85%] max-w-[380px] z-40 bg-fd-background data-[state=open]:animate-fd-sidebar-in data-[state=closed]:animate-fd-sidebar-out',
className,
)}
{...props}
@@ -384,7 +419,7 @@ function SidebarFolderContent({
)}
{...props}
>
<div className="flex flex-col gap-0.5 pt-0.5">{children}</div>
<div className="grid grid-cols-[minmax(0,1fr)] gap-0.5 pt-0.5">{children}</div>
</Base.SidebarFolderContent>
);
}
@@ -438,7 +473,7 @@ function SidebarTabsDropdown({
<ChevronsUpDown className="shrink-0 ms-auto size-4 text-fd-muted-foreground" />
</PopoverTrigger>
)}
<PopoverContent className="flex flex-col gap-1 w-(--radix-popover-trigger-width) p-1 fd-scroll-container">
<PopoverContent className="grid grid-cols-[minmax(0,1fr)] gap-1 w-(--radix-popover-trigger-width) p-1 fd-scroll-container">
{tabs.map((item) => {
const isActive = selected && item.url === selected.url;
if (!isActive && item.unlisted) return;