关于 markdown-content
markdown-content 是用于在 Next.js App Router 项目中构建 MDX 内容页的技能,规定内容处理流程与共享样式,使编译后的 MDX 和 React 组件中的代码块保持一致。
主要功能
- 01定义 MDX 集合和架构
- 02加载并解析内容页面
- 03在构建和运行时高亮代码
- 04渲染标签式代码块和复制控件
- 05设置正文与代码块样式
适用场景
- 01构建博客或更新日志
- 02创建法律或知识库页面
- 03统一展示语法高亮代码
- 04为代码示例添加复制控件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ joyco-studio/ skills --skill markdown-content安装后在 Claude Code 中输入/markdown-content确认已加载。
详细介绍
Markdown 内容页面 — Next.js
本技能介绍如何在 Next.js App Router 项目中使用 MDX 构建由内容驱动的页面(博客文章、更新日志、法律页面、知识库)。
目标是确保以下内容在视觉和行为上保持一致:
- 在
.mdx文件中编写的 Markdown 代码块(在构建时处理) - 页面上渲染的自定义 React 代码组件(在运行时高亮)
技术栈概览
| 层 | 库 | 用途 |
|---|---|---|
| 内容框架 | fumadocs-mdx | MDX 集合定义、frontmatter 模式、构建时处理 |
| 内容运行时 | fumadocs-core | 源加载器、页面树、slug 解析 |
| 语法高亮 | shiki + rehype-pretty-code | 在构建时(MDX)和运行时(组件)使用双主题代码高亮 |
| 模式验证 | zod | 为 frontmatter 提供带默认值和可选字段的类型定义 |
| 样式 | Tailwind CSS v4 | 实用优先的样式、自定义 prose CSS、代码块样式 |
| 框架 | Next.js(App Router) | 基于文件的路由,使用用于内容页面的 catch-all [[...slug]] |
npm install fumadocs-mdx fumadocs-core shiki rehype-pretty-code zod
项目结构
├── app/
│ ├── layout.tsx # Root layout
│ ├── styles/
│ │ ├── globals.css # Tailwind + stylesheet imports (order matters)
│ │ ├── prose.css # Self-contained typography for .prose class
│ │ ├── shiki.css # Code block styling (themes, line numbers, highlights)
│ │ └── theming.css # Color variables (light/dark) — imported first
│ └── (content)/ # Route group for content pages
│ ├── layout.tsx # Content layout wrapper
│ └── [[...slug]]/
│ └── page.tsx # Catch-all page rendering MDX content
├── content/
│ ├── meta.json # Top-level page ordering
│ ├── index.mdx # Landing page
│ └── <category>/ # e.g. blog/, legal/, changelog/
│ ├── meta.json # Category page ordering
│ └── <page>.mdx # Individual content pages
├── components/ # Code block components (see Code Block Components)
├── lib/
│ ├── source.ts # fumadocs-core source loader
│ ├── shiki.ts # Shared shiki config, transformers, highlightCode()
│ └── cn.ts # clsx + tailwind-merge utility
├── mdx-components.tsx # MDX element → React component mappings
└── source.config.ts # fumadocs-mdx collection + rehype pipeline
内容集合
source.config.ts
定义两项内容:
- 内容集合 — 使用
defineDocs()指向content/目录,并采用 Zod 扩展的 frontmatter 模式。 - MDX 管道 — 使用
defineConfig(),通过 rehype-pretty-code 替换 fumadocs 默认的高亮器。
关键模式:
- 在
rehypePlugins中调用plugins.shift(),移除内置的 fumadocs 语法高亮器,然后调用plugins.push()添加 rehype-pretty-code。 - 配置双 Shiki 主题(一个浅色主题、一个深色主题),将两套颜色都嵌入标记中。CSS 会切换显示哪一套。
- 传入自定义的 Shiki 转换器,向 AST 注入元数据(原始源文本),供组件在渲染时读取。
- 使用
onVisitTitle为代码块标题元素添加not-prose,避免 prose 排版影响这些元素。
next.config.ts
使用 fumadocs-mdx/next 中的 createMDX() 包装 Next.js 配置。
lib/source.ts
使用 fumadocs-core/source 中的 loader(),并传入 baseUrl 和集合的 .toFumadocsSource()。导出该 source,以及通过 InferPageType 推导出的 Page 类型。
内容文件
MDX Frontmatter
每个 .mdx 文件都以 YAML frontmatter 开头,并由 source.config.ts 中的 Zod 模式验证。导入语句应放在结束的 --- 之后。React 组件可以与标准 Markdown 一起内联使用。代码块使用带语言标签的围栏语法,title="filename.tsx" 元字符串会添加标题栏。
meta.json
用于控制目录内页面的排序,内容为 slug 字符串数组:
{
"pages": ["index", "terms-of-service", "privacy-policy"]
}
Catch-All 内容页面
[[...slug]]/page.tsx 路由通过 source.getPage(params.slug) 解析 slug;如果找不到,则返回 notFound(),并渲染:
<article className="prose">
<h1>{page.data.title}</h1>
<p className="text-muted-foreground">{page.data.description}</p>
<MDX components={getMDXComponents()} />
</article>
导出使用 source.generateParams() 进行静态生成的 generateStaticParams。
Shiki 配置
lib/shiki.ts
集中管理所有语法高亮配置,供 MDX 管道(构建时) 和自定义代码组件(运行时)共同使用。
此模块应导出:
-
语言映射 — 使用
Record<string, BuiltinLanguage>将文件扩展名(ts、tsx、sh等)映射到 Shiki 语言标识符。此外还应提供getLanguageFromExtension()辅助函数。 -
Shiki 转换器 — 在 MDX 编译期间运行,向 AST 节点附加自定义属性,供组件在渲染时读取。附加原始源字符串(用于复制按钮),以便组件映射可以使用它。
-
highlightCode(code, language)— 一个使用codeToHtml()的异步函数,并采用与 MDX 管道相同的双主题。这是确保一致性的机制:构建时和运行时的高亮都使用相同的 Shiki 配置,并生成相同的数据属性(data-line-numbers、data-line),确保各处的视觉效果一致。
MDX 组件映射
mdx-components.tsx
将 MDX 编译器中的 HTML 元素映射到 React 组件。它在原始 Markdown 和组件系统之间起桥梁作用。
关键映射:
-
code— 渲染带样式的行内<code>元素。 -
pre— 从 props 中读取转换器元数据。渲染<pre>,并叠加一个CopyButton,使用原始源文本。 -
img— 可选择使用 Next.jsImage进行优化。
流程:作者编写围栏代码块 → rehype-pretty-code + shiki 生成带有语法标记的 <pre><code> → shiki 转换器附加元数据 → MDX 调用这些映射 → 组件读取元数据并据此渲染。
代码块组件
为代码显示系统构建以下客户端组件:
CodeBlock
单个代码显示组件。接收 highlightedCode(HTML 字符串)、language、可选的 title、rawCode(用于复制)、maxHeight 和 wrap。渲染一个 <figure data-rehype-pretty-code-figure>,其中包含可选的 <figcaption> 标题栏、通过 dangerouslySetInnerHTML 插入的高亮 HTML,以及一个 CopyButton。
CodeBlockTabs
多文件标签式代码显示组件。接收由 { filename, highlightedCode, rawCode } 标签组成的数组。使用受控标签状态。在 <figcaption> 中渲染标签,并为当前标签显示一个 CopyButton。
CopyButton
带有视觉反馈的剪贴板按钮。使用 useCopyToClipboard hook 调用 navigator.clipboard.writeText(),并显示约 2 秒的勾号。绝对定位于右上角,并在悬停于父级组时通过不透明度过渡显示。
样式架构
globals.css — 导入顺序
@import 'tailwindcss';
@import './theming.css';
@import './prose.css';
@import './shiki.css';
theming.css 必须放在最前面,因为它定义了 prose 和 shiki 使用的颜色变量。
theming.css — 颜色变量
定义由 shiki.css 和组件类使用的浅色与深色模式变量:
--color-code— 代码块背景--color-code-foreground— 代码块文本--color-code-highlight— 高亮行背景
prose.css — 自包含排版样式
手动编写 .prose 类,不依赖 @tailwindcss/typography 或任何 UI 框架。使用 :where() 选择器将优先级保持为零。使用 .not-prose 作为嵌入式组件的隔离方式。
至少涵盖:正文颜色和行高、标题(h1–h6)、段落、链接、粗体、列表(ul/ol/li)、引用块、水平分隔线、表格和代码图表(figure[data-rehype-pretty-code-figure])。
设计决策:
- 正文文本使用较低的不透明度(例如通过
color-mix将--foreground调低至 70%),以获得更柔和的阅读体验 - 标题使用完整的
--foreground颜色,并采用更紧凑的行高 - 代码图表设置垂直外边距,并重置首尾子元素的样式
shiki.css — 代码块样式
统一设置所有代码块的样式 — MDX 渲染和组件渲染的代码块均使用相同的数据属性。
关键规则:
- 双主题切换 — 在
.dark中,将[data-rehype-pretty-code-figure] span的color切换为var(--shiki-dark)。默认使用浅色主题;shiki 会将两套颜色都嵌入为 CSS 变量。 - 容器 —
[data-rehype-pretty-code-figure]使用--color-code设置背景色、圆角,并隐藏溢出内容。 - 行号 — 在
[data-line-numbers] [data-line]::before上使用 CSS 计数器。采用左侧粘性定位,使行号在水平滚动时保持可见。 - 行高亮 —
[data-highlighted-line]设置微妙的背景色和左边框。 - 标题栏 —
[data-rehype-pretty-code-title]使用等宽字体、弱化的颜色和底边框设置样式。 - 文本换行 —
[data-wrap='true'] code启用white-space: pre-wrap。
一致性模型
系统确保 Markdown 围栏代码块和 <CodeBlock> React 组件产生完全相同的视觉效果。具体通过以下方式实现:
- 使用相同的 Shiki 主题 — MDX 管道和
highlightCode()使用相同的双主题组合。 - 使用相同的 CSS 目标 — 两者都会生成由
shiki.css设置样式的[data-rehype-pretty-code-figure]元素。 - 使用相同的数据属性 — 两种路径都会使用
data-line、data-line-numbers、data-highlighted-line、data-wrap。 - 使用相同的颜色变量 —
theming.css中的--color-code系列会统一应用。 - 共用复制按钮 — 每个代码显示区域都使用相同的
CopyButton组件。
实现检查清单
- 安装依赖项:
fumadocs-mdx、fumadocs-core、shiki、rehype-pretty-code、zod - 创建
source.config.ts,配置 frontmatter 模式和 rehype-pretty-code 管道 - 使用
createMDX()包装next.config.ts - 创建包含源加载器的
lib/source.ts - 创建包含语言映射、转换器和
highlightCode()的lib/shiki.ts - 创建
mdx-components.tsx,将pre和code映射到自定义组件 - 创建代码块组件(
CodeBlock、CodeBlockTabs、CopyButton) - 设置
prose.css(独立的排版样式)和shiki.css(代码块样式) - 在
theming.css中定义主题变量,包括--color-code系列 - 创建包含
meta.json文件的内容目录 - 创建全匹配
[[...slug]]/page.tsx路由 - 按正确顺序(theming → prose → shiki)在
globals.css中导入样式表