# 本页目录

本页目录列出当前页面的标题，读者滚动时高亮正在阅读的那一节。和侧边栏一样，你把标题整理成树，再用 `Toc` 组件渲染出来。

## 取出标题

Astro 的 `render()` 会返回页面的标题列表。在路由文件里把它传给布局：

```astro
---
// src/pages/docs/[...slug].astro
const { Content, headings } = await render(Astro.props.entry);
---

<Docs {...Astro.props} headings={headings}>
  <Content />
</Docs>
```

`headings` 是一个扁平的数组，比如 `[{ depth: 2, slug: 'install', text: '安装' }, ...]`。

## 整理成树

`tocTree` 把扁平的标题列表变成树，h3 放在它前面的 h2 下面：

```ts
import { tocTree } from '@minidocs/astro/runtime';

const toc = tocTree(headings);
```

默认只保留 h2 和 h3。想包含 h4，传入层级范围：`tocTree(headings, { depth: [2, 4] })`。

## 渲染

```astro
---
import { Toc } from '@minidocs/astro/components';
---

<Toc.Root label="本页目录">
  <Toc.List>
    {toc.map(function item(heading) {
      return (
        <Toc.Item>
          <Toc.Link slug={heading.slug}>{heading.text}</Toc.Link>
          {heading.children.length > 0 && <Toc.List>{heading.children.map(item)}</Toc.List>}
        </Toc.Item>
      );
    })}
  </Toc.List>
</Toc.Root>
```

每个组件的参数见[组件与样式](/docs/reference/components.md#%E6%9C%AC%E9%A1%B5%E7%9B%AE%E5%BD%95)。

放在 `Layout.Aside` 里时，本页目录在中等宽度下会收进一个按钮，见[页面布局](/docs/guides/layout.md)。

## 高亮当前小节

读者滚动时，正在阅读的那一节的链接会带上 `aria-current="true"`，默认样式会在它旁边显示一个标记。

一个标题滚过页面顶部的阅读线后，它就是当前小节。阅读线的位置取标题的 `scroll-margin-top`，也就是点击目录跳转后标题停下的位置，所以跳转和高亮总是一致的。用了 `Layout` 组件时，默认样式已经设好了这个值。自己写布局并且有固定在顶部的标题栏时，把它设成标题栏的高度：

```css
h2,
h3 {
  scroll-margin-top: 4rem;
}
```

滚到页面底部时，最后一个标题会被高亮，即使它没有到达阅读线。

## 高亮上级标题

只有当前小节的链接带着 `aria-current`。想让它的上级标题也换一种样式，用 `:has()` 选出包含当前项的 `<li>`：

```css
.md-toc-item:has([aria-current]) > .md-toc-link {
  font-weight: 600;
}
```
