はじめに:HTMLメールはWebの常識が通じない
Webサイトと同じ感覚でHTMLメールを作ると、だいたい崩れます。
flexbox、grid、position:使えません<head>の<style>:無視するメーラーがあります@media(ダークモード時の指定):Gmailで無視されます<!--[if mso]>(Outlook用条件分岐):崩れの原因になる場合があります
一方で、一度テンプレートを作ってしまえば使い回しができます。
この記事は、実案件(メルマガ)を複数制作するなかで得た知見をまとめたものです。
サンプルコード一式はgistに置きました。
htmail-sample1.html — GitHub Gist

1. CSS Resetのベース選定
Cerberus、ZURB、Benchmark Email、Antwort、ColorlibHQ など
複数のHTMLメール用テンプレートを調査した結果、Cerberus(MITライセンス)のCSS Resetをベースに採用しました。理由は、構造がシンプルなことと、主要なGmailハックが最初から入っていることです。
Cerberus は Ted Goas 氏が公開しているレスポンシブメールのパターン集で、コードが区画ごとに分かれていて、組み合わせたり入れ子にしたりして1通を組み立てられるようになっています。この「区画ごと」という考え方は、後述の 2-4 でそのまま使います。
デザインの参考には ColorlibHQ の Free Email Templates を見ました。ただしあちらはMJMLで生成されたテンプレートで、カラム制御と <!--[if mso | IE]> の条件分岐が全体に入っています。 <!--[if mso | IE]> は受け付けないメール配信システムがあったりすることから、コードのベースにはしていません。
Cerberusをそのまま使っても日本語制作環境には合わないので、以下のカスタマイズが必要になります。
2. どのクライアントでも崩れない設計
2-1. 幅は max-width: 680px 〜 750px 程度
経験的に、このくらいがPCメーラーとスマートフォンのバランスが良いです。
(あまり幅が小さいと、一部のPCメーラーは幅をいっぱいいっぱいに広げるので、大きく表示されます)
<div style="max-width:680px; margin:0 auto;" class="email-container">
<!-- コンテンツ -->
</div>
2-2. レイアウトは <table> 一択
CSSの display:flex や display:grid はOutlookが無視します。
テーブルレイアウトのみが現状もっとも安全です。
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td style="padding: 20px 5%;">
<!-- コンテンツ -->
</td>
</tr>
</table>
MSO条件分岐(
<!--[if mso]>)は使いません
効かないメーラーが多く、崩れの原因になるケースもあったため削除しました。さらに、『Sansan』のように条件分岐コメントを受け付けないメール配信システムがあります。table構造に統一した方が結果的にシンプルで安定します。
2-3. スタイルはすべてインライン
<head> の <style> を無視するメーラーが存在します(代表例:一部のOutlook・旧来のYahoo!メール)。そのため主要なスタイルはインラインで記述します。
CSS クラスは保守用として記述しても良いですが、表示はインラインに依存させます。
2-4. 1つの <table> = 1つの区画
トップレベルの <table> を、header / section / footer のような意味のブロックごとに分けて書きます。
<div style="max-width:680px; margin:0 auto;" class="email-container">
<table class="header"> ... </table>
<table class="section"> ... </table>
<table class="section"> ... </table>
<table class="section"> ... </table>
<table class="footer"> ... </table>
</div>
各テーブルの内側では、<td> にクラスを振ってブロック内のパーツを表します。.picture(写真)、.impress(見出し画像)、.text(本文・ボタン)といった具合です。
1テーブル1区画にしておくと、順序の入れ替え・追加・削除がブロック単位で完結します。Cerberusの「区画を組み合わせて1通を組み立てる」という考え方をそのまま踏襲しています。
2-5. 2カラム・3カラムにするとき
<table> なので、<td> を横に並べればカラム分割はできます。
<tr>
<td width="33.33%"> ... </td>
<td width="33.33%"> ... </td>
<td width="33.33%"> ... </td>
</tr>
ただ、余白やスペースがばらばらだったり、border-radius が使えなかったりする中でデザイナーのリクエストに応えようとすると、結局セルの中に width:100% の画像を入れて縦積みするのが一番てっとり早い、ということになります。1カラムなら100%、2カラムなら50%、3カラムなら33.33%です。
3. 使えるHTMLタグとCSS
使えるタグ(主要なもの)
table, tr, th, td
img
p, strong, small, span
h1, h2, h3, h4, h5, h6
a, u, hr, br
使えるCSSプロパティ(インライン限定)
background, background-color
border(各辺指定含む)
color
display
font, font-family, font-size, font-style, font-weight
height, width
letter-spacing, line-height
padding(各辺指定含む)
table-layout
text-align, text-decoration, text-indent, text-transform
vertical-align
position、float、flexbox、grid は使いません。
4. Gmailのクセに対するハック集
CerberusのCSS Resetに含まれる代表的なハックです。なぜ必要なのかを簡潔にまとめます。
/* ① 大きい非リンク画像にGmailが「ダウンロード」ボタンを表示するのを防ぐ */
.a6S {
display: none !important;
opacity: 0.01 !important;
}
/* ② スレッド表示時にGmailがテキストカラーを変更するのを防ぐ */
.im {
color: inherit !important;
}
/* ③ GmailのiOSアプリで右に余白ができる問題を防ぐ */
@media only screen and (min-device-width: 375px) and (max-device-width: 413px) {
u~div .email-container {
min-width: 375px !important;
}
}
/* ④ Android 4.4で上下に余白が入る問題を防ぐ */
div[style*="margin: 16px 0"] {
margin: 0 !important;
}
③ は画面幅ごとに1つずつ書きます。
Cerberusでは 320px / 375px / 414px の3つが用意されています。
5. ダークモード対策
Gmail(iOS/Android)は @media (prefers-color-scheme: dark) を無視します。@media でダークモード時のスタイルを切り替えるアプローチは機能しない環境が多いです。
そのため、採用した方針は「ダークモードに左右されないデザインを作る」ことです。
5-1. 薄い背景色は background-color ではなく、background-image で表現する
background-color はメーラーによってダークモード時に自動反転されます(白→黒など)。
一方、画像ファイルはメーラーが反転しません。
→ 薄いクリーム色などの背景は、30x30pxの画像を background-image で指定することで、ライト/ダーク両環境で固定できます。
<td style="
background: #F7F3ED;
background-image: url(https://i.gyazo.com/0d2f1d218a133da4a3ec0d10555a5212.png);
background-size: contain;
">
5-2. グレーのテキストは hsl(0,0%,50%)
ダークモードは、色の明度を反転させます。
ここで #808080(hsl(0, 0%, 50%))を使います。明度50%は、反転しても50%のままです。
#808080 L=50.0% → 反転 → L=50.0% = #808080 (完全に不変)
#888888 L=53.3% → 反転 → L=46.7% = #777777 (わずかにズレる)
#2E211B L=14.3% → 反転 → L=85.7% = #DBDBDB (白く飛ぶ)
#FFFFFF L=100 % → しきい値の上なので、そのまま
つまり 50% は反転の不動点です。ライトモードでもダークモードでも、まったく同じ色で表示されます。
少しだけ濃くする:mix-blend-mode: multiply
#808080 は薄い背景の上でコントラスト比 3.57:1。読めなくはありませんが、余裕はありません。そこで mix-blend-mode: multiply を足します。
<p style="
color: hsl(0,0%,50%);
mix-blend-mode: multiply;
line-height: 1.66;
font-size: 17px;
">
旬のシングルオリジンが入荷しました。
</p>
multiply は 結果 = 文字の色 × 背景の色 という計算です。#808080 が #7C7A77 になって、3.57:1 → 3.87:1。気持ち濃くなります。
filterのほうが効き幅は大きい(#484848相当まで濃くなる)のですが、対応が46%程度で、Gmail・Outlook・Yahoo!メールのいずれでも効きません。mix-blend-modeはGmailアプリと(クラシックでない)Outlookで効くぶん、届く範囲が広いです。
結果として、ライトモード/ダークモードのどちらでも
同じ濃さのグレーテキストが薄い背景の上に乗ります。
(↑ダークモードでもグレーテキストが薄い背景の上に乗っている)
逆に、color:#2E211B(L=14.3%)のような濃い色を薄い背景の上に直接書くと、反転して #DBDBDB(L=85.7%)になる一方、背景は画像なので反転しません。薄い背景に薄い文字が乗って読めなくなります。
補足:濃い背景色(グリーン、ダークブルーなど)の上に
color:#FFFFFFのテキストを置く場合は、このハックは不要です。
(↑濃い背景色の上に、color:#FFFFFF のテキストを置く場合は反転されない)
5-3. 重要な情報は画像で表現する
ヘッダーのタイトル、価格、キャッチコピーなど、
デザイン上重要な情報は画像にしてしまうのが最も確実です。
<img
src="https://i.gyazo.com/0e5d0ebe2cf625dfca38dff5fb3595f8.png"
alt="SINGLE ORIGIN 旬のシングルオリジンが入荷しました。"
style="width: 100%; display: inline-block;"
/>
(↑画像だと間違いない)
ただし、全部を画像にすると別の問題が出ます。7-5 で書きます。
6. 画像の扱い
6-1. 画像は絶対パス(https://)で
相対パスで書くと、メーラーによっては添付ファイル扱いになったり、単に表示されなかったりします。https:// から始まる絶対パスで書きます。
6-2. 幅は style="width:100%" で指定する
<img width="680"> のように属性で指定すると、セルの幅や padding を変えるたびに数字を直すことになって、調整がわりと面倒になります。
<img
src="https://i.gyazo.com/e214c900d4d9ecda3e12e3e09e7f1e95.jpg"
alt="ハンドドリップでコーヒーを淹れる、朝の食卓"
style="width:100%; display:inline-block; vertical-align:middle;"
/>
width:100% にしておけば、親の <td> の幅に素直に追従します。2-5 のカラム分割でも、
セルを 50% や 33.33% にするだけで中身が付いてきます。
6-3. 画像の置き場所
実案件なら、クライアントのサーバーか自分のサーバーに置くのが確実だと思いますが、Webサイトと違い何年も同じ画像を使い続けるHTMLメールというのはあまりないでしょう。
サンプルや記事用なら Gyazo が手軽でいいです。無料プランでも直リンクできます。
無料プランには「画像一覧ページで直近10枚しか見られない」という制限がありますが、これは一覧ページの話で、URLさえ分かっていれば画像は表示できます。Gyazoのヘルプにも、無料アカウントの画像が削除されたりURLからアクセスできなくなることはない、と書いてあります。ブログに貼った画像が見えなくなることもない、とのことです。
Helpfeel Cosense(旧 Scrapbox)で画像を管理しているなら、アップロード先を Gyazo に切り替えておくといいと思います。Cosenseはデフォルトで scrapbox.io にアップロードしますが、ドメインが cosen.se に移行中で、旧URLはリダイレクトで動いている状態らしいです。メールに焼き込むURLがリダイレクト頼みなのは避けたいところです。

(↑『Scrapbox』だと、用途別での画像管理も楽)
6-4. 直リンクのURLに気をつける
Gyazoの画像URLには2種類あります。
https://gyazo.com/61e56c619fa473314019c4f829d285fa.png ← ページのURL
https://i.gyazo.com/61e56c619fa473314019c4f829d285fa.png ← 画像の直リンク
ブラウザで開くと、前者もリダイレクトされて画像が表示されます。だから同じものだと思ってしまうのですが、メーラーの画像プロキシがリダイレクトを追わない場合、前者だと表示されません。i.gyazo.com のほうを使います。
ブラウザで確認しているかぎり気づかない類の話で、自分もしばらく前者で書いていました。

7. 画像が読み込まれないときのフォールバック
「画像をブロックしているメーラー」や「画像OFFで読んでいる受信者」への対策です。
7-1. alt のフォントスタイルは <img> の親要素で指定する
<img> 自体ではなく、その親要素にフォントスタイルを指定すると、alt表示時にも反映されます。
<h2 style="line-height:1.5; font-size:36px; font-weight:500; text-align:center;">
<img
src="https://i.gyazo.com/0e5d0ebe2cf625dfca38dff5fb3595f8.png"
alt="見出しテキスト"
style="width:100%; display:inline-block;"
/>
</h2>
7-2. alt の中で改行する( )
alt="1行目のテキスト 2行目のテキスト"
7-3. 読み込み失敗時に余白を確保する(onerror)
画像が表示されなかった時、altテキストが四方に詰まって読みにくくなるのを防ぎます。
<img
src="https://example.com/image.png"
alt="説明テキスト"
onerror="this.style.margin='15px auto 15px';"
/>
(↑画像が表示されない場合も、フォントスタイル・改行・余白は指定することができる)
7-4. alt のサイズは clamp() で決める
7-1 で親要素にフォントサイズを指定しましたが、そこを固定値ではなく clamp() で書いています。
<h2 style="line-height:1.5; font-size:clamp(21px,3.6vw,36px); font-weight:500;">
<img src="..." alt="..." style="width:100%;" />
</h2>
なぜ @media ではないかというと、@media は <head> の <style> にしか書けないからです。
2-3 のとおり、その <style> を無視するメーラーがあります。clamp() はインラインに書けるので、そこを通り抜けます。
vw値の目安は「最大px ÷ 10」としました。1000pxの幅で最大値に到達する計算になります。
| タグ | 指定 | 下限に到達する幅 |
|---|---|---|
| h1 | clamp(15px, 3.6vw, 36px) |
417px |
| h2 | clamp(21px, 3.6vw, 36px) |
583px |
| h3 | clamp(18px, 3vw, 30px) |
600px |
| h4 | clamp(14px, 2.4vw, 24px) |
583px |
| h5 | clamp(13px, 2.1vw, 21px) |
619px |
| h6 | clamp(13px, 2.1vw, 21px) |
619px |
これは見出し階層のサイズ表ではありません。
h1 が h2 より小さいのは、h1 に入るテキストのほうが長くなるからです(7-5 参照)。
各タグに入る画像の大きさに合わせて個別に決めているので、h1 > h2 > h3 という並びにはなりません。
h1 と h2 は、vw値も最大値も同じで、違うのは下限だけにしてあります。583pxを切ったところで h2 は21pxで止まり、h1 はそこから15pxまで沈み続けます。「h1のほうが文章量が多いので、狭い幅ではもっと縮んでほしい」という要求を、下限値ひとつで表しています。
下限は比例計算ではなく、読める大きさの床として決めます。下げすぎると読めません。
なお、clamp() や vw を解さないメーラーは多いですが(Gmail、Yahoo!メール、Outlookあたり)、その場合はこの指定ごと無視されて、メーラー既定の見出しサイズで表示されるだけになります。alt表示にしか関わらないので実害はありません。
そして、clamp() が効く場所はちゃんとあります。冒頭に置く「うまく表示されない場合はこちら」の遷移先はブラウザだからです。これは負け惜しみの話ではなくて、そもそもこのHTMLはメールとWebページの両方として配信するものです。詳しくは 8 に書きます。
HTMLメールで clamp() を使っている例はあまり見かけませんが、レガシーな書き方しかできない前提の中で、インラインに書けて、効かない環境でも害がない、という条件を満たすパターンです。
ここで
clamp()を使うのは画像を含む見出しタグだけです。画像を含まないライブテキスト(pやsmall)は、素直に固定pxで書いています。alt代替テキストのための仕組みなので、そこに揃える必要はありません。
7-5. h1 はテキストで書く
5-3 で「重要な情報は画像にする」と書きましたが、最初から最後まで画像だと、
受信箱の一覧に出る抜粋に何もテキストが表示されません。これは実際に確認しました。
なので、h1 だけはテキストで書きます。ヘッダーのロゴ画像のすぐ下あたりに、1回だけ置きます。あいさつ文をテキストで書いているのも同じ理由です。
<td style="background:#2E211B; text-align:center;">
<h1 style="line-height:1.5; font-size:clamp(15px,3.6vw,36px); font-weight:700; color:#FFFFFF;">
【PING PONG NIGHT 2026】<br />前回、大いに盛り上がった<br />あのイベントが帰ってきます!
</h1>
</td>
濃い色の帯の上に白文字で置けば、5-2 のグレーテキストのハックも要りませんし、デザインとしても普通に成立します。テキストにしたせいで見た目が犠牲になる、ということにはなりません。
h1 は件名と似た内容になるので、文章量が多くなりがちです。幅が広いときは余裕がありますが、狭くなると収まらなくなってきます。7-4 で h1 の下限を15pxまで下げているのはこのためです。iPhone XR(414px)だと 3.6vw = 14.9px なので、ちょうど下限の15pxで止まります。
7-6. プリヘッダーで、プレビューに出す文字を指定する
受信箱の一覧には、件名の下にメールの中身が少し表示されます。あれを指定できます。
<body>
<center>
<!-- Visually Hidden Preheader Text -->
<div style="max-height:0; overflow:hidden; mso-hide:all;" aria-hidden="true">
旬のシングルオリジンが入荷しました。きほんのハンドドリップの淹れ方もご紹介します。今月のラインナップは THE BAG / EASY DRIP / THE MUG の3点です。
</div>
これを <body> の直後に置くと、プレビューにこのテキストが出ます。本文には表示されません。Macメール、eM Client、Android版のYahoo!メール・Gmail・Outlookの5環境で確認しました。
この <div> が display:none を使わず、max-height:0 と overflow:hidden で潰しているのがポイントです。理由は下に書きます。
指定しないと、上から順に拾われる
プリヘッダーを置かないとどうなるか。実際のプレビューがこれです。
【ROAST JOURNAL】2026.07 VOL.07 | 旬のシングルオリジン入荷/きほんのハンドドリップ特集
HTMLメールがうまく表示されない場合は こちら をご覧ください。 2026.07 / VOL.07
SEASONAL COFFEE, STORIES & BREW GUIDES ○○○様 〼〼〼〼〼〼〼 〼〼〼〼〼〼。 〼〼〼〼〼〼〼 …
1行目が件名で、そのあとは本文のライブテキストが上から順に並んでいます。「HTMLメールがうまく表示されない場合は〜」(冒頭の small)、「2026.07 / VOL.07 SEASONAL COFFEE…」(ヘッダーの small)、「○○○様 〼〼〼…」(あいさつ文の p)の順です。
プリヘッダーを置くと、この先頭に指定したテキストが入ります。
【ROAST JOURNAL】2026.07 VOL.07 | 旬のシングルオリジン入荷/きほんのハンドドリップ特集
旬のシングルオリジンが入荷しました。きほんのハンドドリップの淹れ方もご紹介します。今月の
ラインナップは THE BAG / EASY DRIP / THE MUG の3点です。 HTMLメールがうまく表示されない場合は…
プレビューに表示される文字数はそう多くないので、80文字程度書いておけば、そのあとの「HTMLメールがうまく表示されない場合は〜」はほぼ見えなくなります。
8. HTMLメールは、Webページでもある
ここまで「メーラーでは効かない」ものをいくつか挙げてきました。clamp()、@media、<head> の <style>、<meta name="description">。
ただ、HTMLメールにはたいてい、冒頭にこの1行があります。
<small>HTMLメールがうまく表示されない場合は <a href="https://example.com/htmail/202607.html">こちら</a> をご覧ください。</small>
この遷移先はブラウザです。つまり同じHTMLが、受信箱とブラウザの両方で読まれます。
これを「メーラーで駄目だったときの逃げ場」と考えず、2つ目の配信先と考えると話が変わります。メルマガとして配信しているものを、同じ内容でサイトのトピックス一覧にも並べる。1つ作れば2か所で使える。実際、そういう運用をしているところがあります。そう考えると、Webページとしての情報も持たせておくべきです。
8-1. title / meta description / OGP を書いておく
<title>【ROAST JOURNAL】2026.07 VOL.07 | 旬のシングルオリジン入荷/きほんのハンドドリップ特集</title>
<meta name="description" content="旬のシングルオリジンが入荷しました。きほんのハンドドリップの淹れ方もご紹介します。…">
<link rel="canonical" href="https://example.com/htmail/202607.html">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="article">
<meta property="og:url" content="https://example.com/htmail/202607.html">
<meta property="og:title" content="【ROAST JOURNAL】2026.07 VOL.07 | …">
<meta property="og:description" content="旬のシングルオリジンが入荷しました。…">
<meta property="og:image" content="https://i.gyazo.com/e214c900d4d9ecda3e12e3e09e7f1e95.jpg">
<!-- Twitter(X):twitter:* は property ではなく name で指定する -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="【ROAST JOURNAL】2026.07 VOL.07 | …">
<meta name="twitter:description" content="旬のシングルオリジンが入荷しました。…">
<meta name="twitter:image" content="https://i.gyazo.com/e214c900d4d9ecda3e12e3e09e7f1e95.jpg">
メーラーはこれらを全部無視します。それでいいのです。ブラウザで開かれたとき、検索結果に出たとき、SNSで共有されたときに効きます。デメリットも特にありません。
6-1 で「画像は絶対パスで」と決めたので、メール用に置いた画像がそのまま使えます。
8-2. この1通を代表するテキストは、5か所に出る
整理すると、こうなります。
| 出る場所 | 何で指定するか | メール | ブラウザ |
|---|---|---|---|
| 受信箱の件名 | メーラー/配信システムの件名欄 | ◯ | − |
| 受信箱のプレビュー | Visually Hidden Preheader Text | ◯ | (非表示) |
| ブラウザのタブ | <title> |
× | ◯ |
| 検索結果 | <meta name="description"> |
× | ◯ |
| SNSのカード | OGP | × | ◯ |
8-3. Webフォントも、ここで効いています
サンプルでは <head> でWebフォントを読み込んでいます。
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/yakuhanjp@4.0.0/dist/css/yakuhanmp.css" />
<link rel="stylesheet" href="https://unpkg.com/@sarap422/font-shippori-mincho-otf@1.0.0/font-face.css" />
多くのメーラーはこれを無視して、既定のフォントで表示します。それでも書いてあるのは、clamp() と同じ理由です。ブラウザで開かれたときに効きます。
9. 制作上の注意点まとめ
| 項目 | 推奨 | 理由 |
|---|---|---|
| CSS Reset | Cerberus(MIT) | 構造がシンプルで、Gmailハックが入っている |
| 幅 | max-width: 680px 〜 750px 程度 |
PCメーラーとスマートフォンのバランスが良い |
| レイアウト | <table> 統一 |
Outlookでの互換性 |
| ブロック分け | 1つの <table> = 1つの区画 |
順序の入れ替え・追加・削除が完結する |
| スタイル | インラインのみ | <head>のCSSを無視するメーラーがある |
| 背景色(薄い色) | background-image |
ダークモードで反転されない |
| グレーテキスト | #808080 + mix-blend-mode:multiply |
ライト/ダーク両対応 |
| 重要コンテンツ | 画像化 | 最も確実なダークモード対策 |
| 画像パス | 絶対パス(https://) |
相対パスは添付扱いになる場合がある |
| 画像URL | 直リンク(i.gyazo.com 等) |
リダイレクト頼みだと画像プロキシが追わない場合がある |
| 画像の幅 | style="width:100%" |
width 属性は調整が面倒 |
| alt のサイズ | clamp(下限, 最大px÷10 vw, 最大px) |
@media が使えないため。インラインに書ける |
| h1 | テキストで1回だけ書く | 全部画像だと受信箱の抜粋に何も出ない |
| プレビュー | Visually Hidden Preheader Text | 指定しないと先頭のライブテキストが順に拾われる |
| title / description / OGP | 書いておく | メールでは効かないが、ブラウザ・検索・SNSで効く |
| MSO条件分岐 | 不使用 | 崩れの原因になる。受け付けない配信システムもある |
おわりに
HTMLメールの難しさは「環境ごとに正解が違う」点にあります。@media で丁寧にダークモードを書いても、Gmailでは無視されます。MSO条件分岐でOutlook対応しても、別のメーラーで崩れます。
たどり着いた方針は「どの環境でも同じように見えるHTMLを書く」こと。Webの常識をいったん投げ捨てて設計すると解決します。
ただ、捨てるだけでもありません。clamp() のように、効く環境では効いて、効かない環境でも害がないものは、乗せておいて損はないと思います。
- CSS Reset ベース: Cerberus — Responsive Email Patterns
(Copyright (c) Ted Goas / MIT License) - デザイン参考: ColorlibHQ / Free Email Templates(MIT License)
- サンプルコード: htmail-sample1.html — GitHub Gist