Skip to content

Commit 2f307ae

Browse files
committed
docs: add Vercel docs site
1 parent 33b1b64 commit 2f307ae

33 files changed

Lines changed: 11491 additions & 0 deletions

website/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.next
2+
node_modules
3+
public/_pagefind

website/README.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# csfloat-node-sdk website
2+
3+
This folder contains the Vercel-ready documentation site for `csfloat-node-sdk`.
4+
5+
## Architecture
6+
7+
- framework: Next.js App Router
8+
- docs theme: Nextra
9+
- visual direction: dark-gold, forced dark theme
10+
- canonical docs source: repository root `README.md`, `docs/*.md`, `API_COVERAGE.md`, `CHANGELOG.md`
11+
- published site content: generated into `website/content/`
12+
13+
The site is intentionally a presentation layer, not a second hand-maintained documentation system.
14+
15+
## Local Development
16+
17+
```bash
18+
cd website
19+
npm install
20+
npm run dev
21+
```
22+
23+
Local build:
24+
25+
```bash
26+
cd website
27+
npm run build
28+
```
29+
30+
`npm run sync:content` copies the canonical markdown files from the repository root into `website/content/`.
31+
32+
## Vercel
33+
34+
Recommended setup:
35+
36+
1. create a new Vercel project from the same GitHub repository
37+
2. set `Root Directory` to `website`
38+
3. keep the framework preset as `Next.js`
39+
4. use the default install command or `npm install`
40+
5. use the default build command or `npm run build`
41+
42+
Optional:
43+
44+
- set `NEXT_PUBLIC_SITE_URL` to your final production URL or custom domain so sitemap and metadata use the exact public origin
45+
46+
Deployment notes:
47+
48+
- `website/content/` is committed, so deploys still work even if Vercel does not expose parent directories outside `website/` during build
49+
- when Vercel can see the repository root, the build will resync from the canonical docs automatically
50+
- if you enable Vercel's setting for including source files outside the root directory during the build step, deploys can always regenerate from the root docs
51+
52+
## Files That Matter
53+
54+
- `app/` application routes and theme
55+
- `content/` generated docs content consumed by Nextra
56+
- `scripts/sync-content.mjs` sync layer from root docs to site content
57+
- `content/_meta.ts` sidebar structure
58+
59+
## Editing Rule
60+
61+
Do not hand-edit `website/content/*.md` as the canonical source.
62+
63+
Edit one of these instead:
64+
65+
- `README.md`
66+
- `docs/*.md`
67+
- `API_COVERAGE.md`
68+
- `CHANGELOG.md`
69+
70+
Then run:
71+
72+
```bash
73+
cd website
74+
npm run sync:content
75+
```
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { importPage, generateStaticParamsFor } from "nextra/pages";
2+
import { useMDXComponents as getMDXComponents } from "../../../mdx-components";
3+
4+
export const generateStaticParams = generateStaticParamsFor("mdxPath");
5+
6+
export async function generateMetadata(props: {
7+
params: Promise<{ mdxPath?: string[] }>;
8+
}) {
9+
const params = await props.params;
10+
const { metadata } = await importPage(params.mdxPath);
11+
return metadata;
12+
}
13+
14+
const Wrapper = getMDXComponents({}).wrapper;
15+
16+
export default async function Page(props: {
17+
params: Promise<{ mdxPath?: string[] }>;
18+
}) {
19+
const params = await props.params;
20+
const {
21+
default: MDXContent,
22+
toc,
23+
metadata,
24+
sourceCode,
25+
} = await importPage(params.mdxPath);
26+
27+
return (
28+
<Wrapper toc={toc} metadata={metadata} sourceCode={sourceCode}>
29+
<MDXContent {...props} params={params} />
30+
</Wrapper>
31+
);
32+
}

website/app/docs/layout.tsx

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { Banner } from "nextra/components";
2+
import { Footer, Layout, Navbar } from "nextra-theme-docs";
3+
import { getPageMap } from "nextra/page-map";
4+
import "nextra-theme-docs/style.css";
5+
6+
const banner = (
7+
<Banner storageKey="csfloat-docs-v1">
8+
Canonical source of truth stays in the repository docs. This site is the polished presentation
9+
layer.
10+
</Banner>
11+
);
12+
13+
const navbar = (
14+
<Navbar
15+
logo={
16+
<span className="brand-lockup">
17+
<span className="brand-mark" />
18+
<span className="brand-text">
19+
<strong>csfloat-node-sdk</strong>
20+
<span>Docs</span>
21+
</span>
22+
</span>
23+
}
24+
projectLink="https://github.com/Krablante/csfloat-node-sdk"
25+
/>
26+
);
27+
28+
const footer = (
29+
<Footer>
30+
<div className="footer-grid">
31+
<span>MIT {new Date().getFullYear()} © csfloat-node-sdk</span>
32+
<span>
33+
<a href="https://www.npmjs.com/package/csfloat-node-sdk">npm</a>
34+
{" · "}
35+
<a href="https://github.com/Krablante/csfloat-node-sdk">GitHub</a>
36+
</span>
37+
</div>
38+
</Footer>
39+
);
40+
41+
export default async function DocsLayout({
42+
children,
43+
}: {
44+
children: React.ReactNode;
45+
}) {
46+
return (
47+
<Layout
48+
banner={banner}
49+
navbar={navbar}
50+
footer={footer}
51+
pageMap={await getPageMap("/docs")}
52+
copyPageButton={false}
53+
sidebar={{ defaultMenuCollapseLevel: 1, autoCollapse: true }}
54+
navigation={{ prev: true, next: true }}
55+
toc={{ backToTop: true, float: true }}
56+
darkMode={false}
57+
nextThemes={{ forcedTheme: "dark", defaultTheme: "dark" }}
58+
editLink={null}
59+
feedback={{ content: null }}
60+
>
61+
{children}
62+
</Layout>
63+
);
64+
}

website/app/globals.css

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
:root {
2+
color-scheme: dark;
3+
--nextra-primary-hue: 39deg;
4+
--nextra-primary-saturation: 78%;
5+
--nextra-primary-lightness: 58%;
6+
--nextra-bg: 15, 13, 10;
7+
--nextra-content-width: 90rem;
8+
--font-body-fallback:
9+
"Manrope", "Segoe UI", "Helvetica Neue", sans-serif;
10+
--font-heading-fallback:
11+
"Cormorant Garamond", "Times New Roman", serif;
12+
--site-bg: #0f0d0a;
13+
--site-panel: #17120c;
14+
--site-panel-strong: #21180f;
15+
--site-text: #f6eedf;
16+
--site-text-soft: #d0c7b3;
17+
--site-gold: #dfb567;
18+
--site-gold-strong: #f2d081;
19+
--site-line: rgba(223, 181, 103, 0.18);
20+
}
21+
22+
.dark,
23+
html[data-theme="dark"] {
24+
--nextra-primary-hue: 39deg;
25+
--nextra-primary-saturation: 78%;
26+
--nextra-primary-lightness: 58%;
27+
--nextra-bg: 15, 13, 10;
28+
}
29+
30+
html,
31+
body {
32+
min-height: 100%;
33+
}
34+
35+
html {
36+
background: rgb(var(--nextra-bg));
37+
background:
38+
radial-gradient(circle at top right, rgba(210, 161, 68, 0.18), transparent 25%),
39+
linear-gradient(180deg, #16110b 0%, #0f0d0a 100%);
40+
}
41+
42+
body {
43+
margin: 0;
44+
background: transparent;
45+
color: var(--site-text);
46+
font-family: var(--font-body), var(--font-body-fallback);
47+
}
48+
49+
::selection {
50+
background: hsla(var(--nextra-primary-hue), var(--nextra-primary-saturation), var(--nextra-primary-lightness), 0.3);
51+
}
52+
53+
a {
54+
color: var(--site-gold-strong);
55+
}
56+
57+
code,
58+
pre,
59+
kbd,
60+
samp {
61+
font-family:
62+
"JetBrains Mono", "SFMono-Regular", "SF Mono", "Menlo", monospace;
63+
}
64+
65+
.brand-lockup {
66+
display: inline-flex;
67+
align-items: center;
68+
gap: 0.75rem;
69+
}
70+
71+
.brand-mark {
72+
width: 0.95rem;
73+
height: 0.95rem;
74+
border-radius: 999px;
75+
background:
76+
radial-gradient(circle at 30% 30%, #ffdc94, #d39d3f 62%, #6a4f23 100%);
77+
box-shadow:
78+
0 0 0 1px rgba(255, 229, 170, 0.15),
79+
0 0 22px rgba(223, 181, 103, 0.38);
80+
}
81+
82+
.brand-text {
83+
display: grid;
84+
line-height: 1;
85+
}
86+
87+
.brand-text strong {
88+
color: var(--site-text);
89+
font-size: 0.98rem;
90+
font-weight: 800;
91+
letter-spacing: 0.02em;
92+
}
93+
94+
.brand-text span {
95+
color: #be9a59;
96+
font-size: 0.72rem;
97+
font-weight: 700;
98+
letter-spacing: 0.14em;
99+
text-transform: uppercase;
100+
}
101+
102+
.footer-grid {
103+
display: flex;
104+
flex-wrap: wrap;
105+
justify-content: space-between;
106+
gap: 0.75rem;
107+
width: 100%;
108+
}
109+
110+
.footer-grid a {
111+
color: var(--site-gold-strong);
112+
}
113+
114+
html.dark .nextra-nav-container,
115+
html[data-theme="dark"] .nextra-nav-container {
116+
background: linear-gradient(180deg, rgba(15, 13, 10, 0.88), rgba(15, 13, 10, 0.6)) !important;
117+
backdrop-filter: blur(14px);
118+
}
119+
120+
html.dark .nextra-sidebar,
121+
html[data-theme="dark"] .nextra-sidebar {
122+
background:
123+
linear-gradient(180deg, rgba(20, 16, 11, 0.96), rgba(15, 13, 10, 0.92)) !important;
124+
}
125+
126+
html.dark .nextra-body-typesetting-article h1,
127+
html.dark .nextra-body-typesetting-default h1,
128+
html.dark .nextra-body-typesetting-article h2,
129+
html.dark .nextra-body-typesetting-default h2,
130+
html.dark .nextra-body-typesetting-article h3,
131+
html.dark .nextra-body-typesetting-default h3,
132+
html[data-theme="dark"] .nextra-body-typesetting-article h1,
133+
html[data-theme="dark"] .nextra-body-typesetting-default h1,
134+
html[data-theme="dark"] .nextra-body-typesetting-article h2,
135+
html[data-theme="dark"] .nextra-body-typesetting-default h2,
136+
html[data-theme="dark"] .nextra-body-typesetting-article h3,
137+
html[data-theme="dark"] .nextra-body-typesetting-default h3 {
138+
font-family: var(--font-heading), var(--font-heading-fallback);
139+
color: #f7eede;
140+
letter-spacing: -0.03em;
141+
}
142+
143+
html.dark .nextra-body-typesetting-default,
144+
html.dark .nextra-body-typesetting-article,
145+
html[data-theme="dark"] .nextra-body-typesetting-default,
146+
html[data-theme="dark"] .nextra-body-typesetting-article {
147+
color: var(--site-text-soft);
148+
}
149+
150+
html.dark .nextra-body-typesetting-default a,
151+
html.dark .nextra-body-typesetting-article a,
152+
html[data-theme="dark"] .nextra-body-typesetting-default a,
153+
html[data-theme="dark"] .nextra-body-typesetting-article a {
154+
color: #f1ca72;
155+
}
156+
157+
html.dark .nextra-body-typesetting-default code,
158+
html.dark .nextra-body-typesetting-article code,
159+
html[data-theme="dark"] .nextra-body-typesetting-default code,
160+
html[data-theme="dark"] .nextra-body-typesetting-article code {
161+
background: rgba(248, 214, 133, 0.08);
162+
border: 1px solid rgba(248, 214, 133, 0.12);
163+
}
164+
165+
html.dark .nextra-toc,
166+
html.dark .nextra-sidebar-container,
167+
html.dark .nextra-search,
168+
html[data-theme="dark"] .nextra-toc,
169+
html[data-theme="dark"] .nextra-sidebar-container,
170+
html[data-theme="dark"] .nextra-search {
171+
--tw-border-opacity: 1;
172+
border-color: var(--site-line) !important;
173+
}
174+
175+
html.dark .nextra-callout,
176+
html[data-theme="dark"] .nextra-callout {
177+
border-color: rgba(223, 181, 103, 0.28) !important;
178+
background: rgba(255, 240, 196, 0.04) !important;
179+
}
180+
181+
html.dark .nextra-filetree,
182+
html.dark .nextra-cards,
183+
html.dark .nextra-steps,
184+
html[data-theme="dark"] .nextra-filetree,
185+
html[data-theme="dark"] .nextra-cards,
186+
html[data-theme="dark"] .nextra-steps {
187+
border-color: var(--site-line);
188+
}
189+
190+
html.dark .nextra-scrollbar,
191+
html[data-theme="dark"] .nextra-scrollbar {
192+
scrollbar-color: rgba(223, 181, 103, 0.28) transparent;
193+
}
194+
195+
button[title*="theme"],
196+
button[title*="Theme"] {
197+
display: none !important;
198+
}

website/app/icon.svg

Lines changed: 19 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)