组件库写博客:更优雅的写博客方案
本文提到的组件库是我维护的 March7th UI:一套零依赖 Web Components UI 组件库,适合个人博客、作品集和文档内容增强。组件文档与在线演示地址:https://ui.march7th.online/。
以前写博客,Markdown 负责内容表达已经足够舒服:标题、列表、引用、代码块、图片都很自然。
但一旦文章需要更丰富的表达,比如提示卡片、步骤条、数据卡片、折叠面板、标签页、进度条,纯 Markdown 就开始显得不够用了。继续用 HTML 手写样式会让正文变脏;每篇文章复制一段 CSS 也不好维护;如果把所有内容都改成框架组件,又会让写作成本变高。
所以这次我把自己的 UI 组件库 @mar7th/march7th-ui 接入到了博客正文阅读页。组件库的完整文档和在线演示可以在 March7th UI 查看,里面整理了按钮、表单、通知、卡片、时间线、轮播等适合博客和作品集使用的组件。
现在写文章时,可以直接在 .md 或 .mdx 中写:
<m7-button type="primary">开始阅读</m7-button><m7-notice type="tip" title="提示">这里是一段提示内容。</m7-notice>浏览器加载文章页后,这些标签会自动升级为真正的交互组件。
为什么选择 Web Components
March7th UI 是一套基于 Web Components 的组件库。它最大的好处是:组件本身就是浏览器原生自定义标签,不依赖 Vue、React、Svelte 这类运行时。
如果你也在写个人博客、项目文档或作品集页面,可以直接访问 https://ui.march7th.online/ 预览组件效果和使用方式。
这点非常适合博客正文:
- Markdown 可以保留原始 HTML,自定义标签能直接写进正文。
- MDX 也可以使用小写短横线标签,例如
<m7-button>,不需要额外 import。 - 组件和博客框架解耦,之后博客换技术栈,正文里的 m7 标签仍然有机会继续复用。
- UI 风格集中在组件库维护,不需要每篇文章单独写一堆样式。
- 对读者来说,文章表达更丰富;对作者来说,写作仍然接近 Markdown。
整体方案
这次接入没有把组件库作为 npm 依赖打进博客构建产物,而是使用 unpkg CDN 加载已经发布的 npm 包。
整体结构很简单:
具体来说:
@mar7th/march7th-ui已经发布到 npm。- 在博客项目里新增一个
M7Components.astro。 - 这个组件负责从 unpkg 加载 m7 的 CSS 和 JS。
- 只在博客正文页,也就是 post 阅读页的
<head>中引入它。 - Markdown 或 MDX 正文里直接写
<m7-*>标签。
这样的好处是接入边界非常清晰:只有文章页会加载组件库资源,列表页、关于页、归档页不受影响。
接入代码
第一步,新增一个专门加载 March7th UI 的 Astro 组件。
---import { siteConfig } from "@/config/siteConfig";
const packageName = "@mar7th/march7th-ui";const packageVersion = "1.1.2";const cdnBase = `https://unpkg.com/${packageName}@${packageVersion}`;const defaultHue = siteConfig.themeColor.hue;const defaultMode = siteConfig.themeColor.defaultMode ?? "light";---
<link rel="preconnect" href="https://unpkg.com" crossorigin /><link rel="stylesheet" href={`${cdnBase}/dist/march7th-tokens.css`} /><link rel="stylesheet" href={`${cdnBase}/dist/march7th-ui.css`} />
<script is:inline define:vars={{ defaultHue, defaultMode }}> // 这里同步博客现有主题状态到 m7 变量: // dark -> m7-dark // --hue -> --m7-hue // 同时提供 window.March7thTheme,兼容 m7-theme-switch / m7-hue-picker。</script>
<script is:inline src={`${cdnBase}/dist/march7th-ui.bundle.min.js`} defer></script>这里固定了版本号 1.1.2,这是有意为之。组件库升级后,可以手动改版本并验证,避免 unpkg 默认 latest 变动导致线上文章样式突然变化。
第二步,在文章页引入这个加载组件。
---import M7Components from "@components/misc/M7Components.astro";---
<MainGridLayout banner={processedImage} title={entry.data.title} description={entry.data.description} lang={entry.data.lang} setOGTypeArticle={true} postSlug={entry.slug} headings={headings}> <M7Components slot="head" />
<Markdown class="mb-6 markdown-content onload-animation"> <Content /> </Markdown></MainGridLayout>这一步完成后,文章正文里的 m7 标签就可以正常工作。
主题同步
博客本身已经有亮色、暗色和主题色 hue 配置。March7th UI 也有自己的设计变量,例如:
:root { --m7-hue: 215;}
:root.m7-dark { color-scheme: dark;}所以接入时不能只加载组件脚本,还需要把博客已有的主题状态同步过去。
关键动作有三个:
const root = document.documentElement;
root.classList.toggle("m7-dark", root.classList.contains("dark"));root.style.setProperty("--m7-hue", getComputedStyle(root).getPropertyValue("--hue"));root.setAttribute("data-m7-resolved-mode", root.classList.contains("dark") ? "dark" : "light");这样 m7 组件就会跟随博客现有的亮暗模式和主题色变化,不会出现正文是暗色、组件仍然是亮色的割裂感。
这里还有一个很重要的前提:当前博客和 March7th UI 的设计语言本来就是同源的。它们使用相近的圆角、间距、色彩、卡片层级和交互动效,所以把组件放进正文后,整体观感会比较自然,不像是突然嵌入了一套外来的 UI。
如果在其它博客或文档站中接入,就需要额外考虑页面风格是否一致。组件库能解决“组件能力”和“基础设计规范”的问题,但不能自动保证它和任何站点都完全匹配。实际使用时,最好先检查页面的主色、背景、字号、圆角、阴影、暗色模式等基础风格,再决定是直接使用默认主题,还是做一层变量覆盖。
March7th UI 的样式主要通过 CSS 变量组织,因此可以按站点风格覆盖核心变量:
:root { --m7-hue: 250; --m7-radius-large: 0.75rem; --m7-card-bg: #ffffff; --m7-btn-regular-bg: oklch(0.95 0.025 var(--m7-hue));}
:root.m7-dark { --m7-card-bg: oklch(0.23 0.015 var(--m7-hue)); --m7-btn-regular-bg: oklch(0.33 0.035 var(--m7-hue));}也就是说,最理想的接入方式不是简单地“把组件塞进文章”,而是让组件库的变量和站点已有设计系统对齐。这样组件增强的是内容表达,而不是打断页面风格。
在 Markdown 中怎么写
最简单的方式就是把 m7 组件当作 HTML 标签写进正文。
<m7-notice type="tip" title="写作提示"> 这是一段写在 Markdown 中的 m7 通知组件。</m7-notice>
<m7-button type="primary">主要按钮</m7-button>也可以用普通 HTML 容器组合多个组件:
<div class="m7-stack"> <m7-input label="文章标题" placeholder="输入标题"></m7-input> <m7-textarea label="文章摘要" placeholder="输入摘要" rows="4"></m7-textarea></div>如果只是使用 m7 组件,普通 .md 已经足够。只有在你需要 import 框架组件、写 JSX 表达式、组合更复杂的运行时代码时,才需要换成 .mdx。
效果演示
下面这些不是截图,而是直接写在这篇 Markdown 里的真实组件。
按钮和徽章
提示卡片
标签页
折叠面板
时间线
数据卡片
这个方案的优点
我认为这个方案最舒服的地方,是它没有破坏 Markdown 的写作体验。
总结下来有几个优点:
- 写作仍然轻量:大多数内容继续用 Markdown,只有需要增强表达时才插入 m7 标签。
- 组件统一维护:样式、交互、主题都在
@mar7th/march7th-ui中维护,文章不承担 UI 细节。 - 接入范围可控:只在 post 页面加载资源,不影响其它页面。
- 跨框架友好:Web Components 不绑定具体前端框架。
- 适合文档型内容:教程、组件文档、项目说明、更新日志都可以写得更清晰。
- 版本可锁定:unpkg 路径带上 npm 版本号,升级节奏由自己控制。
需要注意的问题
这个方案也不是完全没有取舍。
首先,CDN 资源依赖网络。如果希望完全自托管,可以把 dist/march7th-ui.bundle.min.js 和 CSS 放到博客的 public 目录,再改成本地路径。
其次,全量 bundle 会加载全部组件。如果文章长期只用少数组件,可以后续做按需构建,把常用组件打成一个更小的 custom bundle。
再次,页面风格需要评估。我的博客和 March7th UI 的设计本来同源,所以接入后视觉一致性比较高;换到其它站点时,不一定可以直接无脑套用。建议至少覆盖 --m7-hue、背景色、文本色、圆角和卡片颜色,让组件看起来像页面的一部分。
最后,文章里不要过度组件化。组件是为了让重点更清楚,而不是为了炫技。真正重要的仍然是内容结构、解释质量和代码示例。
后续计划
后面可以继续做几件事:
- 整理一篇 m7 组件写作速查表。
- 为常用组件补充更适合博客场景的属性。
- 做一个按需构建版本,只给文章页加载文档常用组件。
- 把更多 Markdown 扩展能力沉淀成稳定写作规范。
参考
- March7th UI 组件库:https://ui.march7th.online/
- npm 包:
@mar7th/march7th-ui
组件库写博客
用 March7th UI 增强 Markdown 博客写作体验,把组件能力沉淀到文章内容中。
March7th