文章12
标签11
分类3

Typecho 目录树改造与「笔记本看不到目录」的排查

环境:Typecho 1.3.0 + nexmoe 主题,实测用 Chrome DevTools Protocol 读取真实渲染值
时间:2026-10-07 凌晨

一、起因:四条反馈

文章目录树做完之后,收到四条意见:

  1. 鼠标悬停时那块背景是直角的,要更圆润
  2. 圆点和竖连线是灰色的,应该用主题色
  3. 目录树压到正文了,没必要所有设备都显示 —— 顺带问:原主题是让所有设备都看到目录树吗?
  4. 我这台笔记本(华硕天选3,Windows 缩放 150%)看不到目录树

前三条是样式与显示范围问题,第四条是布局问题 —— 而且根因完全不在目录本身。

二、原主题的目录:确实是「所有设备都显示」

先回答疑问。把用户最初下载的原版主题包解出来对比:

原版 post.php 里一条 @media 都没有,而且目录的定位是:

.nexmoe-post-right .nexmoe-fixed {
    position: fixed;
    z-index: 1000;
    height: 100vh;
    display: inline-block;
    top: 0;
}

top: 0 + height: 100vh —— 从屏幕最顶端开始、占满整屏高度。

结论:原版就是所有设备都显示,在手机上还会直接盖住正文。 这是原版主题的设计缺陷,不是后加的。

所以这次的改法是:只在桌面宽屏显示,手机 / 平板 / 窄窗口一律不显示。

三、圆角与配色

3.1 圆角

.wb-toc a {
    border-radius: 4px;    /* 改前:几乎看不出圆角 */
    border-radius: 10px;   /* 改后:明显的胶囊感 */
}

3.2 圆点和竖连线改用主题色

改前的写法是「跟随文字颜色」:

.wb-toc a::before {
    background: currentColor;   /* 文字是灰的,所以圆点也是灰的 */
    opacity: .35;
}
.wb-toc ul ul::before {
    background: rgba(0,0,0,.09);   /* 竖连线:写死的淡灰 */
}

改成直接取主题色变量:

.wb-toc a::before {
    background: var(--n-theme);
    opacity: .55;
}
.wb-toc ul ul::before {
    background: rgba(var(--n-theme-rgb), .35);
}
.wb-toc a:hover {
    color: var(--n-theme);
    background: rgba(var(--n-theme-rgb), .1);
}
.wb-toc a.active {
    color: var(--n-theme);
    background: rgba(var(--n-theme-rgb), .16);
}

关键前提:主题色变量定义在 :root 上:

<style id="nexmoe-theme-color">
:root {
    --n-theme: <?php echo $__nThemeColor; ?>;
    --n-theme-rgb: <?php echo $__nThemeRgb; ?>;
}
</style>

因为是 :root,固定定位(position: fixed)的元素也能继承到,所以目录可以直接用主题色。用户以后改主题色,目录会自动跟着变。

实测确认:

目录项 border-radius : 10px
圆点 background-color : rgb(255, 78, 106)
竖连线 background-color : rgba(255, 78, 106, 0.35)

四、压到正文 + 笔记本看不到:同一个根因

4.1 先量,不要猜

用 Chrome DevTools Protocol 把文章容器整条结构链的盒子信息全部读出来:

var el = document.querySelector('#nexmoe-content .nexmoe-post');
while (el && el.tagName !== 'BODY') {
    var r = el.getBoundingClientRect(), cs = getComputedStyle(el);
    // 记录 rect / margin / padding / width / position
    el = el.parentElement;
}

读出来的结果(视口 1280):

< html >      width=1265px
< body >      width=1100px  padding-left=240px   ← 病灶在这里
#nexmoe-content   width=860px
.nexmoe-primary   width=860px
.nexmoe-post      width=820px   rect: 343→1163

4.2 根因

主题把 body 硬编码成固定宽度:

body {
    width: 1100px;        /* 固定,不是 100% */
    padding-left: 240px;  /* 左侧留给侧栏 */
    margin: 0 auto;       /* 在视口里居中 */
}

所以:视口再宽,正文右侧的留白也只有 (视口 - 1100) / 2。

  • 1920 视口 → 右侧 402px,目录放得下
  • 1536 视口 → 右侧 245px,勉强
  • 1280 视口 → 右侧只有 118px,放不下

1280 是什么概念?Windows 缩放到 150% 的 1920 屏,CSS 视口宽度正好是 1280。这就是笔记本看不到目录的原因。

4.3 第一次尝试(失败):把文章整体左移

思路很自然:左侧那 240px 看起来是空的,把文章左移就能腾出右侧空间。

/* 第一版(错误方案) */
body.nexmoe-single #nexmoe-content {
    left: max(-150px, min(0px, calc((100vw - 1580px) / 2)));
}

数据上看是成功的:1280 视口下右侧余量从 118px 变成 232px,目录 200px 宽显示正常。

但截图一出来就露馅了:文章左移后,撞上了左侧的「文章分类 / 标签云」侧栏 —— 文章标题和正文左侧被侧栏盖住了一半。

原因:那 240px 并不是空白,它是 .nexmoe-secondary 侧栏的位置,只是在 1024px 以下才会 display: none。

教训:改布局一定要截图看整体,只量「目标元素」的数值会漏掉相邻元素的冲突。

4.4 最终方案:让文章在窄屏下收窄

既然不能左移,就换个方向:把文章本身变窄一点,两侧各让出一部分空间。文章保持居中,右侧就稳定空出一块。

@media screen and (min-width: 1024px) {
    body.nexmoe-single .nexmoe-post {
        max-width: min(820px, max(640px, calc(100vw - 720px)));
        margin-left: auto;
        margin-right: auto;
    }
}

宽度随视口线性变化:

视口文章宽度
≥1540820px(保持原样,大屏完全不受影响)
1440720px
1366646px
≤1280640px(下限)

只作用于文章页 / 独立页(body.nexmoe-single),首页和归档页不受影响。

4.5 实测结果

用调试协议逐档量(13 档,从 1920 到 390):

视口正文位置目录结论
1920663→14831503→1703(宽 200)不重叠,间距 20px
1600503→13231343→1543(宽 200)不重叠
1536473→12891309→1509(宽 200)不重叠
1440473→11931213→1413(宽 200)不重叠
1366473→11191139→1339(宽 200)不重叠
1280(150% 缩放笔记本)433→10731093→1271(宽 178)不重叠
1200 及以下—不显示已隐藏
手机 390—不显示已隐藏

另外还校验了一条硬约束:左侧常驻的抽屉图标条占视口 0~80px,正文左边缘必须大于 82px —— 全部档位最小是 305px,安全。

五、显示门槛的最终参数

var MIN_W = 130, MAX_W = 200, GAP = 20, EDGE = 10;
if (window.innerWidth < 1152) { wrap.style.display = 'none'; return; }

var avail = window.innerWidth - postRect.right - GAP - EDGE;
if (avail < MIN_W) { wrap.style.display = 'none'; return; }
wrap.style.width = Math.round(Math.min(MAX_W, avail)) + 'px';
wrap.style.left = Math.round(postRect.right + GAP) + 'px';
wrap.style.display = 'block';   // 必须显式写 block

配合 CSS 的媒体查询(两份样式表都要写,因为暗色模式是替换样式表、不换 body 类):

@media screen and (max-width: 1279px) {
    .wb-toc-wrap { display: none !important; }
}

改完后的完整效果:

  • ≥1280px 视口:显示目录,宽度 178~200px 自适应
  • ≤1200px:不显示
  • 圆点、连线、悬停底色全部跟随主题色
  • 暗色模式单独一套(文字浅白 + 主题色点缀)

六、经验总结

  1. 「元素异常」先量再改。用调试协议读 getBoundingClientRect() 和 getComputedStyle(),比反复改代码让用户上传试错快得多。
  2. 量一个元素不够,要看整条结构链。这次的根因在 <body> 上,只看 .nexmoe-post 永远找不到。
  3. 改完必须截图看整体。第一次的「左移」方案数据全对,但视觉上撞了侧栏 —— 数据对不等于效果对。
  4. 别再踩 style.display = '' 这个坑:它只会清掉内联样式,反而回落到 CSS 里的 display: none。
  5. 主题色变量定义在 :root 时,position: fixed 的元素也能继承,不用重复注入。
  6. 响应式阈值要对着真实场景定。Windows 常见缩放下:1920@150% = 1280、1920@125% = 1536、1440@125% = 1152 —— 定断点时要按这些实际值算,而不是按屏幕分辨率。

0 评论