ブログシステム

ブログはウェブサイトの主要なコンテンツ機能であり、記事一覧、詳細ページ、カテゴリおよびタグによるフィルタリング、リッチコンテンツレンダリングをサポートしています。

ブログ一覧

ルート: /[locale]/blog

記事をページネーション付きリスト(1ページあたり5件)で表示し、右サイドバーにカテゴリとタグのフィルターを配置します。

各記事は ArticleCard として以下の要素でレンダリングされます:

  • カバー画像(画像がない場合は SVG プレースホルダーにフォールバック)

  • カテゴリリンク(カテゴリフィルターページに遷移)

  • 公開日

  • タイトル(記事詳細へのリンク。::after オーバーレイによりカード全体がクリック可能)

  • 要約(最大3行に制限)

  • タグピル(タグフィルターページへのリンク、緑色背景でホバー時に暗転)

記事詳細

ルート: /[locale]/blog/[slug]

記事全文を以下の構成でレンダリングします:

  • ヘッダー:カテゴリ+日付のメタ情報行、タイトル、著者名

  • 本文:リッチ Markdown コンテンツ(RichMarkdown コンポーネント経由)

  • タグ:コンテンツ下部のピルリンク

  • 右サイドバー:共有ボタン、リンクコピーボタン

  • ページ下部の「お問い合わせ」ボタン

カテゴリビュー

ルート: /[locale]/blog/categories/[categorySlug]

CategoryArticleViewer コンポーネントを使用した3カラムレイアウト:

  • 左サイドバー --- 記事タイトル一覧(クリック可能なボタン、最大3行に制限)。タイトルをクリックすると中央パネルに記事を読み込み、先頭にスクロールします。

  • 中央エリア --- リッチ Markdown レンダリングによる記事全文。タイトルは独立した記事詳細ページへリンクします。

  • 右サイドバー --- 目次(スクロールスパイにより現在の見出しをハイライト)

カテゴリ内の全記事を一括読み込みします(最大100件)。ビューアは Mermaid ダイアグラム、KaTeX 数式、YouTube 埋め込み、生 HTML をサポートしています。

タグフィルター

ルート: /[locale]/blog/tags/[tagSlug]

メインのブログ一覧と同じレイアウトですが、指定されたタグを持つ記事のみにフィルタリングされます。ページネーションは1ページあたり5件です。

リッチ Markdown レンダリング

RichMarkdown コンポーネント(src/components/RichMarkdown.tsx)は react-markdown をラップし、以下のプラグインを統合しています:

プラグイン

機能

remark-gfm

GFM テーブル、取り消し線、タスクリスト、自動リンク

remark-math

インライン $...$ およびディスプレイ $$...$$ 数式構文

rehype-raw

生 HTML パススルー(YouTube iframe、埋め込み)

rehype-katex

LaTeX 数式を HTML にレンダリング

Mermaid ダイアグラム:

言語指定が mermaid のフェンスドコードブロックはインターセプトされ、MermaidBlock コンポーネントによりレンダリングされます。このコンポーネントは:

  1. Mermaid ライブラリを遅延読み込み

  2. Carbon ブルーテーマで初期化(ノード色 #d0e2ff、ボーダー色 #0f62fe、ライン色 #4589ff

  3. mermaid.render() で SVG をレンダリング

  4. パースエラー時は <pre><code> ブロックにフォールバック

対応するダイアグラムタイプ:フローチャート、シーケンス図、ガントチャート、ER図、ステートマシン。

KaTeX 数式:

  • インライン:$T = \frac{N}{t}$ をインラインでレンダリング

  • ディスプレイ:$$F^* = \arg\min_F [...]$$ を中央揃えのブロック数式としてレンダリング

  • KaTeX CSS は katex/dist/katex.min.css からインポート

記事カードのデザイン

ArticleCard コンポーネントはレイヤードクリックターゲットパターンを採用しています:

  • 外側のラッパーは <div>``(``<a> ではない)で、cursor: pointer を指定

  • タイトルの <Link>::after 擬似要素をカード全体に引き伸ばして配置(position: absolute; inset: 0

  • カテゴリとタグの <Link> 要素は position: relative; z-index: 1 でオーバーレイの上に配置し、個別にクリック可能に

これにより、無効なネストされた <a> タグを回避しつつ、カード全体のクリック可能性を維持しています。