はじめに:HTMLメールはWebの常識が通じない

Webサイトと同じ感覚でHTMLメールを作ると、だいたい崩れます。

  • flexboxgridposition:使えません
  • <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:flexdisplay: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

    positionfloatflexboxgrid使いません


    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-modeGmailアプリと(クラシックでない)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&#13;&#10;旬のシングルオリジンが入荷しました。"
      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 の中で改行する(&#13;&#10;

    alt="1行目のテキスト&#13;&#10;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() を使うのは画像を含む見出しタグだけです。画像を含まないライブテキスト(psmall)は、素直に固定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:0overflow: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() のように、効く環境では効いて、効かない環境でも害がないものは、乗せておいて損はないと思います。