Markdownに埋め込んだHTMLの意味は、書き手ではなく表示する場所が決める

手元のエディタで完璧に表示された色付きの囲みが、公開した途端に消えている。犯人は書き手の腕ではありません。Markdownに埋め込んだHTMLの意味を最後に決めるのは、書いた人ではなく、それを変換して表示する側です。この記事では、同じ一行が表示先によって四通りに化ける理由と、表示先を前提として先に決めてから書き方を選ぶ手順を整理します。

同じ一行が、表示先によって四通りの結果になります

Markdownは、2004年にジョン・グルーバー(John Gruber)が考案した書き方です。そのままでも読みやすく、HTMLへ変換しやすいことを目指して作られたので、HTMLを直接書き込める作りになっています。行の途中に入れるインラインのHTMLと、まとまった塊として入れるブロックのHTMLがあります。

これは<em>斜体</em>を混ぜたテキストです。

<div class='note' style='background-color: #f0f0f0; padding: 20px;'>カスタムスタイルの囲みです。</div>

この二行の運命は、書いた瞬間には決まっていません。

結果起こる場面
書いたとおりに表示されるHTMLをそのまま通す環境。Jupyter NotebookのMarkdownのセルでは、HTMLで色付きの囲みや表を作れます
一部が黙って取り除かれる安全のために許すタグと属性を絞っている環境。GitHubでは script、iframe、embed、object のタグや、onclick などのイベント属性が無効にされます
タグが文字のまま見えるHTMLを受け付けず、記号を文字として扱う環境
コードとして表示されるブロックのHTMLを、行頭に4つ以上の空白を入れて書いた場合

Markdownは書いたとおりに表示される、という説明は、表示先が一つしかない世界でだけ正しい話です。表示を決める処理が場所ごとに違う以上、書いた時点で分かるのは候補だけです。

結果を決めるのは、仕様、パーサー、無害化の三段です

一段目は仕様です。オリジナルのMarkdownは曖昧な部分が多く、変換するプログラム(パーサー)ごとに結果がずれていました。詳しい仕様書とテストでこの揺れをなくそうとしたのがCommonMarkで、ブロックのHTMLの扱いを定め、script や style のタグには特別な扱いを決めています。markdown-it、Remark、Pandocなど、多くのパーサーがこの仕様に沿っています。なお、オリジナルの仕様では、HTMLのブロックの中身はMarkdownとして解釈されません。

二段目はパーサーの拡張です。GitHubのGFMは、CommonMarkに表やタスクリストなどを足した仕様です。数式はLaTeXの記法で $ や $$ で囲み、MathJaxやKaTeXといったライブラリが表示します。図はPlantUML、GraphvizのDOT言語、Mermaidなどを、言語名を付けたコードブロックに書き、対応する環境が図に変えます。対応していない環境では、どちらも記号の羅列のまま読者の前に出ます。

埋め込む四つの書き方

三段目が無害化(サニタイズ)で、ここが結果をいちばん大きく変えます。悪意のあるスクリプトをページに埋め込み、読む人のブラウザで動かす攻撃をXSS(クロスサイトスクリプティング)と呼びます。script タグだけでなく、画像の読み込みに失敗したときに動く onerror 属性や、javascript: で始まるリンクも悪用され、セッションの乗っ取りや情報の流出につながるおそれがあります。だから他人の書いたMarkdownを表示するサービスは、許可したタグと属性だけを残す方式(ホワイトリスト方式)で中身を絞ります。JavaScriptならDOMPurifyのようなライブラリが使われます。

しかも、消したことは書き手に知らされません。手元のプレビューで確認して公開しました、という報告は、試着室の鏡で確かめたから街でも同じに見える、と言っているのと同じです。確かめる場所は、読み手が実際に開く画面しかありません。しかも、サービスがどのタグを許すかは予告なく変わることがあり、公開されている説明と実際の挙動が一致しているかも、外からは正確には言えません。今日表示されたことは、来月も表示される保証になりません。

表示先は、書き始める前に決める前提です

判断の前提は、対象、目的、制約、評価基準の四つに分けて書き出せます。拙著でも繰り返し扱う型ですが、この話に当てると、表示先は制約にあたります。制約が欠けた計画は破綻します。表示先を決めずに書いた埋め込みは、どこかで必ず壊れます。

  • 表示先が一つで、HTMLを通すと分かっている場合は、必要な部分だけをHTMLで書く。クラス名の付け方をそろえ、何度も使う部品はテンプレートに分ける
  • 表示先がGitHubのように中身を絞るサービスなら、許されるタグと属性を各サービスの説明で先に確かめる。動きのある表現は、画像やアニメーションGIF、外部ページへのリンクで代える
  • 表示先が複数ある、またはまだ決まっていないなら、HTMLを埋め込まず、基本の記法だけで書く。どこでも同じに読めることを優先する
  • 他人の書いたMarkdownを表示する仕組みを作るなら、無害化を必ず入れる。出どころの分からないコードは貼り付けない

どこで使うか決まっていないけれど、とりあえずリッチに、という依頼は、行き先を決めずに航空券だけ買っておくようなものです。使える記法は表示先が決まるまで決まらないので、リッチにした部分ほど、行き先が決まった時点で作り直しになります。行き先が未定のうちは基本の記法だけで書き、決まってから足すのが安上がりです。

逆に、この記事の主張が弱まる場面もあります。自社のサイトのように、変換するプログラムも無害化の設定も自分で選べる表示先が一つだけなら、書き手が結果を決めていると言ってかまいません。表示する側が決める、という話は、表示する側を自分で握っていないときほど重くなります。

タグを文字として見せたいときは、記号の前にバックスラッシュ(\)を付けるか、&lt; や &gt; のような文字参照に置き換えます。これも、表示先がHTMLとして読むことを前提にした書き方です。

AIへの指示文も、読む側と貼る先を先に渡します

同じ構図は、AIに文章を書かせる場面でそのまま起きます。前提を渡されないAIは、手持ちの知識で空白を埋めます。貼り先を伝えずに頼めば、AIはどこかで見た平均的な表示先を想定して記法を選び、その記法があなたの貼り先で文字化けします。AIが悪いのではなく、制約を渡し忘れた指示文が悪いのです。

指示文そのものも、読み手であるAIが構造を見分けられるように書きます。全体をMarkdownの見出しで役割、背景、手順、出力形式に分け、参考資料や例文のように指示と混ざると困る部分は、タグのような目印で囲んで区切ります。そのうえで、「貼り先はHTMLを受け付けないので基本の記号だけで」「本文はMarkdownで、表だけはHTMLで」と、表示先を背景として渡します。

いま書いている文書は、最終的にいくつの画面で読まれるでしょうか。そのうち、実際に開いて表示を確かめたのはいくつでしょうか。

この記事の背景にある考え方は、拙著『GoogleのAI NotebookLM 意思決定の強化書』の4章2節「定義・対象・目的・制約を言語化する」に書きました。対象、目的、制約の大枠をまず人が書き、資料に隠れた条件をAIに掘り出させ、人が確かめて採用する、という節で、制約と評価基準の見分け方も扱っています。迷ったときの問いとして、そこには「この条件を満たさない案は即座に除外するか?」とあります。この節を読むと、貼り先がHTMLを受け付けないという一行が、好みの問題なのか、書き方の候補を最初から外す線なのかを、書き始める前に自分で決められるようになります。

同じ資料を見ているのに、なぜ結論が割れるのか。

GoogleのAI NotebookLM 意思決定の強化書 表紙

『GoogleのAI NotebookLM 意思決定の強化書』

前提が結論を決める。意思決定を根拠、前提、結論の三層に分け、前提を対象、目的、制約、評価基準の四つで言葉にする本です。

読み終えたとき、手元に残るもの

  • 意思決定の設計図になる前提リスト
  • 資料を事実、意見、噂、ゴミに仕分けるFORT
  • 同じ物差しで比べる選択肢表と採点表
  • 始める条件と止める条件を書いた決定書

NotebookLMは2026年7月にGemini Notebookへ名称が変わりました。本の手法は、使うAIの種類を問いません。

Amazonで見る

出典

著者プロフィール・お問い合わせ