【Astro×Mermaid&自動検証の壁突破】27インチ5K iMacで図解が巨大化する問題と、Playwright『WebP 16,383px上限 0バイト画像クラッシュ』解決録
技術解説や構造図をテキストだけで美しく描ける「Mermaid(マーメイド)図解」は非常に便利ですが、Astro サイトへ導入して 27 インチ 5K iMac(論理解像度 2560×1440)の大画面で開いた瞬間、縦長のフローチャートが横 2,400px × 縦 6,060px という怪物サイズに巨大化してしまう問題 に直面しました。さらに自動ブラウザ検証ツール(Playwright MCP)で縦長記事の全体スクリーンショットを撮ろうとすると、突然 0 バイト の空画像が生成されて AI チャットごとクラッシュする という別の罠まで潜んでいました。
getBBox())と画面の幅・高さから最適サイズを自動算出する動的スケーリング関数と、スマホ・タブレット・フル HD・5K iMac の 4 画面すべてで全 37 個の図解を実機検証して見つけた改善則、そして WebP 規格の「最大 16,383px 上限」による 0 バイト画像クラッシュを 100% 防ぐ鉄則をまとめて解説します。 -
【実機検証】27インチ5K iMacデュアル要塞の構築録!PRO-G40 SSD 8TB×OWCハブ×Luna Displayの速度低下罠と新型Magic Trackpadが動かない原因を完全解決 : 本記事の大画面検証の舞台となった 27 インチ 5K iMac 環境の構築レビューです。
-
【27インチ5K iMac 20,1 メモリ増設の罠】2666MHzが2133MHzに落ちる原因と5K液晶を手放せない理由 : なぜテキスト執筆や設計図の確認において 27 インチ 5K ディスプレイが圧倒的に快適なのかを解説しています。
-
【中古Intel Mac完全初期化とデータ移行の罠】消せない『user(SecureToken)』解決・Tahoe回避・元データを失わないコピー鉄則 : 検証母艦である
iMac 20,1をクリーンインストールして最適 OS で維持する手順です。 -
【GitHub Actions無料枠パンク脱出録】『2秒でFailed』と.npmrc混入エラーを解決し、macOS標準『launchd』で完全無料の自動実行基盤を作った全記録 : 母艦 Mac を活かしてクラウド無料枠制限から脱却したローカル自動化の実践録です。
-
Astro(特に Astro 5 / 6 系)で Mermaid コードブロックを書いたら、Shiki シンタックスハイライターに分解されて図が描画されなかった方
-
レスポンシブ対応のつもりで SVG に
width: 100%を当てた結果、大型モニターで図形と文字が 3〜4 倍に引き伸ばされて困っている方 -
逆にスマホ画面(幅
375px等)で見ると、横長の図やガントチャートが米粒のように潰れたり重なったりしてしまう方 -
Playwright MCP の
browser_take_screenshotで縦長ページを撮影した直後に、AI エージェントがメディアエラーで復帰不能になった経験がある方
なんじゃこりゃあ〜!!27 インチの iMac で自分の記事を開いてみたら、3 個しか四角がないシンプルなフローチャートなのに、画面の端から端まで看板みたいにデカデカと引き伸ばされて、スクロールしてもスクロールしても図が終わらんぞ!!
おじさん、それは CSS でよくある「良かれと思って書いた width: 100% !important の罠」なんです!さらにスマホで開くと今度は逆に文字が豆粒になってしまうんですよ。大画面でもスマホでも「人間が一目で気持ちよく読めるサイズ」に自動調整する仕組みを作りましょう!
5K大画面でMermaid図解が巨大化する2つの原因
AstroのShiki分割と横幅100%強制の罠
まず Astro(Astro 5 / 6 系)で Mermaid を動かす際の最初の関門として、標準のコードハイライターである Shiki がコードブロック内の文字列を色分け用の <span> タグに細かく分解してしまうため、Mermaid 標準の自動初期化(startOnLoad: true)だけでは構文エラーになって描画されません。
そのため、pre[data-language="mermaid"] から純粋なテキスト(graph.textContent)だけを抜き出し、手動で mermaid.render(id, content) を呼んで SVG に置き換えるスクリプトを組むのが定石です。
ところが、描画された SVG に対して「画面からはみ出さないようにしよう」と単純に width: 100% !important; height: auto; を指定すると、広大なモニターで悲劇が起きます。
- 画面を広く使うレイアウトで記事エリアの幅が
2,000px以上あるとき、本来は横450pxでちょうど綺麗に読める縦長フローチャートまで 横2,400px(約 5 倍)へ強制拡大 されます。 - アスペクト比(縦横比)が保たれたまま横に 5 倍伸びるため、縦方向も 5 倍になり、7 段のフロー図がなんと 縦
6,060px(画面 4 枚分以上!) という超巨大サイズに化けてしまったのです。
机上の計算を裏切る4画面サイズ実機撮影の衝撃
そこで最初は計算式だけでスケーリングを入れてみたのですが、「本当にすべての端末で綺麗に見えているか?」を確かめるため、Playwright MCP を使ってサイト内の 全 37 個の図解を 4 つの画面サイズ(スマホ 375×812・iPad 768×1024・フル HD 1920×1080・5K iMac 2560×1440)で実際に開いて総点検 しました。
すると、机上の計算だけでは絶対に気づけなかった「実物の崩れ」がボロボロ見つかったのです!
- 円グラフ(
pieチャート)の謎の巨大余白 : Mermaid が生成する円グラフのviewBox横幅が、実際の円と凡例の描画幅より何百ピクセルも広く取られており、そのせいで円本体が不自然に小さく縮んでいました。 - スマホでの
gantt(工程表)や横長LR図の文字潰れ : 横幅1,500pxある横長の図をスマホ幅340pxにそのまま縮小すると、文字が 3px 以下の米粒になって判読不能になっていました。 - サブグラフ見出しと矢印の貫通・引用符漏れ : 日本語記号を含む
subgraph "見出し(補足)"でダブルクォートを忘れた構文エラーや、横並び要素が多すぎて線が文字を貫通している箇所がありました。
解像度と図形サイズに応じる動的スケーリング実装
getBBox余白除去と3つの上限値による最適化
この 4 画面実機テストの結果を踏まえ、レイアウト側の自動調整関数(fitMermaidSvg)を次の 4 段階アルゴリズム へ進化させました。
-
svgElement.getBBox()による過剰余白の自動トリミング : 元のviewBox幅が、実際に図形が描かれているバウンディングボックス(bbox.width)より80px以上広い場合(円グラフ等)、実描画領域+適度な左右余白だけにviewBoxを自動で切り詰めます。 -
上限①:図本来の自然幅の
1.08倍まで(過拡大防止) : 要素が少ないコンパクトな図が 5K モニターで看板のように引き伸ばされないよう、図本来の幅(vbWidth)の1.08倍を上限とし、図内の文字サイズ(15px)が記事本文と自然に揃うようにします。 -
上限②:画面横幅の
82%かつ最大1,160pxまで : 横に広いシーケンス図や構成図も、大型モニター上で視線が左右に迷わない最大1,160pxで中央寄せにします。 -
上限③:画面高さの
75%(75vh)から逆算した 1 画面収まり幅+最小可読幅の保証 : 縦に長いフロー図は、スクロールせずに全体像が一目で見渡せるよう75vh × (幅 / 高さ)で最適幅を逆算します。ただし、縦長すぎて文字が潰れるのを防ぐため、minReadableWidth(最低440pxまたは元幅の72%)を下限として保証します。
// Astroレイアウト内の is:inline スクリプトで動かす解像度適応型スケーリング関数
const fitMermaidSvg = (svgElement) => {
if (!svgElement) return;
const vw = window.innerWidth || document.documentElement.clientWidth || 1200;
const vh = window.innerHeight || document.documentElement.clientHeight || 800;
const isMobile = vw <= 768;
let vbWidth = 0;
let vbHeight = 0;
try {
const bbox = svgElement.getBBox();
if (bbox && bbox.width > 10 && bbox.height > 10) {
const origVb = svgElement.getAttribute('data-orig-viewbox') || svgElement.getAttribute('viewBox') || '';
if (!svgElement.getAttribute('data-orig-viewbox') && origVb) {
svgElement.setAttribute('data-orig-viewbox', origVb);
}
const parts = origVb.trim().split(/[\s,]+/).map(Number);
// 円グラフ等で元のviewBoxが実描画幅より80px以上広い場合は余白を自動トリミング
if (parts.length === 4 && parts[2] > bbox.width + 80) {
const padLeft = 24, padRight = 64, padY = 20;
vbWidth = Math.ceil(bbox.width + padLeft + padRight);
vbHeight = Math.ceil(bbox.height + padY * 2);
svgElement.setAttribute('viewBox', `${Math.floor(bbox.x - padLeft)} ${Math.floor(bbox.y - padY)} ${vbWidth} ${vbHeight}`);
}
}
} catch (e) {}
if (vbWidth <= 0 || vbHeight <= 0) {
const parts = (svgElement.getAttribute('viewBox') || '').trim().split(/[\s,]+/).map(Number);
if (parts.length === 4 && parts[2] > 0 && parts[3] > 0) {
vbWidth = parts[2];
vbHeight = parts[3];
}
}
svgElement.removeAttribute('height');
svgElement.style.display = 'block';
svgElement.style.margin = '0 auto';
svgElement.style.height = 'auto';
if (isMobile) {
const availW = Math.max(vw - 38, 280);
svgElement.style.width = '100%';
svgElement.style.maxWidth = vbWidth > 0 ? Math.min(availW, Math.round(vbWidth * 1.1)) + 'px' : '100%';
return;
}
const naturalLimit = vbWidth > 0 ? vbWidth * 1.08 : 960;
const screenWidthLimit = Math.min(vw * 0.82, 1160);
let heightBasedLimit = 1160;
if (vbWidth > 0 && vbHeight > 0) {
const maxAllowedHeight = Math.max(vh * 0.75, 460);
const rawHeightLimit = maxAllowedHeight * (vbWidth / vbHeight);
const minReadableWidth = Math.min(vbWidth * 0.9, Math.max(vbWidth * 0.72, 440));
heightBasedLimit = Math.max(rawHeightLimit, minReadableWidth);
}
const optimalMaxWidth = Math.round(Math.min(naturalLimit, screenWidthLimit, heightBasedLimit));
svgElement.style.width = '100%';
svgElement.style.maxWidth = optimalMaxWidth + 'px';
};
あわせて原稿側の図解も、スマホで崩れる gantt を廃止して縦方向のフロー図(flowchart TD)や Markdown テーブルへ組み直しました。
5K画面とスマホで実測した劇的な改善データ
27 インチ 5K iMac(2560×1440)上で計測した改修前後の実寸比較がこちらです。
| 図解のタイプ | 改修前(width: 100% 強制) | 改修後(fitMermaidSvg 適用) | 視覚的な変化 |
|---|---|---|---|
| 標準の縦フロー図 | 横 2,400px × 縦 2,850px | 横 557px × 縦 661px | 本文の文字サイズと綺麗に揃い、中央に品よく収まる。 |
| 縦7段の長大フロー図 | 横 2,400px × 縦 6,060px | 横 410px × 縦 1,035px | 画面4枚分だった図が、スクロールなしで1画面に見渡せる。 |
| 横広のシーケンス図 | 横 2,400px × 縦 1,170px | 横 1,080px × 縦 528px | 首を左右に振らずに一目で追える最適サイズに収束。 |
Playwright縦長スクショの0バイト画像クラッシュ
WebP規格の最大16,383pxという14ビットの壁
そして、この実機検証の過程でもう 1 つ発見した AI 開発者必見の重大トラップ が、Playwright MCP のスクリーンショット機能による 「0 バイト画像クラッシュ」 です。
写真が 30〜60 枚入った長編レビュー記事など、ページ全体の縦の長さ(document.documentElement.scrollHeight)が非常に長いページに対して、Playwright MCP の browser_take_screenshot ツールを fullPage: true かつ type: "webp" で呼び出すと、突然 AI エージェントが Agent execution terminated due to error を出して停止し、何度リトライボタンを押しても二度とそのチャットが動かせなくなる事故が起きました。
原因を追跡すると、WebP 画像フォーマットの国際規格そのものにある「14 ビット最大ピクセル寸法上限(縦・横ともに最大 16,383px まで)」 に行き着きました!
- WebP のヘッダー仕様では、画像の幅と高さ(マイナス 1)をそれぞれ 14 ビット整数( 最大
16,383px、Retina 2 倍解像度なら論理高さ約8,191px相当で到達する場合も)で格納します。 - そのため、高さ
16,383pxを超える縦長ページをfullPage: true, type: "webp"で撮影すると、ブラウザ内部の WebP エンコーダが変換に失敗して 中身が0 バイト(空っぽ)の壊れた.webpファイル を吐き出します。 - その
0 バイト画像がそのまま AI チャットの会話履歴(マルチモーダル入力)に添付されるため、以降の全ターンで API 側が「不正な空メディアデータ」としてリクエストを拒否し、会話ごと復帰不能になっていたのです!
ひゃあ〜!ただ画面全体のスクショを WebP で撮ろうとしただけで、16,383 ピクセルを超えると 0 バイトの爆弾画像ができてチャットごと壊れちゃうなんて、知らないと絶対ハマるわね!!
本当に怖い罠ですよね!でも原因さえ分かってしまえば、対策はとっても簡単で 100% 確実に防げます!
会話を壊さないPNG指定とDOM計測の鉄則
AI エージェントに Playwright MCP で画面確認をさせる際は、ルールファイル(GEMINI.md 等)に次の 3 原則を明記しておくだけで、このクラッシュは永久にゼロになります。
-
fullPage: trueとtype: "webp"の併用を絶対禁止にする : WebP の16,383px上限を踏まないよう、スクリーンショット撮影時は必ずtype: "png"(PNG は 32 ビット寸法のため上限の心配なし)を使うか、fullPage: false(現在見えているビューポートのみ撮影) を基本にします。 -
ページ全体の画像切れや横はみ出し確認には
browser_evaluateを併用する : 何万ピクセルもある巨大画像を丸ごと AI に読ませるよりも、browser_evaluateで JavaScript を走らせて「全<img>のnaturalWidth > 0チェック」「TOC 各行の折り返し高さチェック」「横スクロール発生チェック」を数値で取得し、要点箇所だけをfullPage: false, type: "png"で撮影する方が速くて確実です。 -
保存先
filenameにワークスペース外のパスを指定しない : Playwright MCP のセキュリティ制限によるFile access deniedを防ぐため、filenameは省略するかワークスペース内の相対パスのみを使い、確認後は一時画像を削除してクリーンに保ちます。
まとめ:大画面からスマホまで実物を見て仕上げる
「手元のノート PC 画面で綺麗に見えたからヨシ!」と思って公開していても、27 インチ 5K モニターやスマホの縦画面で開くと、図解が怪物のように膨張していたり豆粒に潰れていたりすることは珍しくありません。
- Mermaid 図解は
width: 100%で放置せず、getBBox()余白トリミング+自然幅・画面幅・画面高さ(75vh)の 3 上限で動的スケーリングする - スマホで文字が重なる
ganttや極端な横長図は、縦フロー(TD)や Markdown テーブルへ潔く組み替える - Playwright での自動検証時は WebP の
16,383px上限を忘れず、type: "png", fullPage: false+ DOM 計測で安全確実にチェックする
図解のサイズがピタッと画面に調和すると、記事全体の品格と読みやすさが一気に跳ね上がります。ぜひご自身のサイトの図解も大画面とスマホの両方で見比べてみてください!

▲ 大きすぎず小さすぎない「ジャストサイズ」が一番心地いいのは、図解も猫様も同じですね