Skip to content

埋め込み#

View Markdown

埋め込みは Markdown 中の HTML 風タグで、変換時に静的 HTML へ展開されます。静的マークアップだけを出す 2 つは既定でオン、それ以外はオプトインです。

埋め込み オプション 既定 書き方
GitHub カード embeds.github true <GitHub repo="owner/name" />
OG リンクカード embeds.openGraph true <OgCard url="https://..." />
パッケージマネージャタブ embeds.pm false <pm>npm install pkg</pm>
Twitter / X embeds.twitter false <Tweet /> または <XPost />
Reddit embeds.reddit false <Reddit url="https://..." />
Bluesky embeds.bluesky false <Bluesky />
Google Maps embeds.googleMaps false <GoogleMaps url="https://..." />
Qiita embeds.qiita false <Qiita url="https://..." />
Zenn embeds.zenn false <Zenn url="https://..." />
パッケージ registry embeds.packageRegistry false <NpmPackage url="https://..." />
Playgrounds embeds.playgrounds false <CodePen url="https://..." />
Vimeo embeds.vimeo false <Vimeo url="https://..." />
Twitch embeds.twitch false <Twitch url="https://..." />
Discord embeds.discord false <Discord url="https://..." />
Fediverse embeds.fediverse false <Mastodon url="https://..." />
Facebook embeds.facebook false <Facebook url="https://..." />
Threads embeds.threads false <Threads url="https://..." />
Instagram embeds.instagram false <Instagram url="https://..." />
Spotify embeds.spotify false <Spotify url="https://..." />
Apple Music embeds.appleMusic false <AppleMusic url="https://..." />
Speaker Deck embeds.speakerDeck false <SpeakerDeck url="https://..." />
Audio embeds.audio false <Audio src="https://..." />
Video embeds.video false <Video src="https://..." />
StackBlitz embeds.stackBlitz false <StackBlitz url="https://..." />
WebContainer embeds.webContainer false <WebContainer />

タブと YouTube 埋め込みは embeds オプションの外です。SSG ビルドと dev preview では常に処理され、設定は不要です。同じ執筆モデルなので で扱います。

<Tweet><OgCard> のようなドキュメント上の PascalCase タグは .md.mdx の両方で動きます。同じ名前のドキュメントローカル import(import Tweet from "./Tweet")は組み込みより優先され、MDX island のまま残ります。

.md では 1 タグ 1 行#

以下の例は読みやすさのため属性を複数行に分けています。この形式には MDX が必要です。素の .md ファイルでは、タグの開始と > を同じ行に収める必要があります。

<Bluesky url="https://bsky.app/profile/danabra.mov/post/3mqzxmtfnxk2b" handle="danabra.mov">…</Bluesky>

CommonMark が生の HTML ブロックを開始するのは、開始タグがその行の中で閉じている場合だけです。行末でタグが開いたままだと本文として扱われ、属性はテキストとして描画され、URL はリンクになり、> だけの行は引用ブロックになります。複数行で書きたい場合は mdx を有効にしてください。

すべての組み込み埋め込みを切るときは embeds: false、個別に設定するときはオブジェクトです。

import { oxContent } from "@ox-content/vite-plugin";

export default {
  plugins: [
    oxContent({
      embeds: {
        github: { maxSourceLines: 120 },
        openGraph: { timeout: 5000 },
        pm: { sync: true },
        twitter: true,
        reddit: true,
        bluesky: true,
        qiita: true,
        zenn: true,
        packageRegistry: true,
        playgrounds: true,
        vimeo: true,
        twitch: { iframe: true, parent: "docs.example.com" },
      },
    }),
  ],
};

GitHub カード#

embeds.github はビルド時に GitHub API からリポジトリカードとソーススニペットを描画します。出力は静的 HTML です。クライアント側 JavaScript も、第三者ウィジェットのスクリプトも使いません。

リポジトリカード:

<GitHub repo="ubugeeei-prod/ox-content" />
ubugeeei-prod/ox-content

all-in-one markdown toolchain ― fastest, tiniest, framework agnostic, powerful, customizable

Rust17711

ref と行範囲を固定したソーススニペット:

<GitHub repo="ubugeeei-prod/ox-content" path="README.md" ref="main" loc="1-10" />
L1-L10 · 10 LOC
<p align="center">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="./assets/oxcontent-light.svg">
    <source media="(prefers-color-scheme: light)" srcset="./assets/oxcontent-dark.svg">
    <img alt="Ox Content logo" src="./assets/oxcontent-dark.svg" height="60">
  </picture>
</p>

<p align="center">
  <strong>High-performance Markdown toolkit</strong><br>

パーマリンク形式も使えます。#L10-L18 行アンカー付きの GitHub blob URL を貼ります。

<GitHub permalink="https://github.com/ubugeeei-prod/ox-content/blob/278098b/npm/vite-plugin-ox-content/src/plugins/github.ts#L10-L18" />
L10-L18 · 9 LOC
import rehypeStringify from "rehype-stringify";
import type { Root, Element } from "hast";

export interface GitHubRepoData {
  name: string;
  full_name: string;
  description: string | null;
  html_url: string;
  stargazers_count: number;

ソースカードのヘッダは blob へリンクし、GitHub API が返すときは、その ref でそのパスを最後に触ったコミットも表示します。

オプション 既定 目的
token "" レート制限とプライベートリポジトリ向け GitHub API トークン。
cache true API 応答をメモリにキャッシュする。
cacheTTL 3600000 キャッシュ寿命(ミリ秒)。
maxSourceBytes 200000 これより大きいファイルは飛ばす。
maxSourceLines 120 範囲指定がないときのインライン行数上限。

明示的な token がなければ process.env.GITHUB_TOKEN を自動で拾います。ビルド中にリポジトリやファイルが取れないとき — オフライン CI、レート制限、不正なパス — 埋め込みはビルドを落とさず、フォールバックのリンクカードを描画します。

GitHub issue、pull request、commit、discussion、gist の URL も、同じ embeds.github オプションで静的カードとして描画できます。

<GitHub url="https://github.com/ubugeeei-prod/ox-content/issues/699" />
<GitHub url="https://github.com/ubugeeei-prod/ox-content/pull/1025" />
<GitHub url="https://github.com/ubugeeei-prod/ox-content/commit/5399e080b5320d730e410a49a5aab42ba670a1f1" />
<GitHub url="https://github.com/ubugeeei-prod/ox-content/discussions/1" />
<GitHub url="https://gist.github.com/ubugeeei/0123456789abcdef0123456789abcdef" />

これらの resource card は、認証なしで読める public metadata だけを取得します。削除済み、非公開、レート制限、未対応の resource は deterministic な link-only card に落ちます。

Open Graph カード#

embeds.openGraph はビルド時にページの Open Graph メタデータを取り、静的リンクカードを描画します。

<OgCard url="https://vite.dev" />
Vite
Next Generation Frontend Tooling
vitejs
オプション 既定 目的
timeout 10000 取得タイムアウト(ミリ秒)。
cache true 取得したメタデータを現在のプロセスのメモリにキャッシュ。
cacheTTL 3600000 鮮度の窓(ミリ秒)。
persistCache false 成功・失敗エントリをビルド間でディスクに残す。
cacheDir .cache/ox-content/ogp 永続メタデータキャッシュディレクトリ。
refresh false 新しいキャッシュがあっても再取得する。
userAgent ox-content-ogp-bot/1.0 ... 対象へ送る User-Agent。

persistCache: true にすると、クリーンビルドや CI ワーカーでもメタデータを再利用できます。成功した取得と届かなかった URL は、正規化した URL ごとに 1 つの JSON として cacheDir へ保存します。新しいエントリはネットワークを飛ばし、期限切れと refresh: true は再取得してファイルをアトミックに置き換えます。壊れたファイルは無視するので、後続ビルドを汚染しません。変換に渡したメタデータは、キャッシュや取得より優先されます。

届かないページはプレーンなリンクカードに落ちます。localhost、プライベート IP 範囲、HTTP(S) 以外のスキームへのリクエストは拒否するので、Markdown 本文がビルド環境のネットワークを探れません。

カードのテキストはページ自身のマークアップからデコードするので、Tips &amp; Tricks と書かれた og:titleTips & Tricks として描画されます。og:image は宣言元のページを基準に解決し(絶対・プロトコル相対・文書相対のいずれの形式も動きます)、フェッチャーが拒否する先へ解決された場合は破棄します。

favicon は対象ページ自身の <link rel="icon"> から取り、無ければその origin の /favicon.ico へフォールバックします。サードパーティの favicon サービスへは接続しないので、カードを描画してもドキュメントページのリンク先が外部ホストへ伝わりません。

埋め込みを解決できない場合#

プロバイダは認識できる入力に対してのみカードを描画します。有効なプロバイダがタグを解決できない場合(対象外のホスト、知らないパス形状など)、タグは未知の要素としてページに残るのではなく、素のリンクへ降格します。

<a
  class="ox-embed-fallback"
  href="https://qiita.com/ubugeeei"
  target="_blank"
  rel="noopener noreferrer"
  >https://qiita.com/ubugeeei</a
>

リンク文言はタグの本文、次に title、最後に URL の順で決まります。フォールバックの class にプロバイダ名は含めません。レンダラは「自分のものではない」ことだけを伝え、ホストが違うのかパスだけが違うのかを区別しないため、名前を付けると偽装ホストがそのプロバイダのスタイルを借りられてしまうからです。

リンクとして安全でない URL(HTTPS 以外のスキーム、埋め込み資格情報など)を持つタグは元のマークアップのまま残り、無効なプロバイダには一切手を触れません。

パッケージマネージャタブ#

embeds.pm は 1 つの npm 風コマンドを、vp(Vite+)、pnpm、bun、npm、yarn 向けのアクセシブルなタブグループへ展開します。

oxContent({
  embeds: {
    pm: true,
  },
});
<pm>npm install -D @ox-content/vite-plugin @ox-content/theme-swiss</pm>
vp install -D @ox-content/vite-plugin @ox-content/theme-swiss
pnpm add -D @ox-content/vite-plugin @ox-content/theme-swiss
bun add -D @ox-content/vite-plugin @ox-content/theme-swiss
npm install -D @ox-content/vite-plugin @ox-content/theme-swiss
yarn add -D @ox-content/vite-plugin @ox-content/theme-swiss

コマンド変換は Rust ネイティブです。npm install -Dvp install -Dpnpm add -Dbun add -Dyarn add -D になり、npx <bin>vp exec -- <bin> になります。タブはクライアント側 JavaScript なしで動きます。選択は CSS :has() です。pm: { sync: true } をオプトインすると、ページ上のすべてのブロックで選んだパッケージマネージャを localStorage 経由で同期します。変換表の全体は Package Manager Tabs を見てください。

タブ#

汎用タブグループはパッケージマネージャタブと同じウィジェットで、SSG ビルドと dev preview では常に使えます。

<tabs>
  <tab label="Install">
    <pre><code>pnpm add -D @ox-content/vite-plugin
pnpm add -D @ox-content/theme-swiss</code></pre>
  </tab>
  <tab label="Config">
    <pre><code>oxContent({ srcDir: "content", embeds: { pm: true } })</code></pre>
  </tab>
  <tab label="Markdown">
    <pre><code>---
title: Install
---

好きなパッケージマネージャで Ox Content を入れます。

&lt;pm&gt;npm install -D @ox-content/vite-plugin&lt;/pm&gt;</code></pre>
  </tab>
  <tab label="Build">
    <pre><code>pnpm vite build
pnpm vite preview</code></pre>
  </tab>
</tabs>
pnpm add -D @ox-content/vite-plugin
pnpm add -D @ox-content/theme-swiss
oxContent({ srcDir: "content", embeds: { pm: true } })
---
title: Install
---
好きなパッケージマネージャで Ox Content を入れます。
pnpm vite build
pnpm vite preview

label 属性のない <tab>Tab 1Tab 2 のように落ちます。

隣り合うコード例は、手書きの <tabs> よりオプトインの ::: code-group を使ってください。コードグループ を見てください。

YouTube#

YouTube 埋め込みは SSG ビルドと dev preview で常に処理されます。iframe はプライバシー強化モード(youtube-nocookie.com)と遅延読み込みが既定です。

<YouTube id="Ny8pjacNIv8" title="An Evening with Ron Carter at Emmet’s Place" />

idurlhref 属性を受け付けます。youtu.bewatch?v=shortsembed の URL 形はどれも認識します。start は非負整数の秒で、iframe URL に ?start= を付けます。不正、負、小数、オーバーフロー、重複した値は無視します。start を省略すると、これまでの URL のままです。

<YouTube id="Ny8pjacNIv8" title="An Evening with Ron Carter at Emmet’s Place" start="4190" />

Twitter / X#

embeds.twitter は投稿を静的カードとして描画し、第三者ウィジェットのスクリプトは決して読みません。twitter: true のとき、埋め込みはプライバシーを意識したカードです。要素本文が投稿本文になり、任意の属性で作者、アバター、日時、リアクション数、元投稿リンクをネットワークなしで出せます。

<XPost
  url="https://x.com/evanyou/status/1688035849638977536"
  displayName="Evan You"
  handle="evanyou"
  dateLabel="Aug 6, 2023"
  replies="134"
  likes="6.2K"
>
  Thank you JavaScript.
</XPost>
Evan You@evanyou
Thank you JavaScript.
134 replies6.2K likes

オブジェクト形式を使うと、ビルド時に本文、著者、アバター、写真、動画ポスターを取り、自分のオリジンから配信します。取ってきたカードには、日時、元投稿リンク、利用可能な返信/リポスト/引用/いいね/表示数、引用投稿の入れ子カード、「Replying to @…」リンクも含まれます。appearance: "full" は sveltweet / react-tweet 形の静的カードです。

oxContent({
  embeds: {
    twitter: {
      fetch: true,
      lang: "en",
      appearance: "compact",
      timeZone: "UTC",
      mediaOutputDir: "public/ox-content/twitter",
      mediaPublicPath: "/ox-content/twitter",
    },
  },
});
オプション 既定 目的
fetch false ビルド時に投稿本文を取る。
lang "en" syndication 言語と表示日付。
timeout 10000 メタデータ要求のタイムアウト(ミリ秒)。
cache true メモリと永続 JSON キャッシュ。
cacheDir .cache/ox-content/twitter 永続メタデータキャッシュディレクトリ。
mediaOutputDir public/ox-content/twitter アバター、写真、動画のローカルディレクトリ。
mediaPublicPath /ox-content/twitter ダウンロードしたメディアに出す URL プレフィックス。
downloadVideo false ビルド時に MP4 動画とアニメーション GIF を取る。
maxVideoBytes 8388608 これより大きい動画はスキップする(8 MiB)。
appearance "compact" "full" で sveltweet 形の静的クロムを出す。
timeZone "UTC" フルカード日時の IANA タイムゾーン。

ダウンロードしたメディアは自分のサイトから出すので、厳しい img-src 'self' CSP も動き続けます。動画とアニメーション GIF は、downloadVideo をオンにしない限り自前のポスターと Watch on X パーマリンクを使い、生成 HTML に video.twimg.com は出しません。削除済みや非公開の投稿は、ビルドを落とさずリンクのみのカードに落ちます。引用投稿が欠けていても、元の投稿カードは残します。フルカード用 CSS は .ox-tweet--full を描画するページにだけ載ります。フルカードのクロムは MIT ライセンスの react-tweetsveltweet の見た目の契約に従います。帰属は クレジット にあります。詳細は Twitter/X Embed を見てください。

組み込み SSG のページでは、フル Tweet カードがあると Copy link 用の progressive enhancement が自動で入ります。独自ホストで Ox Content の HTML を描画する場合は、同じ初期化関数を import できます。

import { initTweetCards } from "@ox-content/vite-plugin/twitter/client";

initTweetCards(document);

独自ホストは @ox-content/vite-plugin/styles/social.css を、appearance: "full" なら styles/twitter-full.css も import します。記事の中に置く場合もこの2つで足ります。フルカードの CSS は、@tailwindcss/typography のような本文用スタイルシートが、カードの置き換えた要素に当てる規則を打ち消します。アバターやメディアへの画像マージン、引用投稿への引用符とその typography、カード自体への figure の余白などです。.prose .ox-tweet--full … のような上書きを下流で書く必要はありません。コンポーネント CSS を見てください。

Reddit#

embeds.reddit は Reddit 投稿を静的カードとして描画します。オプトインで、Reddit のウィジェットスクリプトは読みません。reddit: true ではビルド時に投稿 JSON を取り、Reddit が返す subreddit、作者、タイトル、本文抜粋、スコア、コメント数、日時、画像プレビュー、元リンクを出します。

oxContent({
  embeds: {
    reddit: true,
  },
});
<Reddit url="https://www.reddit.com/r/webdev/comments/abc123/release_notes/" />

reddit.com/r/{subreddit}/comments/{id}/{slug} URL と redd.it/{id} 共有リンクは、出力前に https://www.reddit.com/... へ正規化します。新しい /r/{subreddit}/s/{share} 形式は、URL だけでは投稿 ID が分からないため、リンクのみのカードとして受け付けます。

オプション 既定 目的
fetch true ビルド時に投稿メタデータを取る。
timeout 10000 メタデータ要求のタイムアウト(ミリ秒)。
cache true 取得したメタデータをこのビルドのメモリに残す。
cacheTTL 3600000 鮮度の窓(ミリ秒)。
userAgent ox-content-reddit-bot/1.0 ... Reddit JSON エンドポイントに送る User-Agent。

reddit: { fetch: false } にすると、ネットワークなしのリンクカードだけを描画します。削除済み、非公開、レート制限、その他の取得不能な投稿もビルドを落とさずリンクカードへ落ちます。未対応スキーム、認証情報付き URL、Reddit 以外のホスト、投稿ではないパスは href="#" のエラーカードになります。

Bluesky#

embeds.bluesky は静的カードを描画します。カードに出すテキストは要素本文なので、ネットワーク要求は一切要りません。

<Bluesky url="https://bsky.app/profile/danabra.mov/post/3mqzxmtfnxk2b">
  the urge to fix everything incorrectly
</Bluesky>

プロバイダカード#

プロバイダカードは、地図、記事、package、playground、動画、デザインファイル、 スライド、コミュニティ投稿を静的な preview として描画します。第三者の widget スクリプトは一切読み込みません。カードは transform が出力した HTML そのもので、 値はビルド時に fetch したものか、属性で渡したもののどちらかです。

provider が embed URL を公開しているカードは embed 属性も受け取ります。渡すと metadata の下に遅延読み込みの iframe が付き、リンクではなく現物がページに出ます。 embed は provider ごとに検証され、その provider 自身の embed host と path だけを 受け付けます。それ以外は描画せず捨てます。

以下のカードはすべて実際に描画されたものです。各カードの上にあるタグがその出力元です。

地図#

embeds.googleMapsplaceaddress から場所カードを描画します。Google Maps の embed URL を embed に渡すと、地図そのものを載せたカードになります。

<GoogleMaps
  url="https://www.google.com/maps/place/Tokyo+Station/"
  place="東京駅"
  address="東京都千代田区丸の内 1-9-1"
  embed="https://www.google.com/maps/embed?pb=..."
/>

embed を外すと同じタグがリンクのみのカードになります。閲覧時に Google へ リクエストを飛ばしたくないページ向けです。

embed として受け付けるのは https://www.google.com/maps/embed… だけです。場所 URL、 maps.app.goo.gl の短縮リンク、その他の host は無視され、上のリンクのみのカードに フォールバックします。

記事#

embeds.qiitaembeds.zennembeds.note は既定でタイトル・著者・カウントを fetch します。このサイトのように fetch: false にすると、カードは属性だけから 組み立てられます。ビルドはオフラインのままで、数値も書いた値に固定されます。

この3つはプロバイダカードの枠ではなくリンクプレビューカード(.ox-ogp-card、 修飾子は .ox-ogp-card--qiita など)として描画されます。OGP カードと並べても 見た目の言語が1つに揃います。著者・日付・カウントはカードの meta 行に入り、 fetch したサムネイルはカード画像になります。

<Qiita
  url="https://qiita.com/ubugeeei/items/73a2416fd46cfe6311a8"
  title="【日本語版】All we know about Vue 3’s Vapor Mode"
  author="@ubugeeei"
  tags="Vue.js, compiler, VaporMode"
  likes="32"
  dateTime="2023-12-17"
>
  Vapor Mode は template を DOM 操作へ直接コンパイルします。
</Qiita>
【日本語版】All we know about Vue 3’s Vapor Mode
Vapor Mode は仮想 DOM を組み立てず、template を DOM 操作へ直接コンパイルします。
qiita.com@ubugeeeiTags Vue.js, compiler, VaporModeLikes 32
Reactive Props Destructure を支える技術
分割代入した props がなぜリアクティブなままでいられるのか。
zenn.dev@ubugeeeiTags Vue.js, reactivityLikes 58
【Vue Fes Japan】ハンズオン企画の裏テーマ!?
ハンズオンの題材に Nuxt Tutorial を選んだ理由。
note.com@ubugeeeiLikes 11

パッケージレジストリ#

embeds.packageRegistry は npm、crates.io、PyPI、Docker Hub に対応し、provider が 持つ version / tag URL も扱えます。versionlicenserepositorydownloadsstars がメトリクスとして描画されます。downloadspulls という綴りでも受け取り、 下の Docker Hub カードはそちらを渡しています。

レジストリカードは名前ではなくレジストリ自身のマークを、メトリクスはラベルではなく アイコンを先頭に置きます。語はスクリーンリーダー向けの不可視テキストとして markup に 残るので、読み上げは "Downloads 31M/week" のままです。

<NpmPackage
  url="https://www.npmjs.com/package/vite"
  version="7.1.0"
  license="MIT"
  downloads="31M/week"
/>

Playground#

embeds.playgrounds は CodePen、CodeSandbox、JSFiddle、Observable、Replit を まとめて扱います。それぞれ自身の embed URL を受け取れるので、説明ではなく 動いている sandbox をそのまま見せられます。

<CodePen
  url="https://codepen.io/miriamsuzanne/pen/BEvjbm"
  title="Angled Background CSS-only Mixin"
  author="@miriamsuzanne"
  embed="https://codepen.io/miriamsuzanne/embed/BEvjbm"
/>

CodeSandbox だけは何も fetch しません。カードは URL と渡した属性だけから組み立てる ので、削除済みの sandbox でもビルドを落とさずカードを描画します。sandbox の 指定方法は 4 通りすべて受け付けます(/s/{id}/p/sandbox/{id}/p/devbox/{id}/embed/{id})。

デザインファイルとスライド#

embeds.figmaembeds.googleSlides は provider の共有 URL を受け取ります。 Google Slides は /embed URL も受け取り、デッキをそのままページ内に描画します。

<GoogleSlides
  url="https://docs.google.com/presentation/d/1EAYk.../edit"
  title="Baby album"
  slides="9"
  embed="https://docs.google.com/presentation/d/1EAYk.../embed"
/>

Figma は filedesignboardprotoslides と Community のリンクを 受け付けます。ファイルキーは種別の次のセグメントで、その後ろの人間向け slug は 無視されます。

動画とターミナル録画#

embeds.vimeoembeds.loomembeds.asciinemaembeds.twitchdurationviewsstatus をメトリクスに持つ動画カードを描画します。Vimeo、Loom、 asciinema は player URL を embed として受け取れます。

<Vimeo
  url="https://vimeo.com/76979871"
  title="The New Vimeo Player"
  embed="https://player.vimeo.com/video/76979871"
/>

Twitch だけは例外です。player は埋め込み先ドメインを宣言しないと読み込みを拒否する ため、player URL は embeds.twitch.parent に安全なドメインを指定したときだけ生成 されます。指定がなければカードは静的なままなので、titlechanneldurationstatusviewsimage で見せる価値のあるカードにします。

コミュニティと social post#

embeds.discordembeds.fediverseembeds.facebookembeds.threadsembeds.instagram は同じカード形状を共有します(著者、本文、時刻、リアクション数)。 <Fediverse><Mastodon><Misskey><Mixi2> は同じオプションで、instance は URL から読み取ります。

<Mastodon
  url="https://mastodon.social/@Mastodon/117117221397911074"
  author="@Mastodon@mastodon.social"
  reposts="622"
  likes="954"
>
  Mastodon 5.0 の最初のお披露目。
</Mastodon>

プロバイダのオプション#

オプション 既定値 用途
fetch true 記事・package・playground・動画の metadata を取得
timeout 10000 metadata リクエストのタイムアウト (ms)
cache true このビルド中、metadata をメモリにキャッシュ
cacheTTL 3600000 鮮度のウィンドウ (ms)
persistCache false ビルドをまたいで metadata をディスクに保持
cacheDir .cache/ox-content/providers 永続キャッシュのディレクトリ
iframe false playground / 動画の lazy iframe URL を付与
parent [] Twitch iframe の parent ドメイン

iframe導出 される embed の話です。渡されたページ URL から provider が player URL を組み立てられるようにします。明示的な embed 属性はどちらの設定でも効きます。

cache だけなら 1 ビルド分の寿命です。persistCache: true にすると metadata を ディスクにも書くので、クリーンビルドや新しい CI ワーカーが前回の取得結果を再利用します。 見つからなかった lookup も記憶するため、落ちている provider をビルドのたびに embed の 数だけ再試行することはありません。壊れたエントリはビルドを落とさず破棄して取り直し、 ディレクトリは hash で鍵付けされるので、provider URL がその外へ出ることはありません。

未対応 scheme、認証情報つき URL、別 host の URL、取得できない metadata は、ビルドを 落とさず元のタグかリンクカードへフォールバックします。package metadata fetch の失敗時は、 status や error reason を含む [ox-content] warning も出します。Vimeo card は Vimeo の public oEmbed endpoint から metadata を取得し、Twitch card は既定では認証 API を 呼びません。

Spotify#

embeds.spotify はトラック、アルバム、プレイリスト、エピソード、番組、アーティスト向けの公式 iframe プレーヤーを描画します。

<Spotify url="https://open.spotify.com/track/2VEQTuWiuEC7J8kkA7h7xq" />

出力は遅延読み込み付きで open.spotify.com/embed/... を指す <iframe> です。上の静的カードと違い、本物の第三者プレーヤーなのでオプトインのままです。

フレームには再生対象に応じた名前(Spotify trackSpotify playlist など)が付くので、スクリーンリーダーが「フレーム」より有用な読み上げをします。自分で名前を付けるには title を渡します。

<Spotify url="https://open.spotify.com/album/25Dgs9rR8ETpGCwD0wUv0q" title="Joel Ross — nublues" />

Apple Music#

embeds.appleMusic はアルバム、プレイリスト、曲、アーティスト、ミュージックビデオ向けの公式 iframe プレーヤーを描画します。

<AppleMusic url="https://music.apple.com/us/album/ummg-feat-taylor-eigsti/1769360313?i=1769360314" />

music.apple.com の共有 URL は embed.music.apple.com に書き換えられ、ストアフロントとパス、曲選択の i= クエリは残します。すでに埋め込み用の embed.music.apple.com URL も、同じホスト/パス検査のあと受け付けます。HTTPS でない URL、似せたホスト、認証情報、フラグメント、不正なパスは iframe にせず、書いたまま残します。

プレーヤーは第三者 iframe なので、オプションは既定でオフです。Content-Security-Policy を設定しているサイトでは、プレーヤーを読み込むために frame-src https://embed.music.apple.com(または同等の child-src)が必要です。書き方の詳細は Apple Music Embed を見てください。

Speaker Deck#

embeds.speakerDeck は、プレーヤー URL か oEmbed メタデータが解決できたとき遅延 iframe を描画し、取得や解析に失敗したときは安全なリンクカードに落とします。

<SpeakerDeck url="https://speakerdeck.com/jane/my-talk" title="My Talk" author="Jane Doe" />
My TalkJane Doe

上のデッキは存在しないため、この例はプレーヤーではなくリンクカードのフォールバックを示しています。

speakerdeck.com/{user}/{slug} の共有 URL はビルド時に oEmbedtitle / author_name / プレーヤー ID / サムネイルを取ります。存在しない ID でもプロバイダ自身のエラーページを埋め込んでしまうため、例では共有 URL を使ってください。すでに埋め込み用の speakerdeck.com/player/{id} はネットワークなしで描画します。javascript:data: URL は書いたまま残します。

oEmbed 取得やプレーヤー ID の解析に失敗したときは、元の HTTPS Speaker Deck URL を指すフォールバックリンクカードになります。iframe は遅延読み込みで、sandboxreferrerpolicy="strict-origin-when-cross-origin" を付けます。Content-Security-Policy を設定しているサイトでは frame-src https://speakerdeck.com が必要です。詳細は Speaker Deck Embed を見てください。

Audio / Video#

embeds.audioembeds.video はネイティブの <audio> / <video> プレーヤーを描画します。既定はオフで、第三者 iframe は使いません。

oxContent({ embeds: { audio: true, video: true } });
<Audio
  src="https://cdn.example.com/intro.mp3"
  title="Episode intro"
  transcript="/intro.txt"
  download="/intro.mp3"
/>

<Video
  src="/talk.mp4"
  poster="/talk.jpg"
  captions="/talk.en.vtt"
  srclang="en"
  label="English"
  width="1280"
  height="720"
  title="Release talk"
/>

ソースは HTTPS か同一オリジンの相対パスだけです。javascript:data:http:、プロトコル相対 URL は書いたまま残します。入れ子の <track> で追加のキャプション/字幕を渡せます。ネイティブ controls は title(なければ Audio / Video)でラベルされます。width / height で動画のアスペクト比を確保し、レイアウトシフトを避けます。詳細は Audio and Video Embed を見てください。

StackBlitz#

embeds.stackBlitz は StackBlitz プロジェクト URL を、embed=1 を付けたサンドボックス iframe にします。

<StackBlitz url="https://stackblitz.com/edit/vitejs-vite"></StackBlitz>

WebContainer#

embeds.webContainer は、操作時に WebContainers を起動するサイト向けに、プロジェクトソースとクロスオリジン分離メタデータを持つ遅延プレースホルダを出します。プレースホルダ自体は完全に静的です。

<WebContainer entry="index.html" title="Demo">
  npm install
  npm run dev
</WebContainer>
Demoindex.htmlBoots on interaction
npm install
npm run dev
Entry index.html2 commandsStatic source bundleRequires cross-origin isolation

分離要件は WebContainer Embed を見てください。

関連#

Last updated: