npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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.

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