結論。GitHub Alerts 5種・Zennの:::message系2種・Qiitaの:::note系4種、合計11種の入力パターンは、markdown-itのトークン変換(core ruleとコンテナプラグイン)によって、すべてOrcantusの4色コールアウト(note/tip/warning/strong)のいずれかへ正規化される。 実際にparseNoteへ通すと、どの記法で書いても出力されるHTMLの構造(<div class="callout callout--<tier>">)は共通になることを確認した。
パーサー自体は変えず、トークンを書き換える
実装は独自パーサーではなくmarkdown-it(CommonMark + GFM相当)が土台です(scripts/notes-markdown.mjs)。GitHub Alertsは独自ルールとしてではなく、CommonMark標準の引用(blockquote)として一度パースさせてから、先頭行がマーカーと一致する場合だけトークンの種類を書き換える実装になっています(scripts/notes-github-alerts.mjs)。
md.core.ruler.after('block', 'github_alerts', (state) => {
const tokens = state.tokens
for (let i = 0; i < tokens.length; i++) {
if (tokens[i].type !== 'blockquote_open') continue
// ...先頭のinlineトークンが `[!NOTE]` 等で始まるか調べる
tokens[openIdx].type = 'callout_open'
tokens[openIdx].tag = 'div'
tokens[openIdx].meta = { tier }
tokens[closeIdx].type = 'callout_close'
}
})
マーカーに一致しなければblockquote_openのまま残るため、ただの引用文(マーカーなし)は普通の<blockquote>として表示されるという、GitHub自身の挙動(Alertsのマーカーが無ければ通常の引用)とも一致する副産物があります。
Zenn・Qiitaの:::message / :::note系は、どちらもmarkdown-it-containerが実装する「フェンス付きdiv」記法という、パーサーとしては同一のものです(scripts/notes-containers.mjs)。サービスごとに違うのは色分けの意味づけだけなので、firstArg(info)で2番目の単語(alert・info・warnなど)を読み取り、Orcantusの階層へマッピングする関数を記法ごとに1つずつ用意しています。
function zennMessageTier(info) {
return firstArg(info) === 'alert' ? 'strong' : 'note'
}
function qiitaNoteTier(info) {
const kind = firstArg(info)
if (kind === 'warn') return 'warning'
if (kind === 'alert') return 'strong'
return 'note'
}
最終的な見た目はscripts/notes-callout.mjsの2関数(renderCalloutOpen / renderCalloutClose)にだけ集約されています。GitHub由来・Zenn由来・Qiita由来のどのレンダラーも、最後はこの共通関数を呼ぶ構造です。
実際に11種を通してみる
実装を読むだけでなく、parseNoteに実際のMarkdownを通して確認しました。入力は次の11パターンです。
> [!NOTE]
> GitHubのAlerts記法です。
> [!TIP]
> tip
> [!IMPORTANT]
> important
> [!WARNING]
> warning
> [!CAUTION]
> caution
:::message
通常メッセージ
:::
:::message alert
Zennの警告メッセージです。
:::
:::note
通常note
:::
:::note info
info
:::
:::note warn
Qiitaのwarnです。
:::
:::note alert
alert
:::
node -eでparseNoteを直接呼び出し、frontmatterを除いた本文の出力HTMLを確認すると、以下のようになりました(クラス名のみ抜粋)。
| 入力 | 由来 | 出力クラス |
|---|---|---|
[!NOTE] |
GitHub | callout--note |
[!TIP] |
GitHub | callout--tip |
[!IMPORTANT] |
GitHub | callout--strong |
[!WARNING] |
GitHub | callout--warning |
[!CAUTION] |
GitHub | callout--strong |
:::message |
Zenn | callout--note |
:::message alert |
Zenn | callout--strong |
:::note |
Qiita | callout--note |
:::note info |
Qiita | callout--note |
:::note warn |
Qiita | callout--warning |
:::note alert |
Qiita | callout--strong |
実際に出力された[!NOTE]・:::message alert・:::note warnの3件のHTMLは次の通りです(parseNoteの戻り値bodyHtmlそのまま)。
<div class="callout callout--note"><p class="callout__title">Note</p>
<p>GitHubのAlerts記法です。</p>
</div>
<div class="callout callout--strong"><p class="callout__title">Important</p>
<p>Zennの警告メッセージです。</p>
</div>
<div class="callout callout--warning"><p class="callout__title">Warning</p>
<p>Qiitaのwarnです。</p>
</div>
マーカー文字列([!NOTE]やalert)自体は出力に残らず、タイトル(Note・Important・Warning)はOrcantus側の4段階(CALLOUT_TITLES)に置き換わっています。GitHubの5段階(note/tip/important/warning/caution)のうち、important と caution はどちらも「強い注意」としてstrong1段階へ合流する実装になっており、表の通り両方ともcallout--strongになることを確認しました。
マーカーが無ければ元の記法に戻る
正規化は「マーカーに一致した場合だけ」起こる設計です。マーカーの無い普通の引用を同じparseNoteに通すと、書き換えは起こらず通常のCommonMark出力になります。
> ただの引用で、マーカーなし。
出力:
<blockquote>
<p>ただの引用で、マーカーなし。</p>
</blockquote>
同様に:::details(Zenn由来の折りたたみ記法)はコールアウトではなく<details>要素に変換され、コールアウトの4色とは別経路で処理されています(:::details 展開して見る → <details class="note-details"><summary>展開して見る</summary>...)。「コンテナ記法だからすべてコールアウトになる」わけではなく、名前ごとに個別のレンダラーが登録されている、という実装の構造が出力からも確認できます。
この結果が言っていないこと
- 確認したのは本リポジトリの
scripts/notes-markdown.mjs・scripts/notes-github-alerts.mjs・scripts/notes-containers.mjs・scripts/notes-callout.mjs(2026-09時点のソース)と、そのコードへ実際にMarkdownを通したparseNoteの出力です。GitHub・Zenn・Qiita自身が将来マーカーの種類や意味づけを変更した場合、この記事の対応表は追従していません。 - この記事が示しているのは「Orcantus Notesのビルドパイプラインが何を4色へ正規化するか」であり、GitHub・Zenn・Qiita自身の画面上での実際の見た目(アイコンの有無や配色)を調べたものではありません。各サービス自身の表示仕様については別記事の対象です。
- 対応していない記法(数式・Mermaid・SNS埋め込み)はこの記事の対象外です。書いても壊れず、素のテキスト/コードブロックとして表示されます(
docs/NOTES_PLAYBOOK.md参照)。
由来の違う記法が実装の中でどこまで合流し、どこで分岐したままなのかが、実際にコードを読んで入力を通すとはっきり見えました。