レスポンシブ画像
レスポンシブ画像
docs/assets/ 配下の対象となる画像はすべて自動的にリサイズ/WebP バリアントが
生成され、ページ内の一致する <img> はすべてレスポンシブな <picture> に
書き換えられます - 新しい Markdown 構文は不要で、有効にするための設定も必要ありません。
これは bx-image の上に構築されており、
bx-markdown/bx-esapi/bx-yaml と並ぶ必須の依存関係です
(はじめに を参照)。
仕組み
画像は通常通りに記述します - Markdown 構文でも生の HTML でも、 ページリンク と同じくページからのファイル相対パスで指定します:

ビルド時、screenshot.png は自身の幅より狭い設定済みの各幅にリサイズされ
(アップスケールは行われません)、同サイズの WebP 再エンコードも生成され、
ビルド済みページには次が出力されます:
<picture>
<source type="image/webp" srcset="/assets/screenshot-400w.a3f9c2e1.webp 400w, /assets/screenshot-800w.a3f9c2e1.webp 800w, ...">
<img src="/assets/screenshot.png" srcset="/assets/screenshot-400w.a3f9c2e1.png 400w, /assets/screenshot-800w.a3f9c2e1.png 800w, ..." sizes="(min-width: 800px) 800px, 100vw" alt="ビルドされたばかりのサイト">
</picture>
ブラウザは sizes を満たす最小のバリアントを選び、対応していれば WebP を、
そうでなければプレーンな元の src(以前とまったく同じように配信されます)を
選びます。記述した他のすべての属性 - alt、class、その他何であれ - は、
書き換えられた <img> にそのまま引き継がれます。
自身の幅より狭い設定済み幅がない画像(小さなアイコンなど)でも、
assets.images.formats に "webp" が含まれていればフルサイズの WebP
再エンコードは生成されます - レスポンシブなブレークポイントを提供しなくても、
ファイルサイズの実質的な削減になります。
キャプション、配置、フレーミング
キャプション、フレーム、複数画像のギャラリーは、すべて単なるブロックレベルの HTML です - bx-markdown/Flexmark はこれを完全にそのまま通過させる (CommonMark 自身の「HTML ブロック」規則)ため、bx-sites 独自の構文はまったく 必要ありません:
<figure>
<img src="../assets/screenshot.png" alt="ビルド結果">
<figcaption>ビルドされたばかりのサイト</figcaption>
</figure>
<div data-with-frame="true">
<img src="../assets/screenshot.png" alt="フレーム付き">
</div>
<div class="bxsites-gallery">
<img src="../assets/one.png" alt="">
<img src="../assets/two.png" alt="">
<img src="../assets/three.png" alt="">
</div>
x-data/x-show/@click などその他の Alpine.js 属性についても同様です -
Alpine.js によるインタラクティビティ を参照してください。
リサイズされないもの
- SVG - すでに解像度非依存であるため、無変更でそのままコピーされます。
- アニメーション GIF - bx-image のリサイズ処理はフレームを認識しないため、 リサイズすると単一フレームに潰れてしまいます。この機能が存在する以前と まったく同じく、無変更でそのままコピーされます。
docs/assets/以外にあるもの - リモート画像 URL (<img src="https://...">)は完全に手つかずのまま残されます。extraCss/extraJsが絶対 URL を 「そのまま使用」として扱うのと同じです。- すでに設定済みのどの幅よりも狭い画像 - 生成するものが何もないため、
プレーンな
<img>は以前とまったく同じようにレンダリングされます ("webp"が有効な場合を除きます。上記参照)。
AVIF 対応もまだありません - 本稿執筆時点で bx-image はこの形式を書き出しません。 WebP だけでもサイズ削減の大部分は得られ、対応するツール/ブラウザの幅もはるかに 広いです。bx-image が上流で AVIF に対応すれば、この点は見直す価値があります。
無効化する
{ "assets": { "images": { "enabled": false } } }
プレーンで未加工の docs/assets/** コピーにフォールバックします -
この機能が存在する以前にすべての画像が扱われていたのとまったく同じです。
自分でブレークポイントを選ぶ
{
"assets": {
"images": {
"widths": [ 480, 960, 1440 ],
"formats": [ "webp" ]
}
}
}
widths のデフォルトは [400, 800, 1200, 1600]、formats のデフォルトは
["original", "webp"] です - "original" を外すと、ソース形式でのリサイズ済み
コピー生成を完全にスキップできます(<img> のフォールバックとしてプレーンな
フルサイズの元画像は引き続き保持されます)。"webp" を外すと WebP の <source>
自体をまるごとスキップします。assets.images のすべてのキーについては
設定 を参照してください。
CSS/JS バンドリング
extraCss/extraJs も同じ方法でバンドルされ、デフォルトで有効です
(assets.bundle):
{
"extraCss": [ "assets/a.css", "assets/b.css" ],
"extraJs": [ "assets/app.js" ]
}
エントリごとに 1 つの <link>/<script> タグを出す代わりに、フィンガープリント
付きの assets/bundle.<hash>.css(記載順)と assets/bundle.<hash>.js を
それぞれ 1 つずつビルドします。CSS はコメントが除去され空白が圧縮されますが、
JS では意図的に安全で構造的な空白整理のみを行い、コメント除去は一切行いません -
単純な正規表現では文字列内の //("http://example.com" など)と本物の
コメントを区別できず、判定を誤るとプロジェクト自身のスクリプトを黙って
壊してしまうためです。これはバンドリングと軽い整理であって、本格的な
ミニファイアではありません - これだけでは不十分な場合、Java 製のミニファイ
ライブラリを同梱するのは妥当な将来のアップグレードです。
バンドリングは、リスト内のすべてのエントリがローカルのプロジェクトファイル である場合にのみ有効化されます。外部 URL(CDN リンク)が 1 つでも混ざっていると、 プロジェクトが依存している CSS のカスケード順を黙って並べ替えてしまう危険を 冒すよりも、リスト全体を今日のまったく同じ URL ごとの挙動にフォールバックさせます:
{ "extraCss": [ "assets/custom.css", "https://cdn.example.com/lib.css" ] }
この機能が存在する以前とまったく同じく、バンドルされない 2 つの個別な
<link> タグとしてレンダリングされます。
フィンガープリンティングとキャッシュ
生成されるすべての画像バリアントと CSS/JS バンドルはコンテンツハッシュで
命名されます(assets.fingerprint、デフォルトで有効)- ビルドは、ソースの
コンテンツが実際に変更されたときにのみバリアントのファイル名を変える、という
ことです。これにより、静的ホストで遠い未来を指す Cache-Control ヘッダーを
安全に設定できます。docs/assets/ 配下のプロジェクト自身のオリジナルファイルは、
いずれの場合もそのままのプレーンな名前を保ちます - フィンガープリントが付くのは
パイプラインが生成した出力だけなので、::: file のダウンロードカードや、
ファイル名で画像を直接指すプレーンなリンクは、これまでどおり動作し続けます。
生成されたすべてのバリアントは、プロジェクト自身の .cache/images/
(bxSites clean で site/ と共に削除されます)
配下にディスクキャッシュされ、ソース画像自身のコンテンツハッシュをキーに
しています。そのため、build の再実行(バージョン/ロケールツリーごとに 1 回、
すべて同じ docs/assets/ を共有)や、無関係な編集後の bxSites serve が、
プロジェクト内の変更されていないスクリーンショットまで毎回デコード/リサイズ/
再エンコードすることはなく、実際に変更されたものだけが処理されます。