博客 V2.1 迭代记录 - 主题系统、暗色模式与 MD1 规范化

Kernel 2026-06-11
bookmark bookmark
share share

前言

v2.0 发布后,博客系统进入了密集迭代期。本文汇总了第二轮开发中的所有改动,涵盖主题系统、暗色模式、移动端适配、管理后台组件规范化等核心模块,并附带实际截图。所有改动均严格遵循 Google Material Design 1 设计规范。

1. 11 套 MDL 主题配色系统

博客从单主题扩展为 11 套配色方案。管理后台提供色板选择器,点击即切换,所有页面(首页、文章详情、管理后台)同步响应主题变化。

管理后台 - 主题色板

技术实现上,通过 Python 后端注入 CSS 自定义属性(--md-primary--md-accent 等),替代了所有硬编码的颜色值。主题切换时只需更换引用的 MDL 主题 CSS 文件,其余颜色通过变量自动跟随。

Python: CSS 变量注入
def theme_vars_css(site):
    theme = site.get('theme', 'indigo-pink')
    colors = _THEME_COLORS.get(theme, _THEME_COLORS['indigo-pink'])
    return f'''  :root {{
    --md-primary: {colors['primary']};
    --md-accent: {colors['accent']};
    --md-primary-dark: {colors['primary_dark']};
    --md-accent-dark: {colors['accent_dark']};
  }}'''

2. 完整暗色模式

暗色模式覆盖范围包括:博客首页、文章详情页、管理后台。支持三种模式切换——明亮、黑暗、根据系统设置自动切换。

暗色模式 - 博客首页

文章详情页的暗色模式需要处理更多元素:代码块容器、行内代码、引用块、图片圆角边框、文章导航按钮等,全部通过 html[data-mode="dark"] 选择器覆盖。

暗色模式 - 文章代码块

关键实现细节:

  • 背景图处理:暗色模式下通过 background-image: none !important 移除自定义背景图,因为 background-color 无法遮盖 background-image(CSS 层叠顺序导致)
  • Switch 组件:On 状态使用 color-mix(in srgb, var(--md-primary) 50%, transparent) 替代 opacity: 0.5,避免父级透明度影响子元素 thumb 的颜色
  • 高优先级覆盖:MDL 的 .mdl-color-text--grey-600 等工具类具有高特异性,暗色模式覆盖必须使用 !important
CSS: Switch On 状态的 color-mix 方案
/* 错误方案:opacity 影响子元素 thumb */
.md-switch .md-switch__track {
    opacity: 0.5;  /* thumb 也会变半透明! */
}

/* 正确方案:color-mix 仅影响 track 自身 */
.md-switch .md-switch__track {
    background: color-mix(in srgb, var(--md-primary) 50%, transparent);
}

3. 页脚 Switch + 暗色模式切换

博客前端页脚新增了 MD1 规范的 Switch 组件和图标按钮。Switch 切换时通过 JS 修改 <html>data-mode 属性,并调用后端 API 持久化设置。

JavaScript: snippets.js 暗色模式切换
(function() {
    var checkbox = document.querySelector('.md-switch__input');
    if (!checkbox) return;

    checkbox.addEventListener('change', function() {
        var mode = this.checked ? 'dark' : 'light';
        document.documentElement.setAttribute('data-mode', mode);

        fetch('/api/settings', {
            method: 'PUT',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({darkMode: mode})
        });
    });
})();

4. 管理后台移动端全面适配

管理后台从桌面端单一布局扩展为三级响应式断点:桌面(>768px)、平板(480-768px)、手机(<480px)。

管理后台 - 移动端

适配覆盖的模块包括:

  • 文章列表 / 导航管理的数据表格(列间距压缩)
  • 编辑器底部按钮换行、工具栏自动折叠
  • 日期/时间字段竖向堆叠
  • 主题色板网格自适应
  • 设置页面上传区域堆叠
  • Dialog / 日期选择器 / 时间选择器宽度自适应
CSS: Grid 溢出修复
/* Grid 子元素默认 min-width: auto,会被内容撑开 */
/* 必须显式设置 min-width: 0 防止溢出 */
.md-form-group {
    display: grid;
    grid-template-columns: 120px 1fr;
}
.md-form-group > * {
    min-width: 0;  /* 关键:允许 Grid 子项收缩 */
}

5. MD1 组件规范化实现

管理后台的多个组件严格按照 m1.material.io 规范重新实现或修正:

Snackbar 消息通知

完全遵循 MD1 Snackbar 规范:

  • 桌面端:居中显示,高度 48dp,最小宽度 288dp,最大宽度 568dp,2dp 圆角
  • 移动端:宽度 = 屏幕宽度 - 16dp,底部贴边,无圆角,translateY(100%) 入场动画
  • 操作按钮:Roboto Medium 14sp 全大写,左内边距 48dp
Dialog 对话框

删除确认等场景使用带标题栏的 Alert Dialog:标题 Roboto Medium 20sp,内容 14sp,24dp 内边距,24dp 阴影,操作区域 52dp 高度。

Text Field 文本字段

遵循 MD1 单行 / 多行文本字段规范:浮动标签 12sp 主色,输入文字 16sp 87% 黑色,底部线条 1dp(聚焦时 2dp)。长标签使用 text-overflow: ellipsis 截断。

Datepicker 日期选择器

自研实现,严格遵循 MD1 规范,包含多项细节修正:

CSS: 日期选择器今日 + 选中状态冲突修复
/* 错误:.md-datepicker-day--today 声明在 --selected 之后,同特异性下后者覆盖前者 */
.md-datepicker-day--selected {
    color: #fff;
    background: var(--md-primary);
}
.md-datepicker-day--today {
    color: var(--md-primary);  /* 覆盖了 selected 的白色文字! */
}

/* 正确:用 :not() 排除已选中的今天 */
.md-datepicker-day--today:not(.md-datepicker-day--selected) {
    color: var(--md-primary);
}
  • 月份切换滑动动画(200ms slide-in/slide-out)
  • 移动端下拉菜单使用 position: fixed + getBoundingClientRect() 定位,避免 overflow: hidden 裁剪
  • 暗色模式:选中日期悬停色、月份/年份悬停高亮、禁用/非当月日期透明度
Switch 开关

尺寸严格遵循 MD1 规范:Track 36×14dp,Thumb 20dp(无边框),行程 16dp,48×48dp 触摸目标。附加 MDL Ripple 效果。

CSS: Switch 精确尺寸(MD1 规范)
.md-switch__track {
    width: 36px;
    height: 14px;
    border-radius: 7px;
}
.md-switch__thumb {
    width: 20px;
    height: 20px;
    border: none;          /* MD1: thumb 无边框 */
    top: -3px;             /* (14 - 20) / 2 = -3 */
    transition: left 0.2s;
}
/* On 状态:thumb 左移 16dp */
.md-switch__input:checked + .md-switch__thumb {
    left: 16px;
}

6. Sidebar 导航菜单暗色模式

侧边栏在暗色模式下切换为深色背景(#212121),菜单项文字 70% 白色,悬停 12% 白色高亮,分割线 12% 白色。当前激活项保持主色调指示条。

7. 按钮悬停色跟随主题

引入 --md-primary-dark--md-accent-dark CSS 变量,使按钮 hover 状态、编辑提示栏背景色自动跟随当前主题,无需为每个主题单独写悬停规则。

8. 文章详情页优化

  • minilogo 头像路径修正:修复了文章页小头像因相对路径计算错误导致无法显示的问题
  • 预览页面 1:1 匹配/api/preview 端点现在返回与 blog/posts/*.html 完全相同的模板,包括返回按钮、底部栏、Switch 等
  • 编辑提示栏主题色:编辑中的提示条颜色跟随 --md-primary 变量

9. 导航文案与引用卡片

文章底部导航文案改为中文"前一篇"/"后一篇",移除箭头符号。引用卡片(名言警句)不生成详情页、不出现在导航中、首页无链接。

10. 博客首页移动端

博客首页 - 移动端

首页在移动端保持卡片流式布局,封面图自适应宽度,页脚 Switch 和图标按钮竖向排列。

11. 管理后台暗色模式

管理后台 - 暗色模式

管理后台暗色模式覆盖了所有自定义组件:文本字段、下拉菜单、卡片、 Snackbar、Dialog、日期选择器、进度条、上传区域等。全局使用 --md-primary 变量确保主题色一致性。

总结

本轮迭代的核心方向是规范化全平台覆盖

  • 11 套主题配色 + CSS 变量系统,一键切换全局配色
  • 完整暗色模式(博客前端 + 管理后台 + 所有组件)
  • 管理后台三级响应式断点(桌面 / 平板 / 手机)
  • 6 个自定义组件严格遵循 MD1 规范(Snackbar、Dialog、Text Field、Datepicker、Switch、Tooltip)
  • 页脚 Switch 实时切换 + 后端持久化
  • 多项 CSS 细节修正(opacity 陷阱、Grid 溢出、 specificity 冲突、position: fixed 定位)

博客系统现已具备完整的前后台体验,覆盖桌面和移动端,支持明亮/暗色双模式。后续计划包括评论系统和更多主题定制选项。