一切我想告诉你的事。
“EchoLens”到底是什么
“EchoLens”是一个专注于打造优质阅读体验,记录站长所思所闻所感所想的博客网站,全名为“EchoLens观响”。通过透镜观察我大脑中不断凝聚成思想和文字的回声——这就是观响。
做这个网站的原因
我原本喜欢在微信小号朋友圈发一些自己想分享的东西,不过近期感到越来越不好意思在朋友圈发这些东西了,一是考虑到不是所有人都愿意在朋友圈看到这些内容,二是自身原因,早有开一个个人博客的想法,所以就立马着手干了起来。
“EchoLens”的特点以及整体设计思路
开门见山,我应该很自豪地列出我为EchoLens的优质体验打磨的优点:
- 拥有科学,美观,进行过深度优化的中文排版。
- 设计了许多提高阅读专注力的设计。
- 对横竖屏甚至自由比例窗口进行优秀适配。
- 不设用户名的“回声评论区”,让你的每一次“回声”传递给每个人。
- 东方典雅设计美学,优雅且统一的动画。
- Markdown编辑,排版相当统一。
“EchoLens”阅读体验层面的优化在开发过程中占了很大比重,我查阅了许多资料,反复更换方案测试,最终敲定了这一套我钦定的最适合中文阅读的设计。我的开发路径严格遵守最初为网站前端定的设计优先度:
- 阅读界面文字字体,字号,字体颜色,字距行距,一行该显示多少文字应该受到最高级别重视。
- 好看,好用,稳定且不影响阅读的侧边栏。
- 增强观感体验和不费眼的优雅动画。
- 好看,能降低视觉疲劳,增强阅读欲望的背景。
- 良好的横竖屏适配。
一个专注于中文阅读的网站一定要做好阅读体验和页面交互,我浏览了大量个人博客,我发现其中大多页面做得都很现代化,很美观,但是阅读方面确实不敢恭维,横竖屏适配做的也不多。既然要做,那就一下把要做的东西都做好吧。
下面我把整个设计过程按那个优先度顺序复述一遍,顺便把一些关键的代码片段贴出来——不是想炫技,而是想让你看到"我为什么这么决定"。
一、第一优先级:把“中文阅读”做对
中文在网页上的呈现,长期以来是被低估的话题。大部分博客只关心“字够不够大”,不关心字面、行距、字距、标点位置、孤字断行——而这些恰恰决定了一篇长文读起来累不累。
我为正文专门写了一个 .prose-elegant 样式,把所有“看似无关紧要、其实决定阅读体验”的细节一次性都加进来:
.prose-elegant {
/* 字体:思源宋体 Regular。宋体本身有完整 hinting,1080p 屏不糊 */
font-family: var(--font-serif);
font-weight: 400;
/* 字号 17.2px:研究显示屏幕中文正文的“清晰甜点”在 17~18px */
font-size: 1.075rem;
/* 行高 1.95:17.2px × 1.95 ≈ 33.5px,宋体的中宫加这个行距,
呼吸感舒展但不松散 */
line-height: 1.95;
/* 字间距 0.01em:CJK 字形本身有留白,
太多 tracking 会让“字”散成“笔画” */
letter-spacing: 0.01em;
/* OpenType 特性:palt 让中文标点压成半角,节奏更紧凑、更“印刷感” */
font-feature-settings: "kern" 1, "liga" 1, "calt" 1, "palt" 1;
}
光把字调好还不够,真正让中文“像印的”而不是“像打的”,是这四条规则:
.prose-elegant {
/* 1. 行首避头:句号、逗号不允许出现在行首(GB/T 15834 强制规定) */
line-break: strict;
/* 2. 行末标点悬挂:句末标点挂到右边距外,让版心右齐
—— 现代杂志 / 书籍的中文排版美学 */
hanging-punctuation: allow-end;
/* 3. 标点间距修剪:自动去掉行首标点前的“半字”空间 */
text-spacing-trim: trim-start;
/* 4. CJK 智能断词:浏览器按“语义短语”换行,不把词组拆散 */
word-break: auto-phrase;
}
这四条规则里,有的需要 Chrome 119+ 才支持。我没有为了照顾老浏览器把这些功能砍掉——因为这套优化是网站的核心。老浏览器降级到普通排版,能用;新浏览器拿到完整体验,赢麻。
为了让首段第一眼就有“读印刷品”的感觉,我做了真·首字下沉:
.prose-elegant p:first-of-type::first-letter {
float: left;
margin: 0.1em 0.12em 0 0;
color: var(--accent); /* 朱红 */
font-weight: 700;
font-size: 3.4em; /* 约占 3 行高 */
line-height: 0.88;
}
引用块我特地换成楷体(霞鹜文楷),和正文的宋体形成对比——一个是骨,一个是肉:
.prose-elegant blockquote {
border-left: 2px solid var(--accent);
padding-left: 1.4rem;
font-family: var(--font-kai); /* 楷体,书卷气 */
font-size: 1.05rem;
line-height: 1.9;
/* 引用也要继承标点四件套,不然节奏断了 */
font-feature-settings: "kern" 1, "liga" 1, "calt" 1, "palt" 1;
line-break: strict;
hanging-punctuation: allow-end;
}
光是这一部分的样式,我前后改过不下二十次。每一次都是“读着读着觉得哪里不对”,然后回去微调一两个像素或几个百分点。
二、第二优先级:侧栏要好用,但不能抢戏
文章页的侧栏要承担三件事:看到自己读到哪了、想跳到哪一节、想回去。三件事都要做到,又都不能抢正文的注意力。
阅读进度的计算我没用现成的库,自己写了一段——**用“视口底端当前覆盖到的文章位置 / 文章总高”**来算,比单纯用 "scrollY" 要准:
function updateProgress() {
const article = document.getElementById("article-content");
const rect = article.getBoundingClientRect();
const articleTop = window.scrollY + rect.top;
const articleBottom = articleTop + article.offsetHeight;
// 视口底端“已经覆盖到”的文章内部位置
const covered = window.scrollY + window.innerHeight - articleTop;
const total = articleBottom - articleTop;
const value = (Math.min(covered, total) / total) * 100;
setProgress(Math.max(0, Math.min(100, value)));
}
目录高亮我也想了很久。一开始用“标题进入视口就高亮”,结果你来回滚一下,整篇目录像在抖。后来改成屏幕中心线规则——只有屏幕中线“压”到某一段时,那一段才高亮:
const centerY = window.scrollY + window.innerHeight / 2;
let current = headings[0];
for (const heading of headings) {
const top = window.scrollY + heading.getBoundingClientRect().top;
if (top <= centerY) current = heading;
else break;
}
setActiveHeading(current.id);
这样不管你怎么滚,高亮都是稳的,不会乱跳。
视觉上我没用现代风的“卡片化侧栏”,而是仿了线装书的栏线——侧栏左侧一条贯穿到底的细线,模块之间用更细的横线隔开:
/* 侧栏左侧的细线:用半透明深色,背景越深越可见、越浅越淡,
天然“随页面变色”,不需要 JS */
.article-side::before {
position: absolute;
top: 0; left: 0; bottom: 0;
width: 1px;
background: color-mix(in srgb, var(--muted-ink) 40%, transparent);
}
这里有一个我踩了好久的坑:"sticky" 侧栏想跟着滚,得让它的外层容器拉伸到正文那么高,否则 "sticky" 一下就滑出去了。解法是把外层定成 "absolute" 并显式给 "top: 0; bottom: 0":
.article-side-wrap {
position: absolute;
top: 0;
bottom: 0; /* 关键:拉伸到容器底,sticky 才能跨整篇文章保持可见 */
left: 0;
width: 200px;
}
.article-side {
position: sticky;
top: 2.5rem;
}
这一步调试用了我一个晚上。
三、第三优先级:动画要优雅,但不能“哇塞”
动画是这个站很多人留意到的部分,但我自己在写的时候提醒自己最多的一句话是:动画的存在感越低越好。它应该像呼吸,而不是像烟花。
路由切换:模糊过渡的取舍
我给路由切换加了模糊淡入的过渡(使用 "framer-motion"),但在文章页这种长内容上一跑,如果过度使用 "filter: blur" 很容易掉帧甚至在 PC 端失效。经过无数次测试,我将模糊半径和过渡曲线调校到了最平衡的状态,确保在手机浏览器和电脑 "Edge" 端都能获得优雅的景深过渡体验。
// app/template.tsx
<motion.div
initial={{ opacity: 0, filter: "blur(8px)" }}
animate={{ opacity: 1, filter: "blur(0px)" }}
transition={{ duration: 0.35, ease: "easeOut" }}
>
{children}
</motion.div>
音乐播放器:克制的交互
首页有一个常驻的音乐播放器。如果是常规设计,它可能会占据屏幕底部的整条空间,但在移动端,这会严重干扰阅读。因此我采用了自适应折叠形态——在屏幕较窄时,它会默认缩成一个极其小巧的旋转唱片图标;只有当你点击或者鼠标悬停时,才会丝滑展开完整的信息和控件。这也是“不抢戏”哲学的体现。
标题飞行与正文展开
点首页文章卡片时,标题会从卡片位置“飞”到文章的 H1 位置。这个效果我全部通过 "transform" + "opacity" 完成,完全跑在 GPU 合成层上,不触发布局也不触发重绘。正文展开也是按“自然行”计算后依次淡入,视觉上像是在纸面上渐渐浮现。
四、第四优先级:背景要暖、要安静,且高度统一
我不喜欢纯白底色——长时间盯着会刺眼。也不喜欢纯灰底——显得冷淡。
最后选了一个很淡的米黄渐变 + 一层不可见的纸张噪点,让屏幕看起来像一张被灯光照着的宣纸:
body {
background:
/* 顶部一团暖色光晕,像灯光从上方落下 */
radial-gradient(circle at 50% -10%,
rgba(217, 185, 141, 0.18), transparent 32rem),
/* 主体米黄到深黄的渐变 */
linear-gradient(180deg, var(--paper) 0%, #efe4c5 100%);
}
在诸如“关于”、“联系”弹窗以及全局“歌单抽屉”的设计上,我高度统一了毛玻璃美学。全部统一使用 80% 透明度底色搭配 "backdrop-blur-md",让不同模块间的交互保持绝对一致的质感。全局页首(Header)的高度和边距也在各个页面中保持了严格的对齐。
五、第五优先级:多形态内容呈现与横竖屏适配
我自己平时会把笔记本竖过来读长文。市面上极少有博客考虑到这个场景——它们要么写死 1024px 断点,要么干脆只为手机 / 桌面两种宽度做。我用了一条复合媒体查询,实现了屏幕窄 或 屏幕竖,二选一就走抽屉模式,宁可激进一点切到抽屉,也不能让侧栏撞到正文。
后来我不再局限于单一的“长文章”,而是引入了 "Portal"(门面)和 "Daily"(日常)版块。
- Portal 主页:像一张名片,用简洁的排版融合了关于、联系、近期文章与个人歌单。
- Daily 日常:用纯 CSS 原生瀑布流(
"columns-1 md:columns-2 lg:columns-3")实现了图文混排,不需要借助繁重的第三方"masonry"库就能完美自适应不同屏幕尺寸。
/* 纯 CSS 瀑布流,利用 columns 实现,避免 JS 计算 */
.daily-grid {
columns: 3;
column-gap: 2rem;
}
.daily-card {
break-inside: avoid; /* 防止卡片被硬生生切断 */
}
六、音乐管理:“拿来主义”与绝对控制权
对于“首页音乐”功能,我实现了一套“半自动化”的后台管理逻辑。 我可以输入一串网易云音乐的链接,后端会通过爬虫接口直接抓取封面、歌曲名、专辑信息以及音频链接。但这还不够。
即便抓取了外部数据,我依然为后台开发了拖拽排序与内联编辑的能力:
- 我可以在后台直接给歌曲更换自定义名称或修改歌手信息,而不是死板地照搬网易云的数据。
- 我加了排序功能,可以自由调整歌曲播放列表的上下顺序。
- 如果外部音频防盗链失效,我甚至可以点击上传按钮,把一首本地的
".mp3"或".flac"覆盖上去,做到随时备份与托管。这是我想要的“拿来主义”和“绝对控制权”的结合。
七、回声评论区:不留名字,慢一点
普通博客评论区是“快速、自由、所有人能看”。我反过来做:不让填昵称、必须经审核、文案叫“回声”。
// 提交评论的表单——只有内容字段,没有昵称、邮箱
<textarea
value={content}
maxLength={500}
placeholder="说点什么..."
/>
服务端收到后默认 "approved: false",访客看不到,需要我手动放行。提交成功后弹一个温柔的小窗告知“正在审核”——而不是一句冷冰冰的 "Submitted"。这种节奏不快,但避免了垃圾评论、避免了情绪化对线、保留了留言文化最珍贵的部分。
八、Markdown 写作:让排版不操心
我自己写文章时,最不想做的事就是“调格式”。所以管理后台只接受一种输入:纯 Markdown。
渲染端用 "react-markdown" 把它变成 HTML,并给每个 "h2" / "h3" 打 ID 用作目录锚点。排版样式全靠 ".prose-elegant" 那一套——意思就是不管哪篇文章、谁写的、写多长,最后呈现出来的视觉永远是一致的。我只需要专注内容。
九、写在最后
整个站的工程量并不大,但每一个细节我都打磨过不止一遍。
它不会被很多人看见。
但如果你恰好打开了它,能在某一篇文章里安静地读完一段话,没有被弹窗、广告、动画、横竖屏的不适感打断——
那这个站就值了。
把生活里的微光,慢慢安放。
纸张摊开,让回响落定。
