Skip to content

编辑文档

本教程适合刚接触 Starlight 的文档作者,带你从零开始学会写页面、用组件、配导航。


在开始写作之前,先了解一下 Starlight 项目的文件都放在哪里。你不需要记住所有文件,重点关注下面这几个就够了:

你的项目/
├── astro.config.mjs # Astro 配置文件(包含 Starlight 集成和侧边栏配置)
├── src/
│ ├── content/
│ │ ├── docs/ # ★ 你的所有文档页面都放在这里
│ │ │ ├── guides/
│ │ │ │ ├── 01-getting-started.md
│ │ │ │ └── 02-advanced.md
│ │ │ └── index.mdx
│ │ ├── i18n/ # 可选:国际化翻译数据
│ │ └── content.config.ts # 内容集合配置文件
│ ├── assets/ # 图片、Logo 等资源
│ └── components/ # 自定义组件
└── public/ # 静态资源(字体、图标、PDF 等,不会被 Astro 处理)

关键规则:src/content/docs/ 里的每一个 .md 或 .mdx 文件都会自动变成网站上的一个页面。用子文件夹来组织文件,就会自动生成对应的 URL 路径。

举个例子,文件结构这样放:

src/content/docs/
├── hello-world.md → your-site.com/hello-world
└── reference/
└── faq.md → your-site.com/reference/faq

在 src/content/docs/ 下新建一个 .md 文件,比如 my-first-page.md,然后写上下面的内容:

---
title: 我的第一个页面
description: 这是一个示例页面,用来学习 Starlight 的基本写法。
---
这是页面的正文内容。你可以在这里写任何想说的话。

注意:每个页面都必须在文件开头的 --- 之间提供一个 title(标题)。这个标题会显示在页面顶部、浏览器标签页和页面元数据中。

title 是唯一必填的字段。其他常用字段包括:

---
title: 页面标题 # 必填
description: 页面的简短描述 # 用于 SEO 和搜索结果
slug: custom-url # 自定义 URL(可选)
editUrl: https://github.com/... # 编辑此页面的链接(可选)
tableOfContents: false # 是否显示右侧目录(默认 true)
---

Starlight 支持完整的 Markdown 语法。以下是你最常用的几种写法:

这是 **粗体**,这是 *斜体*,这是 ~~删除线~~。
这是 `行内代码`。

不要用 # 写一级标题。Starlight 会自动把 frontmatter 中的 title 作为页面的一级标题。你的正文应该从 ## 开始:

## 二级标题(会出现在右侧目录中)
### 三级标题(也会出现在目录中)
#### 四级标题(不会出现在目录中)
[链接文字](https://example.com)
![图片描述](./images/screenshot.png)

图片放在 src/content/docs/ 旁边的位置,或者放在 src/assets/ 里都可以。图片描述(alt text)对屏幕阅读器很重要,不要省略。

旁白用来在正文旁边显示辅助信息,是 Starlight 的一个特色功能。语法是用一对 ::: 包裹内容:

:::note
这是一个普通提示。
:::
:::tip
这是一个小技巧。
:::
:::caution
注意!这里可能会踩坑。
:::
:::danger
危险!请谨慎操作。
:::

你还可以自定义标题和图标:

:::tip[你知道吗?]{icon="heart"}
自定义标题和心形图标。
:::

建议:caution 和 danger 不要滥用。如果你发现自己频繁使用它们,可能说明内容需要重新组织一下。

> 这是块引用,通常用来引用其他人的话。
```javascript
// 代码块,开头指定语言
const greeting = "Hello, Starlight!";
```txt

当 Markdown 不够用的时候,可以把文件后缀改成 .mdx,就能在文档里使用组件了。

Starlight 提供了许多内置组件,从 @astrojs/starlight/components 包导入即可:

---
title: 使用组件
---
import { Card, CardGrid, LinkCard, Tabs, TabItem } from '@astrojs/starlight/components';
<CardGrid>
<LinkCard
title="Starlight 官网"
href="https://starlight.astro.build/"
description="官方文档和指南"
/>
<LinkCard
title="Astro 文档"
href="https://docs.astro.build/"
description="Astro 框架的完整文档"
/>
</CardGrid>
组件 用途 简单示例
<Card> / <CardGrid> 卡片布局 <Card title="标题">内容</Card>
<LinkCard> 链接卡片 <LinkCard title="..." href="..." />
<Tabs> / <TabItem> 标签页切换 <Tabs><TabItem label="Tab 1">...</TabItem></Tabs>
<Steps> 步骤列表 <Steps><li>第一步</li></Steps>
<Aside> 旁白(等价于 :::) <Aside type="tip">...</Aside>
<Badge> 徽章标签 <Badge text="New" variant="tip" />
<FileTree> 文件树展示 <FileTree><ul><li>src/</li></ul></FileTree>

4.3 OreUI 交互组件:选择题和复选任务清单

Section titled “4.3 OreUI 交互组件:选择题和复选任务清单”

本站提供基于 Astro 官方文档源码移植的交互组件,外观已适配 OreUI。可先打开交互组件示例体验选择、提交答案和勾选任务。

本站以 Spectrollay-OreUI/OreUI 为主体,沿用原有配色、按钮、面板和交互组件样式。全站样式通过 astro.config.mjs 的 customCss 引入 src/styles/oreui.css,选择题和复选任务清单的样式位于 src/components/tutorial/tutorial.css。

katorlys/OreUI 的 oreui-web@0.1.0-beta.1 依赖作为补充保留,仅按需使用组件样式,不全局加载整库。目前已复用其原生 Progress Bar,用于首次加载和站内页面跳转时的 LOADING 动画;后续按具体需要逐步采用其按钮等组件。引入时应限制作用范围,避免覆盖主体主题。第一库及交互逻辑来源采用 MIT 许可,第二库采用 MPL-2.0,来源与许可说明见 src/styles/vendor/oreui/README.md。

页面加载动画由 src/components/PageLoading.astro 和 Starlight 的 PageFrame 插槽统一接入,文档、首页和图集页面自动生效,无需逐页添加。LOADING 使用 public/B_Minecrafter.Reg(主标题).ttf,通过字体预加载和 document.fonts.load() 检查就绪状态:遮罩及进度条立即显示,文字在指定字体就绪后显示,避免备用字体闪烁;字体请求失败时使用备用字体并正常收起加载层。进度仅表示正在加载,不显示下载百分比,首次进入时使用不透明的全屏遮罩,加载完成后淡出;站内跳转时使用 65% 不透明度的背景遮罩,文字和进度条本身保持不透明,进入时淡入、完成后淡出。淡入为 180ms,淡出为 240ms,跨页面通过一次性的跳转标记延续遮罩类型。页内锚点、外部链接和新标签页操作不触发动画,减少动态效果偏好下停止持续推进和淡入淡出,无 JavaScript 时不显示遮罩。直接输出原始 HTML 的独立 Secret 页面保持原文件内容,不注入加载层。

跨页面加载层的连续衔接使用浏览器原生的跨文档 View Transitions 作为渐进增强:支持该接口且未开启“减少动态效果”时,旧页面的遮罩和文字快照会保留到新文档准备好,再对快照本身执行 240ms 淡出;进度条独立参与过渡,显示新页面中可更新的进度,加载完成时先填满,再与遮罩和文字同步淡出,避免冻结在旧快照中的中间进度;其他浏览器继续使用普通整页跳转和原有淡入淡出。该增强不接管路由、不复用文档 DOM,也不改变选择题等组件的脚本生命周期。字体就绪 promise 会通过 pagereveal 的 waitUntil() 纳入过渡,字体加载失败时仍能结束过渡。若浏览器报告过渡不可用或发生异常,页面会回退到普通加载流程。

这些组件需要在 .mdx 页面中使用。当前「编辑文档」仍是 .md 文件,因此这里的代码用于说明,实际交互放在示例页中。无需添加 client:load 等指令,也无需安装 React 或 Vue。

下面的导入路径适用于 src/content/docs/技术文档/文档站/ 下的 MDX 文件;放到其他目录时,需要按页面位置调整相对路径。

import MultipleChoice from '../../../../components/tutorial/MultipleChoice.astro';
import Option from '../../../../components/tutorial/Option.astro';
import Box from '../../../../components/tutorial/Box.astro';
<Box icon="question-mark">
## 哪个后缀支持导入组件?
<MultipleChoice>
<Option>
<span>.md</span>
<span slot="feedback">普通 Markdown 不支持直接导入组件,再试一次。</span>
</Option>
<Option isCorrect>
<span>.mdx</span>
<span slot="feedback">回答正确,MDX 支持导入组件!</span>
</Option>
</MultipleChoice>
</Box>

MultipleChoice 是单选题容器;每个 Option 是一个选项,一道题应只为一个选项设置 isCorrect。选中选项后,「检查答案」按钮才会启用。提交后会显示结果,重新选择可再次作答。可选的 feedback 插槽用于填写该选项的解释,不填写时使用默认中文反馈。同一页面可以放多道题,选项分组互不影响。

import Checklist from '../../../../components/tutorial/Checklist.astro';
import Box from '../../../../components/tutorial/Box.astro';
<Box icon="check-list">
## 发布前检查
<Checklist key="publish-checks">
- [ ] 已设置页面标题。
- [ ] 已检查图片和链接。
- [ ] 已在本地预览。
</Checklist>
</Box>

任务项使用 Markdown 的 - [ ] 语法,组件标签与任务列表之间保留空行。勾选状态按页面完整路径 + 清单 key保存在当前浏览器的 localStorage 中,刷新后会恢复;不会上传服务器,也不会跨浏览器同步。禁用 JavaScript 时清单保持不可交互。

同一页面上的每个清单必须使用不同且稳定的 key。改变页面路径或 key 会建立新的进度记录;改变某组任务数量会重置该组状态。避免重排任务后沿用原记录,因为勾选状态按任务顺序保存。

可以用 alternative 插槽提供另一组完成方式:

<Checklist key="review-method">
- [ ] 已自行检查文档。
<div slot="alternative">
- [ ] 已请维护者协助检查。
</div>
</Checklist>

两组之间显示「或者」;任意一组全部勾选,存储逻辑就将这个清单判定为完成。当前组件没有额外显示全站教程进度条。

Box 用于包裹题目或任务清单,也可独立使用。icon 支持 question-mark(问题)、check-list(清单)和 puzzle-piece(练习)。组件源码位于 src/components/tutorial/,统一样式位于该目录的 tutorial.css,官方 MIT 许可和来源说明也保存在同目录。

如果组件样式和 Starlight 默认的 Markdown 样式冲突,给组件加上 not-content 类就能禁用默认样式:

<div class="not-content">
<!-- 这里的内容不会应用 Starlight 的默认 Markdown 样式 -->
</div>

默认情况下,Starlight 会根据 src/content/docs/ 的文件结构自动生成侧边栏。如果需要手动控制,可以在 astro.config.mjs 中配置:

astro.config.mjs
import starlight from '@astrojs/starlight';
export default defineConfig({
integrations: [
starlight({
sidebar: [
{
label: '入门指南',
items: [
{ label: '快速开始', slug: 'guides/getting-started' },
{ label: '进阶用法', slug: 'guides/advanced' },
],
},
{
label: '参考资料',
autogenerate: { directory: 'reference' },
},
],
}),
],
});

说明:

  • slug 对应 src/content/docs/ 下的文件路径(不含后缀名)
  • autogenerate 会自动扫描指定目录下的所有页面
  • label 是显示在侧边栏中的文字

大多数情况下你只需要写 .md 或 .mdx 文件就够了。但如果需要完全自定义布局,可以在 src/pages/ 下创建 .astro 文件:

src/
├── content/docs/ # 常规 Markdown 页面
│ └── hello-world.md
└── pages/
├── custom.astro # 自定义页面
└── archived.html # 也可以放 HTML

要在自定义页面中使用 Starlight 的布局和样式,用 <StarlightPage> 组件包裹内容即可。

主页的“列车战绩”按钮进入 /train/,该页面不加入侧边栏。查询主页和玩家详情均隐藏页面左侧导航。玩家详情使用 /train/player/?name=玩家名,可以直接分享链接。

  • 页面路由:src/pages/train/index.astro、src/pages/train/player.astro。
  • 共用界面:src/components/TrainStats.astro;交互逻辑:src/lib/train-page.ts。
  • 接口地址、指标名称和数值格式:src/lib/train-api.ts。目前直接请求 https://train.lista233.cn/ 的 JSON 接口,无须 Worker 代理。更换地址时,需要确保 HTTPS 可用,并允许网站域名跨域访问;本地测试也需要允许开发服务器的来源。
  • 样式:src/styles/train.css,只负责布局和站点主题衔接;按钮、标签页、表格、原生选择框、输入框、折叠区域等复用 oreui-web。筛选框放在表格外,避免弹出菜单与滚动容器发生冲突。

累计榜默认每页显示 10 人、最低参赛 2 局。主页提供每页 10 / 20 / 50 人和最低 2 / 5 / 10 局的快捷选项;高级查询可输入自定义整数:每页人数 1–100,最低参赛 1–1000 局。这两个上限是页面侧限制。自定义值会加入快捷下拉中,并随 URL 保留,刷新和从详情返回均不会丢失。切换筛选条件回到第 1 页。累计榜和日统计直接使用后端 page / limit 分页,以及当前指标的 total / totalPages 显示总人数和总页数,不再额外请求下一页探测。页码越界时按接口总页数调整到最后一页;无数据时回到第 1 页。游玩时长榜不受最低参赛局数限制。误杀相关榜单按数值从高到低展示。日统计以接口返回日期为准,每日 04:00 划分统计日。

查询主页的“高级查询”复用 oreui-web/modal 的原生 dialog.ore-modal,可组合统计范围、排行指标、日期、每页人数和最低参赛局数;填写玩家名时改查个人战绩,隐藏不适用的排行条件。弹窗打开时带入当前条件,点击“查询”才提交并回到第 1 页;“重置”只修改弹窗中的条件,“取消”、Esc 或点击遮罩不会提交。日统计日期留空时由接口选择当前统计日。弹窗的表单布局在手机端改为单列,内容可纵向滚动。

Modal 居中在 src/styles/train.css 中通过 .train-page dialog.ore-modal { margin: auto; } 保持。生产构建中 OreUI 的组件样式层可能被 Starlight 的重置样式层覆盖,不能只依赖库中的 :where(dialog.ore-modal) 自动外边距。tests/train-modal.test.mjs 对构建后的静态页面验证深浅色及桌面、手机的居中和视口范围;构建后设置 PLAYWRIGHT_MODULE 为已安装 Playwright 的模块 URL,再运行该测试。

手机端隐藏次要表格列,点击玩家名进入详情。详情顶部为累计总览,下方为累计战绩和日统计;从日榜进入时保留日期。遇到同名多个账号,累计详情按 UUID 分开展示,而排行榜和日统计仍遵循接口按玩家名合并的口径。

玩家名输入框(普通查询、玩家详情和高级查询)复用 OreUI Dropdown 的菜单及选项样式,通过 src/components/TrainPlayerInput.astro 和 src/lib/train-suggestions.ts 补充输入联想交互。输入停止 200ms 后请求 /api/players/search?keyword=关键词&limit=20;关键词为空时不请求。包含匹配、忽略大小写、前缀优先和去重均由后端负责,前端按接口顺序展示,不再下载排行榜名称或自行过滤排序。total 大于返回名称数时提示继续输入缩小范围。后端名称缓存约 60 秒,搜到的玩家可能没有累计统计数据,这是接口的正常行为。支持点击、方向键与 Enter 选择,Esc 收起候选;再按 Esc 可关闭 Modal。菜单使用原生 Popover,避免表格或 Modal 裁切。切换输入、失焦、关闭弹窗或离开页面会取消待发和在途请求,旧结果不会覆盖新关键词或重新展开菜单。搜索失败或无匹配时仍可手动输入完整名字查询。玩家累计详情不传职业分页参数,继续展示全部职业。

数据在打开页面、切换条件或手动刷新时加载,不定时自动刷新。刷新失败保留已有内容,仅提示“刷新失败”。页面不保存玩家数据到本地持久缓存,不包含逐局记录、赛季趋势或自制图表。


问题 解决方法
页面没有出现在侧边栏 检查文件是否在 src/content/docs/ 下
标题显示不对 确认 frontmatter 中有 title 字段
图片不显示 确认路径正确,图片描述(alt)不能为空
组件不工作 文件后缀必须是 .mdx,且组件需要 import
想隐藏右侧目录 frontmatter 中设置 tableOfContents: false

写 Starlight 文档的核心就三件事:

  1. 在 src/content/docs/ 里创建 .md 或 .mdx 文件,每个文件就是一个页面
  2. 用 frontmatter 配置页面元数据,title 是必填的
  3. 需要交互组件时用 .mdx,从 @astrojs/starlight/components 导入即可

掌握了这些,你就可以开始愉快地写文档了!