Astroで見出しにリンクを付ける

投稿日
2025年1月10日
Astroで見出しにリンクを付けるの見出し画像
目次

はじめに

このブログでは、Astroのドキュメントに見られるような見出しリンク(ホバーで表示されるクリップアイコン)をrehypeプラグイン「rehype-autolink-headings」で挿入しています。

基本的なインストール方法やパラメータは公式リポジトリのREADME.mdで丁寧に説明されているので上記を参照のほど。

本記事では、導入時に詰まった箇所やAstroへ導入する際のポイントのみを解説します。

バージョン情報

astro
5.1.1
nodejs
22.12.0

Astroへ導入する際のポイント

rehype-autolink-headingsではid属性が振られた見出し(<h1>~<h6>)を対象に、リンクなどの任意要素を追加します。 そのため、rehype-autolink-headingsの実行前にid属性を割り振る必要があります。

AstroではMarkdownとMDXを対象に見出しのIDが自動的に割り振られますが、この実行はrehypePlugins実行後であることに注意です。

DeepL翻訳

デフォルトでは、Astroはrehypeプラグインの実行後にid属性を注入します。カスタムrehypeプラグインの1つがAstroによって注入されたIDにアクセスする必要がある場合、AstroのrehypeHeadingIdsプラグインを直接インポートして使用することができます。

原文

By default, Astro injects id attributes after your rehype plugins have run. If one of your custom rehype plugins needs to access the IDs injected by Astro, you can import and use Astro’s rehypeHeadingIds plugin directly.

docs.astro.buildファビコン Markdown in Astro
docs.astro.build

上記の説明通り、rehypeHeadingIdsを明示的にrehypePluginsから呼び出すことで、id挿入タイミングをrehype-autolink-headingsの実行前に設定できます。

astro.config.ts
import { rehypeHeadingIds } from '@astrojs/markdown-remark';
import { h } from 'hastscript';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
export default defineConfig({
markdown: {
rehypePlugins: [
rehypeHeadingIds,
[
rehypeAutolinkHeadings,
{
behavior: 'append',
headingProperties: {
className: ['anchor'],
},
properties: {
className: ['anchor-link'],
ariaHidden: true,
tabIndex: -1,
},
content() {
return h('span.anchor-icon');
},
},
],
],
},
});

hastscriptを使った制御

rehype-autolink-headingsのcontentgroupパラメータでは、hastscriptのNodeインスタンスを返す関数または直接プロパティを設定できます。

単純なテキストを追加する場合

例えば、#というテキストを含んだ<a>タグを見出しの後ろに挿入したい場合は、下記のようになります。

astro.config.ts
import { rehypeHeadingIds } from '@astrojs/markdown-remark';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
export default defineConfig({
markdown: {
rehypePlugins: [
rehypeHeadingIds,
[
rehypeAutolinkHeadings,
{
behavior: 'append',
content: {
type: 'text',
value: '#',
},
},
],
],
},
});

得られるHTMLは下記のようになります。aria-hiddentabindex属性は、一部behaviorオプションにおいてデフォルトで追記されます。

<h2 id="astroへ導入する際のポイント">
Astroへ導入する際のポイント
<a aria-hidden="true" tabindex="-1" href="#astroへ導入する際のポイント">#</a>
</h2>

要素を追加する場合

<a>タグ内のコンテンツに<span class="anchor-icon">というような要素を挿入する場合は下記のように書けます。

astro.config.ts
import { rehypeHeadingIds } from '@astrojs/markdown-remark';
import { h } from 'hastscript';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
export default defineConfig({
markdown: {
rehypePlugins: [
rehypeHeadingIds,
[
rehypeAutolinkHeadings,
{
behavior: 'append',
content() {
return h('span.anchor-icon');
},
// 子として文字列を持ってみる
// content() {
// return h('span.anchor-icon', '#');
// },
},
],
],
},
});

見出しとリンクをラップしたい場合

behaviorafterbeforeを指定して、下記のような構造を持ちたい場合もあります。 デフォルトでは<div>などでラップしてくれないので、これもhastscriptで定義が必要です。

<div class="heading-wrapper">
<h2>見出し</h2>
<a href="#heading4">#</a>
</div>

この場合はgroupプロパティに、ラッパーの要素を返す関数を渡せばOKです。

astro.config.ts
import { rehypeHeadingIds } from '@astrojs/markdown-remark';
import { h } from 'hastscript';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
export default defineConfig({
markdown: {
rehypePlugins: [
rehypeHeadingIds,
[
rehypeAutolinkHeadings,
{
behavior: 'after',
content() {
return h('span.anchor-icon');
},
group() {
return h('div.heading-wrapper');
},
},
],
],
},
});

これで下記のようなHTMLが得られます。

<div class="heading-wrapper">
<h3 id="見出しとリンクをラップしたい場合">
見出しとリンクをラップしたい場合
</h3>
<a href="#見出しとリンクをラップしたい場合">
<span class="anchor-icon"></span>
</a>
</div>

関連記事