本文是 yutsukarin 博客当前视觉语言的设计基线。它以 Material Design 3 为骨架,并针对壁纸、长文阅读和 Web 平台加入实时毛玻璃扩展。后续新增页面和组件应优先复用这里定义的语义、token 与交互规则。
1. 设计目标
本站的视觉目标可以概括为四个词:柔和、清晰、真实、克制。
- 柔和:以淡粉动态主题为默认表达,避免高饱和色大面积争夺注意力。
- 清晰:颜色承担语义,层级依靠色调、模糊和空间关系建立,不依赖廉价的细线框。
- 真实:所有半透明表面都必须实时采样并模糊背后内容,不能只降低不透明度。
- 克制:阅读内容始终优先。文章正文不漂浮、不抬升,装饰和动效不能干扰长文阅读。
本站不是照搬 Material 组件库,而是使用 MD3 的设计 token、色彩角色、形状、层级和动效思想,构建适合 Astro 的 CSS 设计系统。
2. 核心原则
2.1 语义优先
组件不得直接依赖固定色值,而应使用语义角色:
- 主操作使用 primary 与 on-primary。
- 强调容器使用 primary-container 与 on-primary-container。
- 页面背景使用 surface。
- 卡片与导航使用不同等级的 surface-container。
- 正文使用 on-surface,次级说明使用 on-surface-variant。
- 分割线只在确有结构意义时使用 outline-variant。
颜色必须成对使用。例如 primary 上的文本或图标使用 on-primary,容器表面上的内容使用 on-surface 或 on-surface-variant。
2.2 色调层级优先于描边
MD3 主要通过容器色调表达层级。本站的卡片、顶栏、按钮和浮层默认采用 filled 或 elevated 语义,边框保持透明。只有输入框焦点、键盘焦点环、错误状态等功能性边界可以使用清晰描边。
2.3 壁纸与内容分离
壁纸负责情绪与空间,内容表面负责可读性。两者通过真实毛玻璃建立联系,但壁纸纹理不能破坏正文对比度。
2.4 阅读表面不是按钮
文章正文卡片是稳定的阅读平面:
- 悬停时不得上移、缩放或改变深度。
- 不能因鼠标经过而大幅改变亮度。
- 链接、按钮、目录项可以有局部状态反馈。
3. 色彩系统
默认主题色相为 345,即偏玫红的淡粉色。调色盘修改 —hue,其余颜色通过 OKLCH 和语义角色随之生成。
| 语义角色 | 本站用途 |
|---|---|
| —md-sys-color-primary | 当前状态、主操作、强调竖线、图标 |
| —md-sys-color-primary-container | 选中项、强调容器 |
| —md-sys-color-surface | 页面基础背景 |
| —md-sys-color-surface-container-low | 低层级容器 |
| —md-sys-color-surface-container | 普通卡片与导航 |
| —md-sys-color-surface-container-high | 浮层和高强调区域 |
| —md-sys-color-on-surface | 正文与主要标题 |
| —md-sys-color-on-surface-variant | 元数据与次级说明 |
| —md-sys-color-outline | 需要明确识别的功能边界 |
| —md-sys-color-outline-variant | 必要的弱分割线 |
浅色和深色主题共享同一语义结构。固定品牌色只用于不承载关键可读信息的装饰;文本与交互状态必须随主题变化。
4. 实时毛玻璃
毛玻璃是本站在 MD3 之外的重要视觉扩展。只设置半透明背景不算毛玻璃,合格表面必须同时具备:
background: var(--card-bg-transparent);backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate)) contrast(var(--glass-contrast));-webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate)) contrast(var(--glass-contrast));4.1 模糊等级
| Token | 当前值 | 使用场景 |
|---|---|---|
| —glass-blur | 20px | 卡片、设置面板、主浮层 |
| —glass-blur-soft | 14px | 顶栏、按钮、次级浮层、媒体控件 |
| —glass-blur-subtle | 8px | 面板内部控件、轻量状态层 |
| —glass-saturate | 1.72 | 恢复模糊后的壁纸色彩 |
| —glass-contrast | 1.04 | 轻微增强背景分离 |
4.2 表面层级
- 主卡片使用 —card-bg。
- 壁纸覆盖与全屏模式使用 —card-bg-transparent。
- 设置面板等浮层使用 —float-panel-bg。
- 面板内部控件使用 —glass-control-light 或 —glass-control-dark。
- 独立卡片与浮层使用 —glass-blur,按钮、标签和内部控件使用 —glass-blur-subtle。
- 同一视觉链路只允许最近的独立表面拥有实时模糊。嵌套控件不能穿透祖先的 backdrop root,因此必须使用不透明的 MD3 tonal surface,而不是继续叠加半透明背景制造“假玻璃”。
- 模糊半径必须与表面尺寸匹配。窄顶栏和小控件不得使用会把整片壁纸平均成纯色色块的超大半径。
浏览器不支持 backdrop-filter 时,使用更不透明的 tonal surface 降级,保证文字对比度;不得用整体 opacity 代替,因为它会同时削弱内容。
4.3 全局玻璃契约
任何能够看到其后方内容的前景表面,都必须进入统一的实时毛玻璃管线:
- 通用组件使用
card-base、float-panel、btn-card或btn-regular,不得在组件里重新拼一份半透明样式。 - 特殊表面使用
glass-realtime;用glass-realtime--soft或glass-realtime--control选择较轻的模糊等级。 - 使用 —card-bg、—btn-regular-bg 或半透明主题色工具类的前景组件由公共规则自动适配,新增后仍需在复杂壁纸上实机检查。
- 纯图片遮罩、渐变高光、阴影、文字透明度和 pressed/hover 状态层不是独立表面,不使用毛玻璃。需要显式排除时使用
glass-state-layer。 - 完全不透明的 MD3 tonal surface 可以使用
glass-tonal,但它不能同时保留可透出壁纸的 alpha 背景。 - 浮层若由玻璃顶栏触发,必须通过 portal 移到顶栏外再渲染,否则即使计算样式存在 backdrop-filter,也无法采样真实壁纸。
检查时不能只看样式表里是否写了 backdrop-filter。必须在 600、840、1200 和 1600px 宽度下读取实际计算样式,并观察壁纸纹理经过表面时是否被实时模糊。
5. 形状系统
| Token | 数值 | 用途 |
|---|---|---|
| extra-small | 4px | 小提示、紧凑状态 |
| small | 8px | 菜单、输入控件、标签 |
| medium | 12px | 小卡片 |
| large | 16px | 普通浮动组件 |
| large-increased | 20px | 站点主卡片,即 —radius-large |
| extra-large | 28px | 对话框、侧边面板 |
| full | 9999px | 按钮、图标按钮、徽章 |
组件不得散落任意圆角。头像使用圆角矩形,保持柔和但不强制裁成圆形;按钮优先使用 full,信息卡片使用 medium 或 large-increased。
6. 字体与排版
| 内容 | 推荐角色 |
|---|---|
| 首页横幅标题 | Display Large,并响应式缩放 |
| 文章标题 | Headline Large |
| 卡片标题 | Title Medium |
| 正文 | Body Large |
| 卡片说明 | Body Medium |
| 按钮与标签 | Label Large |
| 元数据 | Label Medium 或 Body Small |
正文保持约 1rem / 1.5rem 的基准节奏,长文行宽控制在约 65–80 个字符。数学公式、代码块和表格可以横向滚动,但不得撑破正文容器。
标题前的短竖线属于标题组件,不属于其后的日期或图标。标题隐藏时,竖线也必须一起隐藏,不能因绝对定位漂移到下一行。
7. 间距与响应式布局
本站以 4px 为基础网格,以 8px 为主要节奏:
- 组件内部:4、8、12、16、24px。
- 组件之间:8、12、16、24px。
- 段落和章节:24、32、48px。
- 移动端页面边距:16px。
- 平板与桌面页面边距:24px。
| 等级 | 宽度 | 博客布局 |
|---|---|---|
| Compact | 小于 600px | 单栏文章流,辅助内容折叠或下移 |
| Medium | 600–839px | 主内容优先,可出现第二栏 |
| Expanded | 840–1199px | 主栏与辅助栏并置 |
| Large | 1200–1599px | 复用 Expanded 的界面结构,仅展开顶栏文字 |
| Extra-large | 1600px 以上 | 延续同一结构并约束正文,不新增独立宽屏界面 |
首页属于 Feed 布局;文章页属于 Supporting Pane 布局。右侧统计、日历和目录都是辅助信息,窄屏下不得挤压正文。
8. 组件规范
8.1 卡片
- 默认是 filled 或 elevated card,不使用装饰性一像素边框。
- 静止层级使用 —md-elevation-1;真正浮起的菜单和面板使用 —md-elevation-2。
- 普通内容卡片可在精确指针设备上轻微抬升。
- 文章正文卡片永远保持静止。
- 嵌套卡片通过 tonal surface 和间距区分,避免出现两层重复外壳。
8.2 按钮
- 主操作使用 primary filled。
- 次操作使用 tonal 或 text button。
- 图标按钮最小可交互区域为 48 × 48px。
- 按钮不使用装饰性细边框。
- hover 使用 8% 状态层,pressed 使用 12% 状态层。
8.3 顶栏
- 顶栏是高层级玻璃表面,必须实时模糊壁纸。
- 默认没有轮廓细线。
- 全屏或覆盖壁纸模式不能盖住顶栏。
- 品牌头像使用圆角,并与站点名保持稳定间距。
8.4 设置与调色盘
- 设置面板位于普通卡片、目录和右栏组件之上。
- 面板内部选项依靠填充与状态层区分,不使用大量矩形描边。
- 设置面板在所有断点都从右上角以浮层覆盖内容,不因宽屏切换为独立 Supporting Pane,也不挤压正文;宽屏时其右边缘与顶栏右边缘对齐,保持触发器与浮层的空间来源一致。
- 壁纸模式使用 2 × 2 填充选项组,选中状态同时使用强调填充与图标表达,不能只依靠颜色。
- 面板只保留主题色、壁纸模式与壁纸效果;横幅选项、水波纹与文章列表布局不作为用户设置暴露。
- 面板、壁纸选项和壁纸效果卡片使用同一 large-increased 圆角,数值与复位控件使用 medium 圆角。
- 开启、关闭和快速反向操作必须平滑打断,并从当前视觉状态继续。
- 面板高度受视口限制,超出时内部滚动,不能延伸到页面底部之外。
8.5 日历与目录
- 日历分割线使用与当前背景形成足够对比的 outline-variant 或反相角色。
- 当月、当前日、文章日通过填充、文字权重和状态点建立层级。
- 目录只对当前章节使用高强调状态;其余项目保持低对比。
8.6 随机段落推荐
- 随机段落只出现在宽屏辅助栏,不挤压中窄布局的正文与首页信息流。
- 候选内容来自所有公开文章的自然段,必须排除草稿、加密内容、纯公式与过短文本。
- 外层使用实时毛玻璃卡片,正文直接落在玻璃表面,不额外嵌套大块圆角卡片。
- 段落正文不重复使用标题强调竖线,并按文章原始 Markdown 与 TeX 语义预渲染。
- 来源文章使用 tonal 文本按钮表达,刷新使用具备可访问名称的 48px 图标按钮。
- 连续刷新时取消上一段过渡并从当前状态继续;reduced motion 下立即替换内容。
8.7 音乐播放卡片
- 音乐卡片属于左侧辅助栏;分类、标签与随机段落位于右侧辅助栏。
- 封面固定为 1:1 正方形媒体内容,使用 large-increased 圆角,不在玻璃卡片内重复添加描边容器。
- 歌词与“歌手 / 歌曲 / 专辑”各保持单行,溢出时截断并保留完整 title 提示。
- 歌词切换使用纵向轮动:顺播时旧行向上离开、新行从下方进入;回退时方向反转。动画必须可被拖动进度等新状态打断,reduced motion 下立即切换。
- 播放、暂停、上一首和下一首使用 48px 圆形触控目标;主播放按钮使用 primary filled,切歌使用 standard icon button。
- 进度条必须支持指针拖动与键盘调整,并显示当前位置和总时长。
- 只有一首歌曲时,切歌操作回到同一首开头,不显示失效的假按钮。
9. 层级与浮层
层级顺序从低到高为:
- 壁纸;
- 页面背景与装饰;
- 普通内容卡片;
- 顶栏与固定导航;
- 目录、搜索和普通浮层;
- 设置与调色盘;
- 对话框、遮罩与全局提示。
组件进入全屏、横幅或覆盖模式时,必须显式验证顶栏和设置面板的堆叠关系,不能依赖 DOM 顺序。
10. 动效
| Token | 当前值 | 用途 |
|---|---|---|
| —md-motion-duration-short | 180ms | hover、pressed、颜色变化 |
| —md-motion-duration-medium | 300ms | 面板、卡片和布局变化 |
| —md-motion-duration-long | 500ms | 大范围或强调过渡 |
| —md-motion-emphasized | cubic-bezier(0.2, 0, 0, 1) | 屏幕内状态变化 |
| —md-motion-decelerate | cubic-bezier(0.05, 0.7, 0.1, 1) | 元素进入 |
| —md-motion-accelerate | cubic-bezier(0.3, 0, 0.8, 0.15) | 元素退出 |
可中断动画优先使用 CSS transition、Web Animations API 或可反向状态机。禁止用延迟回调在动画结束后强制写死状态,因为快速点击会产生跳帧、重叠或两层残影。
10.1 空间来源与原路返回
动效必须能够回答三个问题:它从哪里来、为什么这样移动、关闭后回到哪里。
- 来源明确:浮层从触发它的按钮或容器附近出现,transform-origin 必须指向该来源。
- 路径可逆:关闭不是另一套动画,而是沿打开路径反向返回原处。
- 状态连续:快速反向时从当前位移、缩放和透明度继续,不能跳回起点再播放。
- 速度连续:交互改变目标而不是重启动画;浏览器或动画系统保留当前插值速度。
- 形变守恒:面板可以轻微缩放和位移,但不能被压成一条线或凭空改变长宽比例。
- 层级一致:元素靠近观察者时可提高 tonal elevation,返回来源时同步降低。
- 距离决定时间:剩余路程越短,反向完成越快,不强制重新执行完整时长。
本站的典型空间映射如下:
| 组件 | 出现来源 | 返回方向 |
|---|---|---|
| 搜索面板 | 顶栏搜索按钮 | 右上方 |
| 调色盘 | 顶栏调色盘按钮 | 右上方 |
| 移动目录 | 顶栏目录按钮 | 右上方 |
| 导航菜单 | 顶栏菜单按钮 | 右上方 |
| 对话框 | 当前操作位置与视口中心之间 | 沿进入路径返回 |
实现时优先切换目标状态类,让 CSS transition 从当前计算值自然反向。只有需要手势速度、拖拽或真实弹簧时才使用 Web Animations API;不得用定时器等待动画结束后再切换可见性。
当 prefers-reduced-motion: reduce 生效时,应取消位移、缩放和复杂过渡,只保留必要的即时状态反馈。
11. 可访问性
- 普通文本与背景至少达到 WCAG 4.5:1。
- 大号文本和关键 UI 边界至少达到 3:1。
- 所有交互支持键盘,并提供可见的 focus-visible。
- 图标按钮必须有可访问名称。
- 不能只用颜色表达选中、错误或当前状态。
- 毛玻璃必须在壁纸最复杂的区域测试对比度。
- 触控目标保持至少 48 × 48px。
12. 实现约束
必须
- 使用 —md-sys-* 和本站别名 token。
- 半透明表面同时声明标准与 -webkit- 的 backdrop-filter。
- 明暗主题共用语义角色。
- 在 600、840、1200、1600px 验证布局。
- 所有开合面板验证快速重复点击和反向操作。
- 新增固定或浮动组件时验证 z-index。
禁止
- 用 opacity 模拟毛玻璃。
- 给所有卡片和按钮统一加细边框。
- 在组件中大量硬编码颜色和圆角。
- 让文章正文产生悬停抬升动画。
- 让全屏内容覆盖顶栏。
- 用不可打断的定时器拼接面板动画。
- 为宽屏无限拉长正文行宽。
13. 发布前检查表
- 浅色、深色和不同主题色相下文字均清晰。
- 所有半透明卡片都能看到实时模糊后的壁纸变化。
- 卡片与按钮没有多余细线。
- 标题强调竖线没有漂移到元数据图标。
- 正文卡片悬停时保持静止。
- 调色盘可以快速开关并平滑打断动画。
- 设置面板没有被右栏或顶栏遮挡。
- 全屏与横幅模式不会覆盖顶栏。
- 移动端没有双层卡片或横向溢出。
- reduced motion 下没有非必要运动。
- 键盘焦点、触控目标和色彩对比符合要求。
14. 单一事实来源
- 设计 token:src/styles/variables.styl
- 全局卡片、按钮和阅读表面:src/styles/main.css
- 毛玻璃公共规则:src/styles/glass-surfaces.css
- 壁纸与顶栏:src/styles/wallpaper-navbar-transparent.css
- 可中断面板动画:src/styles/panel-animations.css
更新视觉系统时,应先修改 token 和公共规则,再处理组件特例。若文档与实现不一致,应在同一次改动中同步更新本文。