Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions app/docs/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ export default async function Page(props: {
const MDX = page.data.body

return (
<div className="w-full px-4 py-8 md:px-6 md:py-12">
<div className="mx-auto flex w-full max-w-5xl justify-center gap-12">
<div className="flex min-h-full w-full">
<div className="flex min-w-0 flex-1 justify-center px-6 py-8 md:px-10 md:py-12">
<article className="w-full min-w-0 max-w-2xl">
<JsonLd
data={docsJsonLd({
Expand All @@ -54,8 +54,8 @@ export default async function Page(props: {
</div>
<DocsPager tree={source.getPageTree()} url={page.url} />
</article>
<DocsToc items={page.data.toc} />
</div>
<DocsToc items={page.data.toc} />
</div>
)
}
Expand Down
2 changes: 1 addition & 1 deletion app/docs/template.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ViewTransition } from "react"
export default function DocsTemplate({ children }: { children: ReactNode }) {
return (
<ViewTransition enter="page-blur" exit="page-blur" default="none">
<div className="flex min-h-0 flex-1 flex-col">{children}</div>
<div className="min-h-full">{children}</div>
</ViewTransition>
)
}
50 changes: 26 additions & 24 deletions components/docs-shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { DocsSidebarTrigger } from "@/components/docs-sidebar-trigger"
import { GithubStars } from "@/components/github-stars"
import { Logo } from "@/components/logo"
import { SearchTrigger } from "@/components/search-trigger"
import { ThemeToggle } from "@/components/theme-toggle"
import { FrameworkProvider } from "@/lib/framework"
import { cn } from "@/lib/utils"
import {
Expand All @@ -28,7 +29,7 @@ import {
const sidebarTokens: CSSProperties = {
"--sidebar": "var(--background)",
"--sidebar-foreground": "var(--foreground)",
"--sidebar-border": "transparent",
"--sidebar-border": "var(--border)",
"--sidebar-accent": "color-mix(in oklch, var(--foreground) 6%, transparent)",
"--sidebar-accent-foreground": "var(--foreground)",
"--sidebar-ring": "var(--ring)",
Expand Down Expand Up @@ -93,8 +94,8 @@ function WindowEdgeFade({
<div
aria-hidden
className={cn(
"pointer-events-none absolute inset-x-0 z-10 h-16",
isTop ? "top-0 rounded-t-2xl" : "bottom-0 rounded-b-2xl"
"pointer-events-none absolute inset-x-0 z-10 h-16 xl:right-56",
isTop ? "top-0" : "bottom-0"
)}
>
{EDGE_BLUR_LAYERS.map((layer, i) => {
Expand All @@ -117,9 +118,7 @@ function WindowEdgeFade({
}

/**
* Docs chrome: canvas sidebar + a real inset content window.
* The window is a flex column; the article scrolls inside it so the ring,
* corners, and shadow never have to be faked with overlays.
* Docs chrome: full-viewport panes split by borders.
*/
export function DocsShell({
tree,
Expand Down Expand Up @@ -161,11 +160,9 @@ export function DocsShell({
<Sidebar
id="docs-sidebar"
aria-label="Documentation"
variant="inset"
collapsible="offcanvas"
className="p-3"
>
<SidebarHeader className="flex h-14 flex-row items-center gap-2 px-4">
<SidebarHeader className="flex h-14 flex-row items-center gap-2 border-b px-4 py-0">
<Link
href="/docs"
className="flex min-w-0 items-center gap-2 text-sm font-medium"
Expand All @@ -179,24 +176,29 @@ export function DocsShell({
<DocsSidebar tree={tree} embedded />
</SidebarContent>
</Sidebar>
<SidebarInset className="relative z-10 m-3 min-h-0 min-w-0 overflow-hidden rounded-2xl bg-background ring-1 ring-black/10 shadow-[0_1px_2px_rgba(0,0,0,0.04),0_0_24px_rgba(0,0,0,0.06)] md:peer-data-[variant=inset]:m-3 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-2xl md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-3 dark:ring-white/12 dark:shadow-[0_1px_2px_rgba(0,0,0,0.2),0_0_28px_rgba(0,0,0,0.35)]">
<div
ref={scrollRef}
data-docs-window-scroll
className="absolute inset-0 overflow-y-auto overscroll-y-contain pt-14 pb-10"
>
{children}
</div>
<DocsEdgeBlurDefs uid={blurUid} />
<WindowEdgeFade edge="top" uid={blurUid} />
<WindowEdgeFade edge="bottom" uid={blurUid} />
<header className="absolute inset-x-0 top-0 z-20 flex h-14 items-center gap-3 px-4 md:px-6">
<DocsSidebarTrigger showWhenCollapsed />
<div className="ml-auto flex items-center gap-1">
<GithubStars stars={githubStars} />
<SidebarInset className="min-h-0 min-w-0 overflow-hidden">
<header className="flex h-14 min-w-0 shrink-0 border-b">
<div className="flex min-w-0 flex-1 items-center gap-1 px-4">
<DocsSidebarTrigger showWhenCollapsed />
<div className="min-w-0 flex-1" />
<GithubStars stars={githubStars} className="shrink-0" />
<ThemeToggle />
</div>
<div className="flex w-56 shrink-0 items-center border-l px-2">
<SearchTrigger />
</div>
</header>
<div className="relative min-h-0 flex-1 overflow-hidden">
<div
ref={scrollRef}
className="absolute inset-0 overflow-y-auto overscroll-y-contain"
>
{children}
</div>
<DocsEdgeBlurDefs uid={blurUid} />
<WindowEdgeFade edge="top" uid={blurUid} />
<WindowEdgeFade edge="bottom" uid={blurUid} />
</div>
</SidebarInset>
</SidebarProvider>
</FrameworkProvider>
Expand Down
3 changes: 1 addition & 2 deletions components/docs-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -271,11 +271,10 @@ export function DocsSidebar({
<Sidebar
id={id}
aria-label="Documentation"
variant="inset"
collapsible="offcanvas"
className={cn(className)}
>
<SidebarHeader className="flex h-14 flex-row items-center gap-2 px-4">
<SidebarHeader className="flex h-14 flex-row items-center gap-2 border-b px-4 py-0">
<Link
href="/docs"
className="flex min-w-0 items-center gap-2 text-sm font-medium"
Expand Down
4 changes: 2 additions & 2 deletions components/docs-toc.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ export function DocsToc({ items }: { items: TOCItemType[] }) {
if (items.length === 0) return null

return (
<aside className="hidden w-44 shrink-0 xl:block">
<div className="sticky top-16">
<aside className="hidden w-56 shrink-0 border-l xl:block">
<div className="sticky top-0 px-5 py-8">
<p className="mb-3 text-sm font-medium text-muted-foreground">
On this page
</p>
Expand Down
17 changes: 11 additions & 6 deletions components/search-trigger.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { Dialog } from "@base-ui/react/dialog"
import { useSearchContext } from "fumadocs-ui/contexts/search"
import { RiSearchLine } from "@remixicon/react"

export function SearchTrigger() {
import { cn } from "@/lib/utils"

export function SearchTrigger({ className }: { className?: string }) {
const { enabled, dialogHandle, hotKey } = useSearchContext()
const [mounted, setMounted] = useState(false)

Expand All @@ -19,13 +21,16 @@ export function SearchTrigger() {
<Dialog.Trigger
handle={dialogHandle}
type="button"
className="inline-flex h-8 items-center gap-2 rounded-xl px-2.5 text-sm text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground"
className={cn(
"flex h-8 w-full min-w-0 items-center gap-2 rounded-2xl bg-input/50 px-2.5 text-sm text-muted-foreground outline-none transition-[color,box-shadow] hover:text-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/30",
className
)}
aria-label="Open search"
>
<RiSearchLine className="size-4" />
<span className="hidden sm:inline">Search</span>
<RiSearchLine className="size-4 shrink-0" />
<span className="min-w-0 flex-1 truncate text-left">Search</span>
{mounted ? (
<span className="hidden items-center gap-0.5 sm:inline-flex">
<span className="hidden shrink-0 items-center gap-0.5 sm:inline-flex">
{hotKey.map((k, i) => (
<kbd
key={i}
Expand All @@ -36,7 +41,7 @@ export function SearchTrigger() {
))}
</span>
) : (
<span className="hidden h-[18px] w-14 sm:inline-flex" />
<span className="hidden h-[18px] w-14 shrink-0 sm:inline-flex" />
)}
</Dialog.Trigger>
)
Expand Down
27 changes: 27 additions & 0 deletions components/theme-toggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"use client"

import { useTheme } from "next-themes"
import { RiMoonLine, RiSunLine } from "@remixicon/react"

import { Button } from "@/components/ui/button"
import { useHydratedTheme } from "@/hooks/use-hydrated-theme"
import { cn } from "@/lib/utils"

export function ThemeToggle({ className }: { className?: string }) {
const { setTheme } = useTheme()
const theme = useHydratedTheme()
const isDark = theme === "dark"

return (
<Button
type="button"
variant="ghost"
size="icon-sm"
className={cn("text-muted-foreground hover:text-foreground", className)}
aria-label={isDark ? "Switch to light mode" : "Switch to dark mode"}
onClick={() => setTheme(isDark ? "light" : "dark")}
>
{isDark ? <RiMoonLine /> : <RiSunLine />}
</Button>
)
}
Loading