这次给博客换样式,起点是一份叫作 Cerâmica Wabi-Sabi 的设计说明。里面没有现成页面,也没有可以直接套用的组件,只有一组很明确的约束。陶土色、石灰色、米白纸面、炭灰文字,边角要利落,阴影要轻,版面需要留出足够多的空处。页面还要带一点手工痕迹,像陶器表面的细纹,或者金缮留下的那条亮线。

原来的站点用 Hexo 和 Landscape 主题,文章已经有二十五篇。首页、正文、归档、分类和标签都由主题模板生成。直接改 node_modules 很省事,下一次安装依赖时也会一起消失。最后采用的办法是在仓库里放一套本地同名主题。Hexo 会优先读取 themes/landscape,文章和站点配置可以原样保留,模板与样式则全部接管。

这个选择把改动关在了主题层。旧文章没有跟着改格式,public 目录也继续只当构建结果使用。以后执行一次 hexo generate,整站就会按新样式重新生成。

先把颜色压下来

这套风格的主色是陶土棕 #A0522D。它出现在印章、按钮、标题强调和文章卡片的细边上。页面底色用了偏暖的米白 #FAF0E6,正文文字则是炭灰 #36454F。这几个颜色放在一起,视觉会自然靠近纸、泥土和烧制后的陶面。

金色 #D4AF37 用得很少。首页陶罐上的裂纹、标题下面的断线、页脚上方的一小段修补痕迹都由它来画。金色一旦铺得太多,很快就会变成首饰店。这里把它限制在线条和局部标记里,亮度够用,也不会抢走文章。

石灰色负责日期、说明文字和边框,浅粉色只在分类侧栏里留了一点底色。蓝色保留给正文链接。颜色各有固定工作,页面看起来才不会像临时拼出来的色卡。

所有颜色都收进 CSS 自定义属性。后面调一处,首页、正文和归档会一起变化。

1
2
3
4
5
6
7
:root {
--clay: #A0522D;
--cream: #FAF0E6;
--charcoal: #36454F;
--stone: #8B8680;
--gold: #D4AF37;
}

字体决定了页面说话的声音

标题和正文都选了 Cormorant Garamond。它的拉丁字形细长,笔画有明显的粗细变化,放在大标题里很像旧书页。中文部分依次回退到宋体。两种字形放在同一句标题里,会有一点不完全整齐的感觉,正好符合这套主题。

代码、日期和小标签改用 JetBrains Mono。等宽字体只占很小一块,它让技术信息与正文分开,又不会把整页带回常见的开发者文档样式。

首页标题刻意做得很大。桌面端一眼能看到“把复杂的问题,慢慢烧成答案”,右边是一只用 CSS 画出的陶罐。到了手机上,字号会收下来,文字自然换行,陶罐落到标题下面。浏览器在 390 像素宽度下检查过,页面没有横向滚动。

那只陶罐没有图片

首页最显眼的物件是一只陶罐。它由几个普通元素组成。罐口是一段压扁的椭圆,罐身用 clip-path 切出略微失衡的轮廓,两条金色细线交叉穿过表面。旁边还留了一行竖排小字。

没有图片以后,颜色和比例都能跟着响应式规则变化,也少了一次资源请求。陶罐的轮廓没有追求对称。左右高度差一点,底部也略微偏斜,手工感就从这些小偏差里出来了。

背景纹理用了很淡的 SVG 噪点。它固定在页面底层,透明度很低。仔细看能发现纸面并不平,正常阅读时又不会觉得脏。卡片仍然保持直角,只加一层很轻的阴影和细边框。陶器可以圆,承载文字的界面仍要稳。

首页留出呼吸,文章列表保持节奏

首页顶部用了左右不等宽的两列。左边放标题和一句说明,右边放陶罐。首篇文章单独占一块较大的纸面,其余文章进入十二列网格。

文章卡片没有排成整齐的三等份。有的占七列,有的占五列,下一行再交换位置。内容依旧沿着清楚的阅读顺序往下走,边缘却不会形成过分规整的棋盘。卡片悬停时只向上移动三个像素,阴影稍微加深,没有发光,也没有夸张缩放。

原来的首页会把每篇文章全文展开。二十五篇文章里有大量代码,一页读起来很重。新模板直接从正文提取短摘要,首页只留标题、日期、分类和一小段开头。读者决定继续看,再进入正文页。

正文页要先照顾阅读

文章页保留了宽阔的标题区。日期、预计阅读时间和分类放在标题下面,再用三段断开的金线收住。正文区域控制在七百八十像素左右,旁边有一条窄栏,显示日期和字数。

这条窄栏在桌面端会跟随阅读位置,手机上则回到正文上方。它提供信息,也参与版面平衡。正文宽度没有被它挤得太窄。

旧文章里有三百多个代码围栏,代码块必须认真处理。新的代码区使用炭灰底色和米白文字,行号单独放在更暗的一栏。长代码可以横向滚动,不会把整个页面撑宽。正文里的行内代码保持浅色底,只用陶土色强调。

图片、引用、列表、分隔线和多级标题也分别加了样式。二级标题左侧有一条轻微倾斜的陶土色短线,引用块用淡粉底和金色边。它们都在提醒读者内容层级,没有把阅读变成装饰展览。

归档和分类各有自己的秩序

归档页采用两列布局。左边按年份列出文章,右边放分类和常用标签。日期使用等宽字体,标题仍然保持较大的衬线字。鼠标经过一篇文章时,右侧的小箭头轻轻移动,其他位置不动。

分类页和标签页增加了各自的总览入口。每张卡片显示名称与文章数量,宽度按五列和七列交替。进入某个分类或标签以后,页面回到时间列表,同时保留分类侧栏。这样既能从主题出发浏览,也能继续按年份寻找旧文。

404 页面也没有沿用浏览器默认的空白提示。巨大的 404 被一条金线穿过,旁边写明页面可能被移动或改名,并给出回到首页的按钮。它仍然是一个错误页,只是没有突然离开整套视觉语言。

手机上的导航需要真的能用

屏幕窄于 768 像素以后,多列布局全部收成单列。桌面导航换成右上角按钮,打开后由炭灰色面板覆盖整个可视区域。按钮同步更新 aria-expanded,菜单打开时锁住页面滚动,按下 Escape 可以关闭。

页面还放了“跳到正文”链接,键盘聚焦时才会出现。所有可操作元素都有清楚的焦点轮廓。文章页顶部的阅读进度只改变宽度,入场效果只使用透明度和位移。系统开启减少动态效果以后,这些动画会缩到近乎静止。

这些处理看起来都不大,缺一处就会在真实使用时露出来。主题写完以后,我用 Chrome 检查了首页、文章、归档、分类、标签和 404,也单独打开了分类与标签详情页。八类路由在 390 像素视口下都没有横向溢出,移动菜单的打开和关闭状态也与辅助属性一致。最后一次干净构建生成了六十八个文件,其中六十六个是 HTML 页面,本地资源链接全部能找到。

这套主题没有增加前端框架。模板继续用 EJS,样式交给 Stylus,交互脚本只有四十多行。代码量不算少,大部分都花在页面之间的统一和窄屏细节上。

现在再看这个博客,它仍然装着原来的文章,目录和构建命令也没变。只是纸面换了颜色,标题有了新的声音,代码旁边多出一些像手工修补过的痕迹。到这里就够了。