统一博客 UI/UX:从动效、间距到构建产物清理

这次优化主要围绕博客的 UI/UX 一致性 展开。前一轮已经把站点调整到更严格的 no-JS 输出方式,这一轮则进一步处理视觉层面的细节:让交互反馈更稳定、间距和圆角更统一,减少界面里的装饰性干扰。

同时,构建脚本也做了一个小改进:自动清理旧的 hash CSS 文件,避免 _site/assets/ 随着多次增量构建不断积累历史资源。

增加统一的设计变量

之前 CSS 中散落着大量直接写死的 4px8px15px20px40px 等值。它们单独看都合理,但长期维护时容易让组件之间的空间关系变得不一致。

这次在 :root 中增加了一组统一变量:

:root {
  --space-xs: 4px;
  --space-sm: 8px;
  --space-md: 16px;
  --space-lg: 24px;
  --space-xl: 40px;

  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-pill: 999px;

  --content-width: 800px;
  --reading-width: 68ch;
}

随后将页面容器、主内容布局、移动端目录、文章列表、标签云、归档列表、页脚、按钮和上下篇导航等主要组件逐步接入这些变量。

这样做的好处是:以后如果想调整整体密度或圆角风格,只需要改变量本身,而不必在整份 CSS 里逐个搜索数字。

统一当前页导航状态

模板里原本已经输出了 aria-current="page",但 CSS 主要识别的是 .active-nav-link。这会造成语义状态和视觉状态没有完全对齐。

现在导航高亮改为同时支持:

header nav a:hover,
header nav a.active-nav-link,
header nav a[aria-current="page"] {
  color: var(--color-main-text);
  font-weight: 600;
}

这样当前页面既能被辅助技术正确识别,也能在视觉上保持一致。

收敛 hover 动效

之前不同组件的 hover 反馈不完全一致:

  • 文章列表项会横向位移;
  • 标签云会轻微上移;
  • 按钮会向上浮动;
  • 页脚链接会向上浮动;
  • 上下篇导航卡片会整体上浮;
  • 图片 hover 时会缩放。

这些交互单独看都不算问题,但放在同一个极简博客里会有一点“跳动感”。这次统一把主要交互收敛为:

  • 颜色变化;
  • 背景变化;
  • 边框变化;
  • 少量阴影变化。

文章卡片、标签、按钮、页脚链接、归档项和上下篇导航都不再使用位移动效。这样页面在鼠标移动时更稳定,也更符合内容型站点的阅读气质。

清理元信息里的 emoji

首页文章列表之前使用了 📅 表示日期、🏷️ 表示标签,移动端目录标题也用了 📑。这些符号比较醒目,但和站点当前偏文档化、极简化的方向不完全一致。

这次只清理 UI 元信息里的 emoji:

  • 日期只显示日期文本;
  • 标签只显示标签名;
  • 移动端目录标题改为纯文字“目录”。

文章标题和正文里的 emoji 没有强制移除,因为它们属于作者内容的一部分,而不是界面控件。

自动清理旧 CSS 构建产物

CSS 文件使用内容 hash 生成文件名,例如:

style.b069f486.css

这种方式有利于缓存控制,但在增量构建时,旧 hash 文件会留在 _site/assets/ 中。多次构建后,本地目录里可能同时存在多个历史 CSS 文件。

这次在 autobuild.py 中增加了清理逻辑:每次生成新的 style.<hash>.css 后,自动删除同目录下其他旧的 style.*.css

构建日志会显示类似信息:

Removed stale CSS asset: style.b069f486.css

这样可以保持部署产物更干净,也减少无用静态资源。

构建验证

修改完成后重新运行:

python3 autobuild.py

构建通过,并确认:

  • 首页列表中的日期和标签不再显示装饰性 emoji;
  • 当前导航项仍然保留 aria-current="page"
  • CSS 中主要组件已开始使用统一 spacing / radius 变量;
  • 旧 CSS hash 文件会在构建过程中自动清理;
  • 构建产物仍然保持无 JavaScript 运行时依赖。

第二轮细化:去掉模板内联样式

第一轮整理后,模板里还残留了少量内联样式,例如标签页文章数量和分割线:

<p style="...">共 N 篇文章</p>
<hr style="...">

这类写法会让样式分散在模板里,也会让 CSP 难以进一步收紧。现在它们被替换为明确的 CSS class:

<p class="tag-count">共 N 篇文章</p>
<hr class="section-divider">

样式集中回到 assets/style.css 中维护。这样模板更干净,也更符合“结构归 HTML,表现归 CSS”的原则。

第二轮细化:首页标签可点击

之前文章页的标签是链接,而首页列表中的标签只是普通 span。两者看起来相似,但交互行为不同,用户可能会以为首页标签也能点击。

这次把首页和标签页列表的标签也改成链接:

<a href="/tags/ui-ux/" class="tag-badge">ui-ux</a>

同时文章列表项不再整体包裹在 <a> 中,而是改为:

<article class="post-list-item">
  <h2 class="post-title">
    <a href="/posts/04/" class="post-title-link">文章标题</a>
  </h2>
  ...
</article>

这样可以避免嵌套链接,也让标题链接和标签链接各自承担清晰的交互职责。

第二轮细化:表格对齐改用 class

Markdown 表格对齐通常会生成类似这样的内联样式:

<th style="text-align: center;">居中</th>

为了继续收紧 CSP,这次在解析阶段把这些内联样式转换为 class:

<th class="align-center">居中</th>
<td class="align-right">右对齐</td>
<td class="align-left">左对齐</td>

对应样式统一写在 CSS 中:

.post-content .align-left { text-align: left; }
.post-content .align-center { text-align: center; }
.post-content .align-right { text-align: right; }

这样最终 HTML 不再需要 style 属性。

第二轮细化:CSP 去掉 unsafe-inline

因为模板内联样式和表格内联对齐都已经清理,部署配置里的 CSP 也可以进一步收紧。

之前:

style-src 'self' 'unsafe-inline'

现在:

style-src 'self'

这意味着页面只允许加载同源 CSS 文件,不再允许任意内联样式。结合前面已经设置的 script-src 'none',整个站点的运行时约束更清晰。

这次还顺手处理了几个视觉细节:

  • 归档列表加入和文章列表一致的左侧 hover 高亮条;
  • 代码块移除了 macOS 红黄绿点,保留语言标签,减少视觉噪音;
  • 代码块顶部留白缩小,正文节奏更紧凑;
  • 暗色模式下正文图片不再默认降低透明度,避免影响截图、图表和插图的可读性;
  • Footer 链接从背景块改为更轻的文本链接反馈;
  • 文章正文列表移除每个列表项的虚线和 hover 背景,长列表阅读更安静;
  • 锚点偏移改为 --header-offset--header-offset-mobile 变量,便于后续统一调整。

这些改动都不改变内容结构,但会让页面更像一个稳定的文档站,而不是交互组件堆叠出来的界面。

第三轮收尾:更安静的文档站体验

第二轮之后,又继续做了一次偏细节的收尾,目标是让页面更接近“文档站”的气质:信息层级清楚、视觉噪音更少,并且在空数据和移动端场景下也有完整表现。

首先修复了示例文章里的坏图。markdown/02.md 原本会实际渲染:

![Logo](/logo.png)

但仓库里并没有对应的 /logo.png。现在这段只作为 Markdown 语法示例保留在代码块中,不再生成一个失效图片请求。重新构建后,HTML 和 RSS 输出里也不再包含这个坏图。

移动端导航也做了收紧。小屏下 header 不再强制变成占用较高的纵向块,而是让站点标题和导航在空间允许时保持同一行,导航项可以横向换行。到更窄的 600px 以下时,再回到更自然的左对齐换行布局。这样 375px 宽度下顶部区域更轻,正文能更早进入视野。

代码块继续弱化了装饰感:移除了默认阴影和 hover 阴影,只保留边框、浅背景和右上角语言标签。hover 时仅轻微改变边框颜色,长文阅读时不会因为代码块卡片感太重而打断节奏。

标签详情页的“所有标签”入口也从按钮感改成了轻量文本链接。它仍然方便返回标签总览,但不再和文章标题、文章列表争夺视觉权重。

Footer 则进一步简化,只保留三类信息:

  • 版权;
  • RSS 订阅;
  • 返回顶部。

构建时间或自定义 footer 文案不再输出到页面底部。对于这个博客当前的方向来说,Footer 更像文档页脚,而不是功能区。

最后补上了统一空状态。首页或标签页没有文章时,会显示“还没有发布文章”或“这个标签下还没有文章”;归档为空时显示“还没有可归档的文章”;标签列表为空时显示“还没有任何标签”。这些提示统一使用 empty-state 样式,保证模板在极端数据情况下也不会显得残缺。

小结

这次优化没有改变博客的信息架构,也没有引入新的功能模块。它更像一次视觉语言的整理:把动效收回来,把间距和圆角统一起来,把界面元信息做得更安静。

对于一个以 no-JS 和静态文档为目标的博客来说,稳定、克制、一致的 UI,比复杂交互更重要。第二轮细化后,站点不仅看起来更统一,HTML 和 CSP 也更干净。