Standard.site landing page built in Next.js

Add per-page metadata to docs from MDX frontmatter

aka.dad 3497d3cc a765915d

verified
+118
+39
app/docs/[...slug]/page.tsx
··· 1 1 import { notFound } from 'next/navigation' 2 + import { Metadata } from 'next' 3 + import { readFile } from 'fs/promises' 4 + import { join } from 'path' 2 5 import { getAllDocSlugs } from '@/app/data/docs-nav' 3 6 4 7 // Map of slugs to their MDX content imports ··· 15 18 'faq': () => import('@/content/docs/faq.mdx'), 16 19 } 17 20 21 + async function getFrontmatter(slugPath: string): Promise<{ title?: string; description?: string }> { 22 + const filePath = join(process.cwd(), 'content', 'docs', `${slugPath}.mdx`) 23 + const raw = await readFile(filePath, 'utf-8') 24 + 25 + const match = raw.match(/^---\n([\s\S]*?)\n---/) 26 + if (!match) return {} 27 + 28 + const frontmatter: Record<string, string> = {} 29 + for (const line of match[1].split('\n')) { 30 + const [key, ...rest] = line.split(':') 31 + if (key && rest.length) { 32 + frontmatter[key.trim()] = rest.join(':').trim() 33 + } 34 + } 35 + 36 + return frontmatter 37 + } 38 + 18 39 // Generate static params for all doc pages 19 40 export function generateStaticParams() { 20 41 return getAllDocSlugs().map((slug) => ({ slug })) 42 + } 43 + 44 + export async function generateMetadata({ 45 + params, 46 + }: { 47 + params: Promise<{ slug: string[] }> 48 + }): Promise<Metadata> { 49 + const { slug } = await params 50 + const slugPath = slug.join('/') 51 + 52 + if (!docsContent[slugPath]) return {} 53 + 54 + const { title, description } = await getFrontmatter(slugPath) 55 + 56 + return { 57 + title: title ? `${title} - Standard.site` : undefined, 58 + description, 59 + } 21 60 } 22 61 23 62 export default async function DocPage({
+13
bun.lock
··· 16 16 "react-dom": "19.2.3", 17 17 "react-progressive-blur": "^1.0.6", 18 18 "rehype-slug": "^6.0.0", 19 + "remark-frontmatter": "^5.0.0", 19 20 "remark-gfm": "^4.0.1", 20 21 }, 21 22 "devDependencies": { ··· 542 543 543 544 "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], 544 545 546 + "fault": ["fault@2.0.1", "", { "dependencies": { "format": "^0.2.0" } }, "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ=="], 547 + 545 548 "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" } }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], 546 549 547 550 "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], ··· 555 558 "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], 556 559 557 560 "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], 561 + 562 + "format": ["format@0.2.2", "", {}, "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww=="], 558 563 559 564 "framer-motion": ["framer-motion@12.23.26", "", { "dependencies": { "motion-dom": "^12.23.23", "motion-utils": "^12.23.6", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid"] }, "sha512-cPcIhgR42xBn1Uj+PzOyheMtZ73H927+uWPDVhUMqxy8UHt6Okavb6xIz9J/phFUHUj0OncR6UvMfJTXoc/LKA=="], 560 565 ··· 769 774 "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="], 770 775 771 776 "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA=="], 777 + 778 + "mdast-util-frontmatter": ["mdast-util-frontmatter@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "escape-string-regexp": "^5.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-extension-frontmatter": "^2.0.0" } }, "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA=="], 772 779 773 780 "mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", "mdast-util-gfm-strikethrough": "^2.0.0", "mdast-util-gfm-table": "^2.0.0", "mdast-util-gfm-task-list-item": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="], 774 781 ··· 804 811 805 812 "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], 806 813 814 + "micromark-extension-frontmatter": ["micromark-extension-frontmatter@2.0.0", "", { "dependencies": { "fault": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg=="], 815 + 807 816 "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="], 808 817 809 818 "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="], ··· 971 980 "rehype-recma": ["rehype-recma@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "hast-util-to-estree": "^3.0.0" } }, "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw=="], 972 981 973 982 "rehype-slug": ["rehype-slug@6.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "github-slugger": "^2.0.0", "hast-util-heading-rank": "^3.0.0", "hast-util-to-string": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A=="], 983 + 984 + "remark-frontmatter": ["remark-frontmatter@5.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-frontmatter": "^2.0.0", "micromark-extension-frontmatter": "^2.0.0", "unified": "^11.0.0" } }, "sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ=="], 974 985 975 986 "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="], 976 987 ··· 1175 1186 "is-bun-module/semver": ["semver@7.7.3", "", { "bin": "bin/semver.js" }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], 1176 1187 1177 1188 "mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], 1189 + 1190 + "mdast-util-frontmatter/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], 1178 1191 1179 1192 "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], 1180 1193
+6
content/docs/faq.mdx
··· 1 + --- 2 + title: Frequently Asked Questions 3 + description: Common questions about Standard.site and implementing the lexicons. 4 + date: 2026-02-10 5 + --- 6 + 1 7 import { StandardSite } from '@/app/components/docs' 2 8 3 9 # Frequently Asked Questions
+6
content/docs/implementations.mdx
··· 1 + --- 2 + title: Implementations 3 + description: Projects and tools using Standard.site lexicons to build interoperable platforms on AT Protocol. 4 + date: 2026-02-10 5 + --- 6 + 1 7 import { LinkCard } from '@/app/components/docs' 2 8 3 9 import { StandardSite } from '@/app/components/docs'
+6
content/docs/introduction.mdx
··· 1 + --- 2 + title: Introduction 3 + description: A brief introduction into what the Standard.site lexicon is, what it's used for, and how to implement it. 4 + date: 2026-02-10 5 + --- 6 + 1 7 import { StandardSite } from '@/app/components/docs' 2 8 3 9 # Introduction
+6
content/docs/lexicons/document.mdx
··· 1 + --- 2 + title: Document Lexicon 3 + description: Schema reference for site.standard.document, the lexicon that provides metadata for documents published on the web. 4 + date: 2026-02-10 5 + --- 6 + 1 7 import { Table } from '@/app/components/docs' 2 8 3 9 # Document Lexicon
+6
content/docs/lexicons/publication.mdx
··· 1 + --- 2 + title: Publication Lexicon 3 + description: Schema reference for site.standard.publication, the lexicon used to describe information about a particular publication. 4 + date: 2026-02-10 5 + --- 6 + 1 7 import { Table } from '@/app/components/docs' 2 8 3 9 # Publication Lexicon
+6
content/docs/lexicons/subscription.mdx
··· 1 + --- 2 + title: Subscription Lexicon 3 + description: Schema reference for site.standard.graph.subscription, the lexicon used to tracks relationships between users and publications. 4 + date: 2026-02-10 5 + --- 6 + 1 7 import { Table } from '@/app/components/docs' 2 8 3 9 # Subscription Lexicon
+6
content/docs/lexicons/theme.mdx
··· 1 + --- 2 + title: Basic Theme Lexicon 3 + description: Schema reference for site.standard.theme.basic, the lexicon provides a simplified theme definition for publications. 4 + date: 2026-02-10 5 + --- 6 + 1 7 import { Table } from '@/app/components/docs' 2 8 3 9 # Theme Lexicon
+6
content/docs/permissions.mdx
··· 1 + --- 2 + title: Permissions 3 + description: Standard.site provides a permission set for applications to access publications, documents, and subscriptions. 4 + date: 2026-02-10 5 + --- 6 + 1 7 import { Table } from '@/app/components/docs' 2 8 import { StandardSite } from '@/app/components/docs' 3 9
+6
content/docs/quick-start.mdx
··· 1 + --- 2 + title: Quick Start 3 + description: The quick start guide to implement Standard.site lexicons in your site, tool or app. 4 + date: 2026-02-10 5 + --- 6 + 1 7 import { StandardSite } from '@/app/components/docs' 2 8 3 9 # Quick Start
+7
content/docs/verification.mdx
··· 1 + --- 2 + title: Verification 3 + description: Reference guide to understand how Standard.site records are verified to avoid impersonation or spam. 4 + date: 2026-02-10 5 + --- 6 + 7 + 1 8 import { StandardSite } from '@/app/components/docs' 2 9 3 10 # Verification
+4
next.config.ts
··· 8 8 9 9 const withMDX = createMDX({ 10 10 extension: /\.mdx?$/, 11 + options: { 12 + remarkPlugins: ['remark-frontmatter', 'remark-gfm'], 13 + rehypePlugins: ['rehype-slug'], 14 + }, 11 15 }); 12 16 13 17 export default withMDX(nextConfig);
+1
package.json
··· 20 20 "react-dom": "19.2.3", 21 21 "react-progressive-blur": "^1.0.6", 22 22 "rehype-slug": "^6.0.0", 23 + "remark-frontmatter": "^5.0.0", 23 24 "remark-gfm": "^4.0.1" 24 25 }, 25 26 "devDependencies": {