<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>R.devのRSSフィード</title><description>R.devのブログ記事と作品URLのRSSフィードです。</description><link>https://r-dev95.netlify.app/</link><language>ja-jp</language><item><title>Astro - rehypeプラグインを作って単体URLのリンクカード化を実現する</title><link>https://r-dev95.netlify.app/blogs/0007-create-rehype-plugin/</link><guid isPermaLink="true">https://r-dev95.netlify.app/blogs/0007-create-rehype-plugin/</guid><description>Astro上でrehypeプラグインを作って単体URLのリンクカード化を実現しました。</description><pubDate>Thu, 04 Jun 2026 07:05:11 GMT</pubDate><content:encoded>&lt;p&gt;前回、remarkプラグインでGFMのアラート記法を実現しました。&lt;/p&gt;
&lt;a href=&quot;https://r-dev95.netlify.app/blogs/0006-create-remark-plugin/&quot; target=&quot;_blank&quot;&gt;&lt;figure&gt;&lt;/figure&gt;&lt;div&gt;&lt;div&gt;Astro - remarkプラグインを作ってGFMのアラート記法を実現する | R.dev&lt;/div&gt;&lt;p&gt;Astro上でremarkプラグインを作ってGFMのアラート記法を実現しました。&lt;/p&gt;&lt;p&gt;r-dev95.netlify.app&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;
&lt;p&gt;今回は、rehypeプラグインで単体URLのリンクカード化を実現していきます。&lt;/p&gt;
&lt;p&gt;ここでいう単体URLのリンクカード化とは、外部リンクのURLだけ貼ると、タイトルや説明、サムネイルを表示するリッチなリンクカードに変換してくれる機能のことです。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;https://example.com&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;ZennやQiitaのようなプラットフォームでよく見かけるあれです。&lt;/p&gt;
&lt;p&gt;上述のような機能は、Astroにはデフォルトではサポートされていません。&lt;/p&gt;
&lt;p&gt;前回も言及しましたが、&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;既製のプラグインを使う方法もありますが、仕組みさえわかれば&lt;strong&gt;それほど難しい実装ではありません&lt;/strong&gt;。自分で作ればHTML構造、クラス名、アイコン、タイトルなど自由にカスタマイズ可能です。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;remark&lt;/strong&gt; だけでなく、&lt;strong&gt;rehype&lt;/strong&gt; でも同様のことが言えると思います。&lt;/p&gt;
&lt;p&gt;この記事では、rehypeプラグインをゼロから自作する方法を解説します。&lt;/p&gt;
&lt;h2&gt;remarkとrehype、どちらでプラグインを作るか&lt;/h2&gt;
&lt;p&gt;前回の内容を再掲します。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;AstroのMarkdown処理パイプラインは、&lt;strong&gt;unified&lt;/strong&gt; というエコシステムの上に成り立っています。そのなかで主役となるのが &lt;strong&gt;remark&lt;/strong&gt; と &lt;strong&gt;rehype&lt;/strong&gt; の2つです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;remark&lt;/strong&gt; はMarkdownを扱うプロセッサです。入力したMarkdownテキストを &lt;strong&gt;mdast&lt;/strong&gt;（Markdown Abstract Syntax Tree）と呼ばれるツリー構造に変換し、プラグインはそのツリーを自由に操作できます。「Markdownのテキストを読んで構造を変えたい」場合はremarkプラグインが適しています。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;rehype&lt;/strong&gt; はHTMLを扱うプロセッサです。こちらは &lt;strong&gt;hast&lt;/strong&gt;（Hypertext Abstract Syntax Tree）を操作します。mdastがhastに変換されたあとで動作するため、出力HTMLの構造を直接いじりたい場合に使います。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;今回は &lt;strong&gt;rehype&lt;/strong&gt; を選びます。リンクカードへの変換は「&lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; タグの中に &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; タグだけがある」という状態を検出して置き換える処理です。この判定はHTMLのツリー上で行うほうが直感的で、タグ名や属性をそのまま条件に使えます。またOGPのフェッチという非同期処理も絡むため、HTMLが確定したあとのrehypeで一括処理するほうがシンプルです。&lt;/p&gt;
&lt;h2&gt;rehypeプラグインの作り方&lt;/h2&gt;
&lt;p&gt;remarkプラグインとほとんど同様ですが、rehypeプラグインの要点を整理すると次の通りです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;unist-util-visit&lt;/code&gt; で対象ノードをたどる&lt;/li&gt;
&lt;li&gt;「対象ノードを置き換える」または「新たなノードを追加する」&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tagName&lt;/code&gt; / &lt;code&gt;properties&lt;/code&gt; で直接HTMLの構造を制御する&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;プラグインの基本構造&lt;/h3&gt;
&lt;p&gt;remarkと同様、rehypeプラグインも&lt;strong&gt;ツリーを受け取る関数を返す関数&lt;/strong&gt;です。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Root&lt;/code&gt; などの型定義を &lt;code&gt;mdast&lt;/code&gt; ではなく &lt;code&gt;hast&lt;/code&gt; からインポートする点がremarkプラグインとの違いです。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; { Root } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;hast&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rehypeMyPlugin&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;tree&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Root&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// treeを操作する&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;ノードをたどる - unist-util-visit&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;unist-util-visit&lt;/code&gt; の使い方はremarkと同じです。&lt;/p&gt;
&lt;p&gt;第2引数でノードタイプを指定します。ここでは &lt;code&gt;&apos;element&apos;&lt;/code&gt; を指定しているので、すべてのHTMLタグに対応するノードが対象になります。コールバックには&lt;code&gt;node&lt;/code&gt;（対象ノード）、&lt;code&gt;index&lt;/code&gt;（親の中での位置）、&lt;code&gt;parent&lt;/code&gt;（親ノード）が渡されます。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; { Element, Parent, Root } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;hast&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { visit } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;unist-util-visit&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { fetchOgp } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;./fetch-ogp&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rehypeMyPlugin&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;tree&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Root&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;visit&lt;/span&gt;&lt;span&gt;(tree, &lt;/span&gt;&lt;span&gt;&apos;element&apos;&lt;/span&gt;&lt;span&gt;, (&lt;/span&gt;&lt;span&gt;node&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Element&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;parent&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Parent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// elementノードが見つかるたびに呼ばれる&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;ノードを置き換える / 子ノードに独自ノードを追加する&lt;/h3&gt;
&lt;p&gt;remarkと同様に &lt;code&gt;parent.children[index]&lt;/code&gt; に新しいノードを代入します。&lt;/p&gt;
&lt;p&gt;hastでは &lt;code&gt;type: &apos;element&apos;&lt;/code&gt; を使い、&lt;code&gt;tagName&lt;/code&gt; でHTMLタグを指定します。
またremarkと違い、 &lt;code&gt;data.hName&lt;/code&gt; / &lt;code&gt;data.hProperties&lt;/code&gt; は不要です。&lt;code&gt;tagName&lt;/code&gt; と &lt;code&gt;properties&lt;/code&gt; が直接HTMLに対応します。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;(parent.children &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ElementContent&lt;/span&gt;&lt;span&gt;[])[index] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;element&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tagName: &lt;/span&gt;&lt;span&gt;&apos;a&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;properties: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;className: [&lt;/span&gt;&lt;span&gt;&apos;link-card&apos;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;children: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;element&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tagName: &lt;/span&gt;&lt;span&gt;&apos;div&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;properties: { className: [&lt;/span&gt;&lt;span&gt;&apos;link-card-body&apos;&lt;/span&gt;&lt;span&gt;] },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;children: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;element&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tagName: &lt;/span&gt;&lt;span&gt;&apos;h2&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;properties: {},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;children: [{ type: &lt;/span&gt;&lt;span&gt;&apos;text&apos;&lt;/span&gt;&lt;span&gt;, value: &lt;/span&gt;&lt;span&gt;&apos;タイトル&apos;&lt;/span&gt;&lt;span&gt; }],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;非同期処理との組み合わせ&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;visit&lt;/code&gt; のコールバックは同期関数として呼ばれます。コールバック内で &lt;code&gt;await&lt;/code&gt; することはできません。&lt;/p&gt;
&lt;p&gt;そのため、&lt;strong&gt;タスクをPromiseの配列に貯めてから、visitが終わったあとに &lt;code&gt;Promise.all&lt;/code&gt; で一括処理する&lt;/strong&gt;パターンを使います。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;visit&lt;/code&gt; が走り終わった時点では &lt;code&gt;tasks&lt;/code&gt; にPromiseが積まれているだけです。&lt;code&gt;await Promise.all(tasks)&lt;/code&gt; で全件完了を待ってから処理を終えます。Astroのビルド時にプラグインが呼ばれる際も、このPromiseが解決されるまで待ってくれます。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;tree&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Root&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tasks&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Promise&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt;&amp;gt;[] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;visit&lt;/span&gt;&lt;span&gt;(tree, &lt;/span&gt;&lt;span&gt;&apos;element&apos;&lt;/span&gt;&lt;span&gt;, (&lt;/span&gt;&lt;span&gt;node&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;parent&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// 非同期処理が必要なら tasks に積む&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tasks.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;someAsyncOperation&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;result&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;// ノードを書き換える&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// すべての非同期処理が終わるまで待つ&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Promise&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;(tasks);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;実装したプラグイン&lt;/h2&gt;
&lt;p&gt;実装したプラグインと実際に使った場合の表示は次の通りです。&lt;/p&gt;

実装
&lt;div&gt;
OGPのフェッチ処理については詳しく言及しませんが、`cheerio` を使っています。
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; { Element, ElementContent, Parent, Root } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;hast&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { visit } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;unist-util-visit&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { fetchOgp } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;./fetch-ogp&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rehypeLinkCard&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;tree&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Root&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tasks&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Promise&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;void&lt;/span&gt;&lt;span&gt;&amp;gt;[] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;visit&lt;/span&gt;&lt;span&gt;(tree, &lt;/span&gt;&lt;span&gt;&apos;element&apos;&lt;/span&gt;&lt;span&gt;, (&lt;/span&gt;&lt;span&gt;node&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Element&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;parent&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Parent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;parent &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; index &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (node.tagName &lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;p&apos;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (node.children.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;firstChild&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; node.children[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (firstChild.type &lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;element&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; firstChild.tagName &lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;a&apos;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;href&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; firstChild.properties?.href;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; href &lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;string&apos;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tasks.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;fetchOgp&lt;/span&gt;&lt;span&gt;(href).&lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;ogp&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;(parent.children &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ElementContent&lt;/span&gt;&lt;span&gt;[])[index] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;element&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tagName: &lt;/span&gt;&lt;span&gt;&apos;a&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;properties: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;href,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;target: &lt;/span&gt;&lt;span&gt;&apos;_blank&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rel: &lt;/span&gt;&lt;span&gt;&apos;noopener noreferrer&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;className: [&lt;/span&gt;&lt;span&gt;&apos;link-card&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;anim-card-link&apos;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;children: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;(ogp.image&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;element&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tagName: &lt;/span&gt;&lt;span&gt;&apos;figure&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;properties: {},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;children: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;element&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tagName: &lt;/span&gt;&lt;span&gt;&apos;img&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;properties: { src: ogp.image, alt: ogp.title },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;children: [],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; []),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;element&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tagName: &lt;/span&gt;&lt;span&gt;&apos;div&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;properties: { className: [&lt;/span&gt;&lt;span&gt;&apos;link-card-body&apos;&lt;/span&gt;&lt;span&gt;] },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;children: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;element&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tagName: &lt;/span&gt;&lt;span&gt;&apos;div&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;properties: { className: [&lt;/span&gt;&lt;span&gt;&apos;link-card-title&apos;&lt;/span&gt;&lt;span&gt;] },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;children: [{ type: &lt;/span&gt;&lt;span&gt;&apos;text&apos;&lt;/span&gt;&lt;span&gt;, value: ogp.title }],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;element&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tagName: &lt;/span&gt;&lt;span&gt;&apos;p&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;properties: { className: [&lt;/span&gt;&lt;span&gt;&apos;link-card-description&apos;&lt;/span&gt;&lt;span&gt;] },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;children: [{ type: &lt;/span&gt;&lt;span&gt;&apos;text&apos;&lt;/span&gt;&lt;span&gt;, value: ogp.description }],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;element&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tagName: &lt;/span&gt;&lt;span&gt;&apos;p&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;properties: { className: [&lt;/span&gt;&lt;span&gt;&apos;link-card-site&apos;&lt;/span&gt;&lt;span&gt;] },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;children: [{ type: &lt;/span&gt;&lt;span&gt;&apos;text&apos;&lt;/span&gt;&lt;span&gt;, value: ogp.siteName }],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Promise&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;(tasks);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;/div&gt;


マークダウン
&lt;div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;https://example.com&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;https://r-dev95.netlify.app/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;実際の表示：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;OGP画像なし：&lt;/p&gt;
&lt;a href=&quot;https://example.com&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;div&gt;Example Domain&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;example.com&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;
&lt;p&gt;OGP画像あり：&lt;/p&gt;
&lt;a href=&quot;https://r-dev95.netlify.app/&quot; target=&quot;_blank&quot;&gt;&lt;figure&gt;&lt;/figure&gt;&lt;div&gt;&lt;div&gt;R.dev&lt;/div&gt;&lt;p&gt;R.devの個人サイトです。ブログ記事と作品URLを載せていきます。&lt;/p&gt;&lt;p&gt;r-dev95.netlify.app&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;
&lt;h2&gt;【補足】 Astroへの組み込み&lt;/h2&gt;
&lt;p&gt;作成したプラグインは、&lt;code&gt;astro.config.mjs&lt;/code&gt; に次の通り実装すると使えるようになります。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;astro.config.mjs&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { unified } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;@astrojs/markdown-remark&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; remarkLinkCard &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;./path/to/directory/rehype-link-card&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defineConfig&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;markdown: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;processor: &lt;/span&gt;&lt;span&gt;unified&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rehypePlugins: [rehypeLinkCard],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;まとめ&lt;/h2&gt;
&lt;p&gt;remarkプラグインと同様にrehypeプラグインも仕組みを理解してしまえば、アラートの種類の追加やHTML構造の変更も自由自在です。既製プラグインをそのまま使うより、一度自分で実装してみることをおすすめします。&lt;/p&gt;</content:encoded></item><item><title>Astro - remarkプラグインを作ってGFMのアラート記法を実現する</title><link>https://r-dev95.netlify.app/blogs/0006-create-remark-plugin/</link><guid isPermaLink="true">https://r-dev95.netlify.app/blogs/0006-create-remark-plugin/</guid><description>Astro上でremarkプラグインを作ってGFMのアラート記法を実現しました。</description><pubDate>Thu, 04 Jun 2026 04:13:08 GMT</pubDate><content:encoded>&lt;p&gt;私はこのサイトを &lt;code&gt;Astro&lt;/code&gt; で構築していて、ブログ記事をマークダウン（.md, .mdx）で記述しています。&lt;/p&gt;
&lt;p&gt;例えば、GitHub Flavored Markdown（GFM）には、ノートや警告を視覚的に目立たせる&lt;strong&gt;アラート記法&lt;/strong&gt;があります。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; [&lt;/span&gt;&lt;span&gt;!NOTE&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; ここに補足情報を書きます。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; [&lt;/span&gt;&lt;span&gt;!WARNING&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; 注意が必要な内容はこちら。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;GitHubのREADMEでよく見かけるあの色付きブロックです。&lt;/p&gt;
&lt;p&gt;しかし、AstroはMarkdownをHTMLに変換する際、このアラート記法をデフォルトではサポートしていません。変換後は通常の &lt;code&gt;&amp;lt;blockquote&amp;gt;&lt;/code&gt; になってしまいます。&lt;/p&gt;
&lt;p&gt;既製のプラグインを使う方法もありますが、仕組みさえわかれば&lt;strong&gt;それほど難しい実装ではありません&lt;/strong&gt;。自分で作ればHTML構造、クラス名、アイコン、タイトルなど自由にカスタマイズ可能です。&lt;/p&gt;
&lt;p&gt;この記事では、remarkプラグインをゼロから自作する方法を解説します。&lt;/p&gt;
&lt;h2&gt;remark vs rehype - どちらでプラグインを作るか&lt;/h2&gt;
&lt;p&gt;AstroのMarkdown処理パイプラインは、&lt;strong&gt;unified&lt;/strong&gt; というエコシステムの上に成り立っています。そのなかで主役となるのが &lt;strong&gt;remark&lt;/strong&gt; と &lt;strong&gt;rehype&lt;/strong&gt; の2つです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;remark&lt;/strong&gt; はMarkdownを扱うプロセッサです。入力したMarkdownテキストを &lt;strong&gt;mdast&lt;/strong&gt;（Markdown Abstract Syntax Tree）と呼ばれるツリー構造に変換し、プラグインはそのツリーを自由に操作できます。「Markdownのテキストを読んで構造を変えたい」場合はremarkプラグインが適しています。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;rehype&lt;/strong&gt; はHTMLを扱うプロセッサです。こちらは &lt;strong&gt;hast&lt;/strong&gt;（Hypertext Abstract Syntax Tree）を操作します。mdastがhastに変換されたあとで動作するため、出力HTMLの構造を直接いじりたい場合に使います。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;a href=&quot;https://unifiedjs.com/&quot; target=&quot;_blank&quot;&gt;&lt;figure&gt;&lt;/figure&gt;&lt;div&gt;&lt;div&gt;unified&lt;/div&gt;&lt;p&gt;Content as structured data: unified compiles content and provides hundreds of packages to work with content&lt;/p&gt;&lt;p&gt;unified&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;
&lt;p&gt;アラート記法の実装には、どちらも使えます。ただし今回は &lt;strong&gt;remark&lt;/strong&gt; を選びました。理由はシンプルで、判定と変換をMarkdownのツリー上で完結させられるからです。blockquoteノードを見つけて別のノードに置き換えるだけなので、remarkのほうが直感的に書けます。&lt;/p&gt;
&lt;h2&gt;remarkプラグインの作り方&lt;/h2&gt;
&lt;p&gt;remarkプラグインの要点を整理すると次の通りです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;unist-util-visit&lt;/code&gt; で対象ノードをたどる&lt;/li&gt;
&lt;li&gt;「対象ノードを置き換える」または「新たなノードを追加する」&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data.hName&lt;/code&gt; / &lt;code&gt;data.hProperties&lt;/code&gt; でHTML変換後の構造を制御する&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;プラグインの基本構造&lt;/h3&gt;
&lt;p&gt;remarkプラグインは、&lt;strong&gt;ツリーを受け取って何もreturnしない関数を返す関数&lt;/strong&gt;です。&lt;/p&gt;
&lt;p&gt;関数を返す理由は、オプションを受け取れるようにするためです。今回はオプションなしですが、同じ形に従います。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; { Root } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;mdast&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remarkMyPlugin&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;tree&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Root&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// treeを操作する&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;ノードをたどる - unist-util-visit&lt;/h3&gt;
&lt;p&gt;ツリーの特定ノードを探すには &lt;code&gt;unist-util-visit&lt;/code&gt; を使います。&lt;/p&gt;
&lt;p&gt;第2引数でノードタイプを指定します。ここでは &lt;code&gt;&apos;blockquote&apos;&lt;/code&gt; を指定しているので、Markdownの &lt;code&gt;&amp;gt;&lt;/code&gt; から始まるブロックが対象になります。コールバックには&lt;code&gt;node&lt;/code&gt;（対象ノード）、&lt;code&gt;index&lt;/code&gt;（親の中での位置）、&lt;code&gt;parent&lt;/code&gt;（親ノード）が渡されます。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; { Blockquote, Parent, Root } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;mdast&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { visit } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;unist-util-visit&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remarkMyPlugin&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;tree&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Root&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;visit&lt;/span&gt;&lt;span&gt;(tree, &lt;/span&gt;&lt;span&gt;&apos;blockquote&apos;&lt;/span&gt;&lt;span&gt;, (&lt;/span&gt;&lt;span&gt;node&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Blockquote&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;parent&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Parent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// blockquoteノードが見つかるたびに呼ばれる&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;ノードを置き換える&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;visit&lt;/code&gt; のコールバック内で &lt;code&gt;parent.children[index]&lt;/code&gt; を書き換えると、そのノードを別のノードに置き換えられます。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;data.hName&lt;/code&gt; と &lt;code&gt;data.hProperties&lt;/code&gt; は、rehypeがこのノードをHTMLに変換するときに使うメタ情報です。&lt;code&gt;hName&lt;/code&gt; でタグ名を、&lt;code&gt;hProperties&lt;/code&gt; でHTML属性を指定できます。&lt;code&gt;className&lt;/code&gt; を配列で渡すと &lt;code&gt;class&lt;/code&gt; 属性になります。&lt;/p&gt;
&lt;p&gt;TypeScriptでは &lt;code&gt;mdast&lt;/code&gt; の型と合わなくなるので、&lt;code&gt;as unknown as&lt;/code&gt; が必要になる場面があります。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;visit&lt;/span&gt;&lt;span&gt;(tree, &lt;/span&gt;&lt;span&gt;&apos;blockquote&apos;&lt;/span&gt;&lt;span&gt;, (&lt;/span&gt;&lt;span&gt;node&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Blockquote&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;parent&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Parent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;parent &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; index &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// ここで parent.children[index] に新しいノードを代入する&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;parent.children[index] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;myCustomNode&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;data: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;hName: &lt;/span&gt;&lt;span&gt;&apos;div&apos;&lt;/span&gt;&lt;span&gt;,                 &lt;/span&gt;&lt;span&gt;// HTMLに変換されたときのタグ名&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;hProperties: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;className: [&lt;/span&gt;&lt;span&gt;&apos;my-class&apos;&lt;/span&gt;&lt;span&gt;],    &lt;/span&gt;&lt;span&gt;// HTMLに変換された時のクラス名&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;children: node.children,    &lt;/span&gt;&lt;span&gt;// 元のblockquoteの子ノードを引き継ぐ&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unknown&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Blockquote&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;子ノードに独自ノードを追加する&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;children&lt;/code&gt; 配列に好きなノードを追加できます。たとえばタイトル用のdivとコンテンツ用のdivに分けたい場合は次のようにします。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;type&lt;/code&gt; の文字列（&lt;code&gt;&apos;alertCard&apos;&lt;/code&gt;, &lt;code&gt;&apos;alertTitle&apos;&lt;/code&gt; など）は自由に決められます。これらはmdastの世界だけで使われる識別子で、最終的なHTMLには影響しません。HTMLの構造は &lt;code&gt;hName&lt;/code&gt; と &lt;code&gt;hProperties&lt;/code&gt; が決めます。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;parent.children[index] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;alertCard&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;data: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;hName: &lt;/span&gt;&lt;span&gt;&apos;div&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;hProperties: { className: [&lt;/span&gt;&lt;span&gt;&apos;alert-card&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;alert-note&apos;&lt;/span&gt;&lt;span&gt;] },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;children: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;alertTitle&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;data: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;hName: &lt;/span&gt;&lt;span&gt;&apos;div&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;hProperties: { className: [&lt;/span&gt;&lt;span&gt;&apos;alert-title&apos;&lt;/span&gt;&lt;span&gt;] },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;children: [{ type: &lt;/span&gt;&lt;span&gt;&apos;text&apos;&lt;/span&gt;&lt;span&gt;, value: &lt;/span&gt;&lt;span&gt;&apos;Note&apos;&lt;/span&gt;&lt;span&gt; }],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;alertContent&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;data: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;hName: &lt;/span&gt;&lt;span&gt;&apos;div&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;hProperties: { className: [&lt;/span&gt;&lt;span&gt;&apos;alert-content&apos;&lt;/span&gt;&lt;span&gt;] },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;children: node.children,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unknown&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Blockquote&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;画像ノードを埋め込む&lt;/h3&gt;
&lt;p&gt;mdastには &lt;code&gt;image&lt;/code&gt; ノードがあるため、HTMLの &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; をツリー上で表現できます。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;image&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;/icons/alert-note.svg&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;alt&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;hProperties&lt;/span&gt;&lt;span&gt;: { &lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;&apos;alert-icon&apos;&lt;/span&gt;&lt;span&gt;] },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;実装したプラグイン&lt;/h2&gt;
&lt;p&gt;実装したプラグインと実際に使った場合の表示は次の通りです。&lt;/p&gt;
&lt;p&gt;この実装ではタイトルを付与できるようにしました。&lt;/p&gt;

実装
&lt;div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; { BlockContent, Blockquote, Parent, Root } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;mdast&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { visit } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;unist-util-visit&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ALERT_TYPES&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;NOTE: &lt;/span&gt;&lt;span&gt;&apos;note&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;TIP: &lt;/span&gt;&lt;span&gt;&apos;tip&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;IMPORTANT: &lt;/span&gt;&lt;span&gt;&apos;important&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;WARNING: &lt;/span&gt;&lt;span&gt;&apos;warning&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;CAUTION: &lt;/span&gt;&lt;span&gt;&apos;caution&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remarkAlert&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;tree&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Root&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;visit&lt;/span&gt;&lt;span&gt;(tree, &lt;/span&gt;&lt;span&gt;&apos;blockquote&apos;&lt;/span&gt;&lt;span&gt;, (&lt;/span&gt;&lt;span&gt;node&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Blockquote&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;parent&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Parent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;parent &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; index &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;undefined&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;firstParagraph&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; node.children[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;firstParagraph &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; firstParagraph.type &lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;paragraph&apos;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;firstChild&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; firstParagraph.children[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;firstChild &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; firstChild.type &lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;text&apos;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;// アラート宣言行を判定&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lines&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; firstChild.value.&lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; lines[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;].&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;^&lt;/span&gt;&lt;span&gt;\[&lt;/span&gt;&lt;span&gt;!(NOTE&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;TIP&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;IMPORTANT&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;WARNING&lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt;CAUTION)&lt;/span&gt;&lt;span&gt;\]&lt;/span&gt;&lt;span&gt;(?:&lt;/span&gt;&lt;span&gt;\s&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;title=(&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt;?$&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;match) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;// アラートのタイプとタイトルを取得&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; match[&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;keyof&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ALERT_TYPES&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ALERT_TYPES&lt;/span&gt;&lt;span&gt;[type];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;customTitle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; match[&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;]?.&lt;/span&gt;&lt;span&gt;trim&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;??&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;// アラート宣言行を除去&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;lines.&lt;/span&gt;&lt;span&gt;shift&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;remaining&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; lines.&lt;/span&gt;&lt;span&gt;join&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (remaining.&lt;/span&gt;&lt;span&gt;trim&lt;/span&gt;&lt;span&gt;()) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;firstChild.value &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; remaining;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;firstParagraph.children.&lt;/span&gt;&lt;span&gt;shift&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (firstParagraph.children.&lt;/span&gt;&lt;span&gt;length&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;node.children.&lt;/span&gt;&lt;span&gt;shift&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;parent.children[index] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;alertCard&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;data: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;hName: &lt;/span&gt;&lt;span&gt;&apos;div&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;hProperties: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;className: [&lt;/span&gt;&lt;span&gt;&apos;alert-card&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;`alert-${&lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;}`&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;children: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;alertTitle&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;data: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;hName: &lt;/span&gt;&lt;span&gt;&apos;div&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;hProperties: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;className: [&lt;/span&gt;&lt;span&gt;&apos;alert-title&apos;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;children: [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;image&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;url: &lt;/span&gt;&lt;span&gt;`/icons/alert-${&lt;/span&gt;&lt;span&gt;className&lt;/span&gt;&lt;span&gt;}.svg`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;alt: &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;data: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;hProperties: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;className: [&lt;/span&gt;&lt;span&gt;&apos;alert-icon&apos;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;text&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;value: customTitle,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unknown&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;BlockContent&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;type: &lt;/span&gt;&lt;span&gt;&apos;alertContent&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;data: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;hName: &lt;/span&gt;&lt;span&gt;&apos;div&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;hProperties: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;className: [&lt;/span&gt;&lt;span&gt;&apos;alert-content&apos;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;children: node.children,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;          &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unknown&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;BlockContent&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;unknown&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Blockquote&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;/div&gt;


マークダウン
&lt;div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; [&lt;/span&gt;&lt;span&gt;!NOTE&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; note を記述します。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; タイトルなしです。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; [&lt;/span&gt;&lt;span&gt;!TIP&lt;/span&gt;&lt;span&gt;] title=tip title&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; tip を記述します。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; 英語タイトルありです。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; [&lt;/span&gt;&lt;span&gt;!IMPORTANT&lt;/span&gt;&lt;span&gt;] title=重要です&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; important を記述します。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; 日本語タイトルありです。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; [&lt;/span&gt;&lt;span&gt;!WARNING&lt;/span&gt;&lt;span&gt;] title=警告です&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; warning を記述します。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; &amp;gt; blockquote を含みます。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; [&lt;/span&gt;&lt;span&gt;!CAUTION&lt;/span&gt;&lt;span&gt;] title=危険です&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; caution を記述します。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; &amp;gt; [&lt;/span&gt;&lt;span&gt;!NOTE&lt;/span&gt;&lt;span&gt;] title=note title&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; &amp;gt; アラート記法のNOTEを含みます。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;実際の表示：&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;note を記述します。
タイトルなしです。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div&gt;&lt;div&gt;tip title&lt;/div&gt;&lt;div&gt;&lt;p&gt;tip を記述します。
英語タイトルありです。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div&gt;&lt;div&gt;重要です&lt;/div&gt;&lt;div&gt;&lt;p&gt;important を記述します。&lt;/p&gt;&lt;p&gt;日本語タイトルありです。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div&gt;&lt;div&gt;警告です&lt;/div&gt;&lt;div&gt;&lt;p&gt;warning を記述します。&lt;/p&gt;&lt;blockquote&gt;
&lt;p&gt;blockquote を含みます。&lt;/p&gt;
&lt;/blockquote&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div&gt;&lt;div&gt;危険です&lt;/div&gt;&lt;div&gt;&lt;p&gt;caution を記述します。&lt;/p&gt;&lt;div&gt;&lt;div&gt;note title&lt;/div&gt;&lt;div&gt;&lt;p&gt;アラート記法のNOTEを含みます。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;【補足】 Astroへの組み込み&lt;/h2&gt;
&lt;p&gt;作成したプラグインは、&lt;code&gt;astro.config.mjs&lt;/code&gt; に次の通り実装すると使えるようになります。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;astro.config.mjs&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { unified } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;@astrojs/markdown-remark&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; remarkAlert &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;./path/to/directory/remark-alert&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defineConfig&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;markdown: {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;processor: &lt;/span&gt;&lt;span&gt;unified&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;remarkPlugins: [remarkAlert],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2&gt;まとめ&lt;/h2&gt;
&lt;p&gt;仕組みを理解してしまえば、アラートの種類の追加やHTML構造の変更も自由自在です。既製プラグインをそのまま使うより、一度自分で実装してみることをおすすめします。&lt;/p&gt;</content:encoded></item><item><title>このサイトを純粋なCSSからTailwindCSS + DaisyUIへ移行した</title><link>https://r-dev95.netlify.app/blogs/0005-update-mysite-ui/</link><guid isPermaLink="true">https://r-dev95.netlify.app/blogs/0005-update-mysite-ui/</guid><description>このサイトを純粋なCSSからTailwindCSS + DaisyUIへ移行することでデザインを刷新しました。</description><pubDate>Thu, 28 May 2026 23:28:31 GMT</pubDate><content:encoded>&lt;p&gt;このサイトのスタイルを、純粋なCSS運用からTailwindCSS + DaisyUIベースへ移行しました。&lt;/p&gt;
&lt;p&gt;結論、ブログサイトとして視認性のいいシンプルな見た目になって満足しています。&lt;/p&gt;
比較画像&lt;div&gt;&lt;h3&gt;ホーム&lt;/h3&gt;&lt;h3&gt;旧&lt;/h3&gt;&lt;h3&gt;新&lt;/h3&gt;&lt;h3&gt;ブログ一覧&lt;/h3&gt;&lt;h3&gt;旧&lt;/h3&gt;&lt;h3&gt;新&lt;/h3&gt;&lt;h3&gt;ブログ詳細&lt;/h3&gt;&lt;h3&gt;旧&lt;/h3&gt;&lt;h3&gt;新&lt;/h3&gt;&lt;/div&gt;
&lt;h2&gt;純粋なCSSは設計することが多すぎる&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;純粋なCSSを真面目に運用していくと、結局CSSフレームワークを自作しているのと変わらなくなる。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;最初は普通にCSSを書いていました。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;.card&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;padding&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;16&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;border-radius&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;12&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;border&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;solid&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;#000&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;background&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;white&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;もちろん最初はこれで十分です。&lt;/p&gt;
&lt;p&gt;しかし、画面数やコンポーネントが増えていくと、徐々に「ルール」が欲しくなってきます。&lt;/p&gt;
&lt;p&gt;例えば、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;トークン設計
&lt;ul&gt;
&lt;li&gt;spacing（margin / padding基準）&lt;/li&gt;
&lt;li&gt;color（テーマ・ダークモード含む）&lt;/li&gt;
&lt;li&gt;radius / shadow / typography&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;コンポーネント設計
&lt;ul&gt;
&lt;li&gt;variants（button, card等のバリエーション定義）&lt;/li&gt;
&lt;li&gt;state（hover, focus, disabledの統一）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;レイアウト設計
&lt;ul&gt;
&lt;li&gt;レスポンシブの方針（breakpoint・単位の統一）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CSS設計ルール
&lt;ul&gt;
&lt;li&gt;命名規則&lt;/li&gt;
&lt;li&gt;責務分離（何をどこに書くか）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;など。&lt;/p&gt;
&lt;p&gt;やればやるほど考えることが増えていって、無限に増え続ける設計を前に絶望します。&lt;/p&gt;
&lt;p&gt;その結果、「TailwindCSSやDaisyUIでいいや」となります。&lt;/p&gt;
&lt;h2&gt;個人開発でやるにはコストが高すぎる&lt;/h2&gt;
&lt;p&gt;もちろん、「自前CSSフレームワークを作る」こと自体は悪くないです。&lt;/p&gt;
&lt;p&gt;設計コストや開発コストを払ってでも、そのほうが長期的に得ならやればよいと思います。&lt;/p&gt;
&lt;p&gt;ただ、個人開発の範疇だと、かなり割に合わないと感じました。&lt;/p&gt;
&lt;p&gt;新しいUIを作るたびに、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ユーティリティを追加するか？&lt;/li&gt;
&lt;li&gt;コンポーネント化するか？&lt;/li&gt;
&lt;li&gt;variantを増やすか？&lt;/li&gt;
&lt;li&gt;例外CSSで逃げるか？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;みたいな判断が発生して、かなり消耗します。&lt;/p&gt;
&lt;h2&gt;移行してよかったこと&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;意思決定が減った&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;フレームワークのユーティリティクラスを使えば、追加も削除も躊躇する必要がありません。デザイントークン設計を自分でやる必要がなく、&lt;code&gt;p-4&lt;/code&gt;や&lt;code&gt;text-base-content&lt;/code&gt;のような既存の語彙に乗れるのが楽です。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;最低限の見た目が担保される&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;DaisyUIでは&lt;code&gt;btn&lt;/code&gt;や&lt;code&gt;card&lt;/code&gt;といったクラス一つで、それなりに整ったUIが出来上がります。ゼロから&lt;code&gt;hover&lt;/code&gt;状態や&lt;code&gt;focus-visible&lt;/code&gt;を書かなくていいのは、地味に大きいです。&lt;/p&gt;
&lt;h2&gt;気になった点・注意すること&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;クラス名が長くなる&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;TailwindCSSの宿命として、HTMLにクラスが並ぶため実装が長くなります。
気になる人はすごい気にするやつですね。&lt;/p&gt;
&lt;p&gt;私もはじめは気になっていましたが、だんだんと慣れてきたのとあまりに実装が楽になるので気にならなくなってきました。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;フレームワークっぽさが残る&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;当然ながら、そのまま使うとかなり「DaisyUIっぽさ」が出ます。&lt;/p&gt;
&lt;p&gt;細かいカスタマイズをしようとすると、DaisyUIのデフォルトを上書きする必要があり、完全にコントロールしたい箇所は、素のTailwindCSSで書いたほうがいいこともあるかもしれません。&lt;/p&gt;
&lt;h2&gt;まとめ&lt;/h2&gt;
&lt;p&gt;個人ブログを純粋なCSSからTailwindCSS + DaisyUIへ移行して、デザインを刷新しました。&lt;/p&gt;
&lt;p&gt;個人的にはシンプルで見やすいサイトに生まれ変わったなと思っています。&lt;/p&gt;
&lt;p&gt;純粋なCSSを突き詰めるのも面白いですが、個人開発では、&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;「いい感じの見た目を、ほどほどのコストで得る」&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;ことの価値はかなり大きいと思いました。&lt;/p&gt;</content:encoded></item><item><title>Windows 向けの画面録画アプリ OpRec を開発しました</title><link>https://r-dev95.netlify.app/blogs/0004-introduce-oprec/</link><guid isPermaLink="true">https://r-dev95.netlify.app/blogs/0004-introduce-oprec/</guid><description>OpRec は、クリックハイライト・キー表示・ズーム機能を備えた Windows 向け画面録画アプリです。C# / .NET 8 / WinUI 3 で構築されており、操作説明動画の作成に特化した機能を揃えています。</description><pubDate>Sat, 25 Apr 2026 00:35:18 GMT</pubDate><content:encoded>&lt;p&gt;操作説明の動画を作りたいとき、こんな悩みはありませんか？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;「クリックした場所がわかりにくい」&lt;/li&gt;
&lt;li&gt;「どのキーを押したか視聴者に伝えにくい」&lt;/li&gt;
&lt;li&gt;「注目ポイントをズームして見せたい」&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;そんな課題を解消するために、Windows 向けの画面録画アプリ &lt;strong&gt;OpRec&lt;/strong&gt; を開発しました。&lt;/p&gt;
&lt;a href=&quot;https://github.com/r-dev95/OpRec&quot; target=&quot;_blank&quot;&gt;&lt;figure&gt;&lt;/figure&gt;&lt;div&gt;&lt;div&gt;GitHub - r-dev95/OpRec&lt;/div&gt;&lt;p&gt;Contribute to r-dev95/OpRec development by creating an account on GitHub.&lt;/p&gt;&lt;p&gt;GitHub&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;
&lt;h2&gt;OpRec とは&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;OpRec&lt;/strong&gt; は、クリックハイライト・キー表示・ズーム機能を備えた Windows 向け画面録画アプリです。C# / .NET 8 / WinUI 3 で構築されており、操作説明動画の作成に特化した機能を揃えています。&lt;/p&gt;
&lt;h2&gt;主な機能&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;📹 画面録画：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;任意の範囲をドラッグで選択して録画できます。システム音声・マイク音声の同時録音にも対応しています。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;🖱️ クリックハイライト（録画用オーバーレイ）：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;マウスクリックした位置をビジュアルでハイライト表示します。視聴者がどこを操作しているかが一目でわかります。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;⌨️ キー表示（録画用オーバーレイ）：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;押下したキー入力をオーバーレイで表示します。ショートカットキーの説明動画などで重宝します。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;🔍 ズーム表示（録画用オーバーレイ）：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;録画中に特定箇所をズームして見せることができます。細かいUIの操作説明に便利です。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;🗺️ ミニマップ（ガイド用オーバーレイ）：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;ズーム位置を示す枠線とミニマップを、録画対象外のオーバーレイ画面に表示します。録画者自身が現在のズーム位置を把握できます。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;⚙️ 設定の保存と管理：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;ホットキー・映像品質・FPS・音声キャプチャモードなどの設定を保存・管理できます。&lt;/p&gt;
&lt;h2&gt;デモ&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;操作イメージ：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;録画イメージ：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;使い方&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;アプリを起動する&lt;/li&gt;
&lt;li&gt;設定画面でホットキーや品質を調整する&lt;/li&gt;
&lt;li&gt;録画範囲をドラッグして選択する&lt;/li&gt;
&lt;li&gt;ホットキーまたは操作ボタンで録画開始 / 停止する&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;アーキテクチャ&lt;/h2&gt;
&lt;p&gt;クリーンアーキテクチャをベースに、以下の4層で設計しています。&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;レイヤー&lt;/th&gt;&lt;th&gt;役割&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Presentation&lt;/td&gt;&lt;td&gt;UI（WinUI 3）と ViewModel（MVVM）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Application&lt;/td&gt;&lt;td&gt;ユースケース・セッション状態管理・ポート定義&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Infrastructure&lt;/td&gt;&lt;td&gt;録画・入力・設定の実装&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Domain&lt;/td&gt;&lt;td&gt;ドメインモデル・設定値&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;依存の方向は常に内側（Domain）に向かい、Application と Infrastructure の境界はインターフェースで分離しています。詳細は &lt;a href=&quot;https://github.com/r-dev95/OpRec/blob/main/docs/architecture.md&quot;&gt;architecture.md&lt;/a&gt; をご参照ください。&lt;/p&gt;
&lt;h2&gt;技術スタック&lt;/h2&gt;





































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;種別&lt;/th&gt;&lt;th&gt;内容&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;言語&lt;/td&gt;&lt;td&gt;C#&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;フレームワーク&lt;/td&gt;&lt;td&gt;.NET 8 / WinUI 3 (Windows App SDK)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;アーキテクチャ&lt;/td&gt;&lt;td&gt;クリーンアーキテクチャ, MVVM&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;音声処理&lt;/td&gt;&lt;td&gt;Windows.Media, NAudio&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;DI&lt;/td&gt;&lt;td&gt;Microsoft.Extensions.DependencyInjection / Hosting&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ログ&lt;/td&gt;&lt;td&gt;Microsoft.Extensions.Logging, NLog&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;UI部品&lt;/td&gt;&lt;td&gt;CommunityToolkit.WinUI, WinUIEx&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;おわりに&lt;/h2&gt;
&lt;p&gt;既存ツールでは痒いところに手が届かないと感じたことが開発のきっかけです。
ちょっとした操作説明動画を作る際にあったらいいなって思った機能を詰め込んでみました。&lt;/p&gt;
&lt;p&gt;フィードバックや Issue・PR など歓迎しています！&lt;/p&gt;
&lt;a href=&quot;https://github.com/r-dev95/OpRec&quot; target=&quot;_blank&quot;&gt;&lt;figure&gt;&lt;/figure&gt;&lt;div&gt;&lt;div&gt;GitHub - r-dev95/OpRec&lt;/div&gt;&lt;p&gt;Contribute to r-dev95/OpRec development by creating an account on GitHub.&lt;/p&gt;&lt;p&gt;GitHub&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;</content:encoded></item><item><title>このサイトを Vite + Vanilla JS から Astro に移行した話</title><link>https://r-dev95.netlify.app/blogs/0003-migrate-mysite/</link><guid isPermaLink="true">https://r-dev95.netlify.app/blogs/0003-migrate-mysite/</guid><description>このサイトを Vite + Vanilla JS から Astro に移行してみた感想を書いています。</description><pubDate>Tue, 14 Apr 2026 13:14:55 GMT</pubDate><content:encoded>&lt;p&gt;このサイトを &lt;strong&gt;Vite + Vanilla JS&lt;/strong&gt; から &lt;strong&gt;Astro&lt;/strong&gt; に移行しました。&lt;/p&gt;
&lt;p&gt;併せて、ホスティング先を &lt;strong&gt;Github Pages&lt;/strong&gt; から &lt;strong&gt;Netlify&lt;/strong&gt; に変更したので、&lt;strong&gt;&lt;a href=&quot;https://r-dev95.github.io/mysite/&quot;&gt;旧サイト&lt;/a&gt;&lt;/strong&gt; は、まだ存在しますが&lt;strong&gt;今後、運用しません&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;はじめに&lt;/h2&gt;
&lt;p&gt;今回、Vite + Vanilla JS で構築していた個人ブログサイトを Astro に移行してみました。&lt;/p&gt;
&lt;p&gt;私は Web 開発初心者で HTML / CSS / JavaScript の基礎を学習するために、旧サイトでは Vite + Vanilla JS を選択しました。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;詳細：&lt;/strong&gt;&lt;/p&gt;
&lt;a href=&quot;https://r-dev95.netlify.app/blogs/0001-nice-to-meet-you/&quot; target=&quot;_blank&quot;&gt;&lt;figure&gt;&lt;/figure&gt;&lt;div&gt;&lt;div&gt;はじめまして。静的な個人ブログサイトを作りました | R.dev&lt;/div&gt;&lt;p&gt;Web初心者が静的な個人ブログサイトをVite + vanilla JSで構築した話です。&lt;/p&gt;&lt;p&gt;r-dev95.netlify.app&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;
&lt;p&gt;そして当初から「基礎を学んだらモダンな技術を使って実装しなおそう」と思っていました。&lt;/p&gt;
&lt;p&gt;そこで個人のブログサイトを開発するのに一番よさそうなフレームワークとして Astro を選択しました。&lt;/p&gt;
&lt;p&gt;「&lt;a href=&quot;https://docs.astro.build/ja/concepts/why-astro/&quot;&gt;Astroを選ぶ理由&lt;/a&gt;」の冒頭：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Astroは、ブログやマーケティング、eコマースなど、コンテンツ駆動のウェブサイトを作成するためのウェブフレームワークです。Astroは、新しいフロントエンドアーキテクチャを開拓し、他のフレームワークと比較してJavaScriptのオーバーヘッドと複雑さを低減することで知られています。高速でSEOに優れたウェブサイトが必要なら、Astroが最適です。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Next.js などの選択肢も考えられましたが、私が開発したいサイトには機能が過剰かなと思い選択しませんでした。&lt;/p&gt;
&lt;h2&gt;移行前後の構成&lt;/h2&gt;


















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;項目&lt;/th&gt;&lt;th&gt;旧サイト&lt;/th&gt;&lt;th&gt;新サイト&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;ビルドツール&lt;/td&gt;&lt;td&gt;&lt;code&gt;Vite&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Astro&lt;/code&gt; (内部で &lt;code&gt;Vite&lt;/code&gt; を使用)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;フロントエンド&lt;/td&gt;&lt;td&gt;&lt;code&gt;Vanilla JS&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Astro&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;記事管理&lt;/td&gt;&lt;td&gt;&lt;code&gt;Markdown&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Markdown&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Markdown処理&lt;/td&gt;&lt;td&gt;&lt;code&gt;gray-matter&lt;/code&gt; / &lt;code&gt;remark&lt;/code&gt; / &lt;code&gt;rehype&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Astro&lt;/code&gt;  (内部で &lt;code&gt;remark&lt;/code&gt; / &lt;code&gt;rehype&lt;/code&gt; を使用)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;コードハイライト&lt;/td&gt;&lt;td&gt;&lt;code&gt;Shiki&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Shiki&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;OGP画像生成&lt;/td&gt;&lt;td&gt;&lt;code&gt;sharp&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;sharp&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;テスト&lt;/td&gt;&lt;td&gt;&lt;code&gt;Vitest&lt;/code&gt; + &lt;code&gt;jsdom&lt;/code&gt;&lt;/td&gt;&lt;td&gt;-&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ホスティング&lt;/td&gt;&lt;td&gt;&lt;code&gt;GitHub Pages&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Netlify&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;移行してみた感想&lt;/h2&gt;
&lt;p&gt;フレームワークなしとの比較になるので、当たり前かもしれませんが、Astro での実装は圧倒的に開発体験が良かったです。&lt;/p&gt;
&lt;p&gt;特によかった点としては、次が挙げられます。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;コンポーネント化・レイアウト化が容易&lt;/li&gt;
&lt;li&gt;マークダウン記事のページ生成が容易&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;さらに Netlify でのデプロイもかなり容易でした。&lt;/p&gt;
&lt;h3&gt;コンポーネント化・レイアウト化が容易&lt;/h3&gt;
&lt;p&gt;旧サイトでは、ビルドスクリプトでコンポーネントごとに HTML を文字列として作成していました。そのため、ビルドスクリプトの HTML 部分に対して、リントやフォーマット、シンタックスハイライトが効きません。さらにいちいちビルドしないと HTML が生成されないので &lt;code&gt;npm run dev&lt;/code&gt; で開発サーバを立ち上げた状態での開発が意味を成しません。ちょっとした変更でも開発体験が悪く大変でした。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;旧サイトのビルドスクリプトのイメージ&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;renderBaseHead&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;head&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;title&amp;gt;${&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;}&amp;lt;/title&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;meta charset=&quot;UTF-8&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;meta name=&quot;description&quot; content=&quot;${&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;}&quot; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/head&amp;gt;`&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;これを Astro では &lt;code&gt;.astro&lt;/code&gt; という形式のファイルに React の JSX ライクな実装ができます。&lt;/p&gt;
&lt;p&gt;フロントマター部分にはサーバ側で処理されるスクリプトが書けます。その下には、ほとんど生の HTML が書けます。&lt;code&gt;style&lt;/code&gt; タグや &lt;code&gt;script&lt;/code&gt; タグももちろん使えます。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;components/BaseHead.astro&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Astro.props;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&amp;gt;{title}&amp;lt;/&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;charset&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;UTF-8&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;viewport&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;width=device-width, initial-scale=1.0&quot;&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;description&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;={description} /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;head&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;また使い方も通常の HTML タグのように非常に直感的に扱えると思います。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; BaseHead &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;@/components/BaseHead.astro&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;ポストページ&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;ポストページです。&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lang&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;ja&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;BaseHead&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;={title} &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;={description} /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;さらに &lt;code&gt;slot&lt;/code&gt; タグを使用するとレイアウトコンポーネントが簡単に実装できます。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;layouts/Layout.astro&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; BaseHead &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;@/components/BaseHead.astro&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; Header &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;@/components/Header.astro&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; Footer &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;@/components/Footer.astro&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Astro.props;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;lang&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;ja&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;BaseHead&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;={title} &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;={description} /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Header&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;slot&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Footer&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;こちらも非常に簡単に使えて、&lt;code&gt;Layout&lt;/code&gt; タグで囲った子コンポーネントが &lt;code&gt;slot&lt;/code&gt; タグ部分に挿入されます。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; Layout &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;@/layouts/Layout.astro&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;ポストページ&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;ポストページです。&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Layout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;={title} &lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;={description}&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;main&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;main&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Layout&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;このようにコンポーネントやレイアウトを簡単に切り分けられたり、フロントマター内で処理した結果を HTML に組み込めるのは、可読性、保守性、拡張性が上がって開発が楽だなと思いました。&lt;/p&gt;
&lt;h3&gt;マークダウン記事のページ生成が容易&lt;/h3&gt;
&lt;p&gt;旧サイトでは、マークダウン記事のページは次の通り生成していました。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;マークダウンを読み込む&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gray-matter&lt;/code&gt; でマークダウンのフロントマターと本文を分割する&lt;/li&gt;
&lt;li&gt;本文を &lt;code&gt;remark&lt;/code&gt; と &lt;code&gt;rehype&lt;/code&gt; で HTML 化、&lt;code&gt;Shiki&lt;/code&gt; でシンタックスハイライトする&lt;/li&gt;
&lt;li&gt;記事のデータを文字列で構築した HTML に組み込み生成する&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Astro にはコンテンツコレクションという概念が存在します。&lt;/p&gt;
&lt;p&gt;コンテンツコレクションでは、各記事に共通する構造を定義するスキーマを設定できて、&lt;code&gt;Zod&lt;/code&gt; (TypeScript向けのスキーマ宣言・検証ライブラリ) で構造を検証してくれます。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/content.config.ts&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { glob } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;astro/loaders&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { defineCollection } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;astro:content&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { z } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;astro/zod&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defineCollection&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;loader: &lt;/span&gt;&lt;span&gt;glob&lt;/span&gt;&lt;span&gt;({ pattern: &lt;/span&gt;&lt;span&gt;`**/[^_]*.md`&lt;/span&gt;&lt;span&gt;, base: &lt;/span&gt;&lt;span&gt;&apos;./src/posts&apos;&lt;/span&gt;&lt;span&gt; }),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;schema: z.&lt;/span&gt;&lt;span&gt;object&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;title: z.&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;summary: z.&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;tags: z.&lt;/span&gt;&lt;span&gt;array&lt;/span&gt;&lt;span&gt;(z.&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;()),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;createdAt: z.&lt;/span&gt;&lt;span&gt;date&lt;/span&gt;&lt;span&gt;(),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}),&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;collections&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; { posts };&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Astro では&lt;strong&gt;ファイルベースルーティング&lt;/strong&gt;を採用していて、動的ルーティングでパラメータを持たせることで、1つのファイルから複数のページを生成できます。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;getCollection()&lt;/code&gt; でコンテンツデータを取得し、&lt;code&gt;getStaticPaths()&lt;/code&gt; で動的ルーティングで持たせるパラメータ (ここでいうと slug)を設定することで各ページを生成します。&lt;code&gt;[slug]&lt;/code&gt; ではなく &lt;code&gt;[...slug]&lt;/code&gt; とすることで多階層に対応できます。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;render()&lt;/code&gt; でマークダウンの HTML 変換とコードブロックのシンタックスハイライトなどのプラグイン処理が行われ、Astro コンポーネントが返されます。&lt;/p&gt;
&lt;p&gt;HTML 変換関連には &lt;code&gt;remark&lt;/code&gt; と &lt;code&gt;rehype&lt;/code&gt; が、シンタックスハイライトには &lt;code&gt;Shiki&lt;/code&gt; または &lt;code&gt;Prism&lt;/code&gt; (選択式) が採用されています。この構成は旧サイトと同じだったのでレンダリング結果も特に違和感なく使用できました。&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/pages/posts/[...slug].astro&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { getCollection, render } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;astro:content&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; PostLayout &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;@/layouts/PostLayout.astro&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getStaticPaths&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;posts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;getCollection&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;posts&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; posts.&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;params&lt;/span&gt;&lt;span&gt;: { &lt;/span&gt;&lt;span&gt;slug&lt;/span&gt;&lt;span&gt;: post.id },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;props&lt;/span&gt;&lt;span&gt;: { post },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Astro.props;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;Content&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;(post);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;PostLayout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;post&lt;/span&gt;&lt;span&gt;={post}&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Content&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;PostLayout&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h3&gt;Netlify でのホスティングが容易&lt;/h3&gt;
&lt;p&gt;旧サイトでは、Github Pages にホスティングしていましたが、フリー枠ではプライベートリポジトリでのホスティングができません。
そのため Github Actions を利用して、プライベートリポジトリでソースコードを管理して、パブリックリポジトリにビルド結果のみをプッシュして、サイトを公開する形をとりました。&lt;/p&gt;
&lt;p&gt;一方、Netlify では Github のリポジトリを連携するだけで、公開ができました。プライベートリポジトリでも連携可能です。
さらにリポジトリにプッシュすれば、Netlify のビルド処理が走り、レビュー用のサイトURLが提供されるため、マージ前の確認が容易にできます。&lt;/p&gt;
&lt;p&gt;旧サイトのデプロイでも特に難しいことがあったわけではないですが、Netlify でのデプロイがあまりにも簡単で驚きました。&lt;/p&gt;
&lt;h2&gt;おわりに&lt;/h2&gt;
&lt;p&gt;今回、ブログサイトを Vite + Vanilla JS から Astro へ移行してみて、フレームワークありでの開発があまりにも体験がいいということを改めて感じさせられました。&lt;/p&gt;
&lt;p&gt;まずは「移行を完了させる」ことを優先したため、CSSはほとんど以前のものを持ってきています。そのため Astro の特徴に合わせたコンポーネント単位のスタイル整理はできていません。&lt;/p&gt;
&lt;p&gt;今後は次を進めていけたらと思います。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;コンポーネントごとのスタイル責務の整理&lt;/li&gt;
&lt;li&gt;CSS 構成の見直し&lt;/li&gt;
&lt;li&gt;Tailwind CSS 導入&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>このサイトのUIとOGP画像を修正しました</title><link>https://r-dev95.netlify.app/blogs/0002-change-mysite-ui/</link><guid isPermaLink="true">https://r-dev95.netlify.app/blogs/0002-change-mysite-ui/</guid><description>このサイトのUIと、ブログ詳細ページ用のOGP画像の文字化けを修正した内容と反省点をまとめました。</description><pubDate>Thu, 09 Apr 2026 05:15:55 GMT</pubDate><content:encoded>&lt;p&gt;この記事は本サイトへの移行に当たって、旧サイトの記事をそのまま持ってきたものになります。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;旧サイト：&lt;/strong&gt;&lt;/p&gt;
&lt;a href=&quot;https://r-dev95.github.io/mysite/&quot; target=&quot;_blank&quot;&gt;&lt;figure&gt;&lt;/figure&gt;&lt;div&gt;&lt;div&gt;R.dev&lt;/div&gt;&lt;p&gt;R.dev の個人ブログサイトです。&lt;/p&gt;&lt;p&gt;r-dev95.github.io&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;
&lt;hr /&gt;
&lt;p&gt;このサイトの UI と OGP 画像について、気になっていた部分を修正したので、作業内容とあわせて反省点もまとめておきます。&lt;/p&gt;
&lt;h2&gt;UIの修正&lt;/h2&gt;
&lt;h3&gt;記事リンクカードのタイトル表示&lt;/h3&gt;
&lt;p&gt;記事リンクカードは、タイトルが長くなると折り返さず省略するようにしていました。そのため視聴端末によっては幅が狭く、タイトルの半分も見えないような状態でした。&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;そこで、長いタイトルが入っても2行分は見られるように表示を調整しました。&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;ページ移動ボタンの見た目&lt;/h3&gt;
&lt;p&gt;記事詳細ページの前後移動ボタンも、最初は状態の違いが少し分かりにくい見た目になっていました。&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;特に、移動できないときのボタンが通常時とあまり変わらず、押せるのか押せないのかが伝わりにくかったため、&lt;code&gt;disabled&lt;/code&gt; 相当の状態が見た目でも分かるように調整しました。&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;OGP画像の修正&lt;/h2&gt;
&lt;p&gt;ブログ詳細ページ用の OGP 画像はローカルでは問題なく生成できていたのですが、GitHub Actions を使って Ubuntu 上でビルドしたときに文字化けしてしまう問題がありました。&lt;/p&gt;
&lt;p&gt;調べてみると、原因はビルド環境ごとのフォント差分でした。Windows では表示できていても、Ubuntu 側では同じフォントが使えず、日本語の描画が崩れていました。&lt;/p&gt;
&lt;p&gt;そのため、Ubuntu 上でも利用できる日本語フォントを使うように見直し、GitHub Actions 側でもフォントをインストールするよう修正しました。&lt;/p&gt;
&lt;h2&gt;まとめ&lt;/h2&gt;
&lt;p&gt;今回の修正を通して、いくつか反省点がありました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;短いタイトルや限られた表示パターンだけで確認すると、実際の運用で崩れるポイントを見落としやすい&lt;/li&gt;
&lt;li&gt;ボタンやリンクは、動くだけでなく状態が見た目で伝わることも大事&lt;/li&gt;
&lt;li&gt;ローカルで問題がなくても、CI や本番に近い環境では別の問題が起きることがある&lt;/li&gt;
&lt;li&gt;見た目に関する機能でも、環境依存やフォント差分まで考える必要がある&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;UI を整える作業は見た目の微調整に見えますが、実際には使いやすさや保守性にも関わる部分だとあらためて感じました。&lt;/p&gt;
&lt;p&gt;今後は、作ったあとにすぐ公開するのではなく、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;長いタイトルや例外的なデータでも確認する&lt;/li&gt;
&lt;li&gt;状態違いを含めて UI を見る&lt;/li&gt;
&lt;li&gt;ローカルだけでなく CI 環境でも確認する&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;といった流れを、最初から意識して進めていきたいです。&lt;/p&gt;</content:encoded></item><item><title>はじめまして。静的な個人ブログサイトを作りました</title><link>https://r-dev95.netlify.app/blogs/0001-nice-to-meet-you/</link><guid isPermaLink="true">https://r-dev95.netlify.app/blogs/0001-nice-to-meet-you/</guid><description>Web初心者が静的な個人ブログサイトをVite + vanilla JSで構築した話です。</description><pubDate>Wed, 08 Apr 2026 14:26:36 GMT</pubDate><content:encoded>&lt;p&gt;この記事は本サイトへの移行に当たって、旧サイトの記事をそのまま持ってきたものになります。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;旧サイト：&lt;/strong&gt;&lt;/p&gt;
&lt;a href=&quot;https://r-dev95.github.io/mysite/&quot; target=&quot;_blank&quot;&gt;&lt;figure&gt;&lt;/figure&gt;&lt;div&gt;&lt;div&gt;R.dev&lt;/div&gt;&lt;p&gt;R.dev の個人ブログサイトです。&lt;/p&gt;&lt;p&gt;r-dev95.github.io&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;
&lt;hr /&gt;
&lt;p&gt;はじめまして。
Web技術を学ぶために、静的な個人ブログサイトを作りました。&lt;/p&gt;
&lt;p&gt;これまでに TypeScript、React、Next.js、Tailwind CSS、shadcn など、いわゆるモダンで流行りの技術に触れたことはありました。実際、見た目が整ったものや、ひとまず動くものを作ること自体はできていました。&lt;/p&gt;
&lt;p&gt;ただ、その一方で「自分はいま何をフレームワークに助けられていて、何がWebの基礎そのものなのか」をきちんと理解できているかというと、あまり自信がありませんでした。&lt;/p&gt;
&lt;p&gt;特に、JavaScript、HTML、CSS の理解が浅いままだと、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;どこまでがモダンな技術の独自要素なのか&lt;/li&gt;
&lt;li&gt;その技術が何を楽にしてくれているのか&lt;/li&gt;
&lt;li&gt;そもそも何がうれしいのか&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;が、感覚としてつかみにくいと感じました。&lt;/p&gt;
&lt;p&gt;そこで今回は、いったん基礎から学習しなおすつもりで、最初から大きな仕組みを入れすぎず、まずは自分で理解しながら作れる構成にしたいと考えました。そこで今回は、&lt;strong&gt;Vite + Vanilla JS&lt;/strong&gt; を使って、できるだけシンプルに、でも実際の運用まで見据えたブログサイトを組み立てています。&lt;/p&gt;
&lt;p&gt;この記事では、最初の投稿として&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;なぜ静的サイトにしたのか&lt;/li&gt;
&lt;li&gt;どんな機能を実装したのか&lt;/li&gt;
&lt;li&gt;どんな技術構成にしたのか&lt;/li&gt;
&lt;li&gt;作ってみて感じたこと&lt;/li&gt;
&lt;li&gt;今後どんな形で作り直してみたいか&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;をまとめてみます。&lt;/p&gt;
&lt;h2&gt;なぜ静的サイトにしたのか&lt;/h2&gt;
&lt;p&gt;今回、まず大事にしたかったのは「自分で把握できる範囲で安全に作ること」でした。&lt;/p&gt;
&lt;p&gt;Webアプリにはログイン、データベース、フォーム送信、API通信など、学ぶと面白そうな要素がたくさんあります。ただ、初心者の段階でそれらを一気に扱うと、仕組みを理解する前に複雑さだけが増えてしまいそうだと感じました。&lt;/p&gt;
&lt;p&gt;そこで最初のブログは、サーバーサイドの処理を持たない&lt;strong&gt;静的サイト&lt;/strong&gt;として構築しました。静的サイトであれば、攻撃対象になりやすい要素をかなり減らせますし、配信もシンプルになります。もちろん静的サイトだから絶対安全というわけではありませんが、少なくとも最初の学習対象としてはとても扱いやすい選択だったと思っています。&lt;/p&gt;
&lt;p&gt;また、HTML・CSS・JavaScript の基本を自分の手で触って理解したかったので、あえてフレームワークに頼りすぎず、Vanilla JS で組んでみました。&lt;/p&gt;
&lt;h2&gt;今回のサイトで実装したこと&lt;/h2&gt;
&lt;p&gt;見た目はシンプルなブログですが、中ではいくつかの機能を自作しています。&lt;/p&gt;
&lt;h3&gt;1. トップページ&lt;/h3&gt;
&lt;p&gt;トップページには、自己紹介用の &lt;code&gt;About&lt;/code&gt; セクションと、最新記事を表示する &lt;code&gt;Post&lt;/code&gt; セクションを用意しました。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Post&lt;/code&gt; セクションには新しい記事を最大3件表示するようにしていて、一覧ページへの導線も置いています。個人サイトらしく、SNS へのリンクもヘッダーやトップページからたどれるようにしています。&lt;/p&gt;
&lt;h3&gt;2. 記事一覧ページ&lt;/h3&gt;
&lt;p&gt;記事一覧ページでは、投稿済みの記事をまとめて確認できます。&lt;/p&gt;
&lt;p&gt;実装としては、ビルド時に生成した記事データをもとに一覧を表示していて、&lt;strong&gt;タグによる絞り込み&lt;/strong&gt;もできるようにしました。タグの切り替えはクライアントサイドの JavaScript で動かしていて、静的サイトのままでも少しインタラクティブな操作ができるようになっています。&lt;/p&gt;
&lt;h3&gt;3. 記事詳細ページ&lt;/h3&gt;
&lt;p&gt;各記事には個別ページを用意しています。&lt;/p&gt;
&lt;p&gt;記事詳細ページでは、以下のような情報を表示しています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;タイトル&lt;/li&gt;
&lt;li&gt;投稿日&lt;/li&gt;
&lt;li&gt;タグ&lt;/li&gt;
&lt;li&gt;本文&lt;/li&gt;
&lt;li&gt;前後の記事への移動リンク&lt;/li&gt;
&lt;li&gt;X 共有リンク&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;単に Markdown を表示するだけでなく、ブログとして最低限使いやすい導線も意識しました。&lt;/p&gt;
&lt;h3&gt;4. Markdown から記事ページを生成&lt;/h3&gt;
&lt;p&gt;記事は Markdown で書けるようにしています。&lt;/p&gt;
&lt;p&gt;各記事は &lt;code&gt;content/posts/&amp;lt;slug&amp;gt;/index.md&lt;/code&gt; のような構成で管理し、ビルド時に&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;トップページ&lt;/li&gt;
&lt;li&gt;記事一覧ページ&lt;/li&gt;
&lt;li&gt;各記事の詳細ページ&lt;/li&gt;
&lt;li&gt;記事一覧用の JSON データ&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;をまとめて生成する仕組みにしています。&lt;/p&gt;
&lt;p&gt;記事本文の変換には &lt;code&gt;gray-matter&lt;/code&gt;、&lt;code&gt;remark&lt;/code&gt;、&lt;code&gt;rehype&lt;/code&gt; を使っていて、Markdown の frontmatter からタイトルや概要、タグ、投稿日などを扱えるようにしています。さらに、コードブロックには &lt;code&gt;Shiki&lt;/code&gt; を使ってシンタックスハイライトも入れました。&lt;/p&gt;
&lt;h3&gt;5. 記事ごとの OGP 画像生成&lt;/h3&gt;
&lt;p&gt;各記事ページごとに、記事タイトルを使った&lt;strong&gt;OGP画像を自動生成&lt;/strong&gt;する仕組みを入れています。テンプレート画像にタイトル文字を合成して、SNS 共有時に記事ごとの見た目が変わるようにしました。&lt;/p&gt;
&lt;h2&gt;技術構成&lt;/h2&gt;
&lt;p&gt;今回の主な構成は次のようになっています。&lt;/p&gt;









































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;項目&lt;/th&gt;&lt;th&gt;内容&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;ビルドツール&lt;/td&gt;&lt;td&gt;&lt;code&gt;Vite&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;フロントエンド&lt;/td&gt;&lt;td&gt;&lt;code&gt;Vanilla JS&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;記事管理&lt;/td&gt;&lt;td&gt;&lt;code&gt;Markdown&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Markdown処理&lt;/td&gt;&lt;td&gt;&lt;code&gt;gray-matter&lt;/code&gt; / &lt;code&gt;remark&lt;/code&gt; / &lt;code&gt;rehype&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;コードハイライト&lt;/td&gt;&lt;td&gt;&lt;code&gt;Shiki&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;OGP画像生成&lt;/td&gt;&lt;td&gt;&lt;code&gt;sharp&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;テスト&lt;/td&gt;&lt;td&gt;&lt;code&gt;Vitest&lt;/code&gt; + &lt;code&gt;jsdom&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ホスティング&lt;/td&gt;&lt;td&gt;&lt;code&gt;GitHub Pages&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;フロントエンドはかなり素朴な構成ですが、そのぶん「何がどこで動いているのか」を追いやすいのがよかったです。&lt;/p&gt;
&lt;p&gt;CSS も分割して管理していて、全体の土台、レイアウト、共通コンポーネント、記事一覧、記事詳細、本文表示というように役割ごとに整理しています。大きなライブラリに頼らず、少しずつ積み上げていく練習にもなりました。&lt;/p&gt;
&lt;h2&gt;公開フローと運用&lt;/h2&gt;
&lt;p&gt;今回、個人的にこだわったポイントのひとつが公開フローです。&lt;/p&gt;
&lt;p&gt;ソースコード本体は&lt;strong&gt;プライベートリポジトリ&lt;/strong&gt;で管理し、実際に公開するビルド結果は&lt;strong&gt;パブリックリポジトリ&lt;/strong&gt;で管理するようにしました。GitHub Pages で公開したかったので、GitHub Actions を使って CI/CD も組んでいます。&lt;/p&gt;
&lt;p&gt;流れとしては、プライベートリポジトリの &lt;code&gt;main&lt;/code&gt; に push すると、&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;依存関係をインストール&lt;/li&gt;
&lt;li&gt;記事ページや OGP 画像を生成&lt;/li&gt;
&lt;li&gt;サイト全体をビルド&lt;/li&gt;
&lt;li&gt;パブリックリポジトリへ成果物を同期&lt;/li&gt;
&lt;li&gt;GitHub Pages 側に公開&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;という形でデプロイされます。&lt;/p&gt;
&lt;p&gt;「ソースは公開しすぎたくないけれど、成果物は公開したい」という形を自分で組んでみたかったので、ここはかなり勉強になりました。&lt;/p&gt;
&lt;h2&gt;テストも入れてみた&lt;/h2&gt;
&lt;p&gt;まだ学習中ではありますが、今回は&lt;strong&gt;テストもなるべく書く&lt;/strong&gt;ようにしてみました。&lt;/p&gt;
&lt;p&gt;具体的には、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Markdown の変換処理&lt;/li&gt;
&lt;li&gt;記事一覧の描画&lt;/li&gt;
&lt;li&gt;記事詳細ページの描画&lt;/li&gt;
&lt;li&gt;タグ絞り込み&lt;/li&gt;
&lt;li&gt;OGP 画像生成まわり&lt;/li&gt;
&lt;li&gt;補助的なユーティリティ処理&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;などに対して、Vitest と jsdom を使ったテストを用意しています。&lt;/p&gt;
&lt;p&gt;個人開発でここまでやる必要があるかは人それぞれだと思いますが、今回は「あとで安心して触れる状態を作ること」も学習の一部だと考えました。実際、ちょっとした修正でもテストがあると気持ちがかなり楽になります。&lt;/p&gt;
&lt;h2&gt;作ってみて感じたこと&lt;/h2&gt;
&lt;p&gt;今回いちばん大きかったのは、&lt;strong&gt;シンプルな技術だけでも、思っていたよりちゃんとブログらしいものが作れる&lt;/strong&gt;と実感できたことです。&lt;/p&gt;
&lt;p&gt;最初は「Vanilla JS だけだとかなり大変なのでは」と思っていましたが、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;自分で理解しながら進めやすい&lt;/li&gt;
&lt;li&gt;どこでHTMLを作っているかが見えやすい&lt;/li&gt;
&lt;li&gt;ビルド処理の流れを学びやすい&lt;/li&gt;
&lt;li&gt;後から設計を見直しやすい&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;という点で、初心者の自分にはかなり合っていました。&lt;/p&gt;
&lt;p&gt;一方で、作っていく中で「ここはもっとモダンな技術を使うと楽になりそう」と感じる場面もたくさんありました。テンプレート管理、コンポーネントの再利用、スタイル設計、記事周りの構成などは、今後さらに学んでいきたい部分です。&lt;/p&gt;
&lt;h2&gt;今後やってみたいこと&lt;/h2&gt;
&lt;p&gt;次のステップとしては、&lt;strong&gt;Astro や Tailwind CSS を使ってブログを再構築してみたい&lt;/strong&gt;と思っています。&lt;/p&gt;
&lt;p&gt;Astro はコンテンツサイトとの相性が良さそうですし、コンポーネントベースで整理しやすそうなので、今回自作した仕組みと比べながら学べそうだと感じています。Tailwind CSS についても、ユーティリティファーストな書き方を一度しっかり触ってみたいです。&lt;/p&gt;
&lt;p&gt;今回のブログは、完成版というよりも「自分が次に進むための土台」だと思っています。&lt;/p&gt;
&lt;p&gt;まずは Vite + Vanilla JS で、静的サイトの構成、Markdown からのページ生成、デプロイ、自動化、テストまで一通り体験できました。次はそこから一歩進んで、より保守しやすく、より表現しやすい構成に挑戦していきたいです。&lt;/p&gt;
&lt;h2&gt;おわりに&lt;/h2&gt;
&lt;p&gt;Web初心者の自分にとって、今回のブログ制作は「ページを作る」だけでなく、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;構成を考える&lt;/li&gt;
&lt;li&gt;安全性を意識する&lt;/li&gt;
&lt;li&gt;公開方法を設計する&lt;/li&gt;
&lt;li&gt;自動化する&lt;/li&gt;
&lt;li&gt;継続しやすい形を考える&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ところまで含めて、とても良い学習になりました。&lt;/p&gt;
&lt;p&gt;まだまだ改善したいところはたくさんありますが、まずは自分の手でここまで形にできたことを素直にうれしく思っています。&lt;/p&gt;
&lt;p&gt;これからこのブログでは、Web 技術の学習メモや制作記録、試したこと、つまずいたことなどを少しずつ残していく予定です。&lt;/p&gt;
&lt;p&gt;もし同じように「まずはシンプルな静的サイトから始めてみたい」と考えている方がいたら、この記録が少しでも参考になればうれしいです。&lt;/p&gt;</content:encoded></item><item><title>OpRec - Windows向け画面録画アプリ</title><link>https://github.com/r-dev95/OpRec</link><guid isPermaLink="true">https://github.com/r-dev95/OpRec</guid><description>チュートリアル動画作成に特化したクリックハイライト・キー字幕・ズーム機能を持つ録画アプリです。</description><pubDate>Fri, 29 May 2026 04:25:40 GMT</pubDate></item><item><title>ZennとQiitaの記事のクロスポスト用Github Actions</title><link>https://github.com/r-dev95/sync-zenn-qiita-articles</link><guid isPermaLink="true">https://github.com/r-dev95/sync-zenn-qiita-articles</guid><description>ZennとQiitaの記事を同期して、クロスポストを実現します。</description><pubDate>Fri, 29 May 2026 04:09:29 GMT</pubDate></item></channel></rss>