UI コンポーネントとカスタム開発
EpoCanvas Docs の UI はゼロから作ったものではなく、Starlight のネイティブコンポーネントをベースにピンポイントでオーバーライドしたものです。Starlight のページ骨格とコンテンツ処理の能力を残しつつ、トップバー・サイドバー・目次・検索などの表示コンポーネントを差し替えることで、狙いどおりの 3 カラムレイアウトとインタラクションを実現しています。このページでは、このコンポーネント体系の構造と変更方法を説明します。
コンポーネントのオーバーライド機構
Starlight では、astro.config.mjs の components フィールドで任意のネイティブコンポーネントをカスタム実装に置き換えられます。このプロジェクトでは 7 つのコンポーネントをオーバーライドしています:
// astro.config.mjs(抜粋)components: { Header: './src/components/starlight/Header.astro', Sidebar: './src/components/starlight/Sidebar.astro', TableOfContents: './src/components/starlight/TableOfContents.astro', PageTitle: './src/components/starlight/PageTitle.astro', TwoColumnContent: './src/components/starlight/TwoColumnContent.astro', Search: './src/components/starlight/Search.astro', Pagination: './src/components/starlight/Pagination.astro',},ビルド時、Starlight がページをレンダリングする各位置では、ここで指定されたファイルが優先的に使われます。オーバーライドしていないコンポーネント(フッターの Footer やモバイルメニューなど)は引き続きネイティブ実装が使われます。
7 つのカスタムコンポーネントの役割
ソースコードはすべて src/components/starlight/ にあり、規模と役割は次のとおりです:
| コンポーネントファイル | 規模 | 役割 |
|---|---|---|
Header.astro | 約 646 行 | トップバーの全内容: ロゴ、検索ボックス、メインナビ、バージョン バッジ、言語切替、テーマ切替、GitHub と Telegram への入口 |
Search.astro | 約 837 行 | 2 モード検索: トップバーのページ内検索(ハイライトとカウント)+ Ctrl+K のサイト全体検索ポップアップ(Pagefind UI) |
Pagination.astro | 約 123 行 | 下部の「前のページ / 次のページ」カード。フラットな細枠、テーマカラーのタイトル、↙/↘ の斜め矢印でページ送りの方向を表示 |
TwoColumnContent.astro | 約 77 行 | 本文と右側目次の 2 カラム骨格。右カラムの固定幅とスクロールを制御 |
TableOfContents.astro | 約 65 行 | 「目次」の見出し、アイコンと目次リスト。ページ自身のタイトルは除外 |
PageTitle.astro | 約 65 行 | ページの大きな見出し(Frontmatter の title を使用)と「最終更新」タイムスタンプ |
Sidebar.astro | 約 22 行 | 薄いラッパー。Starlight 公式の SidebarPersister を再利用し、ページ移動時にサイドバーのスクロール位置を維持 |
データフロー: 3 つの設定ファイルが UI 全体を駆動
カスタムコンポーネント自体は業務データを持たず、UI の内容は 3 つの設定ファイルによって駆動されます:
astro.config.mjs ──→ locales + sidebar 配列 ──→ Sidebar.astro が左側カタログをレンダリング(各言語は対応する訳語ラベルを使用)src/config/navigation.ts ──→ navigationConfig ──→ Header.astro がトップナビとハイライトをレンダリング(リンクは自動的に言語プレフィックス付き)src/utils/i18n.ts ──→ UI_TRANSLATIONS 辞書 ──→ 各コンポーネントはビルド時に現在の言語で語句を取得- 左側カタログは
astro.config.mjsのsidebar宣言のみを参照します。新規ドキュメントは必ずここに登録する必要があり、各エントリのtranslationsフィールドで 10 言語分のメニュー文言を提供します。 - トップナビの各項目の表示文言は
labelKeyでi18n.tsの辞書から翻訳を取得し、match関数が現在ページでどのボタンをハイライトするかを決定します(判定の前に言語プレフィックスを除去します)。 - UI 文言(検索ボックスのプレースホルダー、「目次」の見出し、テーマ切替の案内など)は、各コンポーネントが
getTranslation(key, lang)を呼び出してビルド時に対応する言語を直接出力します。ページ内に実行時の置換スクリプトはありません。
つまり、UI の内容を変えたい場合はまず対応する設定ファイルを探し、見た目(余白・色・アイコン)だけを変える場合にのみコンポーネントのソースコードを触ります。
各コンポーネントの実装のポイント
PageTitle: ページタイトルと実際の更新日時
ページの大きな見出しは Frontmatter の title をそのまま読み込むため、本文中に # の見出し(レベル 1)を書かないでください。「最終更新」のタイムスタンプはビルド時の Git コミット履歴から取得されます(astro.config.mjs で lastUpdated: true が有効)。コミットのたびに自動更新されるため、手動での管理は不要です。
Sidebar: スクロール位置記憶の仕組み
Sidebar.astro は 20 行余りしかなく、核心は Starlight 公式の SidebarPersister コンポーネントを再利用している点です。ページ切替時にサイドバーの DOM を再構築しないため、スクロール位置が保たれます。これが左側カタログが「ページを移動してもガタつかない」仕組みです。
TableOfContents: 目次の生成
目次データは、Starlight がビルド時に本文の見出し(## と ###)を解析して生成します。コンポーネントはページ自身のタイトルを除外してレンダリングするだけです。スクロール時のハイライトは starlight-toc カスタム要素がブラウザ側で処理し、フレームワークには依存しません。
TwoColumnContent: 右カラム幅の唯一の定義元
右側目次カラムの幅は @media (min-width: 72rem) で 20rem に固定され(超ワイド画面の 90rem 以上では 21rem)、本文エリアの最大幅はそこから右カラム幅が差し引かれます。右カラム幅を調整したい場合は、この 1 ファイルだけを変更すればよく、他のスタイルシートに点在する形で上書きしないでください。
Header: ナビ、テーマ、言語
- ナビボタンは
navigationConfigを走査してレンダリングされ、アクティブ状態のスタイルはmatch関数の戻り値で決まります。リンクはlocalizedHref()により現在言語のプレフィックスが自動付加されます。 - テーマ切替は LocalStorage の
starlight-themeキーに書き込まれ、ページ読み込み時は「ローカルでの選択 → システム設定」の順に初期テーマを決定します。 - 言語ドロップダウンの各項目は、現在ページの対応言語版への実リンクです。クリックするとそのまま遷移し、追加の状態保存はありません。
- トップバー右側の GitHub と Telegram の両アイコンは
src/components/starlight/Header.astroに直接書き込まれています。アドレスを変えるにはこのファイルを編集してください。Telegram は@epocanvasアカウントページを指し、ツールチップの文言は語条social.telegramです。astro.config.mjsのsocialが制御するのはサイドバー下部のアイコン群で、両者は互いに影響しません。
Search: 2 モード検索
1 つのコンポーネント内に 2 つの検索を実装しています(詳しくは全文検索とショートカットキーの使い方を参照):
- ページ内検索: トップバーの入力ボックス。Enter キーで現在ページ内の一致文字列間を移動し、ハイライトはスクリプトがマークを打つことで実現します。
- サイト全体検索:
<dialog>ポップアップ + Pagefind のデフォルト UI。index はpnpm run buildの段階で生成されます。
Pagination: ページ送りカード
前 / 次のページのデータは、Starlight がビルド時に sidebar の順序に基づいて計算済みで(Astro.locals.starlightRoute.pagination)、コンポーネントはレンダリングだけを担当します。同幅の 2 枚のカード、細枠で影なし、タイトルはテーマカラー、↙ / ↘ の斜め矢印はホバー時にページ送りの方向へ移動します。矢印はインライン SVG パスで、サイトを RTL 言語で使う場合は自動的に方向が反転します。
カスタマイズ時の注意点
- スタイル変更はまず CSS 変数で: 色・フォント・レイアウトサイズは
src/styles/custom.cssの:root変数に集約されています。サイト全体の設定とスタイルのカスタマイズを参照してください。多くのカスタマイズはコンポーネントを触る必要がありません。 - インタラクションを変えるときだけコンポーネントを触る: ボタンの追加や構造の調整の際は、UI 文言を
getTranslation(key, lang)で取得し、i18n.tsに 10 言語分の語句を追加します。漏れた言語は fallback で中国語が表示されます。 - 変更後は必ずローカルで検証:
pnpm run devでインタラクションを確認し、pnpm run buildで型チェックとビルドの通過を確認します(ローカル用コマンドはよくある質問とトラブルシューティングを参照)。
具体的なカスタマイズ手順は、よくあるカスタマイズ早見表を参照してください。