主题
第二套皮肤:终端风
这一页和上一页的深色玻璃是同一套机制生出来的两种味道:页面根节点上换一个类名、变量换一组值,组件代码一行都没改。
看完这一页你就掌握了换肤的完整套路:类名划范围、变量定风格、组件读变量。给第三页、第十页换肤都是同样三步。
一、和上一套皮肤比,改了什么
两页的 frontmatter 只差一个类名:
md
第一页:pageClass: ui-demo dark → <div class="Layout ui-demo dark">
这一页:pageClass: ui-term dark → <div class="Layout ui-term dark">具体变化:
| 部位 | 深色玻璃皮肤(上一页) | 终端皮肤(这一页) |
|---|---|---|
| 背景 | 1r.webp 图片 + 深色蒙版 | 近黑底 + 扫描线 + 顶部绿色辉光(纯 CSS,无图片) |
| 主色 | 淡紫 #c4b5fd | 终端绿 #4ade80 |
| 字体 | 主题默认无衬线 | 整页等宽字体(改的是 --vp-font-family-base) |
| 圆角 | 卡片 20px 圆润 | 组件 4px 直角、卡片 6px,像终端窗口 |
| 正文区 | 玻璃卡片 | 伪造成终端窗口,顶栏写着 user@learn-web:~$ cat 本页.md |
| 标题 | 渐变色 | 绿色荧光 + 光标闪烁(h1::after 做一个 ▋) |
| 组件 | DemoCard 深紫玻璃版 | 同一个 DemoCard,绿色描边直角版 |
被换掉的变量(同样是这套色卡墙,换个页面就换一组色):
--vp-c-brand-1
--vp-c-brand-2
--vp-c-brand-3
--vp-c-brand-soft
背景底色
pageClass: ui-term dark换掉字体变量组件仅改变量
二、关键:换肤不是"再抄一遍样式",而是换变量
做上一套皮肤时,我给组件写过一段专门针对那一页的样式:
css
/* ❌ 旧写法:每来一套皮肤就要为组件再写一遍 */
.Layout.ui-demo .demo-card {
border-color: rgba(196, 181, 253, 0.28);
background: linear-gradient(135deg, rgba(32,25,56,.78), rgba(24,19,44,.78));
}加第二套皮肤时问题就来了:每组皮肤都要重写一遍组件样式,皮肤越多越乱。 所以这次先做了一次重构——把组件里写死的颜色抽成组件级变量(design token):
css
/* ✅ 新写法:组件只认变量 */
.demo-card {
--demo-card-bg: linear-gradient(135deg, #fff, #f6f1ff); /* 默认值:浅色版 */
--demo-card-border: rgba(109, 74, 255, 0.22);
--demo-card-radius: 16px;
--demo-card-title: var(--vp-c-text-1);
--demo-card-btn: linear-gradient(135deg, #7c4dff, #a78bfa);
border-radius: var(--demo-card-radius);
border: 1px solid var(--demo-card-border);
background: var(--demo-card-bg);
}于是每套皮肤只需要覆盖这几个变量,组件本身一个字都不动:
css
/* 深色玻璃皮肤 */
.Layout.ui-demo {
--demo-card-bg: linear-gradient(135deg, rgba(32,25,56,.78), rgba(24,19,44,.78));
--demo-card-border: rgba(196, 181, 253, 0.28);
--demo-card-title: #f4f1ff;
}
/* 终端皮肤 */
.Layout.ui-term {
--demo-card-bg: linear-gradient(135deg, rgba(6,24,16,.9), rgba(4,16,11,.92));
--demo-card-border: rgba(74, 222, 128, 0.35);
--demo-card-radius: 4px;
--demo-card-title: #d1fae5;
--demo-card-btn: linear-gradient(135deg, #16a34a, #4ade80);
}下面这个卡片就是和上一页完全同一个组件,你现在看到的绿色直角版样式,全部来自上面那五行变量:
DemoCard.vue
同一个组件,两套皮肤
组件里没有一行判断"我现在在哪套皮肤里"——它只负责读变量。这就是"结构"和"皮肤"分离后的好处。
三、加第三套皮肤:三步
- 起个类名,比如
ui-sunset(避开ui-前缀之外的名字,免得和主题自己的类冲突)。 - 在
custom.css里写一个变量块:
css
.Layout.ui-sunset {
--vp-c-brand-1: #fb923c; /* 主题品牌色 */
--vp-c-brand-2: #f97316;
--vp-c-brand-3: #ea580c;
--demo-card-bg: linear-gradient(135deg, #fff7ed, #ffedd5);
--demo-card-border: rgba(251, 146, 60, 0.35);
--demo-card-radius: 12px;
/* 需要的话还可以换字体、背景图、正文卡片…… */
}- 页面 frontmatter 写上类名:
pageClass: ui-sunset(要深色文字变量就再带上dark)。
就这三步,别的什么都不用动。想整站都用?把 .Layout.ui-sunset 改成 :root,再把 --vp-c-brand-* 与 --demo-card-* 的默认值换掉即可——同一个变量层,作用范围从"一页"变成"全站"。
四、为什么这样写不会失控
变量是分层的,每层管一段,改动的爆炸半径是可预期的:
| 层 | 例子 | 作用范围 | 谁受益 |
|---|---|---|---|
| 主题变量 | --vp-c-brand-1、--vp-font-family-base | 写了变量的那棵子树 | 全站所有组件(导航、链接、按钮、搜索框…) |
| 组件 token | --demo-card-bg、--demo-card-radius | 同上 | 只影响这一个组件的皮肤 |
| 页面类名 | .Layout.ui-term | 只影响带这个类的页面 | 决定"这套皮肤贴在哪" |
再顺手说一个副产品:因为 ProgressList(学习进度表那个组件)也是全程用主题变量写的,所以它换到任何一套皮肤下都会自动适配——不用为它单独做皮肤。这就是"能用变量就别写死颜色"的实际回报。
五、去浏览器里验证
F12→ Elements,最外层是<div class="Layout ui-term dark">;选中它,右侧 Styles 里能看到一堆--vp-c-brand-*、--demo-card-*定义在.Layout.ui-term上。- 找到
--demo-card-radius: 4px,把它改成24px,页面上的卡片立刻变圆——组件没重新编译,只是变量的值变了。 - 看
h1的样式面板,能找到::after那条content: "▋"和@keyframes ui-term-blink:连"光标闪烁"都是 CSS 做的,没有用图片或 JavaScript。
💡 动手实验 1:把终端绿整套换成琥珀色(
#fbbf24 / #f59e0b / #d97706),包括--demo-card-*那几条。改完刷新,你会得到一套"老式 amber 显示器"皮肤——全程只改变量值。💡 动手实验 2:把正文字体那行
--vp-font-family-base删掉,刷新看字体变化;再把它改成Georgia, serif,体会"换字体变量 = 换整页气质"。注意别把变量名写错,写错会静默失效(CSS 找不到变量时不会报错,只会用默认值或什么都不显示)。💡 动手实验 3:新建
docs/stage2/my-term.md,frontmatter 写pageClass: ui-term dark,随便写两段。访问/stage2/my-term——你没写一行 CSS,却得到了整套终端皮肤。再试试故意把类名写错(ui-terminal),看看页面退回默认主题的样子:这就是"类名是开关"的直接证据。
六、小结
text
换肤 = 一个类名 + 一组变量
├─ 类名(ui-term/ui-demo) → 决定贴在哪些页面(pageClass → .Layout)
├─ 主题变量(--vp-c-*) → 决定主色、字体、深浅这类"大风格"
└─ 组件 token(--demo-card-*) → 决定单个组件长得像谁
组件代码不参与换肤 → 所以皮肤可以无限加,组件不用改三句话:
- 皮肤是数据,不是复制粘贴:颜色字号都进变量,组件只读变量;
- 作用范围由类名决定,一页、一个目录、还是整站,全看类挂在哪个节点上;
- 先抽象再加皮肤:如果第一套皮肤时就把颜色抽成 token,第二套就是五行代码的事。
两套皮肤都在这一阶段里:回看深色玻璃版实战,或者去 CSS:页面皮肤把选择器、优先级、变量的基本功再补一遍。
📎 本课对应知识库阅读:知识库里的《界面伪装与钓鱼页》——同一套"改样式就能改身份"的能力,你拿来练手是换肤,攻击者拿来用就是伪装成你信任的页面。