最近看到一个 AIQA 能力总览的 landing 页:顶部卡片阵列、中间带坐标的 SVG 流程图、配色和阴影都很克制。我想把这种表达力带进博客,但不想每次写文章都重画一遍 SVG。于是重新梳理了一下 Hugo 这边的做法,落地成两个 shortcode 和一段作用域化的 CSS。
一、问题是什么
照搬示例页面有几个硬伤:
- 硬坐标 SVG(
x="340" y="230"那种)一改文案就要重算坐标,和 markdown 的轻量写作完全反着来。 - 样式耦合:示例把所有 CSS 变量都挂在
:root上,塞进 post 会污染全站命名空间。 - Goldmark
unsafe=true虽然已经开了,但直接在 md 里糊 200 行 HTML,语义层级乱掉。
所以目标是:流程图文本化、卡片样式作用域化、写作体验仍然是 markdown。
二、技术栈取舍
Mermaid 代替 SVG
Shortcode 组合卡片
capability-grid + capability-card 两级组合,cols 和 color 用参数控制,写法贴近 markdown。CSS 作用域隔离
.cap-overview 下,不会污染主题的 :root,未来换主题也不冲突。三、流程图:Mermaid 代替手画 SVG
写作时只要这样:
{{< mermaid >}}
graph LR
A[Markdown 源文件] --> B[Hugo 构建]
B --> C[Shortcode 展开]
C --> D[静态 HTML]
D --> E[浏览器渲染]
E --> F{含 Mermaid?}
F -- 是 --> G[CDN 加载 mermaid.esm.min.mjs]
F -- 否 --> H[零额外开销]
G --> I[文本语法转 SVG]
{{< /mermaid >}}渲染结果:
关键实现细节是用 .Page.Scratch 保证同一页面里只引一次 mermaid 的 JS,不会每个图都重复加载:
{{- if not (.Page.Scratch.Get "mermaid_loaded") -}}
{{- .Page.Scratch.Set "mermaid_loaded" true -}}
<script type="module">
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@10/...";
mermaid.initialize({ startOnLoad: true });
</script>
{{- end -}}四、卡片:两级 shortcode
capability-grid 负责栅格和作用域容器,capability-card 负责单张卡片内容,.Inner 用 markdownify 再渲染,所以卡片正文可以继续写 markdown。
{{< capability-grid cols="2" >}}
{{< capability-card icon="⚡" title="标题" color="blue" >}}
正文支持 **markdown** 和 `代码`。
{{< /capability-card >}}
...
{{< /capability-grid >}}四种配色(blue / green / orange / violet)只改顶部 3px 边线和 icon 背景,视觉统一:
Blue
Green
Orange
Violet
五、CSS 作用域这件事
示例里配色变量直接挂 :root,搬过来会和主题的变量重名。我的做法是把它们挪到 .cap-overview 下:
.cap-overview {
--cap-blue: #4c8bf5;
--cap-blue-soft: #eaf2ff;
/* ... */
}
.cap-card--blue { border-top: 3px solid var(--cap-blue); }这样整个主题的变量空间没被污染,未来想换一套色板也只需改这个作用域。
顺带踩过的坑:
custom.css必须放在assets/而不是static/,否则 Hugo 的resources.Get会静默跳过,HTML 根本不会<link>它。这条之前就吃过亏,项目 CLAUDE.md 里也记了。
六、要不要整个做成 layout?
早期版本我考虑过给"能力总览"做一个独立的 layouts/capability-overview/single.html,front matter 里填数据、模板里渲染。最终放弃的理由:
| 方案 | 适用场景 | 代价 |
|---|---|---|
| 专用 layout | 强结构化数据,多处复用,需要 JSON/YAML 数据源 | 写一次模板,但和文章正文割裂 |
| Shortcode(当前) | 一篇文章里插一段卡片总览 | 模板轻量,但需要手工组合 |
| 独立 HTML 丢 static/ | 真的只做一次 | 不走 markdown 管线,搜索/RSS 都没了 |
对我这种"偶尔在博客里插一张能力图"的需求,shortcode 是最合适的颗粒度。真要做一个对外展示的 landing,再升级成 layout 不迟。
七、成果
layouts/shortcodes/mermaid.html—— 文本化流程图layouts/shortcodes/capability-grid.html+capability-card.html—— 卡片阵列assets/css/custom.css新增.cap-overview作用域 +.mermaid容器样式- 本文就是第一个使用者,上面所有的卡片和流程图都是 shortcode 渲染的
下次写架构、能力盘点类文章时,就不用再纠结"这一段要不要画图"——直接写 Mermaid;要做对比矩阵,就用 capability-grid。写作体验仍然是 markdown,表达力上了一个台阶。