絵文字😇を含むテキストを表示する @font-face 設定(Unicode 10.0対応版)

CSSで絵文字を表示するための @font-face 設定を紹介します。この方法はモダンな閲覧環境ではほぼ問題なく表示できます。またJavaScriptを使用して絵文字を画像に置換するタイプ(EmojiOne、Twemoji)と比較して、表示速度や利便性などの面で大きく有利です。

絵文字を含むテキストを表示する @font-face 設定(Unicode 10.0対応版)

デモページもご覧ください。

以下のCSSを指定すると絵文字がきれいに表示されます。

@font-face {
  font-family: MyFontFamily;
  src: local("Hiragino Kaku Gothic ProN"),
       local("Meiryo");
}

@font-face {
  font-family: MyFontFamily;
  font-weight: bold;
  src: local("Hiragino Kaku Gothic ProN"),
       local("Meiryo Bold");
}

@font-face {
  font-family: MyFontFamily;
  src: local("Segoe UI Emoji"),
       local("Segoe UI Symbol"),
       local("Apple Color Emoji"),
       local("Noto Color Emoji"),
       local("Noto Emoji");
  unicode-range: /*U+23, U+2A, U+30-39,*/ U+A9, U+AE, U+200D, U+203C, U+2049, U+20E3, U+2122, U+2139, U+2194-2199, U+21A9-21AA, U+231A-231B, U+2328, U+23CF, U+23E9-23F3, U+23F8-23FA, U+24C2, U+25AA-25AB, U+25B6, U+25C0, U+25FB-25FE, U+2600-2604, U+260E, U+2611, U+2614-2615, U+2618, U+261D, U+2620, U+2622-2623, U+2626, U+262A, U+262E-262F, U+2638-263A, U+2640, U+2642, U+2648-2653, U+2660, U+2663, U+2665-2666, U+2668, U+267B, U+267F, U+2692-2697, U+2699, U+269B-269C, U+26A0-26A1, U+26AA-26AB, U+26B0-26B1, U+26BD-26BE, U+26C4-26C5, U+26C8, U+26CE-26CF, U+26D1, U+26D3-26D4, U+26E9-26EA, U+26F0-26F5, U+26F7-26FA, U+26FD, U+2702, U+2705, U+2708-270D, U+270F, U+2712, U+2714, U+2716, U+271D, U+2721, U+2728, U+2733-2734, U+2744, U+2747, U+274C, U+274E, U+2753-2755, U+2757, U+2763-2764, U+2795-2797, U+27A1, U+27B0, U+27BF, U+2934-2935, U+2B05-2B07, U+2B1B-2B1C, U+2B50, U+2B55, U+3030, U+303D, U+3297, U+3299, U+FE0F, U+1F004, U+1F0CF, U+1F170-1F171, U+1F17E-1F17F, U+1F18E, U+1F191-1F19A, U+1F1E6-1F1FF, U+1F201-1F202, U+1F21A, U+1F22F, U+1F232-1F23A, U+1F250-1F251, U+1F300-1F321, U+1F324-1F393, U+1F396-1F397, U+1F399-1F39B, U+1F39E-1F3F0, U+1F3F3-1F3F5, U+1F3F7-1F4FD, U+1F4FF-1F53D, U+1F549-1F54E, U+1F550-1F567, U+1F56F-1F570, U+1F573-1F57A, U+1F587, U+1F58A-1F58D, U+1F590, U+1F595-1F596, U+1F5A4-1F5A5, U+1F5A8, U+1F5B1-1F5B2, U+1F5BC, U+1F5C2-1F5C4, U+1F5D1-1F5D3, U+1F5DC-1F5DE, U+1F5E1, U+1F5E3, U+1F5E8, U+1F5EF, U+1F5F3, U+1F5FA-1F64F, U+1F680-1F6C5, U+1F6CB-1F6D2, U+1F6E0-1F6E5, U+1F6E9, U+1F6EB-1F6EC, U+1F6F0, U+1F6F3-1F6F8, U+1F910-1F93A, U+1F93C-1F93E, U+1F940-1F945, U+1F947-1F94C, U+1F950-1F96B, U+1F980-1F997, U+1F9C0, U+1F9D0-1F9E6, U+E0062-E0063, U+E0065, U+E0067, U+E006C, U+E006E, U+E0073-E0074, U+E0077, U+E007F;
}

@font-face {
  font-family: MyFontFamily;
  font-weight: bold;
  src: local("Segoe UI Emoji"),
       local("Segoe UI Symbol"),
       local("Apple Color Emoji"),
       local("Noto Color Emoji")
       local("Noto Emoji");
  unicode-range: /*U+23, U+2A, U+30-39,*/ U+A9, U+AE, U+200D, U+203C, U+2049, U+20E3, U+2122, U+2139, U+2194-2199, U+21A9-21AA, U+231A-231B, U+2328, U+23CF, U+23E9-23F3, U+23F8-23FA, U+24C2, U+25AA-25AB, U+25B6, U+25C0, U+25FB-25FE, U+2600-2604, U+260E, U+2611, U+2614-2615, U+2618, U+261D, U+2620, U+2622-2623, U+2626, U+262A, U+262E-262F, U+2638-263A, U+2640, U+2642, U+2648-2653, U+2660, U+2663, U+2665-2666, U+2668, U+267B, U+267F, U+2692-2697, U+2699, U+269B-269C, U+26A0-26A1, U+26AA-26AB, U+26B0-26B1, U+26BD-26BE, U+26C4-26C5, U+26C8, U+26CE-26CF, U+26D1, U+26D3-26D4, U+26E9-26EA, U+26F0-26F5, U+26F7-26FA, U+26FD, U+2702, U+2705, U+2708-270D, U+270F, U+2712, U+2714, U+2716, U+271D, U+2721, U+2728, U+2733-2734, U+2744, U+2747, U+274C, U+274E, U+2753-2755, U+2757, U+2763-2764, U+2795-2797, U+27A1, U+27B0, U+27BF, U+2934-2935, U+2B05-2B07, U+2B1B-2B1C, U+2B50, U+2B55, U+3030, U+303D, U+3297, U+3299, U+FE0F, U+1F004, U+1F0CF, U+1F170-1F171, U+1F17E-1F17F, U+1F18E, U+1F191-1F19A, U+1F1E6-1F1FF, U+1F201-1F202, U+1F21A, U+1F22F, U+1F232-1F23A, U+1F250-1F251, U+1F300-1F321, U+1F324-1F393, U+1F396-1F397, U+1F399-1F39B, U+1F39E-1F3F0, U+1F3F3-1F3F5, U+1F3F7-1F4FD, U+1F4FF-1F53D, U+1F549-1F54E, U+1F550-1F567, U+1F56F-1F570, U+1F573-1F57A, U+1F587, U+1F58A-1F58D, U+1F590, U+1F595-1F596, U+1F5A4-1F5A5, U+1F5A8, U+1F5B1-1F5B2, U+1F5BC, U+1F5C2-1F5C4, U+1F5D1-1F5D3, U+1F5DC-1F5DE, U+1F5E1, U+1F5E3, U+1F5E8, U+1F5EF, U+1F5F3, U+1F5FA-1F64F, U+1F680-1F6C5, U+1F6CB-1F6D2, U+1F6E0-1F6E5, U+1F6E9, U+1F6EB-1F6EC, U+1F6F0, U+1F6F3-1F6F8, U+1F910-1F93A, U+1F93C-1F93E, U+1F940-1F945, U+1F947-1F94C, U+1F950-1F96B, U+1F980-1F997, U+1F9C0, U+1F9D0-1F9E6, U+E0062-E0063, U+E0065, U+E0067, U+E006C, U+E006E, U+E0073-E0074, U+E0077, U+E007F;
}

html {
  font-family: MyFontFamily, sans-serif;
}

長い……ですね。しかしちゃんと表示されます。以下、順を追って解説します。

普通に絵文字を入力する

何も考えずに知っている絵文字をHTMLに入力してみます。今どきのIMEなら「りんご」の変換で「🍎」が出るのではないでしょうか。

コード

<!DOCTYPE html>
<meta charset="UTF-8">
<title>Emoji</title>
<p>🍎</p>

結果

普通に入力したら、普通に表示されました。しかし、IEで見ると……。

あまりおいしくなさそうなりんごになってしまいました。

font-familyを明示する

CSSのfont-familyプロパティを使って絵文字のフォント名を明示してやると、IEでもちゃんと表示されるようになります。

コード

<!DOCTYPE html>
<meta charset="UTF-8">
<title>Emoji</title>
<style>
  html {
    font-family: "Segoe UI Emoji",
                 "Segoe UI Symbol",
                 "Apple Color Emoji",
                 "Noto Color Emoji",
                 "Noto Emoji",
                 sans-serif;
 }
</style>
<p>🍎</p>

font-familyプロパティは、先頭に近いほうのフォントが優先して適用され、足りない文字を後ろのフォントから順番に探して適用していくようになっています。上述のコードは、絵文字のフォントを優先して適用し、不足する文字(つまり絵文字以外の文字)には、デフォルトのサンセリフ(ゴシック)体を使えという指定です。

結果

IEでもおいしそうなりんごになりました! これでバッチリ! かと思いきや……。

コード

<style>
  .emoji { font-family: "Segoe UI Emoji", /* 絵文字フォント省略 */, sans-serif }
  .normal { font-family: "Hiragino Kaku Gothic ProN", "Meiryo", sans-serif }
</style>
<p class="emoji">🍎 123 !@# ABC あいう 安以宇 ♨</p>
<p class="normal">🍎 123 !@# ABC あいう 安以宇 ♨</p>

結果

Chrome

iOS

上が絵文字のfont-familyを指定したもの。下はブラウザデフォルトのサンセリフ体。なんか、絵文字以外のフォントも微妙に変わってしまっています。iOSに至っては、数字やシャープ、半角スペースまでもが、幅が大きく表示されてしまっています。これでは使い物になりません。

font-familyの指定順を工夫する

前の問題は、数字や半角スペースにも絵文字フォントが適用されてしまっているためだと考えられます。ではfont-familyの指定順を工夫することで解決できないでしょうか?

コード

<style>
 .emoji { font-family: "Hiragino Kaku Gothic ProN", "Meiryo", "Segoe UI Emoji", "Segoe UI Symbol", "Apple Color Emoji", "Noto Color Emoji", "Noto Emoji", sans-serif }
 .normal { font-family: "Hiragino Kaku Gothic ProN", "Meiryo", sans-serif }
</style>
<p class="emoji">🍎 123 !@# ABC あいう 安以宇 ♨</p>
<p class="normal">🍎 123 !@# ABC あいう 安以宇 ♨</p>

emojiクラスのfont-familyの先頭に、日本語フォントの指定を入れてみました。これなら、日本語フォントが優先され、存在しなければ絵文字フォントを使ってくれることになります。

結果

みごと、きれいに表示されました! しかし、気になるところがひとつ。温泉マーク「♨」がモノクロで表示されてしまっています。どうせならこちらも、絵文字で表示させてあげたいものです。

unicode-rangeで絵文字のコード範囲を指定する

こうなってしまう原因は、font-familyの前方に指定した日本語フォントの中に、「♨」のグリフが含まれてしまっていることです。そのため日本語フォントのほうの温泉マークが優先され、絵文字フォントにまで到達していないということですね。

これを防ぐために、unicode-range記述子を使います。unicode-rangeは@font-faceルールの中で使います。細かいことは省略しますが、この記述子を使って絵文字フォントを使用する範囲を文字コード指定で制限します。

コード

<style>
  @font-face {
    font-family: MyFontFamily;
    src: local("Hiragino Kaku Gothic ProN"),
         local("Meiryo");
  }

  @font-face {
    font-family: MyFontFamily;
    font-weight: bold;
    src: local("Hiragino Kaku Gothic ProN"),
         local("Meiryo Bold");
  }

  @font-face {
    font-family: MyFontFamily;
    src: local("Segoe UI Emoji"),
         local("Segoe UI Symbol"),
         local("Apple Color Emoji"),
         local("Noto Color Emoji"),
         local("Noto Emoji");
    unicode-range: /*U+23, U+2A, U+30-39,*/ U+A9, U+AE, U+200D, U+203C, U+2049, U+20E3, U+2122, U+2139, U+2194-2199, U+21A9-21AA, U+231A-231B, U+2328, U+23CF, U+23E9-23F3, U+23F8-23FA, U+24C2, U+25AA-25AB, U+25B6, U+25C0, U+25FB-25FE, U+2600-2604, U+260E, U+2611, U+2614-2615, U+2618, U+261D, U+2620, U+2622-2623, U+2626, U+262A, U+262E-262F, U+2638-263A, U+2640, U+2642, U+2648-2653, U+2660, U+2663, U+2665-2666, U+2668, U+267B, U+267F, U+2692-2697, U+2699, U+269B-269C, U+26A0-26A1, U+26AA-26AB, U+26B0-26B1, U+26BD-26BE, U+26C4-26C5, U+26C8, U+26CE-26CF, U+26D1, U+26D3-26D4, U+26E9-26EA, U+26F0-26F5, U+26F7-26FA, U+26FD, U+2702, U+2705, U+2708-270D, U+270F, U+2712, U+2714, U+2716, U+271D, U+2721, U+2728, U+2733-2734, U+2744, U+2747, U+274C, U+274E, U+2753-2755, U+2757, U+2763-2764, U+2795-2797, U+27A1, U+27B0, U+27BF, U+2934-2935, U+2B05-2B07, U+2B1B-2B1C, U+2B50, U+2B55, U+3030, U+303D, U+3297, U+3299, U+FE0F, U+1F004, U+1F0CF, U+1F170-1F171, U+1F17E-1F17F, U+1F18E, U+1F191-1F19A, U+1F1E6-1F1FF, U+1F201-1F202, U+1F21A, U+1F22F, U+1F232-1F23A, U+1F250-1F251, U+1F300-1F321, U+1F324-1F393, U+1F396-1F397, U+1F399-1F39B, U+1F39E-1F3F0, U+1F3F3-1F3F5, U+1F3F7-1F4FD, U+1F4FF-1F53D, U+1F549-1F54E, U+1F550-1F567, U+1F56F-1F570, U+1F573-1F57A, U+1F587, U+1F58A-1F58D, U+1F590, U+1F595-1F596, U+1F5A4-1F5A5, U+1F5A8, U+1F5B1-1F5B2, U+1F5BC, U+1F5C2-1F5C4, U+1F5D1-1F5D3, U+1F5DC-1F5DE, U+1F5E1, U+1F5E3, U+1F5E8, U+1F5EF, U+1F5F3, U+1F5FA-1F64F, U+1F680-1F6C5, U+1F6CB-1F6D2, U+1F6E0-1F6E5, U+1F6E9, U+1F6EB-1F6EC, U+1F6F0, U+1F6F3-1F6F8, U+1F910-1F93A, U+1F93C-1F93E, U+1F940-1F945, U+1F947-1F94C, U+1F950-1F96B, U+1F980-1F997, U+1F9C0, U+1F9D0-1F9E6, U+E0062-E0063, U+E0065, U+E0067, U+E006C, U+E006E, U+E0073-E0074, U+E0077, U+E007F;
  }

  @font-face {
    font-family: MyFontFamily;
    font-weight: bold;
    src: local("Segoe UI Emoji"),
         local("Segoe UI Symbol"),
         local("Apple Color Emoji"),
         local("Noto Color Emoji"),
         local("Noto Emoji");
    unicode-range: /*U+23, U+2A, U+30-39,*/ U+A9, U+AE, U+200D, U+203C, U+2049, U+20E3, U+2122, U+2139, U+2194-2199, U+21A9-21AA, U+231A-231B, U+2328, U+23CF, U+23E9-23F3, U+23F8-23FA, U+24C2, U+25AA-25AB, U+25B6, U+25C0, U+25FB-25FE, U+2600-2604, U+260E, U+2611, U+2614-2615, U+2618, U+261D, U+2620, U+2622-2623, U+2626, U+262A, U+262E-262F, U+2638-263A, U+2640, U+2642, U+2648-2653, U+2660, U+2663, U+2665-2666, U+2668, U+267B, U+267F, U+2692-2697, U+2699, U+269B-269C, U+26A0-26A1, U+26AA-26AB, U+26B0-26B1, U+26BD-26BE, U+26C4-26C5, U+26C8, U+26CE-26CF, U+26D1, U+26D3-26D4, U+26E9-26EA, U+26F0-26F5, U+26F7-26FA, U+26FD, U+2702, U+2705, U+2708-270D, U+270F, U+2712, U+2714, U+2716, U+271D, U+2721, U+2728, U+2733-2734, U+2744, U+2747, U+274C, U+274E, U+2753-2755, U+2757, U+2763-2764, U+2795-2797, U+27A1, U+27B0, U+27BF, U+2934-2935, U+2B05-2B07, U+2B1B-2B1C, U+2B50, U+2B55, U+3030, U+303D, U+3297, U+3299, U+FE0F, U+1F004, U+1F0CF, U+1F170-1F171, U+1F17E-1F17F, U+1F18E, U+1F191-1F19A, U+1F1E6-1F1FF, U+1F201-1F202, U+1F21A, U+1F22F, U+1F232-1F23A, U+1F250-1F251, U+1F300-1F321, U+1F324-1F393, U+1F396-1F397, U+1F399-1F39B, U+1F39E-1F3F0, U+1F3F3-1F3F5, U+1F3F7-1F4FD, U+1F4FF-1F53D, U+1F549-1F54E, U+1F550-1F567, U+1F56F-1F570, U+1F573-1F57A, U+1F587, U+1F58A-1F58D, U+1F590, U+1F595-1F596, U+1F5A4-1F5A5, U+1F5A8, U+1F5B1-1F5B2, U+1F5BC, U+1F5C2-1F5C4, U+1F5D1-1F5D3, U+1F5DC-1F5DE, U+1F5E1, U+1F5E3, U+1F5E8, U+1F5EF, U+1F5F3, U+1F5FA-1F64F, U+1F680-1F6C5, U+1F6CB-1F6D2, U+1F6E0-1F6E5, U+1F6E9, U+1F6EB-1F6EC, U+1F6F0, U+1F6F3-1F6F8, U+1F910-1F93A, U+1F93C-1F93E, U+1F940-1F945, U+1F947-1F94C, U+1F950-1F96B, U+1F980-1F997, U+1F9C0, U+1F9D0-1F9E6, U+E0062-E0063, U+E0065, U+E0067, U+E006C, U+E006E, U+E0073-E0074, U+E0077, U+E007F;
  }

  .emoji { font-family: MyFontFamily, sans-serif }
  .normal { font-family: "Hiragino Kaku Gothic ProN", "Meiryo", sans-serif }
</style>
<p class="emoji">🍎 123 !@# ABC あいう 安以宇 ♨</p>
<p class="normal">🍎 123 !@# ABC あいう 安以宇 ♨</p>

結果

これでおしまいです。

上記のコードで、一部がコメントアウトされています。/*U+23, U+2A, U+30-39, U+A9, U+AE,*/ という部分です。これは「#, *, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9」の文字が該当します。これらの文字をコメントアウトしておかないと、通常の文章の中のこれらの文字が、絵文字フォントのほうが優先されてしまい、前述の問題が起きてしまうことがわかっています。

残念ながらこの手法は完全ではなく、以下に挙げるチケットが正しく絵文字として表示されません。

#, *, 0-9 がそれぞれ罫線で囲まれた絵文字

もしこれらの文字を絵文字で表示したい場合は、絵文字の仕様箇所をHTML要素で囲んで範囲を限定したうえで、以下のCSS指定を行ってください。

font-family: "Segoe UI Emoji", "Segoe UI Symbol", "Apple Color Emoji", "Noto Color Emoji", sans-serif;

テキストによる絵文字表示を推進すべき理由

ちと蛇足です。

冒頭に挙げたEmojiOneやTwemojiなどのライブラリではなく、特別な理由がない限り、テキストを使って絵文字を表示したほうが良いと考えています。理由は次の通り。

軽量で高速

画像と違い追加リクエストが不要なので、非常に軽量に絵文字を表示できます。またJavaScriptがテキストを舐めて画像に置換する処理も不要で高速です。

コピペができる

画像で表示されている絵文字はコピーができません。一方でテキストで書かれた絵文字は問題なくコピーできます。

テキストエリア上に入力できる

Slackのように「:slightly_smiling_face:」などとする必要はありません。

OS標準のUIを使って絵文字を入力できる

iOSもAndroidも絵文字専用キーボードを持っています。Windowsのネイティブ入力対応はいまひとつですが、macOSも標準で絵文字入力の仕組みを持っています。ユーザーが絵文字を入力するときは、使い慣れているUIを使わせてあげたいものです。

テキストによる絵文字表示が使える環境

ほぼOSのバージョンに依存します。ざっくりと以下のような対応状況です。

モノクロ カラー
Windows 7, 8 8.1以降 ※
macOS/OS X ‐ 10.7 Lion以降
iPhone ‐ 4以降
Android 4.1 Jelly Bean以降 4.4 KitKat 以降

※…Windows 10 Anniversary Updateで改良あり

細かく見ていくとブラウザのバージョンにも拠りますが、モダンブラウザはもれなく対応完了しているので特に言及しません。


本当に「正しい」絵文字の表示方法について

記事執筆中に色々と調べていくうちに、ここまで解説してきた方法は邪道であるということがわかってきました(なんやねん……)。

記事内で、温泉マークのような「もともと記号の文字として定義されていた場所に絵文字が割り当てられた」ケースについて触れていました。Unicodeの規格によると、「テキストで出す」「絵文字で出す」を明示する方法があるんだそうです。

絵文字バリエーション・シーケンスとは何か

これを加味して考えると、本来は、CSSで絵文字のためにがんばるべきではない、という結論となります。すべてはOSとブラウザの処理に任せるべきで、テキストで表示したいか、絵文字で表示したいかはユーザー側で制御すべきである……と。まあ、その通りですとしか言いようがありません。

とはいえ、どうやら絵文字バリエーション・シーケンス(規格上はEmoji Presentation Sequences)はまだOSやブラウザのサポートが十分ではないようで、確認できた範囲でも「この挙動バグじゃない?」というような点が多数見受けられました。正しい実装が出そろうまでは、この記事の内容は捨てたものではないようです。

よろしくお願いいたします。