@ceynri/astro-toc-rail
v0.1.0
Published
A restrained, PC-only rail-style table of contents for long-form Astro articles, with 2D scroll progress and a reading percentage.
Maintainers
Readme
@ceynri/astro-toc-rail
面向长文的 PC 端「轨道式」文章目录(TOC rail),带二维滚动进度与阅读百分比。
English: see README.md
收起态是一组贴右侧、垂直居中的纯线条——文章标题的 minimap;鼠标移入时向左展开为文字目录浮层。随页面滚动呈现二维进度(哪些章节在屏、各看了多少),并显示整篇阅读百分比。
定位:克制、不打扰阅读的页内导航 + 进度感知。
特性
- 收起态轨道:H2 线最长,逐级变短(H3…H6),右对齐使短线左缘自然内缩,形成层级缩进观感。存在感低。
- hover 浮层:向左展开覆盖在正文之上,文章 H1 作卡片标题头;当前在屏章节高亮;点击目录项平滑滚动跳转。
- 二维进度:把当前视口窗口投影到正文,得到一段可跨多条线的高亮带(边界线按可见比例部分填充);高亮带中点在轨道内垂直居中(头尾钳制)。
- 阅读百分比:以视口垂直中线为焦点、正文范围为基准计算。
- 可配置采集深度:组件 prop 与文章 FrontMatter 逐文覆盖。
- 渲染门槛:标题数过少不渲染;视口宽度低于断点不挂载。
- CSS 变量主题:不硬编码颜色。
- 不绑定统计实现:派发 DOM
CustomEvent,由消费侧自行接入分析/埋点。
环境要求
- Astro
^5 || ^6。 - 标题
id由 Astro 内置的rehypeHeadingIds生成,无需额外的 slug 插件。
安装
pnpm add @ceynri/astro-toc-rail这是源码直发的包(发布 .astro + .ts,无构建步骤),由 Astro/Vite 随你的应用一起编译。
用法
---
import { render } from 'astro:content';
import { TocRail } from '@ceynri/astro-toc-rail';
const post = Astro.props.post;
const { title, tocDepth, toc } = post.data;
const { headings } = await render(post);
---
<article data-toc-body>
<Content />
</article>
{toc !== false && (
<TocRail headings={headings} title={title} maxDepth={tocDepth} />
)}要点:
- 给正文容器加
data-toc-body:它界定阅读百分比与高亮的基准范围(排除封面图、评论区、页脚等)。 headings直接取自 Astro 的render()。
Props
| Prop | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| headings | MarkdownHeading[] | — | render() 返回的标题(含全部层级,组件内部过滤) |
| title | string | — | 文章标题(H1),作浮层标题头 |
| maxDepth | number(2–6) | 3 | 采集到的最深标题层级。把文章 FrontMatter 的值传进来即可逐文覆盖 |
| minHeadings | number | 2 | 标题数低于此值时整组件不渲染 |
| breakpoint | number | 1024 | 视口宽度低于此值时不挂载组件 |
| bodySelector | string | [data-toc-body] | 正文范围选择器 |
| label | string | Table of contents | nav 地标的 aria-label;非英文站点可覆盖为本地语言 |
| class | string | — | 透传到根 <nav> |
H1 不入轨道、不计入采集、不引入缩进层级,仅作浮层标题头。
事件(埋点 / 分析接入)
组件不调用任何统计库,而是派发冒泡的 DOM CustomEvent,由你接入自己的埋点。节流/去重是消费方的职责——组件每次都如实上报原始信号。
| 事件 | detail | 触发时机 |
| --- | --- | --- |
| toc:anchor-click | { slug: string } | 点击目录项 |
| toc:rail-hover | — | hover 展开轨道(每次 hover 都派发) |
// 只要从 '@ceynri/astro-toc-rail' 引入过任意内容,e.detail 即有类型提示。
document.addEventListener('toc:anchor-click', (e) => {
window.umami?.track('toc-anchor-click', e.detail); // { slug }
});
// 每次 hover 都会触发——若埋点只想「每页一次」,自行去重:
let hoverTracked = false;
document.addEventListener('toc:rail-hover', () => {
if (hoverTracked) return;
hoverTracked = true;
window.umami?.track('toc-rail-hover');
});逐文配置(可选)
若用 FrontMatter 驱动 maxDepth / 开关,字段须在内容集合 schema 中声明,否则 zod 会将其 strip 掉:
tocDepth: z.number().int().min(2).max(6).optional(),
toc: z.boolean().optional().default(true),tocDepth:本文采集到第几级标题(2–6),传给maxDepth。toc: false:关闭本文目录(用toc !== false门控)。
主题(CSS 变量)
组件把全部 --toc-* 默认值集中声明在 :root 一处,并以 var(--toc-*) 读取。消费侧通过在 .toc-rail 上设置同名变量来覆盖。由于元素自身的值天然优先于从祖先继承的值,消费侧的覆盖必然生效——与 CSS @layer 顺序、作用域特异性都无关。
颜色变量(命名 = 元素-部位):
| 变量 | 元素 |
| --- | --- |
| --toc-rail-line-color | 收起态轨道的基础细线 |
| --toc-rail-highlight-color | 移动的二维高亮条 |
| --toc-percent-color | 阅读百分比文字 |
| --toc-panel-bg / --toc-panel-shadow | hover 浮层背景 / 阴影 |
| --toc-panel-title-color | 浮层标题头(H1) |
| --toc-panel-item-color | 普通目录项 |
| --toc-panel-item-active-color | 当前(在屏)章节项 |
| --toc-panel-item-hover-color | 鼠标悬停项 |
尺寸变量:
--toc-len-2…--toc-len-6 · --toc-line-thickness · --toc-line-gap · --toc-max-height · --toc-right · --toc-panel-width · --toc-indent
示例(绑定到自己的设计 token 以随明暗切换):
.toc-rail {
--toc-rail-highlight-color: var(--title-color);
--toc-rail-line-color: var(--border-color);
--toc-panel-item-hover-color: var(--accent-color);
}边界
- 仅 PC:低于
breakpoint不挂载。移动端目录如有需要,由消费项目另行实现。 - 轨道不响应滚轮,仅由代码自动定位。线条总高超过
--toc-max-height时轨道内部自动滚动,上下缘以渐变蒙层提示尚有内容。 - 假设为 MPA(无视图过渡)。若启用 Astro
<ClientRouter />,需在astro:page-load重新挂载、astro:before-swap清理监听器/Observer。
导出
@ceynri/astro-toc-rail→{ TocRail }及./core的全部导出。@ceynri/astro-toc-rail/toc-rail.astro→ 直接引组件文件。@ceynri/astro-toc-rail/core→ 零依赖纯逻辑(章节区间、高亮带、居中钳制、百分比),可复用来自定义目录 UI。
本地开发
仓库根即包本体;src/pages/ 下有一个小的 Astro dev 页面用于本地预览(不发布)。
pnpm install
pnpm dev # 在 http://localhost:4321 预览 dev 页面
pnpm test # 纯逻辑单测(core.ts)
pnpm typecheck # astro check
pnpm build # astro build(同时验证组件可正常集成)
pnpm check # Biome lint + format许可
MIT © Ceynri
