feat(docs): add a Diátaxis documentation section to the public site

A new public /docs area covering the whole platform, organized by Diátaxis:
tutorials (learning), how-to guides (tasks), reference (facts), and explanation
(the why). 29 pages, each grounded in the actual routes, actions, schema, and
UI — verified against the code and corrected for accuracy (e.g. admin-only vs
admin+board gating, where the permissions matrix actually lives, how Stripe
reconciliation matches).

Implementation:
- lib/docs/nav.ts is the single source of truth (categories + pages), driving
  the sidebar, static params, and prev/next.
- Content authored as content/docs/<category>/<slug>.md, read at build time
  (the routes are statically generated), rendered by <DocsMarkdown> — reuses the
  existing react-markdown/GFM stack, no new dependencies.
- (docs) route group with its own theme (docs.css) matching the public site's
  dark / monospace / lime aesthetic; sidebar with active-state, breadcrumb,
  prev/next, and a Diátaxis home. Public — signed-in users can read it too.
- "Docs" link added to the landing nav.

typecheck 0 · lint 0 errors · build ok (all /docs pages prerendered)
This commit is contained in:
Moheeb Zara
2026-08-05 00:42:42 -07:00
parent fc6adaf16e
commit 16229ae523
38 changed files with 2928 additions and 0 deletions
+38
View File
@@ -0,0 +1,38 @@
import ReactMarkdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
import { makeHeadingSlugger, isInDocumentHref } from '@/lib/markdown-anchors'
// Docs-brand markdown renderer. Unlike components/markdown.tsx (which is bound
// to the management app's theme tokens), this emits headings with stable anchor
// ids and otherwise leans on the `.docs-prose` stylesheet in docs.css so the
// output matches the public site's dark / monospace / lime aesthetic.
//
// Content here is author-trusted (checked-in .md), but we still never enable
// rehypeRaw, so any stray HTML stays inert.
export function DocsMarkdown({ content }: { content: string }) {
const slug = makeHeadingSlugger()
return (
<div className="docs-prose">
<ReactMarkdown
remarkPlugins={[remarkGfm]}
components={{
h1: ({ children }) => <h1 id={slug(children)}>{children}</h1>,
h2: ({ children }) => <h2 id={slug(children)}>{children}</h2>,
h3: ({ children }) => <h3 id={slug(children)}>{children}</h3>,
h4: ({ children }) => <h4 id={slug(children)}>{children}</h4>,
a: ({ href, children }) =>
isInDocumentHref(href) ? (
<a href={href}>{children}</a>
) : (
<a href={href} target="_blank" rel="noopener noreferrer">
{children}
</a>
),
}}
>
{content}
</ReactMarkdown>
</div>
)
}