Skip to content

Commit 57367d3

Browse files
Refresh docs UI with shadcn styling
Adopt the shadcn/fumadocs styling stack for the site, including new theme variables, shared `cn`/button utilities, and component config. This also restyles docs content, sidebar, and page actions, adds Geist as the sans font, and updates the header with a light/dark theme switch and GitHub icon button.
1 parent f7965e8 commit 57367d3

13 files changed

Lines changed: 2080 additions & 58 deletions

File tree

app/docs/[[...slug]]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export default async function DocPage(props: PageProps) {
7070
</p>
7171
) : null}
7272
</header>
73-
<div className="prose prose-neutral max-w-none dark:prose-invert prose-headings:scroll-mt-20 prose-a:text-primary">
73+
<div className="doc-content prose-no-margin text-fd-foreground">
7474
<MDX components={getMDXComponents() as never} />
7575
</div>
7676
<DocPageActions

app/globals.css

Lines changed: 157 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,169 @@
11
@import "tailwindcss";
2-
@import "fumadocs-ui/css/neutral.css";
2+
@import "fumadocs-ui/css/shadcn.css";
33
@import "fumadocs-ui/css/preset.css";
4-
@plugin "@tailwindcss/typography";
4+
@import "tw-animate-css";
5+
@import "shadcn/tailwind.css";
56

6-
@theme inline {
7-
--color-background: var(--background);
8-
--color-foreground: var(--foreground);
9-
--color-muted: var(--muted);
10-
--color-muted-foreground: var(--muted-foreground);
11-
--color-border: var(--border);
12-
--color-primary: var(--primary);
13-
}
7+
@custom-variant dark (&:is(.dark *));
148

15-
:root {
16-
--background: #ffffff;
17-
--foreground: #0a0a0a;
18-
--muted: #f4f4f5;
19-
--muted-foreground: #71717a;
20-
--border: #e4e4e7;
21-
--primary: #2563eb;
9+
@theme inline {
10+
--color-background: var(--color-fd-background);
11+
--color-foreground: var(--color-fd-foreground);
12+
--color-muted: var(--color-fd-muted);
13+
--color-muted-foreground: var(--color-fd-muted-foreground);
14+
--color-border: var(--color-fd-border);
15+
--color-primary: var(--color-fd-primary);
16+
--color-primary-foreground: var(--color-fd-primary-foreground);
17+
--color-card: var(--color-fd-card);
18+
--color-card-foreground: var(--color-fd-card-foreground);
19+
--color-accent: var(--color-fd-accent);
20+
--color-accent-foreground: var(--color-fd-accent-foreground);
21+
--color-ring: var(--color-fd-ring);
22+
--radius-sm: calc(var(--radius) - 4px);
23+
--radius-md: calc(var(--radius) - 2px);
24+
--radius-lg: var(--radius);
25+
--radius-xl: calc(var(--radius) + 4px);
26+
--font-heading: var(--font-sans);
27+
--font-sans: var(--font-sans);
28+
--color-sidebar-ring: var(--sidebar-ring);
29+
--color-sidebar-border: var(--sidebar-border);
30+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
31+
--color-sidebar-accent: var(--sidebar-accent);
32+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
33+
--color-sidebar-primary: var(--sidebar-primary);
34+
--color-sidebar-foreground: var(--sidebar-foreground);
35+
--color-sidebar: var(--sidebar);
36+
--color-chart-5: var(--chart-5);
37+
--color-chart-4: var(--chart-4);
38+
--color-chart-3: var(--chart-3);
39+
--color-chart-2: var(--chart-2);
40+
--color-chart-1: var(--chart-1);
41+
--color-input: var(--input);
42+
--color-destructive: var(--destructive);
43+
--color-secondary-foreground: var(--secondary-foreground);
44+
--color-secondary: var(--secondary);
45+
--color-popover-foreground: var(--popover-foreground);
46+
--color-popover: var(--popover);
47+
--radius-2xl: calc(var(--radius) * 1.8);
48+
--radius-3xl: calc(var(--radius) * 2.2);
49+
--radius-4xl: calc(var(--radius) * 2.6);
2250
}
2351

24-
@media (prefers-color-scheme: dark) {
52+
@layer base {
2553
:root {
26-
--background: #0a0a0a;
27-
--foreground: #fafafa;
28-
--muted: #18181b;
29-
--muted-foreground: #a1a1aa;
30-
--border: #27272a;
31-
--primary: #3b82f6;
54+
--background: oklch(1 0 0);
55+
--foreground: oklch(0.145 0 0);
56+
--card: oklch(1 0 0);
57+
--card-foreground: oklch(0.145 0 0);
58+
--popover: oklch(1 0 0);
59+
--popover-foreground: oklch(0.145 0 0);
60+
--primary: oklch(0.546 0.245 262.881);
61+
--primary-foreground: oklch(0.985 0 0);
62+
--secondary: oklch(0.967 0.001 286.375);
63+
--secondary-foreground: oklch(0.21 0.006 285.885);
64+
--muted: oklch(0.967 0.001 286.375);
65+
--muted-foreground: oklch(0.552 0.016 285.938);
66+
--accent: oklch(0.967 0.001 286.375);
67+
--accent-foreground: oklch(0.21 0.006 285.885);
68+
--destructive: oklch(0.577 0.245 27.325);
69+
--border: oklch(0.92 0.004 286.32);
70+
--input: oklch(0.92 0.004 286.32);
71+
--ring: oklch(0.546 0.245 262.881);
72+
--radius: 0.625rem;
73+
--sidebar: oklch(0.985 0 0);
74+
--sidebar-foreground: oklch(0.145 0 0);
75+
--sidebar-primary: oklch(0.546 0.245 262.881);
76+
--sidebar-primary-foreground: oklch(0.985 0 0);
77+
--sidebar-accent: oklch(0.967 0.001 286.375);
78+
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
79+
--sidebar-border: oklch(0.92 0.004 286.32);
80+
--sidebar-ring: oklch(0.546 0.245 262.881);
81+
}
82+
83+
.dark {
84+
--background: oklch(0.145 0 0);
85+
--foreground: oklch(0.985 0 0);
86+
--card: oklch(0.205 0 0);
87+
--card-foreground: oklch(0.985 0 0);
88+
--popover: oklch(0.205 0 0);
89+
--popover-foreground: oklch(0.985 0 0);
90+
--primary: oklch(0.623 0.214 259.815);
91+
--primary-foreground: oklch(0.985 0 0);
92+
--secondary: oklch(0.274 0.006 286.033);
93+
--secondary-foreground: oklch(0.985 0 0);
94+
--muted: oklch(0.274 0.006 286.033);
95+
--muted-foreground: oklch(0.705 0.015 286.067);
96+
--accent: oklch(0.274 0.006 286.033);
97+
--accent-foreground: oklch(0.985 0 0);
98+
--destructive: oklch(0.704 0.191 22.216);
99+
--border: oklch(1 0 0 / 10%);
100+
--input: oklch(1 0 0 / 15%);
101+
--ring: oklch(0.623 0.214 259.815);
102+
--sidebar: oklch(0.205 0 0);
103+
--sidebar-foreground: oklch(0.985 0 0);
104+
--sidebar-primary: oklch(0.623 0.214 259.815);
105+
--sidebar-primary-foreground: oklch(0.985 0 0);
106+
--sidebar-accent: oklch(0.274 0.006 286.033);
107+
--sidebar-accent-foreground: oklch(0.985 0 0);
108+
--sidebar-border: oklch(1 0 0 / 10%);
109+
--sidebar-ring: oklch(0.623 0.214 259.815);
110+
}
111+
* {
112+
@apply border-border outline-ring/50;
113+
}
114+
body {
115+
@apply bg-background text-foreground;
116+
}
117+
html {
118+
@apply font-sans;
32119
}
33120
}
34121

35-
body {
36-
background: var(--background);
37-
color: var(--foreground);
122+
@layer components {
123+
.doc-content table th,
124+
.doc-content table td {
125+
@apply px-4 py-2.5 text-left;
126+
}
127+
128+
.doc-content table th {
129+
@apply border-b border-fd-border font-medium;
130+
}
131+
132+
.doc-content table td {
133+
@apply border-b border-fd-border;
134+
}
135+
136+
.doc-content a {
137+
@apply text-fd-primary underline-offset-4 hover:underline;
138+
}
139+
140+
.doc-content h2,
141+
.doc-content h3,
142+
.doc-content h4 {
143+
@apply scroll-mt-20 font-semibold tracking-tight text-fd-foreground;
144+
}
145+
146+
.doc-content h2 {
147+
@apply mt-10 mb-4 text-2xl;
148+
}
149+
150+
.doc-content h3 {
151+
@apply mt-8 mb-3 text-xl;
152+
}
153+
154+
.doc-content p,
155+
.doc-content ul,
156+
.doc-content ol {
157+
@apply my-4 text-fd-foreground/90;
158+
}
159+
160+
.doc-content ul {
161+
@apply list-disc pl-6;
162+
}
163+
164+
.doc-content ol {
165+
@apply list-decimal pl-6;
166+
}
38167
}
39168

40169
.text-hero {
@@ -66,7 +195,7 @@ body {
66195
}
67196

68197
.btn-primary {
69-
@apply inline-flex items-center gap-2 rounded-lg bg-primary px-5 py-3 text-sm font-semibold text-white transition-opacity hover:opacity-90;
198+
@apply inline-flex items-center gap-2 rounded-lg bg-primary px-5 py-3 text-sm font-semibold text-primary-foreground transition-opacity hover:opacity-90;
70199
}
71200

72201
.btn-secondary {

app/layout.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ import { siteConfig } from "@/content/homepage";
77
import { buildPageMetadata, DEFAULT_OG_IMAGE } from "@/lib/page-metadata";
88
import { websiteSchemas } from "@/lib/structured-data";
99
import "./globals.css";
10+
import { Geist } from "next/font/google";
11+
import { cn } from "@/lib/utils";
12+
13+
const geist = Geist({subsets:['latin'],variable:'--font-sans'});
1014

1115
export const metadata: Metadata = {
1216
...buildPageMetadata({
@@ -33,7 +37,7 @@ export default function RootLayout({
3337
children: React.ReactNode;
3438
}>) {
3539
return (
36-
<html lang="en" suppressHydrationWarning>
40+
<html lang="en" suppressHydrationWarning className={cn("font-sans", geist.variable)}>
3741
<body className="flex min-h-screen flex-col bg-background font-sans text-foreground antialiased">
3842
<JsonLd data={websiteSchemas()} />
3943
<RootProvider>

components.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"$schema": "https://ui.shadcn.com/schema.json",
3+
"style": "base-nova",
4+
"rsc": true,
5+
"tsx": true,
6+
"tailwind": {
7+
"config": "",
8+
"css": "app/globals.css",
9+
"baseColor": "neutral",
10+
"cssVariables": true,
11+
"prefix": ""
12+
},
13+
"iconLibrary": "lucide",
14+
"rtl": false,
15+
"aliases": {
16+
"components": "@/components",
17+
"utils": "@/lib/utils",
18+
"ui": "@/components/ui",
19+
"lib": "@/lib",
20+
"hooks": "@/hooks"
21+
},
22+
"menuColor": "default",
23+
"menuAccent": "subtle",
24+
"registries": {}
25+
}

components/doc-page-actions.tsx

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
import { Check, Link2, Pencil } from "lucide-react";
44
import Link from "next/link";
55
import { useState } from "react";
6+
import { buttonVariants } from "@/components/ui/button";
67
import { siteConfig } from "@/content/homepage";
8+
import { cn } from "@/lib/utils";
79

810
type DocPageActionsProps = {
911
slug: string;
@@ -30,28 +32,24 @@ export function DocPageActions({
3032
}
3133

3234
return (
33-
<div className="mt-12 space-y-6 border-t border-border pt-6">
34-
<div className="flex flex-wrap items-center gap-3 text-sm text-muted-foreground">
35+
<div className="mt-12 space-y-6 border-t border-fd-border pt-6">
36+
<div className="flex flex-wrap items-center gap-2 text-sm text-muted-foreground">
3537
{lastUpdated ? <span>Last updated: {lastUpdated}</span> : null}
3638
<a
3739
href={editUrl}
3840
target="_blank"
3941
rel="noopener noreferrer"
40-
className="inline-flex items-center gap-1 hover:text-foreground"
42+
className={cn(buttonVariants({ variant: "ghost", size: "sm" }))}
4143
>
42-
<Pencil className="h-3.5 w-3.5" />
44+
<Pencil className="size-3.5" />
4345
Edit this page
4446
</a>
4547
<button
4648
type="button"
4749
onClick={copyLink}
48-
className="inline-flex items-center gap-1 hover:text-foreground"
50+
className={cn(buttonVariants({ variant: "ghost", size: "sm" }))}
4951
>
50-
{copied ? (
51-
<Check className="h-3.5 w-3.5" />
52-
) : (
53-
<Link2 className="h-3.5 w-3.5" />
54-
)}
52+
{copied ? <Check className="size-3.5" /> : <Link2 className="size-3.5" />}
5553
{copied ? "Copied" : "Copy page link"}
5654
</button>
5755
</div>
@@ -60,7 +58,7 @@ export function DocPageActions({
6058
{prev ? (
6159
<Link
6260
href={prev.href}
63-
className="rounded-lg border border-border p-4 text-sm transition-colors hover:bg-muted/50"
61+
className="rounded-lg border border-fd-border bg-card p-4 text-sm transition-colors hover:bg-muted/50"
6462
>
6563
<span className="text-muted-foreground">Previous</span>
6664
<p className="mt-1 font-medium text-foreground">{prev.title}</p>
@@ -71,7 +69,7 @@ export function DocPageActions({
7169
{next ? (
7270
<Link
7371
href={next.href}
74-
className="rounded-lg border border-border p-4 text-sm text-right transition-colors hover:bg-muted/50 sm:text-right"
72+
className="rounded-lg border border-fd-border bg-card p-4 text-sm text-right transition-colors hover:bg-muted/50 sm:text-right"
7573
>
7674
<span className="text-muted-foreground">Next</span>
7775
<p className="mt-1 font-medium text-foreground">{next.title}</p>

components/docs-sidebar.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ function findTitleForSlug(slug: string): string {
1818
}
1919
}
2020
}
21-
return slug;
21+
return slug
22+
.split("/")
23+
.pop()!
24+
.replace(/-/g, " ")
25+
.replace(/\b\w/g, (c) => c.toUpperCase());
2226
}
2327

2428
export function DocsSidebar({ activeSlug }: { activeSlug: string }) {
@@ -27,7 +31,7 @@ export function DocsSidebar({ activeSlug }: { activeSlug: string }) {
2731
{docsNav.map((section) => (
2832
<div key={section.title}>
2933
<p className="mb-2 font-semibold text-foreground">{section.title}</p>
30-
<ul className="space-y-1 border-l border-border pl-3">
34+
<ul className="space-y-1 border-l border-fd-border pl-3">
3135
{section.items.flatMap((item) =>
3236
item.children
3337
? item.children.map((child) => (
@@ -36,7 +40,7 @@ export function DocsSidebar({ activeSlug }: { activeSlug: string }) {
3640
href={`/docs/${child.slug}`}
3741
className={
3842
activeSlug === child.slug
39-
? "font-medium text-primary"
43+
? "font-medium text-fd-primary"
4044
: "text-muted-foreground hover:text-foreground"
4145
}
4246
>
@@ -50,7 +54,7 @@ export function DocsSidebar({ activeSlug }: { activeSlug: string }) {
5054
href={item.slug ? `/docs/${item.slug}` : "/docs"}
5155
className={
5256
activeSlug === item.slug
53-
? "font-medium text-primary"
57+
? "font-medium text-fd-primary"
5458
: "text-muted-foreground hover:text-foreground"
5559
}
5660
>

components/github-icon.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export function GitHubIcon({ className }: { className?: string }) {
2+
return (
3+
<svg
4+
viewBox="0 0 24 24"
5+
aria-hidden="true"
6+
className={className}
7+
fill="currentColor"
8+
>
9+
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-1.125-.195-2.805-.78-2.805-3.465 0-.78.285-1.41.75-1.905-.075-.195-.33-.975.075-2.025 0 0 .615-.195 2.025.735.585-.165 1.215-.255 1.845-.255.63 0 1.26.09 1.845.255 1.41-.945 2.025-.735 2.025-.735.405 1.05.15 1.83.075 2.025.465.495.75 1.125.75 1.905 0 2.685-1.695 3.27-2.82 3.465-.45.195-1.05.66-1.23 1.695-.195.675-.975 1.965-4.035 1.41 0 1.005-.015 1.95-.015 2.235 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z" />
10+
</svg>
11+
);
12+
}

components/markdown-page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export function MarkdownPage({ title, markdown, description }: MarkdownPageProps
1919
<p className="mt-2 text-muted-foreground">{description}</p>
2020
) : null}
2121
</header>
22-
<div className="prose prose-neutral max-w-none dark:prose-invert prose-headings:scroll-mt-20 prose-a:text-primary">
22+
<div className="doc-content prose-no-margin text-fd-foreground">
2323
<ReactMarkdown remarkPlugins={[remarkGfm]}>{body}</ReactMarkdown>
2424
</div>
2525
</article>
@@ -28,7 +28,7 @@ export function MarkdownPage({ title, markdown, description }: MarkdownPageProps
2828

2929
export function MarkdownPageSimple({ markdown }: { markdown: string }) {
3030
return (
31-
<div className="prose prose-neutral max-w-none dark:prose-invert prose-headings:scroll-mt-20 prose-a:text-primary">
31+
<div className="doc-content prose-no-margin text-fd-foreground">
3232
<ReactMarkdown remarkPlugins={[remarkGfm]}>{markdown}</ReactMarkdown>
3333
</div>
3434
);

0 commit comments

Comments
 (0)