mcpField
M
markdown-content

markdown-content

github.com/joyco-studio开发1246 次安装8 周前更新
代码仓库

关于 markdown-content

markdown-content 是用于在 Next.js App Router 项目中构建 MDX 内容页的技能,规定内容处理流程与共享样式,使编译后的 MDX 和 React 组件中的代码块保持一致。

主要功能

  1. 01定义 MDX 集合和架构
  2. 02加载并解析内容页面
  3. 03在构建和运行时高亮代码
  4. 04渲染标签式代码块和复制控件
  5. 05设置正文与代码块样式

适用场景

  1. 01构建博客或更新日志
  2. 02创建法律或知识库页面
  3. 03统一展示语法高亮代码
  4. 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-mdxMDX 集合定义、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

定义两项内容:

  1. 内容集合 — 使用 defineDocs() 指向 content/ 目录,并采用 Zod 扩展的 frontmatter 模式。
  2. 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 管道(构建时) 和自定义代码组件(运行时)共同使用。

此模块应导出:

  1. 语言映射 — 使用 Record<string, BuiltinLanguage> 将文件扩展名(ts、tsx、sh 等)映射到 Shiki 语言标识符。此外还应提供 getLanguageFromExtension() 辅助函数。

  2. Shiki 转换器 — 在 MDX 编译期间运行,向 AST 节点附加自定义属性,供组件在渲染时读取。附加原始源字符串(用于复制按钮),以便组件映射可以使用它。

  3. 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.js Image 进行优化。

流程:作者编写围栏代码块 → 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 组件产生完全相同的视觉效果。具体通过以下方式实现:

  1. 使用相同的 Shiki 主题 — MDX 管道和 highlightCode() 使用相同的双主题组合。
  2. 使用相同的 CSS 目标 — 两者都会生成由 shiki.css 设置样式的 [data-rehype-pretty-code-figure] 元素。
  3. 使用相同的数据属性 — 两种路径都会使用 data-line、data-line-numbers、data-highlighted-line、data-wrap。
  4. 使用相同的颜色变量 — theming.css 中的 --color-code 系列会统一应用。
  5. 共用复制按钮 — 每个代码显示区域都使用相同的 CopyButton 组件。

实现检查清单

  1. 安装依赖项:fumadocs-mdx、fumadocs-core、shiki、rehype-pretty-code、zod
  2. 创建 source.config.ts,配置 frontmatter 模式和 rehype-pretty-code 管道
  3. 使用 createMDX() 包装 next.config.ts
  4. 创建包含源加载器的 lib/source.ts
  5. 创建包含语言映射、转换器和 highlightCode() 的 lib/shiki.ts
  6. 创建 mdx-components.tsx,将 pre 和 code 映射到自定义组件
  7. 创建代码块组件(CodeBlock、CodeBlockTabs、CopyButton)
  8. 设置 prose.css(独立的排版样式)和 shiki.css(代码块样式)
  9. 在 theming.css 中定义主题变量,包括 --color-code 系列
  10. 创建包含 meta.json 文件的内容目录
  11. 创建全匹配 [[...slug]]/page.tsx 路由
  12. 按正确顺序(theming → prose → shiki)在 globals.css 中导入样式表

评论

支持 Markdown