目录
aside.ak-toc:一份 DOM、三种形态,不做第二份目录。皮肤设 toc: false 关掉 MW 的内联目录,自己用 data-toc(或脚本扫正文标题)渲染这一份。
≥1400 右侧导轨:绝对定位进
.ak-main右内边距让出来的那条导轨,.ak-toc__inner粘性跟随滚动。标题是 overline 小标签 + 2px 活动主色条;下面一道 2px 的阅读进度条;当前小节(scrollspy)= 青色左条 + 青字加粗;层级只靠缩进(竖线只画在最外层);长标题截断。导轨限高在底部让出--ak-toc-clear(默认 = 右下角.ak-fab占的 76px),目录长到出内滚时末几项不会被回到顶部按钮盖住;页面没有那枚按钮可设回var(--ak-space-2)。640–1400 按钮 + 浮层:目录收成一枚独立的按钮
label.ak-toc-btn——与右下角「回到顶部」同款的 44px 反色方块、同一列,图标是方点列表。它平时停在正文纸张的右上角、顶边与纸张的上框线平齐,往下滚就贴在页眉下沿跟着走(Vector 2022 的目录按钮:标题旁 → 滚过后钉在角上);目录是从它下面拉出的浮层,右缘同按钮,宽 360px 定宽,高最多到视口底(100dvh),超出只在浮层里内滚。浮层没有遮罩,开着时页面照常滚(滚轮在浮层上滚到头也带动页面)。首项「回到顶部」;浮层开着时.ak-fab让位,<1120 起.ak-fab不再出现、只剩这一项(右侧已经有一枚目录按钮)。≤639 手机:入口留在页眉那一行——品牌右侧、搜索之前的方点列表图标(
label.ak-local-nav__toc),不出浮动按钮。浮层固定在页眉下沿、拉满宽度(左右各留一个 gutter),开着时锁住页面滚动。
为什么是独立按钮,不放进页眉
以前 <1400 的入口都是页眉里的「本页目录 ⌄」,浮层贴着页眉下沿拉下。它和外观 / 通知 / 用户菜单挤在同一簇,读起来像站点级的控件,而目录属于这一页。现在 640–1400 的入口挪回页面里,页眉在 1120–1400 与 ≥1400 完全相同。
手机(≤639)不跟着改:44px 的方块在 390 宽的屏上压住正文第一行的行尾,滚动时一直挡着右上角;页眉那一行本来就全是图标,目录图标(方点列表)留在里面不占正文。
按钮不是 position: fixed,而是挂在一条零高度的粘性锚 .ak-toc-dock 上(position: sticky,粘在页眉下沿再往下 12px 处,紧跟页面标题):
- 页面顶部它落在纸张右上角;
fixed的话会压住页眉正下方的状态指示器 / 动作簇(「更多」就在那一角)。 - 浮层绝对定位在 dock 里,跟着按钮走,不用另算位置。
- dock 的层级比下拉低一档(
--ak-z-dropdown − 1):「更多」卡片盖得住它,正文里的粘性表头在它之下。
没有 JS 也能开合
.ak-toc-dock > input.ak-toc-cb + label.ak-toc-btn + aside.ak-toc 三者是兄弟,开合就是 .ak-toc-cb:checked ~ .ak-toc——纯 CSS,不需要 :has(),旧内核的手机浏览器、无 JS 都是真浮层。手机上页眉里的 label.ak-local-nav__toc[for=ak-toc-toggle] 开合的是同一个 checkbox(一个控件可以有多个 label),浮层照样靠 :checked ~ 显示;只有它自己的「开着」变色与焦点框够不着 checkbox,走 html.ak-toc-open / body:has()。checkbox 视觉隐藏但可 Tab 聚焦、空格切换,焦点框画在按钮上;按钮只有图标,label 里不放字——无障碍名写在 checkbox 的 aria-label 上,悬停提示是 label 的 title。
JS 只负责生成条目、scrollspy、阅读进度,以及这些收尾:
- 点 dock 外 / Esc / 跳转后 / 回到 ≥1400 时收起(「dock 外」要放行页眉里那个 label:否则这里先收起,label 随后派发给 checkbox 的那次 click 又把它点开);
- 把状态镜像到
html.ak-toc-open,让右下角的.ak-fab让位、让手机上页眉里的目录图标变色(它们与 checkbox 不是兄弟;无 JS 时body:has(.ak-toc-cb:checked)桥接,两条选择器必须分开写——选择器列表里混进不认识的:has()会让整条规则作废); - 浮层限高:dock 还没贴住页眉时比页眉低一截,JS 把这一截写进 dock 的
--_y(开着时随滚动更新),浮层底才不探出视口。无 JS 时它是 0,页面顶部打开长目录会探出去一截——页面没锁,往下滚一点就全在视口里了; - ≤639 开着时锁页面滚动。
皮肤按核心 data-toc 渲染的目录里,带子节的一级项右侧有折叠钮 button.ak-toc__toggle(标题多时默认收起子节):收着是 +、展开是 −,与侧栏树形态的切换钮、Panel 是同一个记号——两条渐变拼的,不依赖字体;强制色下自己取系统色。
scrollspy 取「基准线以上最后一个标题」(参考 VitePress / Docusaurus),基准线 = 每个标题自己的 scroll-margin-top:点目录跳到哪项就一定亮哪项;页顶不高亮,页底高亮最后一项;收起的折叠块 / 标签页里的标题跳过。
结构
<div class="ak-toc-dock"> <!-- ≥1400 不占高度;<1400 粘性锚 -->
<input type="checkbox" id="ak-toc-toggle" class="ak-toc-cb" aria-label="本页目录" aria-controls="ak-toc">
<label class="ak-toc-btn" for="ak-toc-toggle" title="本页目录"></label> <!-- 仅 640–1400;只有图标 -->
<aside class="ak-toc" id="ak-toc" aria-labelledby="ak-toc-label">
<a class="ak-toc__top" href="#">…回到顶部</a> <!-- 仅 <1400 -->
<div class="ak-toc__inner">
<div class="ak-toc__title" id="ak-toc-label">目录 · Contents</div>
<div class="ak-toc__progress" aria-hidden="true"><i></i></div> <!-- 宽度 = --_p -->
<ul class="ak-toc__list" data-toc>…</ul> <!-- li.is-active 为当前小节 -->
</div>
</aside>
</div>手机上的入口在页眉的 .ak-local-nav 里(见页眉):
<label class="ak-local-nav__btn ak-local-nav__toc" for="ak-toc-toggle" title="本页目录"></label> <!-- 仅 ≤639 -->页面没有目录时 .ak-toc-dock 与页眉里的这个 label 都不输出(.ak-layout--no-toc 只藏得掉前者)。
CSS
| 块 | 元素 __ | 修饰 -- |
|---|
