はじめに
「PCでは綺麗に見えるのに、Outlookで開くと画像がズレる」「Gmailアプリだとボタンが表示されない」。HTMLメールを自前で組んだことがある人なら、一度はこの理不尽さに付き合ったことがあるはずです。Webのレイアウト技術がFlexboxやGridに進化した今も、メールクライアントの世界では入れ子のtableとインラインCSSが現役という、時が止まったような状況が続いています。
MJMLは、この「メールクライアントごとの互換性地獄」を自分で解かなくて済むようにしてくれるマークアップ言語です。<mj-section>や<mj-column>のような意味の分かるタグでレイアウトを書くと、内部で複雑なtableネストとインラインCSSに変換してくれます。
とはいえ、文章で説明するより実際に触った方が早いと思います。mj-*タグを書き換えると右側のプレビューがその場で更新されるサンプルを用意したので、先に動きを見たい方はこちらからどうぞ。
MJMLとは
MJML(Mailjet Markup Language)は、メール配信サービスMailjetの開発チームが2015年に公開したオープンソースのマークアップ言語です。GitHubリポジトリ(mjmlio/mjml)は15,000以上のスターを持ち、現在も継続的にリリースが続いている現役プロジェクトです。<mj-section>や<mj-column>といったセマンティックなタグでレイアウトを記述すると、mjml2htmlというエンジンがOutlook・Gmail・Apple Mailなど主要クライアントで崩れにくいHTML(テーブルレイアウト+インラインCSS)に変換してくれます。
主な特徴
- セマンティックな構文 -
<table>の入れ子やクライアント別のハックを書かずに、意味の分かるタグでレイアウトを組める - デフォルトでレスポンシブ -
mj-columnは指定なしでも画面幅に応じて自動的に縦積みに切り替わる - 豊富な標準コンポーネント -
mj-text、mj-image、mj-button、mj-socialなど、メールでよく使う部品が一通り揃っている - 複数の実行環境 - Node.jsのCLI/API(
mjmlパッケージ)に加えて、ブラウザで完結するmjml-browserパッケージも提供されている
インストール
Node.jsで使う場合はmjmlパッケージをインストールします。
npm install mjml
ブラウザ上(管理画面のメールテンプレートエディタなど)で完結させたい場合は、mjml-browserパッケージを使います。
npm install mjml-browser
MJMLのサンプルを動かす
以下は、mjml-browserパッケージのmjml2html()にMJMLのソース文字列を渡し、返ってきたhtmlをiframeに描画する最小構成です。mj-sectionが横並びのブロック、mj-columnがその中のカラム、mj-textとmj-buttonが実際のコンテンツにあたります。
import mjml2html from 'mjml-browser@5.4.0'
const { html, errors } = await mjml2html(`
<mjml>
<mj-body>
<mj-section>
<mj-column>
<mj-text>Hello World</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
`)
console.log(errors) // タグの綴り間違いなどはここに入る
document.body.innerHTML = html // 変換後のHTMLメールをそのまま描画できる
実際に動かせるものが下です。左のテキストエリアでmj-textの文言やmj-buttonの色を書き換えると、右のプレビューがその場で再変換されます。mj-columnを丸ごと削除して1カラムにする、background-colorを変えてみるなど、自由に崩してみてください。
mj-buttonのbackground-colorを#e74c3cのような別の色に変えたり、mj-textをもう1つmj-column内に追加したりすると、変換結果のHTMLがその場で変わるのが分かります。存在しないタグ名(mj-unknownなど)を書くと、下のエラー欄にmjml2htmlが検出した構文エラーが表示されます。
基本的な使い方
Node.js環境では、mjmlパッケージのmjml2html()にMJML文字列を渡すだけでHTMLに変換できます(mjml-browserと違い、こちらは同期関数です)。
const mjml2html = require('mjml')
const { html, errors } = mjml2html(`
<mjml>
<mj-body>
<mj-section>
<mj-column>
<mj-text>Hello World</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
`, {
validationLevel: 'soft',
})
ビルドパイプラインやCIに組み込みたい場合は、CLIから直接ファイルを変換することもできます。
mjml input.mjml -o output.html
実践的なユースケース
マルチカラムのレスポンシブレイアウトを組む
mj-columnはwidthを指定しない限り均等幅で横に並び、画面幅が狭くなると自動的に縦積みに切り替わります。商品紹介やニュースレターのように、カラム数をコンテンツ量に合わせて変えたい場面で使うパターンです。
// 要点: mj-columnを並べるだけでレスポンシブなグリッドになる
`<mj-section>
<mj-column><mj-text>カラム1</mj-text></mj-column>
<mj-column><mj-text>カラム2</mj-text></mj-column>
<mj-column><mj-text>カラム3</mj-text></mj-column>
</mj-section>`
ボタンを押すと、mj-columnの数が1〜3個に切り替わり、mjml2htmlが再変換した結果がプレビューに反映されます。カラム数が増えるほど各カラムの横幅が自動的に狭くなる点に注目してください。
CTAボタンのブランドカラーを差し込む
プロモーションメールでは、ボタンの色や文言をキャンペーンごとに差し替えたいことがよくあります。mj-buttonはbackground-colorやborder-radiusなどの属性をそのまま持っているので、テンプレート側でこれらを変数として組み立てるだけで対応できます。
// 要点: mj-buttonの属性を動的に埋め込むだけで見た目のバリエーションを作れる
`<mj-button background-color="${color}" border-radius="${radius}px" href="#">
${label}
</mj-button>`
カラーピッカーとテキスト入力を変更すると、mj-buttonのbackground-colorと本文がその場で書き換わり、プレビューのボタンに反映されます。
画像とソーシャルリンクを使ったニュースレターヘッダー
会員向けニュースレターでは、ヘッダー画像とSNSリンクをまとめたヘッダーブロックを毎号使い回すことが多いです。mj-imageで画像を、mj-socialとmj-social-elementでSNSアイコンの並びを組み立てられます。
// 要点: mj-social-elementのnameでアイコン付きのSNSリンクを並べられる
`<mj-social font-size="13px" mode="horizontal">
<mj-social-element name="twitter" href="#">Twitter</mj-social-element>
<mj-social-element name="facebook" href="#">Facebook</mj-social-element>
</mj-social>`
チェックボックスでヘッダー画像とSNSリンクの表示・非表示を切り替えると、mjml2htmlが変換するMJML自体が変わり、プレビューの構成も合わせて変化します。
まとめ
MJMLは、mj-section・mj-column・mj-text・mj-buttonといったセマンティックなタグを組み合わせるだけで、Outlookを含む主要メールクライアントに対応したHTMLメールをmjml2htmlが自動生成してくれるマークアップ言語です。Node.js向けのmjmlパッケージと、ブラウザで完結するmjml-browserパッケージの両方が用意されているため、CIでのビルド組み込みから管理画面のテンプレートエディタまで、幅広い実行環境で使えます。
複雑なtableネストやクライアント別のハックを自分で書き続けるのに疲れたら、そのレイアウト部分をMJMLに任せてみると、メール制作にかかる時間がかなり短縮されるはずです。