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
8 changes: 3 additions & 5 deletions src/app/(docs)/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from 'fumadocs-ui/layouts/docs/page';
import { notFound } from 'next/navigation';
import { getMDXComponents } from '@/components/mdx';
import { DocsBreadcrumbs } from '@/components/docs-breadcrumbs';
import { DocsSiteFooter } from '@/components/docs-site-footer';
import type { Metadata } from 'next';
import { createRelativeLink } from 'fumadocs-ui/mdx';
Expand All @@ -29,11 +30,7 @@ export default async function Page(props: PageProps<'/[[...slug]]'>) {
toc={page.data.toc}
full={page.data.full}
className="docs-article"
breadcrumb={{
includeRoot: true,
includePage: true,
className: 'docs-breadcrumb',
}}
breadcrumb={{ enabled: false }}
tableOfContentPopover={{ enabled: false }}
footer={{ children: <DocsSiteFooter /> }}
tableOfContent={{
Expand All @@ -46,6 +43,7 @@ export default async function Page(props: PageProps<'/[[...slug]]'>) {
),
}}
>
<DocsBreadcrumbs url={page.url} />
<DocsTitle className="docs-title">{page.data.title}</DocsTitle>
<DocsDescription className="docs-lead">{page.data.description}</DocsDescription>
<DocsBody className="docs-body">
Expand Down
95 changes: 27 additions & 68 deletions src/app/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -397,54 +397,11 @@ html > body[data-scroll-locked] {
background: url('/icon.png') center / contain no-repeat;
}

.docs-header-center {
position: absolute;
inset-inline: 0;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
}

.docs-section-picker,
.docs-dashboard-menu {
position: relative;
pointer-events: auto;
}

.docs-section-trigger {
display: inline-flex;
align-items: center;
gap: 4px;
height: 32px;
padding: 6px 11px;
border: 0;
border-radius: 8px;
background: transparent;
color: var(--text-primary);
font-family: inherit;
font-size: 14px;
font-weight: 500;
line-height: 20px;
white-space: nowrap;
cursor: pointer;
transition:
color 150ms ease,
background-color 150ms ease;
}

.docs-section-trigger:hover,
.docs-section-trigger[aria-expanded='true'] {
background: var(--bg-hover);
}

.docs-section-trigger svg {
width: 14px;
height: 14px;
transition: transform 150ms cubic-bezier(0.16, 1, 0.3, 1);
}

.docs-section-trigger[aria-expanded='true'] svg,
.docs-dashboard-more[aria-expanded='true'] svg {
transform: rotate(180deg);
}
Expand Down Expand Up @@ -485,17 +442,6 @@ html > body[data-scroll-locked] {
color: var(--text-primary);
}

.docs-header-popover a[data-active='true'] {
background: var(--accent-soft);
color: var(--accent);
}

.docs-section-popover {
left: 50%;
width: 160px;
transform: translateX(-50%);
}

.docs-header-actions {
display: flex;
flex: none;
Expand Down Expand Up @@ -802,21 +748,35 @@ html > body[data-scroll-locked] {
}

.docs-breadcrumb {
gap: 6px !important;
margin-bottom: 16px;
color: var(--text-tertiary) !important;
font-family: var(--font-mono) !important;
font-size: 11px !important;
font-weight: 400 !important;
letter-spacing: 0.04em;
text-transform: uppercase;
line-height: 16px !important;
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 20px;
color: var(--text-tertiary);
font-size: 13px;
font-weight: 400;
line-height: 20px;
}

.docs-breadcrumb a,
.docs-breadcrumb span {
color: var(--text-tertiary) !important;
font-weight: 400 !important;
.docs-breadcrumb-item {
display: inline-flex;
align-items: center;
gap: 6px;
}

.docs-breadcrumb a {
color: var(--text-tertiary);
text-decoration: none;
transition: color 150ms ease;
}

.docs-breadcrumb a:hover {
color: var(--text-primary);
}

.docs-breadcrumb [aria-current='page'] {
color: var(--text-secondary);
}

.docs-breadcrumb svg {
Expand Down Expand Up @@ -1425,7 +1385,6 @@ html > body[data-scroll-locked] {
padding-inline: 16px;
}

.docs-header-center,
.docs-theme-switcher,
.docs-search-trigger-full,
.docs-dashboard-menu {
Expand Down
47 changes: 47 additions & 0 deletions src/components/docs-breadcrumbs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import Link from 'next/link';
import { ChevronRight } from 'lucide-react';
import type { Node } from 'fumadocs-core/page-tree';
import type { ReactNode } from 'react';
import { source } from '@/lib/source';

type Crumb = { name: ReactNode; url?: string };

function findTrail(nodes: Node[], url: string, trail: Crumb[]): Crumb[] | null {
for (const node of nodes) {
if (node.type === 'page' && node.url === url) {
return [...trail, { name: node.name, url: node.url }];
}
if (node.type === 'folder') {
const found = findTrail(node.children, url, [
...trail,
{ name: node.name, url: node.index?.url },
]);
if (found) return found;
}
}
return null;
}

export function DocsBreadcrumbs({ url }: { url: string }) {
const trail = findTrail(source.getPageTree().children, url, []) ?? [];

return (
<nav className="docs-breadcrumb" aria-label="Breadcrumb">
<Link href="/">Docs</Link>
{trail.map((crumb, index) => {
const isLast = index === trail.length - 1;

return (
<span key={index} className="docs-breadcrumb-item">
<ChevronRight aria-hidden="true" strokeWidth={1.5} />
{crumb.url && !isLast ? (
<Link href={crumb.url}>{crumb.name}</Link>
) : (
<span aria-current={isLast ? 'page' : undefined}>{crumb.name}</span>
)}
</span>
);
})}
</nav>
);
}
58 changes: 1 addition & 57 deletions src/components/docs-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,11 @@

import Link from 'next/link';
import { ChevronDown, Menu } from 'lucide-react';
import { usePathname } from 'next/navigation';
import { useDocsLayout } from 'fumadocs-ui/layouts/docs';
import { useEffect, useId, useRef, useState } from 'react';
import { docsSectionItems, MogplexWordmark } from '@/lib/layout.shared';
import { MogplexWordmark } from '@/lib/layout.shared';
import { ThemeSwitcher } from '@/components/theme-switcher';

function isActivePath(pathname: string, href: string) {
if (href === '/') return pathname === href;
return pathname === href || pathname.startsWith(`${href}/`);
}

function useDisclosureMenu() {
const [open, setOpen] = useState(false);
const containerRef = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -44,51 +38,6 @@ function useDisclosureMenu() {
return { containerRef, open, setOpen, triggerRef };
}

function DocsSectionPicker({ pathname }: { pathname: string }) {
const menuId = useId();
const { containerRef, open, setOpen, triggerRef } = useDisclosureMenu();

return (
<div className="docs-section-picker" ref={containerRef}>
<button
ref={triggerRef}
type="button"
className="docs-section-trigger"
aria-label="Select documentation section"
aria-controls={menuId}
aria-expanded={open}
onClick={() => setOpen((value) => !value)}
>
<span>Docs</span>
<ChevronDown aria-hidden="true" strokeWidth={1.5} />
</button>

<nav
id={menuId}
className="docs-header-popover docs-section-popover"
aria-label="Documentation sections"
hidden={!open}
>
{docsSectionItems.map((item) => {
const active = isActivePath(pathname, item.href);

return (
<Link
key={item.href}
href={item.href}
data-active={active}
aria-current={active ? 'page' : undefined}
onClick={() => setOpen(false)}
>
{item.label}
</Link>
);
})}
</nav>
</div>
);
}

function DashboardMenu() {
const menuId = useId();
const { containerRef, open, setOpen, triggerRef } = useDisclosureMenu();
Expand Down Expand Up @@ -134,7 +83,6 @@ function DashboardMenu() {
}

export function DocsHeader() {
const pathname = usePathname();
const { isNavTransparent, slots } = useDocsLayout();
const SidebarTrigger = slots.sidebar.trigger;
const SearchTrigger = slots.searchTrigger && slots.searchTrigger.sm;
Expand Down Expand Up @@ -168,10 +116,6 @@ export function DocsHeader() {
<MogplexWordmark />
</Link>

<div className="docs-header-center">
<DocsSectionPicker pathname={pathname} />
</div>

<div className="docs-header-actions">
<ThemeSwitcher />

Expand Down
2 changes: 0 additions & 2 deletions src/lib/layout.shared.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ export const docsNavItems = [
{ label: 'Reference', href: '/reference' },
] as const;

export const docsSectionItems = [{ label: 'Overview', href: '/' }, ...docsNavItems] as const;

export const docsSearchLinks: [string, string][] = [
['Overview', '/'],
['Quickstart', '/quickstart'],
Expand Down