Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
b379650
WIP
delucis Mar 31, 2025
02a325b
Add `markdown.headingLinks` flag to disable the feature
delucis Apr 2, 2025
271b916
Add virtual module for optional CSS like anchor link styles
delucis Apr 2, 2025
29e01ab
Add anchor link styles
delucis Apr 3, 2025
368bc29
Fix anchor link label replacement
delucis Apr 3, 2025
43b3a8e
Remove `is:raw` attribute
delucis Apr 3, 2025
25c420d
Delete leftover example markup
delucis Apr 3, 2025
f34c491
Fix double escaping of accessible anchor labels
delucis Apr 3, 2025
f4f2f8a
Add `AnchorHeading` component
delucis Apr 4, 2025
a633ab4
Add anchor heading support to Markdoc preset
delucis Apr 4, 2025
445c6d7
More config reference detail
delucis Apr 4, 2025
4d75b05
Use Astro’s built-in heading slugger instead of `rehype-slug`
delucis Apr 4, 2025
2c60647
Update config snapshot test
delucis Apr 4, 2025
9ee3508
Fix dependency
delucis Apr 4, 2025
f4fc43d
Actually pass Astro config to headings plugin
delucis Apr 4, 2025
04f0cd1
Add French translation of accessible anchor label UI string
delucis Apr 4, 2025
28ff839
Add basic tests
delucis Apr 4, 2025
6f7347c
Remove superfluous `tabindex="-1"` in anchor link wrapper
delucis Apr 4, 2025
36d9b82
Remove unnecessary change
delucis Apr 4, 2025
2183885
Remove `rehype-autolink-headings` in favour of doing all the manipula…
delucis Apr 4, 2025
6763bbf
Bring across a few more translations from the Astro docs
delucis Apr 4, 2025
9b387f0
Move where “required” is specified in the AnchorHeading docs
delucis Apr 10, 2025
0757ea3
Add simple examples for `StarlightPage` and `AnchorHeading`
delucis Apr 10, 2025
c17af3c
Also move “required” in `frontmatter` prop heading
delucis Apr 10, 2025
7c8475b
Don’t infer `HeadingLevel` type
delucis Apr 10, 2025
7a72829
Ensure `<AnchorHeading>` output matches rehype output
delucis Apr 10, 2025
693809b
Merge branch 'main' into chris/anchor-headings
delucis Apr 10, 2025
b53da1f
Merge anchor heading and tabs tests into single file
delucis Apr 10, 2025
25fb28a
Small component refactor for readability
delucis Apr 10, 2025
e668881
Bump Starlight peer dependency version in Markdoc package
delucis Apr 10, 2025
9709134
Bump Astro peer dependency in Starlight package
delucis Apr 10, 2025
bbf898a
Add changesets
delucis Apr 10, 2025
5af8110
fix capitalisation
delucis Apr 10, 2025
a13aa9d
Merge branch 'main' into chris/anchor-headings
delucis Apr 15, 2025
9210441
Increase CSS size limit by 250 bytes
delucis Apr 15, 2025
85c9eed
Wrap anchor link CSS in `@layer starlight.content`
delucis Apr 16, 2025
9af5fae
Merge branch 'main' into chris/anchor-headings
delucis Apr 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .changeset/late-onions-sparkle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
'@astrojs/starlight': minor
---

Adds support for generating clickable anchor links for headings.

By default, Starlight now renders an anchor link beside headings in Markdown and MDX content. A new `<AnchorHeading>` component is available to achieve the same thing in custom pages built using `<StarlightPage>`.

If you want to disable this new Markdown processing set the `markdown.headingLinks` option in your Starlight config to `false`:

```js
starlight({
title: 'My docs',
markdown: {
headingLinks: false,
},
}),
```

⚠️ **BREAKING CHANGE:** The minimum supported version of Astro is now v5.5.0.

Please update Starlight and Astro together:

```sh
npx @astrojs/upgrade
```
24 changes: 24 additions & 0 deletions .changeset/quick-items-dream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
'@astrojs/starlight-markdoc': minor
---

Adds support for generating clickable anchor links for headings.

By default, the Starlight Markdoc preset now includes a default `heading` node, which renders an anchor link beside headings in your Markdoc content.

If you want to disable this new feature, pass `headingLinks: false` in your Markdoc config:

```js
export default defineMarkdocConfig({
// Disable the default heading anchor link support
extends: [starlightMarkdoc({ headingLinks: false })],
});
```

⚠️ **BREAKING CHANGE:** The minimum supported peer version of Starlight is now v0.34.0.

Please update Starlight and the Starlight Markdoc preset together:

```sh
npx @astrojs/upgrade
```
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@ pnpm-lock.yaml
# https://github.com/withastro/prettier-plugin-astro/issues/337
packages/starlight/user-components/Tabs.astro

# Prettier forces whitespace between elements we want to avoid for consistency with the rehype version
packages/starlight/components/AnchorHeading.astro

# Malformed YAML file used for testing
packages/starlight/__tests__/i18n/malformed-yaml-src/content/i18n/*.yml
19 changes: 19 additions & 0 deletions docs/src/content/docs/guides/authoring-content.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -650,3 +650,22 @@ If you already have a Starlight site and want to add Markdoc, follow these steps
</Steps>

To learn more about the Markdoc syntax and features, see the [Markdoc documentation](https://markdoc.dev/docs/syntax) or the [Astro Markdoc integration guide](https://docs.astro.build/en/guides/integrations-guide/markdoc/).

### Configuring the Markdoc preset

The `starlightMarkdoc()` preset accepts the following configuration options:

#### `headingLinks`

**type:** `boolean`
**default:** `true`

Controls whether or not headings are rendered with a clickable anchor link.
Equivalent to the [`markdown.headingLinks`](/reference/configuration/#markdown) option, which applies to Markdown and MDX files.

```js "headingLinks: false"
export default defineMarkdocConfig({
// Disable the default heading anchor link support
extends: [starlightMarkdoc({ headingLinks: false })],
});
```
59 changes: 56 additions & 3 deletions docs/src/content/docs/guides/pages.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ title: Pages
description: Learn how to create and manage your documentation site’s pages with Starlight.
sidebar:
order: 1
tableOfContents:
maxHeadingLevel: 4
---

Starlight generates your site’s HTML pages based on your content, with flexible options provided via Markdown frontmatter.
Expand Down Expand Up @@ -73,28 +75,49 @@ Read more in the [“Pages” guide in the Astro docs](https://docs.astro.build/

### Using Starlight’s design in custom pages

To use the Starlight layout in custom pages, wrap your page content with the `<StarlightPage />` component.
To use the Starlight layout in custom pages, wrap your page content with the [`<StarlightPage>` component](#starlightpage-component).
This can be helpful if you are generating content dynamically but still want to use Starlight’s design.

To add anchor links to headings that match Starlight’s Markdown anchor link styles, you can use the [`<AnchorHeading>` component](#anchorheading-component) in your custom pages.

```astro
---
// src/pages/custom-page/example.astro
import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
import AnchorHeading from '@astrojs/starlight/components/AnchorHeading.astro';
import CustomComponent from './CustomComponent.astro';
---

<StarlightPage frontmatter={{ title: 'My custom page' }}>
<p>This is a custom page with a custom component:</p>
<CustomComponent />

<AnchorHeading level="2" id="learn-more">Learn more</AnchorHeading>
<p>
<a href="https://starlight.astro.build/">Read more in the Starlight docs</a>
</p>
</StarlightPage>
```

#### Props
#### `<StarlightPage>` component

The `<StarlightPage />` component renders a full page of content using Starlight’s layout and styles.

```astro
---
import StarlightPage from '@astrojs/starlight/components/AnchorHeading.astro';
---

<StarlightPage frontmatter={{ title: 'My custom page' }}>
<!-- Custom page content -->
</StarlightPage>
```

The `<StarlightPage />` component accepts the following props.

##### `frontmatter` (required)
##### `frontmatter`

**required**
**type:** `StarlightPageFrontmatter`

Set the [frontmatter properties](/reference/frontmatter/) for this page, similar to frontmatter in Markdown pages.
Expand Down Expand Up @@ -173,3 +196,33 @@ Set the BCP-47 language tag for this page’s content, e.g. `en`, `zh-CN`, or `p
**default:** `false`

Indicate if this page is using [fallback content](/guides/i18n/#fallback-content) because there is no translation for the current language.

#### `<AnchorHeading>` component

The `<AnchorHeading />` component renders an HTML heading element with a clickable anchor link that matches Starlight’s Markdown styles.

```astro
---
import AnchorHeading from '@astrojs/starlight/components/AnchorHeading.astro';
---

<AnchorHeading level="2" id="sub-heading">Sub heading</AnchorHeading>
```

It accepts the following props as well as any other valid [global HTML attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes).

##### `level`

**required**
**type:** `1 | 2 | 3 | 4 | 5 | 6`

The heading level to render.
For example, `level="1"` would render an `<h1>` element.

##### `id`

**required**
**type:** `string`

The unique ID for this heading.
This will be used as the `id` attribute of the rendered heading and the anchor icon will link to it.
23 changes: 23 additions & 0 deletions docs/src/content/docs/reference/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,29 @@ starlight({
});
```

### `markdown`

**type:** `{ headingLinks?: boolean }`
**default:** `{ headingLinks: true }`

Configure Starlight’s Markdown processing.

#### `headingLinks`

**type:** `boolean`
**default:** `true`

Controls whether or not headings are rendered with a clickable anchor link.

```js
starlight({
markdown: {
// Disable Starlight’s clickable heading anchor links.
headingLinks: false,
},
}),
```

### `expressiveCode`

**type:** `StarlightExpressiveCodeOptions | boolean`
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"examples/basics/dist/_astro/*.css",
"!examples/basics/dist/_astro/print.*.css"
],
"limit": "14.5 kB",
"limit": "14.75 kB",
"gzip": true
}
],
Expand Down
1 change: 1 addition & 0 deletions packages/markdoc/components.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { default as Code } from './Code.astro';
export { default as Heading } from '@astrojs/starlight/components/AnchorHeading.astro';
24 changes: 20 additions & 4 deletions packages/markdoc/index.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { component } from '@astrojs/markdoc/config';
import { component, nodes } from '@astrojs/markdoc/config';
import { WellKnownElementAttributes, WellKnownAnchorAttributes } from './html.mjs';

/**
Expand Down Expand Up @@ -287,7 +287,23 @@ export const StarlightMarkdocPreset = {
},
};

/** @return {import('@astrojs/markdoc/config').AstroMarkdocConfig} */
export default function starlightMarkdoc() {
return StarlightMarkdocPreset;
/**
* Markdoc preset that configures Starlight’s built-in components.
* @return {import('@astrojs/markdoc/config').AstroMarkdocConfig}
*/
export default function starlightMarkdoc({ headingLinks = true } = {}) {
return {
...StarlightMarkdocPreset,
nodes: {
...StarlightMarkdocPreset.nodes,
...(headingLinks
? {
heading: {
...nodes.heading,
render: component('@astrojs/starlight-markdoc/components', 'Heading'),
},
}
: {}),
},
};
}
2 changes: 1 addition & 1 deletion packages/markdoc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
},
"peerDependencies": {
"@astrojs/markdoc": ">=0.12.1",
"@astrojs/starlight": ">=0.30.0"
"@astrojs/starlight": ">=0.34.0"
},
"publishConfig": {
"provenance": true
Expand Down
19 changes: 19 additions & 0 deletions packages/starlight/__e2e__/components.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,25 @@ test.describe('whitespaces', () => {
});
});

test.describe('anchor headings', () => {
test('renders the same content for Markdown headings and Astro component', async ({
getProdServer,
page,
}) => {
const starlight = await getProdServer();

await starlight.goto('/anchor-heading');
const markdownContent = page.locator('.sl-markdown-content');
const markdownHtml = await markdownContent.innerHTML();

await starlight.goto('/anchor-heading-component');
const componentContent = page.locator('.sl-markdown-content');
const componentHtml = await componentContent.innerHTML();

expect(markdownHtml).toEqual(componentHtml);
});
});

async function expectSelectedTab(tabs: Locator, label: string, panel?: string) {
expect(
(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
title: Anchor Headings Component
---

import AnchorHeading from '@astrojs/starlight/components/AnchorHeading.astro';

<AnchorHeading level="2" id="an-anchor-heading">An anchor heading</AnchorHeading>

<AnchorHeading level="3" id="another-anchor-heading">Another anchor heading</AnchorHeading>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: Anchor Headings
---

## An anchor heading

### Another anchor heading
3 changes: 3 additions & 0 deletions packages/starlight/__tests__/basics/config-errors.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ test('parses bare minimum valid config successfully', () => {
"isUsingBuiltInDefaultLocale": true,
"lastUpdated": false,
"locales": undefined,
"markdown": {
"headingLinks": true,
},
"pagefind": {
"ranking": {
"pageLength": 0.1,
Expand Down
Loading