`<HtmlPreview>` は、分離された iframe 内で HTML/CSS のライブデモを描画するコンポーネントです。ビューポートプリセット（Mobile / Tablet / Full）とシンタックスハイライト付きの折りたたみ可能なソースコードパネルを備えています。すべての MDX ファイルでインポートなしで使用できます。ルートにバインドされた MDX のバインディングはプレビューのクロームをローカライズしますが、ルートにバインドされていないコンポーネントを直接使う場合はロケール情報がなく、`labels`を渡さない限り英語ラベルが使われます。

## ローカライズされたコントロール

ルートにバインドされた MDX のバインディングでコンポーネントを描画すると、アクティブなページのロケールから次の7つのプレビュー用クローム文字列が提供されます。

| 翻訳キー | UIでの用途 |
|----------|------------|
| `htmlPreview.viewport.mobile` | Mobileビューポートボタン |
| `htmlPreview.viewport.tablet` | Tabletビューポートボタン |
| `htmlPreview.viewport.full` | Full幅ビューポートボタン |
| `htmlPreview.viewport.label` | ビューポートプリセットグループの`aria-label` |
| `htmlPreview.source.show` | 折りたたまれたソース切り替えのラベル |
| `htmlPreview.source.hide` | 展開されたソース切り替えのラベル |
| `htmlPreview.iframe.title` | 表示用の`title`が省略されたときの外側のiframeタイトル。また、作成者の`<title>`も空白でない`title`も指定されていないときの生成ドキュメントタイトルのフォールバック |

値の検索順は、要求されたロケール → 設定済みのデフォルトロケール → パッケージの英語テーブル → 生のキーです。

作成者が渡す`title`プロップはタイトルバーを制御します。空白でない値は、マージ済みの`head`に作成者の`<title>`がない場合、生成される iframe ドキュメントタイトルにも使われます。作成者のタイトルがある場合は、そちらが優先されます。技術的なコードパネル見出し（`HTML`、`CSS`、`Head`、`JS`）は意図的に固定されており、これらの翻訳キーではローカライズされません。

1つのプレビューだけをカスタマイズするには、部分的な`labels`オブジェクトを渡します。省略したキー（`undefined`を設定したキーを含む）は、ルートのロケール値を消去せず、その値を維持します。

````mdx
<HtmlPreview
  html="<p>One preview</p>"
  labels={{
    mobile: "Phone",
    preview: "Live preview",
  }}
/>
````

ルートにバインドされていないコンポーネントを直接インポートする場合、`labels`を省略したキーには組み込みの英語デフォルトが使われます。その利用箇所を別の言語にするには、同じ部分オブジェクト（または7つすべてのキー）を渡してください。

## プレビュードキュメントのメタデータ

生成されるすべてのプレビュードキュメントには、空でない`<html lang>`の値が設定されます。信頼された`head`が開き`<title>`を管理していない場合は、空でないドキュメント`<title>`も追加されます。`lang`の値は、コンポーネントの利用方法に応じて次のように選択されます。

| 利用方法 | 言語の優先順位 |
|---------|----------------|
| ルートにバインドされた MDX `<HtmlPreview>` ラッパー | 空白でない明示的な`lang` → アクティブなルートロケール → `en` |
| 低レベルの`HtmlPreview`を直接インポート | 空白でない明示的な`lang` → `en` |

空の、または空白文字だけの`lang`は省略されたものとして扱われます。空白でない言語タグは任意の値を受け付け、属性コンテキストに応じてエスケープしたうえでシリアライズされます。この値は生成されるプレビュードキュメントの`<html lang>`のメタデータであり、外側のページの言語を変更するものではありません。

生成されるドキュメントタイトルの優先順位は次のとおりです。

1. マージ済みの`head`にある信頼された作成者の開き`<title>`は、作成者が管理する権威ある値としてそのまま残り、生成タイトルは追加されません。
2. 空白でない`title`プロップが生成ドキュメントタイトルを指定します。
3. マージ済みのローカライズ済み／呼び出し単位の`labels.preview`が生成ドキュメントタイトルを指定します。
4. 最終フォールバックとしてリテラル値`Preview`が使われます。

空の、または空白文字だけの`title`と`labels.preview`は、次の候補へフォールスルーします。公開 API には独立した`documentTitle`プロップは意図的にありません。作成者がドキュメントの`<title>`を管理する必要がある場合は`head`を使ってください。コンポーネントが生成するメタデータだけがコンテキストに応じてエスケープされます（`lang`は HTML 属性として、生成タイトルは HTML テキストとして扱われます）。自由形式の`head`は信頼された呼び出し元のコンテンツとして、指定どおりのバイト列で保持されます。

完全な eager プレビューと、`loading="visible"`の可視性ゲートが開いた後にマウントされるプレビューでは、同じメタデータの優先順位とエスケープが適用されます。visible モードのサーバー予約領域には、マウントされるまでプレビュードキュメントは含まれません。

```mdx
<HtmlPreview
  lang="fr-CA"
  title="Visible title"
  labels={{ preview: "Localized fallback" }}
  head={'<title>Author title</title>'}
  html="<p>Language and title metadata are serialized.</p>"
/>
```

## 遅延読み込み

ルートにバインドされた MDX の `<HtmlPreview>` ラッパーは、`loading="eager"` または `loading="visible"` を受け付けます。デフォルトは `"eager"` で、省略した場合は `"eager"` を渡した場合と同じです。これはラッパーのライフサイクルポリシーであり、iframe のネイティブな `loading` 属性ではありません。また、iframe に転送されません。低レベルの `HtmlPreview` を直接インポートした場合、このプロップは使えません。

デフォルトの `"eager"` モードでは、iframe、`srcdoc`、任意のクロームを含むプレビューのサブツリー全体をサーバーが描画します。クライアント側のハイドレーションには、引き続き zfb の通常の可視時アイランドタイミングが使われます。

`"visible"` では、サーバー出力は不活性で `aria-hidden` な高さ予約（明示した正の `height`、または 200px のフォールバック）だけになります。プレビューの iframe や `srcdoc` は含まれず、プレビューのコントロール、インラインスクリプト、外部リソース用タグも、プレビューがマウントされる前に実行・読み込みされることはありません。予約領域がビューポートと交差すると、ラッパーは完全なプレビューを一度だけマウントし、通常の動作を開始します。

zfb 2.14.2 は、可視マーカーの値に関係なく skip-SSR の render ターゲットを直ちにマウントします。この遅延を維持するため、ラッパーの内部ターゲットは予約領域に対して一度だけの `IntersectionObserver` ゲートを適用し、最初に交差したエントリの後で切断します。`IntersectionObserver` が利用できない場合はフェイルオープンし、プレビューを直ちにマウントします。

どちらのモードでも、プレビューが描画された後は既存のルートローカライズ済みラベルと任意の `showSource` / `showViewportControls` クロームが維持されます。

## 基本的な使い方

[Dynamic content unavailable in text export]

````mdx
<HtmlPreview
  title="Basic Box"
  html={`
    <div class="box">Hello, world!</div>
  `}
  css={`
    .box {
      padding: 24px;
      background: #3b82f6;
      color: #fff;
      border-radius: 8px;
      font-family: system-ui, sans-serif;
      text-align: center;
    }
  `}
/>
````

## レスポンシブレイアウト

ビューポートボタン（Mobile / Tablet / Full）を使って、異なる幅でコンテンツがどのように変化するかを確認できます。プレビュー領域の右下にあるドラッグハンドルでリサイズすることもできます。

[Dynamic content unavailable in text export]

## HTML のみ

`css`、`head`、`js` のいずれのプロップも指定しない場合は、HTML のソースのみが表示されます。

[Dynamic content unavailable in text export]

## デフォルトでコードを表示

`defaultOpen` を指定すると、ソースコードが最初から展開された状態で表示されます。

[Dynamic content unavailable in text export]

## 任意のコントロール領域

2つの任意のコントロール領域はデフォルトで表示されます。`showSource={false}`を設定すると、ソース切り替え、コードパネル、ハイライト用マークアップが構造的に省略されます。プレビューの iframe は残ります。`defaultOpen`はコードパネルの状態を初期化するだけなので、ソース領域を隠した場合は効果がありません。

`showViewportControls={false}`を設定すると、Mobile / Tablet / Full のプリセットグループとボタンが構造的に省略されます。プレビューはFull幅（`100%`）のコンテナと水平方向のドラッグリサイズ操作を保持します。プリセットを隠してもリサイズは無効になりません。

2つのフラグは同時に`false`にできます。iframeは引き続き描画されます。タイトルバーはビューポートコントロールが表示されているか、空でない`title`が指定されている場合に描画されます。したがって、プリセットを隠してもタイトルを指定すればタイトルだけのバーが残り、プリセットと`title`の両方を省略すれば空のバーは削除されます。

## 固定高さ

`height` プロップを使って、自動調整の代わりに iframe の高さを固定できます。iframe は指定したピクセル値を正確な高さとして維持し、高さ自動調整コントローラーは `ResizeObserver` を設定せず、コンテンツの増減でも高さを変更しません。

不透明なサンドボックスでは固定高さが必須です。`allow-same-origin` を外すと親から iframe ドキュメントを読み取れなくなります。不透明オリジン向けの `postMessage` によるリサイズ経路はないため、より厳しい `sandbox` には `height` を併用してください。

[Dynamic content unavailable in text export]

## 高さ自動調整のライフサイクル

`height` を省略し、`fullHeight` が `false` の場合、読み取り可能な同一オリジンのプレビューは iframe の読み込み後にドキュメントを計測し、iframe の高さを同期し続けます。デフォルトのサンドボックスには `allow-same-origin` が含まれているため、親からこの目的でプレビュードキュメントを読み取れます。

プレビューは iframe ドキュメントの `body` と `html` を `ResizeObserver` で監視します。そのため、後から発生するコンテンツの伸縮に加えて、遅れて読み込まれるスタイルシート、フォント、画像、スクリプトによるジオメトリの変化も反映されます。ビューポートプリセットを変更した場合も、コンテンツのリフロー後に再計測がスケジュールされます。スクリプトを含むプレビューでは、読み込み直後の計測に加えて、読み込み後300msに1回の追従計測も行われます。

iframe が `ResizeObserver` を提供しない場合も、直後の計測と、スクリプトを含む場合の遅延した一度だけの計測は維持されますが、継続的な追従はできません。不透明なサンドボックスは計測できないため、代わりに固定の `height` を使ってください。

## 高さ100%

`fullHeight` プロップを使うと、プレビュードキュメントの `html`/`body` を iframe いっぱいに広げられます。`height: 100%` がビューポートまで届くレイアウト（利用可能なスペースを埋めるフレックスカラムなど）に便利です。

:::warning[明示的な `height` と併用する]
`fullHeight` は高さ自動調整の仕組みと干渉します — 高さ自動調整は iframe の body を計測して iframe の高さをそれに合わせますが、`fullHeight` は逆に body の高さを iframe 側から導出させます。両方を組み合わせると、安定した解決策のないフィードバックループが発生します。固定の `height` がない場合、`fullHeight` は意図的に高さ自動調整を無効にし、デフォルトの200px高さで `ResizeObserver` なしになります。正しい使い方は `fullHeight` と明示的な `height` を併用することです。
:::

[Dynamic content unavailable in text export]

## 外部リソース

CSS フレームワーク、Web フォント、スクリプトなどの外部リソースをプレビューに読み込めます。`zfb.config.ts` でグローバルに設定するか、コンポーネントのプロップで個別に指定します。

### グローバル設定

`zfb.config.ts` で `htmlPreview` を設定すると、すべてのプレビューにリソースが適用されます：

```ts title="zfb.config.ts"
export default defineConfig(
  zudoDoc({
    htmlPreview: {
      head: `<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&display=swap">`,
      css: `body { font-family: 'Noto Sans JP', sans-serif; }`,
      js: `console.log('preview loaded');`,
    },
  }),
);
```

### コンポーネント単位のプロップ

`head` と `js` プロップを使って、個別のプレビューにリソースを追加できます。グローバル設定の後にマージされます。

[Dynamic content unavailable in text export]

````mdx
<HtmlPreview
  title="With JS"
  html={`
    <div id="output">Waiting...</div>
  `}
  css={`
    #output {
      padding: 16px;
      font-family: system-ui, sans-serif;
      color: #334155;
    }
  `}
  js={`
    document.getElementById('output').textContent = 'Hello from JS!';
  `}
/>
````

### 外部スタイルシートとスクリプト

`head` と `js` は文字列ベースのプロップで、常に可視の「Head」/「JS」コードブロックとして表示され、プレビューには preflight リセット（すべてのプレビューに注入される Tailwind v4 の CSS リセット）が常に上乗せで適用され、独自リセットを持つフレームワークでもスキップする手段がありません。`externalStyles`・`externalScripts`・`preflight`・`showResources` は、CDN リソース（CSS フレームワーク、Web フォント、`@tailwindcss/browser`・Bootstrap・htmx のようなスクリプト）向けに、コードパネルを意識した構造化された代替手段を提供します。

- **`externalStyles`** — スタイルシート URL の配列。`<link rel="stylesheet" href="...">` タグとして出力されます。author の `css` より**前に**読み込まれるため、`css` でフレームワークを上書きできます。
- **`externalScripts`** — スクリプト URL の配列。`<script src="...">` タグとして出力されます。インラインの `js` プロップと同じサンドボックス／`syncDelay` の導出ロジックを通るため、プレビューは自動的に `sandbox="allow-scripts allow-same-origin"` と 300ms の高さ再同期ディレイを得ます。
- **`preflight`** — `false` を指定すると、注入される preflight リセットを完全にスキップします。Tailwind のように独自のベーススタイルを持つフレームワーク向けです。
- **`showResources`** — 両方の配列は（`head`/`js` と異なり）**デフォルトでは可視のコードパネルから除外されます**。読者に読み込まれているリソースを示したい場合は `true` を指定すると、「HTML」パネルの先頭に `<link>`/`<script src>` の生の行として表示されます。

これら4つのプロップは**コンポーネント単位のみ**です — `head`/`css`/`js` と異なり、`zfb.config.ts` のグローバル `htmlPreview` 設定には含まれません。

:::warning[外部リソースはクライアントサイドで読み込まれ、ビルド時には読み込まれない]
`externalStyles`/`externalScripts` は、**プレビューの iframe が描画されるときに**ブラウザが行うネットワークリクエストであり、ビルド時にバンドルされるアセットではありません。リソースの読み込み中はプレビューが一瞬スタイル未適用または一部適用の状態で表示されることがあり、そのURLでリソースが利用可能であり続けることに依存します。
:::

`loading="visible"` では可視性ゲートが開くまで iframe が描画されないため、外部リソースへのリクエストとインラインの `head` / `js` の処理は、プレビューがマウントされるまで待機します。`loading="eager"` では完全なサーバー描画プレビューの動作が維持されます。

`@tailwindcss/browser` は独自のリセットを持つため `preflight={false}` を指定する、Tailwind CDN の1行デモ:

[Dynamic content unavailable in text export]

````mdx
<HtmlPreview
  title="Tailwind CDN"
  externalScripts={["https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"]}
  preflight={false}
  html={`
    <div class="flex gap-4 p-6 bg-slate-100">
      <div class="px-4 py-2 bg-blue-500 text-white rounded-lg font-sans">Tailwind</div>
      <div class="px-4 py-2 bg-emerald-500 text-white rounded-lg font-sans">via CDN</div>
    </div>
  `}
/>
````

`showResources` を指定すると、CDN の URL が非表示ではなく「HTML」コードパネルの先頭に生の行として表示されます:

[Dynamic content unavailable in text export]

## セキュリティと `sandbox` プロップ

プレビューは分離された `<iframe srcdoc>` 内に描画されます。その `sandbox` 属性は、プレビューにスクリプトが含まれる場合（`js` プロップ、`head` 内の `<script>`、または空でない `externalScripts`）は `allow-scripts allow-same-origin`、含まれない場合は `allow-same-origin` を**デフォルト**とします。

:::danger[信頼の前提]
`allow-scripts` と `allow-same-origin` を組み合わせると、**iframe のサンドボックスは実質的に無効化されます** — プレビュー内のスクリプトが親ページのオリジンを共有し、親ドキュメントにアクセスできます。zudo-doc がこのデフォルトを採用しているのは、プレビューの内容が**作成者が信頼できる MDX** であるためで、また `allow-same-origin` が高さの自動計測を可能にしているためです。

**半信頼またはユーザー投稿の** HTML を描画するプロジェクトでは、`sandbox` プロップでより厳しい値に上書きしてください。
:::

```mdx
<!-- Maximally restrictive: no script execution, opaque origin -->
<HtmlPreview html={untrusted} sandbox="" height={400} />

<!-- Allow scripts but keep an opaque origin (script can't reach the parent) -->
<HtmlPreview html={untrusted} sandbox="allow-scripts" height={400} />
```

`allow-same-origin` を外すと iframe のオリジンが不透明になり、親から `iframe.contentDocument` を読み取れなくなります — つまり**高さの自動調整が無効になります**。厳しい `sandbox` を指定するときは、必ず固定の `height` も併用してください。空文字列 `""` はそのまま尊重され、プロップを省略したときだけ計算済みのデフォルトにフォールバックします。

## Props

次の表は、ルートにバインドされた MDX の `<HtmlPreview>` ラッパーについて説明しています。`loading` はラッパー専用です。低レベルの `HtmlPreview` を直接インポートした場合、以下のプレビュー用プロップは使えますが、`loading` は使えません。

| プロップ | 型 | デフォルト | 説明 |
|------|------|---------|-------------|
| `html` | string | （必須） | プレビュー iframe 内に描画する HTML コンテンツ |
| `loading` | `"eager" \| "visible"` | `"eager"` | ラッパーのライフサイクルポリシー。`"visible"` はビューポートとの交差までプレビューのサブツリーを遅延する。ネイティブ iframe の `loading` 属性としては転送されない |
| `css` | string | `undefined` | プレビュー iframe 内に適用する CSS スタイル |
| `head` | string | `undefined` | `<head>` に挿入する信頼された生の HTML（リンク、メタ、フォント）。作成者の開き`<title>`は呼び出し元が管理し、生成メタデータより優先される |
| `js` | string | `undefined` | プレビュー iframe 内で実行する JavaScript |
| `title` | string | `undefined` | プレビューヘッダーバーに表示するタイトル。作成者の`<title>`がない場合は生成される iframe ドキュメントタイトルにも使われる |
| `lang` | string | ルートロケール（直接利用では`en`） | 生成されるプレビュードキュメントの`<html lang>`に使う言語タグ。バインドされた利用では空白でない明示的な`lang` → アクティブなルートロケール → `en`、直接利用では空白でない明示的な`lang` → `en`の順で解決され、空白値はフォールスルーする |
| `height` | number | auto | iframe の固定高さ（ピクセル）。省略時はコンテンツに合わせて自動調整 |
| `defaultOpen` | boolean | `false` | ソースコードパネルをデフォルトで展開表示する |
| `labels` | `Partial<HtmlPreviewLabels>` | `undefined` | ビューポート、ソース、iframeコントロールの呼び出し単位のラベル。未指定キーはルートロケールのラベル（直接利用では英語デフォルト）を維持し、優先度の高いタイトル候補がない場合は`preview`が生成される iframe ドキュメントタイトルにも使われる |
| `showSource` | boolean | `true` | ソース切り替えとコードパネルを描画する。`false`では構造的に削除するがiframeは残り、その場合`defaultOpen`は効果がない |
| `showViewportControls` | boolean | `true` | Mobile / Tablet / Fullのプリセットコントロールを描画する。`false`でもFull幅とドラッグリサイズ操作は残る |
| `fullHeight` | boolean | `false` | プレビュードキュメントの `html`/`body` を高さ100%まで広げる。高さ自動調整と干渉するため、必ず明示的な `height` と併用する |
| `sandbox` | string | auto | iframe の `sandbox` 属性。省略時は計算済みのデフォルト（スクリプトありなら `allow-scripts allow-same-origin`、なしなら `allow-same-origin`）。信頼できないコンテンツにはより厳しい値を指定 — ただし `allow-same-origin` を外すと高さ自動調整が無効になるため `height` も併せて指定する |
| `externalStyles` | string[] | `undefined` | 外部スタイルシート URL。`head`/`css` より前に `<link rel="stylesheet">` として挿入される。コンポーネント単位のみ — ビルドバンドルされず、表示時にクライアントサイドで読み込まれる |
| `externalScripts` | string[] | `undefined` | 外部スクリプト URL。`<script src>` として挿入される。`js` と同様にサンドボックス／`syncDelay` の導出を切り替える。コンポーネント単位のみ — ビルドバンドルされず、表示時にクライアントサイドで読み込まれる |
| `preflight` | boolean | `true` | `false` を指定すると、注入される preflight リセットをスキップする — `externalStyles`/`externalScripts` で読み込む独自リセットを持つフレームワーク向け |
| `showResources` | boolean | `false` | `externalStyles`/`externalScripts` を「HTML」コードパネルの先頭に生の行として表示する。デフォルトではパネルから除外される |

## シンタックスハイライトと遅延 WASM

ソースコードパネルは公開サブパス `@takazudo/zfb-md-wasm/highlight` を遅延インポートし、次の言語をセマンティックハイライターで処理します：

- `html` — HTML パネルおよび Head パネル
- `css` — CSS パネル
- `javascript` — JS パネル

レンダラーは安全にエスケープされた `pre.hi-root` / `hi-*` マークアップを出力し、ドキュメントフェンスと同じ `--zd-syntax-*` パレットを共有します。遅延読み込みの境界はパネルを開く操作です。それより前に、生成された JavaScript glue と WASM companion リソースがリクエストされることはありません。未知の言語に対する warning は、エスケープ済みのセマンティックフォールバックマークアップを使用します。インポート、初期化、不正なオプション、現在の呼び出しの失敗時には、JSX でエスケープされたプレーンな `<pre><code>` フォールバックを維持します。一時的なインポート失敗は、後続のパネル描画で再試行できます。

コードパネルの`HTML`、`CSS`、`Head`、`JS`見出しは技術的なラベルであり、意図的に固定されています。`htmlPreview.*`翻訳キーは周囲のコントロールを対象とし、これらの見出しは対象外です。

独自の配信基盤で本番出力を提供する場合は、生成された `.mjs` / `.wasm` アセットを保持し、JavaScript と `application/wasm` の正しい MIME type で配信してください。パッケージ内部の glue パスを手作業でコピーしないでください。リソースグラフは zfb のビルドが管理します。

このブラウザ実行時の言語セットは、通常のビルド時コードブロックで利用できる[言語一覧](https://zudo-doc.takazudomodular.com/ja/docs/components/basic-components#%E3%82%B3%E3%83%BC%E3%83%89%E3%83%96%E3%83%AD%E3%83%83%E3%82%AF-%E3%82%B5%E3%83%9D%E3%83%BC%E3%83%88%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E8%A8%80%E8%AA%9E)より限定されています。

## 注意事項

- プレビューは CSS リセット（Tailwind v4 preflight）を含む分離された `<iframe>` 内で描画されるため、スタイルが外部に漏れることはありません。
- `loading="visible"` はラッパーの一度だけの可視性ゲートが開くまで不活性な高さ予約を保持します。ネイティブ iframe の遅延読み込み属性は追加しません。
- ルートにバインドされた MDX プレビューは7つの`htmlPreview.*`コントロールラベルにアクティブなロケールを使います。ルートにバインドされていないコンポーネントを直接使う場合は、`labels`を渡さない限り英語がデフォルトです。
- `showSource={false}`はiframeを削除せず、ソース切り替えとハイライト用サブツリーを構造的に削除します。隠されたソース領域を`defaultOpen`で再び開くことはできません。
- `showViewportControls={false}`はプリセットボタンを削除しますが、Full幅のコンテナと水平方向のドラッグリサイズ操作は残ります。2つのフラグを同時に`false`にできます。空でない`title`を指定すればタイトルだけのバーが残り、空のバーは省略されます。
- プレビューはデフォルトで `sandbox="allow-same-origin"`（`js` プロップまたは `<script>` がある場合は `sandbox="allow-scripts allow-same-origin"`）を使用し、`contentDocument` 経由で iframe の高さを自動同期します。`srcdoc` の内容は作成者が管理する MDX です。信頼できないコンテンツには `sandbox` プロップで上書きしてください — 上記の[セキュリティと `sandbox` プロップ](https://zudo-doc.takazudomodular.com/ja/docs/components/html-preview#%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E3%81%A8-sandbox-%E3%83%97%E3%83%AD%E3%83%83%E3%83%97)を参照。
- `height` を省略し `fullHeight` が `false` の場合は、同一オリジンの高さ自動調整が有効になり、後から変わる body のジオメトリとビューポートのリフローが監視されます。固定の `height` は正確な値で、オブザーバーを使いません。不透明なサンドボックスでは `postMessage` によるリサイズを使わないため、固定の高さが必要です。
- `htmlPreview` 設定フィールドのグローバルリソースは、コンポーネント単位のプロップの前に挿入されます。
- `html`、`css`、`js` プロップはインデント付きのテンプレートリテラルに対応しています。先頭の空白はソースコード表示時に自動的に除去されます。
- クライアントサイドのハイドレーションはコンポーネントラッパーにより自動的に処理されるため、MDX 内で `client:load` ディレクティブを付ける必要はありません。
- `externalStyles`/`externalScripts` は表示時にクライアントサイドで読み込まれます — プレビュー iframe が描画されるときにブラウザが行うネットワークリクエストであり、ビルド時にバンドルされるアセットではありません。上記の[外部スタイルシートとスクリプト](https://zudo-doc.takazudomodular.com/ja/docs/components/html-preview#%E5%A4%96%E9%83%A8%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9-%E5%A4%96%E9%83%A8%E3%82%B9%E3%82%BF%E3%82%A4%E3%83%AB%E3%82%B7%E3%83%BC%E3%83%88%E3%81%A8%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88)を参照。
- srcdoc への挿入順序: preflight リセット（`preflight={false}` でない限り）→ `fullHeight` スタイル → `externalStyles` → `externalScripts` → `head` → `css`。