关于 teaching-site-design-system
该技能为教学网站定义视觉设计系统,涵盖颜色令牌、字体、布局尺寸、组件和暗色模式策略。它适合构建或调整课程网站,并提供设计选择背后的原则,便于跨网页与电子书保持一致。
主要功能
- 01基于 OKLCH 的颜色令牌
- 02语义表面与双强调色
- 03字体与几何尺寸规范
- 04标志性教学组件样式
- 05暗色模式与跨格式令牌复用
适用场景
- 01为新教学网站建立视觉基础
- 02为新增卡片或提示块选择一致样式
- 03调整暗色模式而不改动组件布局
- 04让网页与电子书沿用同一视觉令牌
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ kevintsai1202/ teaching-site-skills --skill teaching-site-design-system安装后在 Claude Code 中输入/teaching-site-design-system确认已加载。
附带文件templates/tokens.css
详细介绍
教学网站设计系统
Token 起步模板(复制此文件,无需重新推导):
templates/tokens.css— 完整的:root区块 + 深色主题 + 标志性组件(day-hero、glass-card、prompt-card、task-list、learning-goal、concept、unit、material、table)。将其粘贴到index.html的<style>区块中(原生 SPA 模式),或将其用作外部style.css。Schema 权威来源:此技能定义视觉 Token;数据字段名称位于
_shared/domain-primitives.md中。为组件设置样式时,请阅读该文件以了解所渲染的数据结构。参考实现:
d:/GitHub/ai-workshop/index.html:11-1700— 此技能的 Token 提取自其中完整的生产 CSS。
此技能定义教学网站的视觉层:颜色 Token、字体排印、几何样式、组件、深色模式策略,以及至关重要的每项决策背后的理由。设计理由比具体数值更重要;未来的网站可以重新调整数值,但这些理由能延续到不同项目中。
何时调用
- 搭建新的教学网站(编写第一段 CSS 前先阅读 Token)。
- 添加新的视觉元素(徽章、卡片、主视觉区)——在自行设计之前,先检查是否已有适用模式。
- 调整深色模式(确认要覆盖哪些 Token,哪些不覆盖)。
- 制作企业版或电子书(相同的 Token 也可用于打印 CSS)。
- 检查视觉一致性问题(例如“这里看起来不一致”)。
基础决策:使用 OKLCH,而非 HEX/RGB
:root {
--bg: oklch(99% 0.002 240);
--surface: oklch(100% 0 0);
--fg: oklch(18% 0.012 250);
--accent: oklch(58% 0.18 255);
--accent-2: oklch(66% 0.14 65);
}
为什么使用 OKLCH(而不是 #1a73e8 或 hsl(...)):
- 感知均匀——
oklch(60% ...)和oklch(70% ...)看起来亮度差距相等;HSL 则不是这样(黄色从 60 到 70 的视觉变化很大,而蓝色几乎没有变化)。 - 深色模式可预测——对于亮色 Token
L=N%,对应的深色版本大致为L=(100-N)%,色度和色相保持不变。可机械转换。 - 原生支持
color-mix(in oklab, ...)——例如,柔和的强调色底色(color-mix(in oklab, var(--accent) 18%, transparent))可以自然组合,无需手动计算 rgba。 - 现代浏览器支持良好(Chrome 111+、Safari 15.4+、Firefox 113+)。对于静态教学网站,这已经足够。
如果利益相关者提供的是十六进制品牌色:转换为 OKLCH 后就不必再回头调整。
Token 架构(双轴)
轴 1:语义表面 Token
--bg /* page background */
--surface /* card / panel */
--surface-2 /* nested card / hover state */
--fg /* primary text */
--muted /* secondary text */
--muted-2 /* tertiary text / placeholder */
--border /* default border */
--border-strong /* emphasis border */
这些 Token 是结构性的——描述它们在布局中的作用,而非颜色。深色模式会替换这些值;组件 CSS 无需更改。
轴 2:强调色 Token(双强调色策略)
--accent /* primary action color — blue/violet typically */
--accent-soft /* faint tint, for hover backgrounds, badges */
--accent-deep /* high-contrast variant, for hover text */
--accent-2 /* secondary accent — orange/amber typically */
--accent-2-soft
--accent-2-deep
为什么使用两种强调色(而不是一种加灰度色):教学网站有两条同等重要的注意力路径:
- 操作路径(
--accent):“点击我”“复制此内容”“前往此处”——通常使用冷色(蓝色)。 - 内容路径(
--accent-2):“请看这里,这里有丰富的内容”——用于 Day 主视觉区数字、PROMPT 徽章、提示框背景。通常使用暖色(橙色/琥珀色)。
单强调色设计最终会使用灰度色来突出内容,让所有东西看起来都像按钮。双强调色则能区分“可点击的内容”和“需要仔细阅读的内容”。
状态 Token
--success, --success-soft /* learning goals ✓, copy success */
--warning, --warning-soft /* gotchas, optional warnings */
--danger, --danger-soft /* errors, destructive actions */
背景始终使用 -soft 变体;基础色用于文本/图标。
字体排印:一套字体栈,统领所有样式
--font-display: "Nunito", "jf-openhuninn-1.1", "jf-openhuninn",
"Gen Jyuu Gothic", "Taipei Sans TC Beta",
"PingFang TC", "Noto Sans TC",
system-ui, sans-serif;
/* All four roles point to the same stack */
--font-body: var(--font-display);
--font-serif: var(--font-display);
--font-mono: var(--font-display);
中文教学网站为何所有内容都使用同一字体栈:
- 90% 以上的段落都会混排 Latin 和中文。每更换一种字体,就会出现“字体断裂感”。
- 回退链可处理双语渲染——Latin 字符使用 Nunito,中文字符使用 openhuninn。
- 等宽/衬线变体主要用于间距/字重效果,而非真正使用不同字体。应使用
font-feature-settings、letter-spacing、font-weight来实现,而不是切换字体。
例外:正式印刷的电子书 PDF 可以使用真正的衬线字体(Source Han Serif)——只有这种情况可以打破此规则。
几何令牌
--r-sm: 6px; /* buttons, small chips */
--r-md: 10px; /* cards, panels */
--r-lg: 14px; /* heroes, large containers */
--sidebar-w: 268px;
--header-h: 56px;
--content-max: 880px; /* reading line length cap */
--shadow-lift:
0 6px 18px -10px oklch(20% 0.02 250 / 0.18),
0 2px 4px -2px oklch(20% 0.02 250 / 0.06);
三个圆角半径就够了——更多会造成视觉混乱。根据元素的尺寸选择圆角半径:按钮 → sm,卡片 → md,主视觉区 → lg。
--content-max: 880px 是经过深思熟虑的——舒适的阅读行宽约为 60–75 个 CJK 字符。更宽会让人觉得像在读文档,更窄则像把手机界面硬塞进了桌面端。
双层阴影(--shadow-lift):一层宽而柔和,另一层更紧密、更清晰。单层阴影看起来很有数字感;双层阴影则模拟卡片被实体抬起的效果。始终使用负的 y 轴偏移(-10px、-2px),避免阴影延伸到元素下方。
标志性组件
1. Day 主视觉数字
.day-hero-numeral {
font-family: var(--font-serif);
font-size: clamp(120px, 16vw, 200px);
font-weight: 500;
line-height: 0.85;
letter-spacing: -0.05em;
color: var(--accent-2);
text-shadow: 0 1px 0 color-mix(in oklab, var(--accent-2) 18%, transparent);
}
.day-hero:hover .day-hero-numeral {
color: var(--accent-deep);
text-shadow:
0 2px 0 color-mix(in oklab, var(--accent-2) 22%, transparent),
0 18px 40px color-mix(in oklab, var(--accent) 22%, transparent);
}
Day 标题使用醒目的大号数字,而非装饰性 SVG 或插画:抽象的 SVG 看起来像占位符(“他们忘了放真正的图片”)。200px 的“D2”则显得有意为之、自信且具有导航性。具体胜过抽象。
为什么使用 clamp(120px, 16vw, 200px):尺寸随视口宽度缩放,同时设置了上下限,因此在移动端(尺寸为 120px)不会显得异常,在 4K 屏幕上(最大为 200px)也不会大得离谱。
为什么使用 @keyframes 淡入,而不是 opacity: 0 → transition:如果 IntersectionObserver 未能触发(例如正在使用 CSS zoom,参见 static-spa-interactions),数字就会永远保持 opacity: 0。使用 @keyframes 时,默认状态是可见的;动画只会在触发时“播放一次”。
2. PROMPT 区块(橙色徽标 + 深色代码)
.prompt-block {
background: oklch(15% 0.02 250); /* always dark, even in light mode */
color: oklch(92% 0.005 250);
border-left: 4px solid var(--accent-2);
padding: 14px 16px;
border-radius: var(--r-md);
}
.prompt-block::before {
content: 'PROMPT';
display: inline-block;
background: var(--accent-2);
color: white;
padding: 2px 10px;
border-radius: 999px;
font-size: 11px;
font-family: var(--font-mono);
letter-spacing: 0.1em;
margin-bottom: 8px;
}
为什么 PROMPT 使用橙色(--accent-2):蓝色(--accent)专用于“可点击/导航”。橙色表示“这是一个示例——请仔细阅读并原样复制”。几个月后,学习者会自然地将橙色与提示词区块联系起来。
为什么浅色模式下也使用深色背景:代码类内容看起来就应该像代码。切换浅色/深色背景,会让提示词在浅色模式下显得“不那么严肃”。
3. 玻璃卡片(glass-card)
.glass-card {
background: color-mix(in oklab, var(--surface) 60%, transparent);
backdrop-filter: blur(12px) saturate(140%);
border: 1px solid color-mix(in oklab, var(--border) 80%, transparent);
border-radius: var(--r-md);
padding: 16px 20px;
box-shadow: var(--shadow-lift);
}
为什么使用半透明效果 + backdrop-filter:在背景丰富(渐变、点状图案、插画)的页面上,不透明卡片会显得厚重。玻璃卡片让内容更通透,并传达出“内容悬浮在文档上方”的感觉。
注意事项:需要浏览器支持 backdrop-filter。Safari 需要 -webkit-backdrop-filter 前缀。在不支持的浏览器中,会回退为 60% 不透明度的颜色,效果会平稳降级。
4. 任务复选框(三态视觉效果)
.task-checkbox {
width: 18px; height: 18px;
border: 1.5px solid var(--border-strong);
border-radius: var(--r-sm);
display: grid; place-items: center;
transition: all 120ms ease;
}
.task-item:hover .task-checkbox { border-color: var(--accent); }
.task-item.done .task-checkbox {
background: var(--success);
border-color: var(--success);
}
.task-item.done .task-label { text-decoration: line-through; color: var(--muted); }
三种状态:默认(空框 + 粗边框)、悬停(强调色边框,暗示可交互)、完成(绿色填充 + 删除线)。悬停状态很重要——没有它,学习者就不确定复选框是否可点击。
5. 目标徽标(绿色 ✓)
.learning-goal::before {
content: '✓';
display: inline-block;
width: 20px; height: 20px;
background: var(--success-soft);
color: var(--success);
border-radius: 999px;
text-align: center;
line-height: 20px;
margin-right: 8px;
font-weight: bold;
}
统一使用浅绿色底上的绿色圆盘表示“学习成果”/“你将能够……”。不要将其用于表示“已完成”——那是 task-checkbox 的职责。绿色有两种不同的语义用途,外观必须有所区别:目标(徽标前缀)与完成(填充的复选框)。
深色模式策略
:root { /* light tokens here */ }
[data-theme="dark"] {
--bg: oklch(16% 0.014 250);
--surface: oklch(19.5% 0.014 250);
--fg: oklch(96% 0.005 250);
--border: oklch(28% 0.014 250);
/* accents usually stay the same — accent-deep may flip to brighter */
--accent-deep: oklch(72% 0.18 255);
}
使用 [data-theme] 属性,而不是 prefers-color-scheme:手动切换才是唯一依据。自动跟随操作系统主题意味着用户无法针对每个网站自行决定。切换按钮会读取/写入 localStorage。
深色模式下会变化的内容:
- ✅ 所有表面令牌(bg / surface / fg / muted / border)
- ✅
accent-deep(高对比度变体)——调整为更亮的 L 值 - ❌ 状态颜色(success / danger / warning)——这些颜色具有语义,应保持色相;随着表面颜色变化,
-soft变体会自然变暗 - ❌
accent-2——双强调色的特征应在深色模式下保留(橙色品牌仍然是橙色)
保持不变的内容:
- 所有几何令牌(圆角半径、侧边栏宽度、阴影定义——不过可能需要调整阴影的不透明度)
- 所有组件布局与结构
经验法则:变化的是令牌值,组件 CSS 不变。如果你发现自己在写 [data-theme="dark"] .task-checkbox { ... },说明抽象层已经泄漏——应重构组件,让变化的属性使用令牌。
跨格式令牌复用(Web → Ebook)
电子书印刷 CSS(course-ebook-publishing)读取相同的令牌。具体来说:
/* style-ebook.css (print only) */
@media print {
:root {
/* Override only what print needs differently */
--bg: white;
--surface: white;
/* But colors of badges, prompts, headings stay the same */
}
.prompt-block { /* same .prompt-block style — looks identical to web */ }
.learning-goal::before { /* same green disc */ }
}
原因:先看到网页、然后打开 PDF 手册的学习者,应该感受到“这是同一个产品”。如果网页上的 PROMPT 是橙色,而 PDF 中的 PROMPT 是灰色,视觉品牌就会支离破碎。
企业版(course-corporate-edition)也采用相同做法——即使使用了品牌定制资源,设计令牌仍然保持不变。
三个听起来微不足道、实则影响重大的决策
决策 1:使用 color-mix(in oklab, X 18%, transparent) 创建柔和色调
不要手动编写 rgba(...)。使用 color-mix,让色调从源令牌派生:
background: color-mix(in oklab, var(--accent-2) 18%, transparent);
以后调整 --accent-2 时,每种色调都会随之变化。若使用手动设置的 rgba 值,你就得更新 50 处。
决策 2:不使用网格系统,只用 --content-max
避免引入 12 列网格框架。对于采用线性阅读流程的教学网站,max-width: var(--content-max); margin: 0 auto 就足够了。列布局只在特定组件内部使用 grid-template-columns 实现(如插图网格、工具集展示区)——页面本身仍是连续的内容流。
决策 3:动画预算=“几乎没有”
允许:
- 滚动时淡入(每个元素仅触发一次)
- 悬停微交互(颜色/阴影变化,120–200ms)
- 侧边栏滑动(transform,200ms)
不允许:
- 区块之间的页面过渡
- 装饰性的空闲动画
- 时长超过 400ms 的任何动画
教学内容信息密集;动画会分散注意力。把注意力预算花在内容上,而不是动态效果上。
反模式
- 为“这一处区块”创造新颜色——从现有令牌中选择,或重构以添加一个命名恰当的令牌。临时加入的十六进制值会让系统逐渐腐朽。
- 将
--accent用于内容强调(例如 PROMPT 徽章)——这会破坏双强调色的语义。请使用--accent-2。 - 为代码块使用不同字体——参见排版部分。使用相同字体栈,通过
font-weight/letter-spacing实现区分,或者接受其视觉代价。 - 按组件手动设置深色模式样式——参见深色模式部分。如果组件需要特殊的深色模式处理,说明组件某处使用了非令牌化的值。
- 忘记
-soft变体——每种强调色/状态颜色都需要柔和变体。没有它,设计师就会采用临时的透明度值。
交接
当此技能被其他技能(SPA/交互/电子书/企业版)使用时:
- 引用
:root令牌,绝不使用原始值。 - 如果你发现自己正在编写字面形式的
#...十六进制值或rgb(...),就说明你破坏了系统——它要么应该成为一个新令牌,要么是你在重新创建已有令牌。 - 新组件应优先组合现有模式(glass-card + prompt-block + task-list),而不是另创新样式。
令牌文件( :root { ... } 块)是唯一可信来源。把它打印出来贴在墙上。随着品牌演进调整值,但绝不要随意扩大令牌集合。