跳转到内容

02 · Starlight Hero 定制

本站文档站首页(/,对应 src/content/docs/index.mdx)使用 Starlight 的 splash 落地页 + Hero 区域。实现上采用官方推荐的 约定式配置:内容写在 MDX frontmatter,渲染逻辑在 astro.config.mjs 注册一次组件覆盖,页面里不需要 import Hero

主题说明链接
页面布局 template: splash无侧栏宽版,适合首页/404Customizing Starlight — Page layout
hero frontmatter标题、标语、图片、按钮Frontmatter Reference — hero
组件覆盖 components.Hero全局替换默认 HeroOverriding Components
覆盖项列表可覆盖的组件名(含 HeroOverrides Reference
Astro.locals.starlightRoute覆盖组件内读取当前页数据Route Data
默认 Hero 源码对照 fork 的起点starlight/components/Hero.astro
Astro 官方示例更复杂的 Hero 定制withastro/docs Hero.astro

先分清两件事:首页文件 ≠ Hero 组件

Section titled “先分清两件事:首页文件 ≠ Hero 组件”
flowchart TB
  subgraph content["内容层(MDX)"]
    Index["src/content/docs/index.mdx<br/>template: splash + hero:"]
  end

  subgraph config["配置层(一次性注册)"]
    Astro["astro.config.mjs<br/>components.Hero → Hero.astro"]
  end

  subgraph render["渲染层"]
    SL["Starlight 布局"]
    Hero["src/components/starlight/Hero.astro"]
  end

  Index -->|"frontmatter 数据"| SL
  Astro -->|"覆盖默认 Hero"| Hero
  SL --> Hero
  Hero -->|"Astro.locals.starlightRoute.entry"| Index
角色路径作用
首页内容src/content/docs/index.mdx声明 titletemplate: splashhero(标语、图、按钮);路由为文档站根路径
Hero 实现src/components/starlight/Hero.astro全站替换 Starlight 内置 Hero;凡带 hero: 的页面都会走这份实现(本站目前只有首页)

因此:Hero.astro 不是「首页文件」,而是 Starlight 的 可覆盖插槽;首页只是通过 frontmatter 消费 它。

约定式配置:为何 MDX 里不用 import

Section titled “约定式配置:为何 MDX 里不用 import”

Starlight 的 Hero 与 Header、Footer 一样,属于 集成级组件覆盖,在 starlight({ components: { … } }) 里注册即可。

// astro.config.mjs(节选)
starlight({
components: {
Header: './src/components/Header.astro',
Footer: './src/components/Footer.astro',
Hero: './src/components/starlight/Hero.astro',
},
})

数据流:

  1. 某篇 MDX 的 frontmatter 含 hero:(通常配合 template: splash)。
  2. Starlight 布局在页面顶部渲染 已注册的 Hero 组件。
  3. Hero.astro 通过 Astro.locals.starlightRoute.entry 读取该页的 data(含 herotitledraft 等)。

这与在 MDX 里写 import Hero from '…' 再手动挂载 不是同一条路;后者适用于正文里的 MDX 组件(见 Using Components),不用于替换 Starlight 布局插槽。

首页 frontmatter:内容与布局分离

Section titled “首页 frontmatter:内容与布局分离”
src/content/docs/index.mdx
---
title: 与光同行
template: splash # 宽版、无文档侧栏与正文 TOC
next: false # 隐藏系列「下一篇」
hero:
tagline: 记录个人学习笔记
image:
file: ../../assets/logo.svg
alt: generated
actions:
- text: 书签导航
link: /bookmarks/nav/
icon: right-arrow
- text: 博客
link: /blog/
icon: right-arrow
- text: 随机主题
link: '#random-theme'
variant: minimal
---

Page layout:默认文档页是 doc(侧栏 + 目录),splash 去掉侧栏,给 Hero 更大横向空间。404 页也常用同一模板(Custom 404 page)。

类型定义见 Frontmatter — hero / HeroConfig

字段本站用法
title省略时用页面 title(「与光同行」)
tagline副标题 HTML
image.file + image.alt见下文「generated 约定」
image.dark / image.light官方支持明暗双图;本站用 generated 路径未启用
image.html原始 HTML/SVG 插槽
actions[]textlinkiconvariantattrslink 可映射为自定义行为

改首页文案、按钮链接:只改 index.mdx,无需动 Hero.astro(除非要新增交互类型)。

本站实现以 Starlight 默认 Hero.astro 为基底,保留网格布局、LinkButtonastro:assetsImage@layer starlight.core 样式,并增加三处 项目约定

  • 文件夹src/components/starlight/
    • Hero.astro
  • 文件夹src/content/docs/
    • index.mdx
  • 文件夹src/components/
    • GeneratedLogo.astro
  • 文件夹src/theme/
    • HeroRandomThemeButton.tsx
const { data } = Astro.locals.starlightRoute.entry
const { title = data.title, tagline, image, actions = [] } = data.hero || {}
Section titled “2. 图片:alt: generated → 动态 Logo”

官方 image 支持 filedark/lighthtml。本站约定:image.alt === 'generated' 时不走 Image,改为渲染 GeneratedLogo(描边 SVG,随全站主题色变化)。

const useGeneratedLogo = image?.alt === 'generated'
// …
{useGeneratedLogo && <GeneratedLogo class="hero-logo" />}

index.mdx 仍保留 file: ../../assets/logo.svg 以满足 schema;实际展示由 GeneratedLogo 负责。这是 frontmatter 与覆盖组件之间的私有约定,不在 Starlight 官方 schema 内。

3. 按钮:#random-theme → React 岛屿

Section titled “3. 按钮:#random-theme → React 岛屿”

actions 默认渲染 Starlight 的 LinkButton@astrojs/starlight/components)。本站对 link === '#random-theme' 做分支,挂载客户端组件:

if (href === '#random-theme') {
return <HeroRandomThemeButton client:load />
}

HeroRandomThemeButton 调用 randomThemeCustomizerState(),只随机 Primary / Neutral / Radius,不改 Color Mode(实现见 src/theme/HeroRandomThemeButton.tsx)。
#random-theme 同样是 项目内约定 URL,不会真的跳转。

保留 Starlight 虚拟组件 DraftContentNoticevirtual:starlight/components/DraftContentNotice),在 data.draft 时显示草稿条。

样式写在组件内 <style>@layer starlight.core,与默认 Hero 一致,避免打乱 Starlight 层级。大屏为 7:4 两列(文案左、图右),小屏单列居中——逻辑与上游默认 Hero 相同。

能力Starlight 默认本站 Hero.astro
数据来源data.hero相同
图片file / dark+light / html上述 + alt: generated → GeneratedLogo
操作按钮全部 LinkButtonLinkButton + #random-theme → React
草稿支持 DraftContentNotice保留
注册方式内置components.Hero 覆盖
  1. 只改文案/链接
    编辑 src/content/docs/index.mdxhero / title / tagline / actions

  2. 改 Hero 布局或样式
    编辑 src/components/starlight/Hero.astro 的 markup 或 @layer starlight.core 样式。

  3. 新增一种「特殊按钮」
    actions 里约定新的 link 值(如 #foo),在 Hero.astroactions.map 里分支渲染对应组件。

  4. 换 Logo 策略
    去掉 alt: generated,改用官方 filedark/light;或扩展新的 alt 约定。

  5. 仅首页用定制 Hero
    Hero.astro 顶部判断 Astro.locals.starlightRoute.id === ''(或 slug),非首页 import Default from '@astrojs/starlight/components/Hero.astro' 渲染默认实现(需查当前 Starlight 版本是否导出默认组件,或复制默认 markup)。

  • 顶栏主题Header.astro + ColorThemeSelect / ThemeCustomizerPopover(另一套覆盖),与 Hero 内「随机主题」共用 src/theme/ 状态,但入口不同。
  • 书签 / 管理端:不在 Starlight 布局内,不走 Hero;首页 actions 链到 /bookmarks/nav//blog/
问题答案
Hero.astro 是首页吗?否;首页是 index.mdx,Hero 是全局覆盖的渲染组件。
要在 MDX 里 import Hero 吗?不需要;用 hero frontmatter + components.Hero 注册。
内容写在哪?index.mdxtemplate / hero / actions
逻辑写在哪?src/components/starlight/Hero.astro(及 GeneratedLogoHeroRandomThemeButton)。

frontmatter 提供 data.heroHero.astro 通过 Astro.locals.starlightRoute.entry 读取并渲染。

相关: 01 · 站点导航 · 随机主题见 src/theme/HeroRandomThemeButton.tsx