搜索 K
主题
技术博客写多了就会发现,光有正文和代码块不够用:推荐阅读要卡片、重点提示要色块、安装教程要步骤列表、长配置文件要能折叠。Nuxt 官方文档的 Prose 组件把这套排版语言打磨得很成熟,@vaultkit/vitepress-plugin-nuxt-typography 把它带进 VitePress:五种容器(card / card-group / callout / steps / code-collapse),视觉逐槽位复刻 @nuxt/ui,但实现是构建期纯 HTML + CSS——不带 40+ Prose 组件进首屏,图标也不走运行时 CDN。
本文是「VaultKit 包指南」系列第三篇。每个容器逐属性详解并实活演示(正文里的容器都是本站构建时真实渲染的),最后附一份构建期报错清单——这个插件信奉 fail-fast,写错属性不会静默吞掉,而是让构建直接失败。
直接复用 @nuxt/ui 的 Prose 组件行不行?行,但对静态站点是净损耗:Vue 模式的 Prose* 依赖 prose: true(全部 40+ Prose 组件打进首屏 bundle),图标走 @iconify/vue 运行时从 CDN 拉取。本插件反其道而行:
--vp-c-*),明暗模式自动翻转。pnpm add -D @vaultkit/vitepress-plugin-nuxt-typography如果你用的是 @vaultkit/vitepress-theme-vault 主题,本插件开箱即带(markdown.typography 缺省开启,simple-icons 图标集与中文按钮文案已配好),可跳过下面的接入步骤。
// .vitepress/config.ts
import { icons as simpleIcons } from '@iconify-json/simple-icons'
import { nuxtTypography } from '@vaultkit/vitepress-plugin-nuxt-typography'
import { defineConfig } from 'vitepress'
export default defineConfig({
markdown: {
config(md) {
md.use(nuxtTypography, {
icons: { 'simple-icons': simpleIcons },
codeCollapse: { name: '代码', openText: '展开', closeText: '收起' },
})
},
},
vite: {
// client 部分含 .vue 源文件,SSR 需交给打包器处理
ssr: { noExternal: ['@vaultkit/vitepress-plugin-nuxt-typography'] },
},
})// .vitepress/theme/index.ts
import { NuxtTypographyClient } from '@vaultkit/vitepress-plugin-nuxt-typography/client'
import '@vaultkit/vitepress-plugin-nuxt-typography/styles/index.css'
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
app.use(NuxtTypographyClient) // 全局注册 <CodeCollapse>
},
}语法契约一句话版:容器名后可跟标题(仅 card 有标题槽),属性一律包在行尾花括号里(key="value" 形式),嵌套时外层容器多一个冒号。
card 是最常用的容器:标题直接跟在容器名后(走 info 尾巴,与 ::: info 标题 同轨,可省略),正文是普通 Markdown。花括号属性共 4 个:
| 属性 | 说明 |
|---|---|
icon | 左上角图标,构建期内联 SVG(写法见下文「双图标写法」) |
to | 链接目标,整卡可点;http(s) 外链缺省新开页 + 右上角角标 |
href | to 的别名,二选一(同时给时 to 优先) |
target | 显式覆盖打开方式(如外链改 _self、站内链接改 _blank) |
全属性演示(外链自动 target="_blank",注意右上角的外链角标):
href 与 target 覆盖演示(外链但显式 target="_self",本页打开、无角标):
标题与属性都可省略,退化成一个边框容器:
无标题、无属性的极简卡片,只放一段说明文字。
:::: 嵌套 card-group 自身没有任何专属属性,只是一个两列网格包裹层(移动端单列)。它演示了本插件的嵌套规则:外层容器要比内层多一个冒号——:::: 包 ::::
:::: card-group
::: card 卡片一 {icon="i-lucide-notebook-pen"}
…
:::
::: card 卡片二 {icon="i-lucide-panels-top-left"}
…
:::
::::实活效果:
注意与 Obsidian 的 > [!type] callout 区分:那是带标题的引述块(见 上一篇),这里是 Nuxt 风格的无标题槽单块提示——正文直接写在容器里,给它写标题会构建报错。花括号属性共 5 个:
| 属性 | 说明 |
|---|---|
icon | 行首内联小图标,与首行文字齐排 |
to | 链接目标,整块可点,链接态虚线边框 |
href | to 的别名,二选一 |
target | 显式覆盖打开方式 |
color | 色系,7 个值:neutral(缺省)/ primary / secondary / info / success / warning / error |
七个颜色值逐色演示:
neutral——缺省值,中性底色 + 分割线边框,不抢戏的补充说明。
primary——品牌色(--vp-c-brand-1),主推操作或核心提示。
secondary——紫色系,次级强调。
info——靛蓝色系,客观信息。
success——绿色系,成功与最佳实践。
warning——黄色系,注意事项。
error——红色系,错误与破坏性操作。
非 neutral 色系的边框 / 底色由主色 color-mix 派生(25% 边框、10% 底色),行内代码自动贴合底色。带 to 时整块可点、边框变虚线,外链同样有右上角角标:
steps 给容器内对应级别的直系标题挂序号圆点与左侧连接线,标题之间的任意块级内容都是步骤正文。唯一属性:
| 属性 | 说明 |
|---|---|
level | 步骤标题级别,值域 2 / 3 / 4,缺省 3(与 ProseSteps 一致) |
level 的语义是「哪一级标题算一步」:序号由 CSS 计数器实现(counter-increment),只命中 data-level 对应的直系子标题,其他级别的标题与正文不编号。级别应顺承上文标题层级——本节是三级标题的语境,所以下面用 {level="4"};跳级虽然能渲染,但会破坏文档大纲的无障碍语义。
pnpm add -D @vaultkit/vitepress-plugin-nuxt-typographymd.use(nuxtTypography, { icons: { 'simple-icons': simpleIcons } })import { NuxtTypographyClient } from '@vaultkit/vitepress-plugin-nuxt-typography/client'
import '@vaultkit/vitepress-plugin-nuxt-typography/styles/index.css'steps 与 card 一样没有标题槽——::: steps 标题 会构建报错,步骤标题写在容器内部。
唯一发射客户端组件的容器:长代码默认收起 200px + 底部渐变遮罩,点按钮展开(展开态上限 80vh 可滚动)。代码块本身仍由宿主 fence 管线渲染,shiki 高亮、行号、复制按钮全部保留。花括号属性共 3 个:
| 属性 | 说明 |
|---|---|
name | 按钮文案里的对象名(「展开代码」的「代码」位) |
open-text | 收起态按钮动词(缺省 Expand,可经插件选项站点级覆盖) |
close-text | 展开态按钮动词(缺省 Collapse,同上) |
实活演示(自定义三个属性):
type DateChain = 'date' | 'created' | 'published' | 'start'
export function resolveDate(frontmatter: Record<string, unknown>): string | undefined {
const chain: DateChain[] = ['date', 'created', 'published', 'start']
for (const key of chain) {
const value = frontmatter[key]
if (typeof value === 'string' && value)
return value
}
return undefined
}
export function compareByDateDesc(a: { date?: string }, b: { date?: string }): number {
return (b.date ?? '').localeCompare(a.date ?? '')
}按钮文案的合并顺序:行内属性 > 插件选项 codeCollapse > 英文缺省。中西文混排时组件会自动决定动词与对象名之间是否补空格(Expand code 补、「展开代码」不补)。
icon 属性接受 Nuxt UI 的两种写法,等价互换:
i-simple-icons-github(按已注册集合前缀匹配,长名优先);simple-icons:github。内置 lucide 集,其余集合经插件选项 icons 注册(数据源 @iconify-json/<collection>):
import { icons as carbonIcons } from '@iconify-json/carbon'
import { icons as simpleIcons } from '@iconify-json/simple-icons'
md.use(nuxtTypography, {
icons: { 'simple-icons': simpleIcons, 'carbon': carbonIcons },
})插件选项全表:
| 选项 | 类型 | 缺省 | 说明 |
|---|---|---|---|
icons | Record<string, IconifyJSON> | 内置 lucide | 额外图标集,注册后两种图标写法都可用 |
codeCollapse | { name?, openText?, closeText? } | 'code' / 'Expand' / 'Collapse' | code-collapse 按钮站点级缺省文案(作者行内属性优先) |
此外所有容器都接受 class / id 直通属性({class="my-card" id="anchor"}),用于自定义样式钩子与锚点。
写错不会静默降级,而是构建直接失败并给出定位信息——这是刻意设计(错误排版上线比构建失败更贵)。全部触发条件:
| 写法 | 报错 |
|---|---|
属性键不在白名单(如 card 写 color) | 未知的 card 属性 "color"(支持:icon / to / href / target) |
callout 的 color 不在 7 值内 | 未知的 callout 颜色 "brand"(支持:neutral / primary / …) |
steps 的 level 不在 2/3/4 | 非法的 steps 级别 "5"(支持:2 / 3 / 4) |
| 给 callout / steps / code-collapse 写标题 | callout 没有标题槽(正文直接写在容器内部) |
忘写花括号(标题区出现 key="value") | 属性需包在行尾花括号里(::: card 标题 {icon="…"}) |
花括号内不是 key="value" 形式 | 属性解析失败(须为 key="value" 形式) |
| 图标集未注册 / 集合内无此图标 | 图标集未注册:carbon / @iconify-json/lucide 中不存在图标:xxx |
<a> 实现并带 aria-label;图标 SVG 带 aria-hidden="true";折叠按钮带 aria-expanded;外链 _blank 自动补 rel="noreferrer";z-index: 1,正文行内链接 z-index: 2 浮在其上,两者都可独立点击;.vp-doc 作用域与 --vp-c-* 变量,非 VitePress 宿主需自备等价变量或自写样式(markdown-it 侧 DOM 契约稳定)。::: code-collapse 渲染出了原始的 <CodeCollapse> 标签?客户端三件套缺一:vite.ssr.noExternal 包含本包、主题入口引入 styles/index.css、enhanceApp 里 app.use(NuxtTypographyClient)。用 vitepress-theme-vault 主题则三件套已内置。
用 > [!tip] 引述语法(markdown-it-obsidian-callouts 渲染)。本插件的 ::: callout 是无标题的 Nuxt 风格单块提示,两者语义互补、可同站共存。
card-group 固定两列网格(移动端单列),与 ProseCardGroup 一致。需要更多列可用 class 直通属性挂自定义样式。