zudo-doc
GitHub リポジトリ

検索したい単語を入力

いつでも検索バーを開ける

ホストクロームバインディング

作成 2026年7月2日更新 2026年7月11日Claude

パッケージ所有ルートのドキュメントヘッダーやホームヒーローにホスト側のコンテンツを注入する。単一のchromeBindingsモジュール経由で届けられます。

プロジェクトで packageOwnedRoutes: true を設定すると、@takazudo/zudo-doc がドキュメントルートを自前で注入し、プロジェクト側はほぼ空の pages/ ディレクトリだけを持つことになります。これは便利な反面、通常であればプロジェクト固有のレンダリングを差し込むホストファイルが失われます。ホストクロームバインディングはそれを取り戻すための継ぎ目です。型付けされた小さなスロット群(ChromeHostBindings)を通じて、コンポーネントをエジェクトすることなく、パッケージ所有のクロームにホスト独自のコンテンツやコーラブルを注入できます。

このページでは、2つのコンテンツ注入用の継ぎ目 — docContentHeaderExtrashomeExtras — に加え、それらのバインディングを注入ルートへ届ける配信チャネルである settings.chromeBindingsModule を扱います。

Note

これらの継ぎ目が主に意味を持つのは packageOwnedRoutes: true のときです。自前の pages/*.tsx スタブを引き続き持つプロジェクトは、レンダリング先のホストファイルをすでに持っており、createChrome(context, hostBindings) を直接呼び出せます。パッケージ所有ルートがどのように列挙されるかはルーティング規約を参照してください。

ChromeHostBindings

ここで説明する継ぎ目はすべて ChromeHostBindings インターフェースのフィールドで、@takazudo/zudo-doc/factory-context からエクスポートされています。

import type { ChromeHostBindings } from "@takazudo/zudo-doc/factory-context";

すべてのフィールドは任意です。省略したスロットは、継ぎ目導入前の挙動をバイト単位で再現するパッケージデフォルトにフォールバックするため、部分的なバインディングオブジェクトでも常に安全です。

ChromeHostBindings は、クローム自身の呼び出し地点が内部で必要とする広い構造的な形状で各スロットを型付けします。実際の狭い型を持つコンポーネントやコーラブルをそのまま直接代入する — あるいは代入をコンパイル可能にするために asas unknown as キャストに頼る — と、本当に重要なチェック、すなわち「渡した値がクロームから渡されるプロパティを実際に受け取れるかどうか」が消えてしまいます。代わりに @takazudo/zudo-doc/chrome-bindings からエクスポートされる defineChromeBindings でバインディングを組み立ててください。その入力型 ChromeBindingsInput は、各スロットの実際の呼び出し地点が渡す正確なプロパティや引数を宣言しているため、クロームが決して渡さないプロパティをコンポーネントが要求している場合はコンパイルエラーになります(#2674 のドリフト検出チェック)。広い ChromeHostBindings 形状は、あなたが気にする必要のない内部の単一のワイドニングステップによって生成されます。

配信チャネル: chromeBindingsModule

packageOwnedRoutes: true ではクロームがパッケージ内部で組み立てられるため、バインディングを渡すためのホスト側の呼び出し地点が存在しません。プラグインがルートへ渡すルートコンテキストはシリアライズ可能なデータのみ(settings、translations、タグ語彙)を運ぶため、関数やコンポーネントを運ぶことはできません。このチャネルはその制約を回避します。コーラブルそのものをシリアライズするのではなく、それらをエクスポートするモジュールへのパスをシリアライズするのです。

zudoDoc() 設定の chromeBindingsModule に、defineChromeBindings で組み立てた名前付き chromeBindings エクスポートを持つモジュールを指すプロジェクトルート相対パスを設定します。

zfb.config.ts
export default defineConfig(
  zudoDoc({
    // packageOwnedRoutes はデフォルトで true
    chromeBindingsModule: "./src/chrome-bindings.tsx",
  }),
);
src/chrome-bindings.tsx
import { defineChromeBindings } from "@takazudo/zudo-doc/chrome-bindings";

export const chromeBindings = defineChromeBindings({
  // ...ここに各継ぎ目を記述(後述)
});

ビルド時にルートプラグインが chromeBindings を再エクスポートする仮想モジュールを登録し、注入されたクロームシムがそれをクロームファクトリへスプレッドします。文字列パスはシリアライズ可能なので、「ルートコンテキストはデータのみ」というルールは依然として保たれます — 異なるのはローダーのソースだけです。

既存スロットの「取り残し」も解消する

docContentHeaderExtrashomeExtras は新しい継ぎ目ですが、ChromeHostBindings にはこのチャネルが存在する以前から、注入ルートへ届く手段がなかったスロットがいくつかありました — それらはスタブのデフォルトのまま黙って据え置かれていました。chromeBindingsModule はそれらをまとめて解消します。

スロットチャネルなしでのデフォルト
frontmatterRenderers{} — カスタムフロントマタープレビューレンダラーなし
buildFrontmatterPreviewEntries() => [] — プレビューテーブルが描画されない
SearchWidgetパッケージデフォルトのみ
loadTagsForLocale / tagVocabulary空 — フッターのタグ列なし
sidebarsConfig{} — 自動生成ツリーのみ
docHistoryMeta{} — Created/Updated ブロックなし

つまり、docContentHeaderExtras のために作成するモジュールは、たとえば以前はホスト所有ルートでしか機能しなかったカスタムフロントマターレンダラーを登録する場所でもあります。

Warning

SSRの表示専用契約のみ。バインディングモジュールの内部で定義したクライアントアイランドは、注入ルートでハイドレートされる保証はありません — 仮想再エクスポートは zfb の静的インポートスキャナーが到達可能なグラフの外側に位置するためです。バインディングモジュールはサーバーレンダリングされるコンテンツとコーラブルに使ってください。注入ルート上でハイドレートするアイランドが必要な場合は、依然として静的にインポートされる登録経路が必要です。

Info

ファイル欠落時も空文字列指定時も、挙動は明示的chromeBindingsModule が設定されているのに解決先のファイルが存在しない場合、ビルドはプラグインのセットアップ時に、解決された絶対パスを示すエラーで失敗します — 黙って空にフォールバックすることはありません。chromeBindingsModule に空文字列や空白のみの文字列が設定されている場合も、ビルドはプラグインのセットアップ時に、設定名を示すエラーで失敗します。設定自体が省略されている場合(未設定の場合)、チャネルは export const chromeBindings = {} を出力し、設定を省略した場合とバイト単位で同一の挙動になります。

docContentHeaderExtras — コンテンツヘッダーへの注入

docContentHeaderExtras は、ドキュメントのコンテンツヘッダー内、ページの <h1> とメタ情報ブロックの間に追加コンテンツを描画します。これはページエントリを受け取ってレンダリング可能な出力を返すレンダラーであり、プロパティから再導出するのではなく、現在のページのフロントマターを自然にキーとして扱えます。

docContentHeaderExtras?: (args: {
  entry: DocPageEntry;
  slug: string;
  locale: string;
  isFallback?: boolean;
  version?: string;
}) => unknown;

よくある用途は、カスタムフロントマターフィールドから導出するティアバッジです。フロントマターで tier: core または tier: opt-in を宣言するページに対して次のように書けます。

src/chrome-bindings.tsx
import { defineChromeBindings } from "@takazudo/zudo-doc/chrome-bindings";

export const chromeBindings = defineChromeBindings({
  docContentHeaderExtras: ({ entry }) => {
    const tier = entry.data.tier;
    if (tier !== "core" && tier !== "opt-in") return null;
    const label = tier === "core" ? "Core" : "Opt-in";
    const tone = tier === "core" ? "bg-accent text-bg" : "bg-surface text-fg";
    return (
      <span
        class={`inline-block px-hsp-sm py-vsp-2xs text-caption rounded-full ${tone}`}
      >
        {label}
      </span>
    );
  },
});

デフォルト: 未設定 → 何も描画されず、ヘッダー出力は継ぎ目導入前のヘッダーとバイト単位で同一になります。

バージョン付きページ

このレンダラーは4つのドキュメントルートすべてで entry ドキュメントページに対して呼び出され、バージョン付きページも含みます。バージョン付きページでは version 引数を受け取ります。バージョン付きページではメタ情報ブロックとタグは非表示になります — そこで描画するかどうか、どう描画するかはレンダラー自身が判断します。バージョン付きルートのモデルについてはバージョニングを参照してください。

homeExtrasHomePageView — ホームヒーローへの注入

homeExtras は、ホームヒーロー内、overview/GitHub のリンク行の後、ヒーローのテキストカラム内に追加コンテンツを描画します — 例えばサイト説明の下にブランドやソーシャルのリンクを置く用途です。

homeExtras?: (args: { locale: string }) => unknown;
src/chrome-bindings.tsx
export const chromeBindings = defineChromeBindings({
  homeExtras: ({ locale }) => (
    <p class="mt-vsp-sm text-small">
      <a
        href="https://example.com/blog"
        class="text-fg underline hover:text-accent"
      >
        Read the blog
      </a>
    </p>
  ),
});

優先順位: extras プロパティが勝つ

共有のホーム本体は HomePageView が組み立て、パッケージルートとホストページのどちらもこれを呼び出します。HomePageViewextras プロパティ — ホストページが直接渡す描画済みの値 — を受け取ります。両方が存在する場合はプロパティが勝ちます

// HomePageView 内で次のように解決される:
extras ?? hostBindings.homeExtras?.({ locale })

この非対称性は意図的なものです。ホストページは手元に JSX(値)を持っている一方、注入/バインディング経路はレンダリング時に locale 文字列しか持たず、そこから自前でコンテンツを導出しなければなりません(レンダラー)。

/ ルートのトポロジー

/ にあるデフォルトホームは、ルートプラグインによって決して注入されません — zfb が / パターンを拒否するためです(アップストリーム Takazudo/zudo-front-builder#1227)。これにより、知っておくべき分岐が生じます。

  • / ホームは常にホストの薄い pages/index.tsx アダプター経由で描画されるため、extras は HomePageViewextras プロパティ(またはホスト自身の createChrome 呼び出し)経由で届きます。

  • 注入された /[locale] ホームchromeBindingsModule を通じて hostBindings.homeExtras を読み取ります。

i18n サイトでは、バインディングモジュールで homeExtras を設定し、かつ pages/index.tsxextras プロパティに同じコンテンツを渡すことが、デフォルトホームとロケールホームを一貫させる方法です。

Design Token Panel チャネル(designTokenPanelConfigModule

Design Token Panel には独自のホストモジュールチャネルがあり、chromeBindingsModule とまったく同じ仕組みで動作します — 同じメカニクス、同じ「ファイルが無ければ大きな声で失敗する」挙動 — ただしクロームバインディングの代わりにパネルの設定ビルダーを運びます。

パネルは動作にホスト設定ファイルを必要としませんdesignTokenPanel: true にすると、注入された DesignTokenPanelBootstrap アイランドは、出荷済みのトークンマニフェストとバンドルされた Default Light / Default Dark スキームから導出されたパッケージデフォルトのビルダー@takazudo/zudo-doc/design-token-panel-config)を使います。これがゼロコンフィグの経路です。

パネルを完全にカスタマイズするには、designTokenPanelConfigModule に、名前付きの buildDesignTokenPanelConfig(mode) をエクスポートするホストモジュールを指すプロジェクトルート相対パスを設定します:

src/design-token-panel-config.ts
import type { PanelConfig } from "@takazudo/zdtp";

type PanelMode = "light" | "dark";

export function buildDesignTokenPanelConfig(mode: PanelMode): PanelConfig {
  // モードスコープのパネル設定(spacing/font/size/color ティア)を返す
  // ...
}

ルートプラグインは3番目の仮想モジュール(virtual:zudo-doc-design-token-panel-config)を登録し、あなたのビルダーを re-export します。chromeBindingsModule と同様、設定を通じて移動するのはパスだけで、ビルダー自体はモジュールグラフ経由でインポートされます。

Note

designTokenPanelConfigModulechromeBindingsModule と同様、ZudoDocConfig の第一級フィールドです — インラインの zudoDoc({ … }) キーとしてそのまま渡せます。パネルの有効化(designTokenPanel: true)は従来どおりインラインで動作します。

chromeBindingsModule との違いで知っておくべき点が2つあります:

  • デフォルトは空オブジェクトではなくパッケージビルダーです。 設定が無い場合、ローダーはパッケージデフォルト(完全に動作するパネル)を re-export します — フォールバックすべき意味のある「空の」ビルダーは存在しません。

  • スキャナー到達可能性がこのコントラクトの一部です。 実際の DesignTokenPanelBootstrap アイランドは(設定仮想モジュールを経由せず)パッケージのクロームによって静的にインポートされるため、常に zfb のアイランドスキャナーから到達可能です。チャネルを通じて移動するのはパネル設定のデータ(モードスコープのビルダー)だけであり、これがクロームバインディングのアイランドがハイドレートしない注入ルート上でも、カスタマイズされたパネルがハイドレートする理由です。

同じガード規則が適用されます: 明示的な空文字列、存在しないファイル、ディレクトリパスは、プラグインのセットアップ時に解決済みパスを明示して大きな声で失敗します。storagePrefix: "zudo-doc-tweak" はパッケージデフォルトで維持されるため、既存のユーザー保存は引き継がれます。

関連ページ

Revision History

Claude作成: 2026-07-02T06:07:27Z更新: 2026-07-11T19:06:43Z

AI Assistant

Ask a question about the documentation.