Skip to content

Latest commit

 

History

History
181 lines (121 loc) · 10.9 KB

File metadata and controls

181 lines (121 loc) · 10.9 KB

Starlight Customizations

This doc explains every way we've extended or patched Starlight. There are six distinct layers, ordered roughly from most to least invasive.

1. patch-package — Direct node_modules patches

Trigger: postinstall in package.json runs npx patch-package after every npm install.

Patch files live in patches/. These are the nuclear option — used when Starlight gives us no other extension point.

patches/@astrojs+starlight+0.36.0.patch

Three hunks against three different files:

components/SidebarSublist.astro — Sidebar icons

Starlight has no native API for per-item icons in the sidebar. We inject ~/components/SidebarIcon.astro (Lottie animations) before each sidebar label:

-<span>{entry.label}</span>
+<span>{entry.icon && <SidebarIcon {...entry.icon} />}{entry.label}</span>

The icon property comes from sidebar entry attrs set in our sidebar generation logic.

integrations/remark-rehype-utils.ts — Remark/rehype pipeline scope

Starlight's remark/rehype processing originally bails out for any file not inside the docs collection. We widen the guard to also process partials/ and changelog/:

-if (!normalizePath(file.path).startsWith(docsCollectionPath)) return false;
+if (
+  !normalizePath(file.path).startsWith(docsCollectionPath) &&
+  !normalizePath(file.path).includes("/src/content/partials/") &&
+  !normalizePath(file.path).includes("/src/content/changelog/")
+) return false;

Without this, heading transforms, tab components, etc. don't run inside partials or changelogs.

user-components/Tabs.astro — Injectable icon component

Makes the Icon component inside <Tabs> replaceable via prop, so callers (e.g. TypeScriptExample) can swap in a custom icon renderer:

+IconComponent?: typeof Icon;
-const { syncKey } = Astro.props;
+const { syncKey, IconComponent = Icon } = Astro.props;
 ...
-{icon && <Icon name={icon} />}
+{icon && <IconComponent name={icon} />}

patches/@astrojs+starlight-docsearch+0.6.0.patch

DocSearch.astro — Keyboard shortcut hint UI

We disable the / shortcut in DocSearch (re-routing it to the sidebar search input instead). The original button always rendered a / slash icon. The patch makes the hint adaptive:

  • Default: renders styled <kbd>-style key badges (Ctrl K)
  • Only shows the slash SVG icon when / is enabled AND Ctrl/Cmd+K is disabled

The slash icon CSS is moved from a static <style> block into a JS conditional that runs after docsearch() initializes and reads options.keyboardShortcuts.

index.ts — Schema extension

Adds keyboardShortcuts to the DocSearch Zod config schema so the above is type-safe:

keyboardShortcuts: z.object({
	"Ctrl/Cmd+K": z.boolean().optional(),
	"/": z.boolean().optional(),
}).optional();

Upgrade note: Both patches are pinned to exact package versions (starlight@0.36.0, starlight-docsearch@0.6.0). Upgrading either package requires regenerating the patch with npx patch-package @astrojs/starlight after manually re-applying the changes.

2. Vite alias — Page.astro (non-overridable slot)

Location: astro.config.tsvite.resolve.alias

Starlight doesn't expose Page.astro in its official components: override map. We shim it at the Vite bundler level:

vite: {
  resolve: {
    alias: {
      "./Page.astro": fileURLToPath(new URL("./src/components/overrides/Page.astro", import.meta.url)),
      "../components/Page.astro": fileURLToPath(new URL("./src/components/overrides/Page.astro", import.meta.url)),
    },
  },
},

Both alias paths are needed because different internal Starlight files import Page.astro using different relative depths.

What src/components/overrides/Page.astro does:

It wraps Starlight's original Page.astro and mutates the starlightRoute locals before passing them through:

  • ToC regeneration — calls generateTableOfContents(html) against the fully-rendered HTML instead of using Starlight's AST-derived ToC. This means our custom heading transforms (slugs, shift-headings) are reflected correctly.
  • Sidebar replacement — calls getSidebar(Astro) to build a product-specific sidebar, replacing Starlight's static autogenerated one.
  • Pagination — flattens the custom sidebar and computes prev/next from it, respecting data-group-label attrs for section labels.
  • Description generation — if frontmatter.description is set, renders it through Markdown. If not, extracts a description from the rendered HTML.
  • lastUpdated suppression — sets data.lastUpdated = undefined when a reviewed date is present, so only one recency signal shows in the UI.
  • Full-width layout — injects --sl-content-width: 67.5rem on pages without a ToC.

3. Starlight components: overrides

Registered in astro.config.ts under starlight({ components: { ... } }). All files are in src/components/overrides/.

These are overrides that use the official Starlight components: API. See STARLIGHT_OVERRIDES.md.

4. Starlight plugins:

Registered in astro.config.ts under starlight({ plugins: [...] }).

Plugin When active Notes
starlightLinksValidator RUN_LINK_CHECK=true only Validates internal links at build time. Long exclude list for dynamic routes, API paths, wildcard patterns. errorOnInvalidHashes and errorOnLocalLinks both disabled.
starlightDocSearch Always Algolia DocSearch via clientOptionsModule: "./src/plugins/docsearch/index.ts" — sets app ID/key, rewrites result URLs to current origin, adds "View all results" footer, disables the / keyboard shortcut.
starlightImageZoom Always Enables the zoom functionality consumed by MarkdownContent.astro.
starlightScrollToTop Always Custom double-chevron SVG path, progress ring (white), tooltip "Back to top", hidden on homepage.

5. Starlight routeMiddleware

File: src/plugins/starlight/route-data.ts Registered: astro.config.tsstarlight({ routeMiddleware: "..." })

Runs on every route via defineRouteMiddleware. Validates and normalizes the tags frontmatter array:

  1. For each tag, searches the allowedTags allowlist (from src/schemas/tags.ts) for a case-insensitive match against label or any variants.
  2. If found, replaces the raw tag string with the canonical label.
  3. If not found, throws a build error with a link to the style guide.

This is why you can write javascript or JavaScript in frontmatter and it gets normalized to the canonical casing before it hits the UI or search index.

6. Expressive Code plugins

File: ec.config.mjs

Starlight's code block rendering runs through Expressive Code. We register six plugins:

Plugin File Purpose
pluginWorkersPlayground src/plugins/expressive-code/workers-playground.js Adds "Open in Workers Playground" button to eligible code blocks
pluginOutputFrame src/plugins/expressive-code/output-frame.js Custom "output" frame type for terminal output blocks
pluginDefaultTitles src/plugins/expressive-code/default-titles.js Auto-assigns language-based titles (e.g. "TypeScript") to blocks without an explicit title=
pluginCollapsibleSections @expressive-code/plugin-collapsible-sections Upstream plugin enabling collapse ranges in code blocks
pluginGraphqlApiExplorer src/plugins/expressive-code/graphql-api-explorer.js Adds "Open in GraphQL Explorer" button to graphql blocks
pluginLineNumbers @expressive-code/plugin-line-numbers Upstream plugin; line numbers are off by default (showLineNumbers: false)

Other config:

  • Themes — Cloudflare's custom solarflare-theme dark and light VSCode themes
  • curl aliascurl blocks are highlighted as sh
  • extractFileNameFromCode: false — disables auto-extracting titles from code comments
  • Style overrides — 1px border radius 0.25rem, adjusted text marker luminance for light/dark

Remark/Rehype pipeline

These aren't Starlight-specific but affect how MDX content is processed. Registered in astro.config.tsmarkdown:

Plugin File Purpose
remarkValidateImages src/plugins/remark/validate-images Build-time validation that image paths resolve
rehypeMermaid src/plugins/rehype/mermaid.ts Converts mermaid code blocks to diagrams
rehypeExternalLinks src/plugins/rehype/external-links.ts Adds target="_blank" rel="noopener" to external links
rehypeHeadingSlugs src/plugins/rehype/heading-slugs.ts Custom slug generation for heading IDs
rehypeAutolinkHeadings src/plugins/rehype/autolink-headings.ts Injects anchor links next to headings (used by MarkdownContent.astro CSS)
rehypeTitleFigure rehype-title-figure (upstream) Wraps images with title attrs in <figure>/<figcaption>
rehypeShiftHeadings src/plugins/rehype/shift-headings.ts Shifts heading levels (e.g. H1→H2) for content that starts at H1

These run via Starlight's markdown.headingLinks: false (disabled — we handle anchor links ourselves via rehypeAutolinkHeadings).