安全でアクセシブルなアイコン・フォント

Translation of: Bulletproof Accessible Icon Fonts by Filament Group

Published on: 2014-02-07

アイコン・フォントを利用する場合、あらゆるユーザーに適切にアイコンを提供しようとすると、かなり気をつける必要があります。そのフォントが読み込まれなかった時にどうなりますか?@font-faceがまだサポートされていないブラウザーでは?どうすれば安全に(bulletproofに)アイコン・フォントを利用できるかをこれから解説したいと思います。

効率的で機能的なウェブサイトを制作するという、この終わることのない探求において、ベクター形式のアイコンを提供する手段として、簡便であるフォントを利用することが何度も提案されてきました。対して私達は通常ベクター形式のアイコンとしてSVGIan Featherがブログ書いたような理由から選んで(また、薦めて)おり、既にアイコン・フォントを利用しているチームと共同して制作する場合でも、時にはそうしてきました。そのような時のために、どうやればアイコン・フォントをあまねくアクセシブルな形で利用できるかの調査も行ってきました。

あなたはほとんどのアイコン・フォントの実装方法がスクリーン・リーダーにうまく適応していないこと、そしてうまく適応させ、かつアクセシブルにするには追加のマークアップが必要なことを知っていますか?また、私達は古い(そして新しい)モバイル・ブラウザー—例えばOpera MiniやNokia XPress Browser、Blackberry 4–5、Android 2.1、Windows Phone 7–7.8—が@font-faceをまったくサポートしていないことを発見しました。この記事を書こうとした時にちょっと調べてみたところ、これらのブラウザーが少なくとも全世界で3.7億ものユーザーに提供されていることを知り、驚きました。

この記事では、アイコン・フォントがどのような場合にうまく動き、またうまく動かないのか、@font-faceがサポートされていない(またはフォントが読み込まれなかった)時にアイコン・フォントがどうなるのかや、このような問題にどう取り組んで安全な方法でアイコン・フォントを提供するのか、私達が学んだことについて解説したいと思います。

アイコン・フォントの問題

アクセシビリティ

以下のようなマークアップについて考えてみましょう:

<span class="icon-star">Favorite</span>

多くのアイコン・フォントはこのようなマークアップを使い、:before:afterを組み合わせてアイコンを表示させています。しかし、この場合contentの内容はスクリーン・リーダーからは隠れず、音読されてしまうことがあります。「でもこれユニコードですよ!スクリーン・リーダーはわざわざユニコードの文字列までをも解釈するんですか?」とあなたは言うかもしれません。そうです。スクリーン・リーダーは解釈します(後述しますが、仮に私用領域の文字であっても解釈します)。

例えば、上記例に以下のようなCSSを適用したとしましょう:

.icon-star:before { content: "★ "; }

多くのブラウザーでは以下のように表示され、まったく問題ありません:

VoiceOver (Mac OS X)では「Black Star Favorite」としゃべってくれることになります。ちょっと良くないですよね。

残念ながら不必要な見た目のためだけのcontentをスクリーン・リーダーから隠すというようなことはCSSだけでは実現できません(speak: noneのサポートは限られたものです)。今のところ最も妥当な方法はaria-hidden="true"を使うことでしょう。この場合、アイコンのために追加のHTML要素が必要になり、その親要素へ:before:afterを使う代わりにそれに対して使うことになります。

つまり、HTMLとCSSはこのようになります:

<style>
.icon-star:before { content: "★ "; }
</style>
<span><span class="icon-star" aria-hidden="true"></span>Favorite</span>

見た目は望み通りそのまま維持され、VoiceOverは「Favorite」と読み上げてくれます。

注: ChromeFirefoxには<button>要素の子にaria-hiddencontentがある場合に既知の問題があります。ですがそれでもaria-hiddenを使うのがスクリーン・リーダーからcontentを隠すにはもっとも良い方法でしょう。

フォールバック

次は@font-faceをサポートしていないブラウザーやフォント・ファイルの読み込みに失敗した時にどうなるかについて考えてみましょう。

アイコン・フォントからアイコンを利用するための方法として、ユニコード文字にアイコンを割り当てる方法と、特定の文字列のリガチャに割り当てるという、2つの手法が一般的です。

ユニコードを使い、絵文字または空白でフォールバック

ユニコードにはその仕様ではいかなる文字も規定されていないセクションが3つあります。それらは私用領域(PUA)と呼ばれており、フォントがこれらの領域にユニコード仕様には存在しないグリフを持つことを許可しています。最初の私用領域(BMP PUAと呼ばれることがあります)はUTF-8エンコーディングで扱うことが出来ます。

多くの有名なアイコン・フォントはそのアイコンを私用領域の範囲に割り当てることにより、既存のユニコード定義との衝突を回避しています。例えば、アイコン・フォントを利用するとユニコードで定義されているBlack Starが虹で上書きされてしまうなどということはありません(もしかしたらあなたは上書きして欲しいかもしれませんが、そうなるべきではありません)。

私用領域を利用することによりセマンティクス(訳注: ユニコード定義におけるセマンティクス)での衝突は回避できますが、その見た目についての衝突は解決されません。例えば、あるOSに含まれるフォントは私用領域に独自の文字を定義しています。アイコンをこれら独自の文字列の位置にマッピングしていて、かつフォントのロードに失敗した場合、デフォルトのそれら独自のグリフが表示されることになるでしょう。例えばiOS 7では私用領域に絵文字を定義しています:

Screenshot of iOS 7 PUA showing Emoji Characters

ここで先述のサンプルに戻ってみましょう。独自の黒い星のアイコンをユニコード私用領域にマップしたとすると、マークアップは以下のようになります:

<style>
.icon-star:before { content: "\e001"; }
</style>
<span><span class="icon-star" aria-hidden="true"></span>Favorite</span>

アイコン・フォントのリクエストの失敗すると、デフォルトのユニコード文字列が代わりに表示されます。iOSでは以下のように表示されるでしょう:

私用領域における文字との衝突がまったくなかったとしても、空白になるというわけではありません—その変わりに不明なグリフに使われる文字が見えてしまうでしょう。

大抵の場合、四角(とてもひどいデフォルトの四角グリフ)になります(訳注: 日本では豆腐などと呼ばれています):

場合によってはもっととんでもないことになってしまいます:

アイコン・フォントはFlash of Invisible Text (FOIT) (詳しくはWebKit's alternative to the Flash of Unstyled Text, or FOUTを参照してください)に依存する形で、@font-faceの処理中にこういったフォールバックを表示させないように実装していることもあります。FOITはウェブ開発者達の間ではよく論争の火種になっていますね。万が一、将来ブラウザーがFOITの代わりにFOUTを利用するようになった場合、アイコン・フォントは@font-faceの処理中にフォールバックされてしまうグリフを隠すために別の方法を考えださないとならないでしょう。

リガチャを使い。Real Text™でフォールバック

限られた文字を使ったフォールバックに頼らないようにするため、フォールバックにリガチャを使うという素晴らしいアイディアを考えだした人がいました。リガチャとは2文字以上の文字を連結して単一のグリフにするものです。これを利用すると、特定の連続した文字が文に現れた場合にグリフを置き換えることが可能になります。

例えば、リガチャを使うと「love」という文字のつながりが出現した場合に♥に置き換えることができます。

リガチャがサポートされていないような環境(やフォントが読み込まれなかった場合)では、文字列がフォールバックとしてそのまま表示されます。

これは一見とても素晴らしく思えますが、いくつか制限もあります。

一時、多くのウェブサイトでリガチャを有効にするためにtext-rendering: optimizelegibility;を指定するように薦められていました。text-renderingプロパティーはSVGの仕様(CSSの仕様ではありません)で定義されています。この手法はletter-spacingプロパティーが効かなくなったり違う文字で文章が表示されたり、他にも様々な問題が確認されています。

リガチャを有効にする別の方法としてfont-featur-settingsプロパティーの利用が提案されています。信頼できそうではありますが、残念なことにデスクトップ向けのブラウザーですらサポートが完全ではありません。IE9以下、Android 4.3以下、Blackberry 10まで、そして最新の(訳注: 2014/01/13現在)IE Mobileではまったく表示することが出来ず空白になります。この記事を書いた時点では、つまり全世界の30%程度のユーザーにしかフォールバックが行われないということになります(残念)。

リガチャにおける別の懸念としては、コンテンツと見た目を不可分にしてしまうのではないかというものもあります。特に国際化においてこれは問題になるでしょう。例えば言語ごとに違うリガチャを作る必要があるだろうと考えられるので、メンテナンス性や作業効率における大きな問題を引き起こします。またひとつの言語に対応することに比べ、10の言語に対応する場合、フォント・ファイルのサイズにも大きな違いが表れるでしょう。言語ごとにフォントを作成すれば問題なさそうだと思いますか?多分ダメでしょう—それでも多少は何かしらダウンロードすることになり、しかもそれはまったく使われないからです。

最後にリガチャが空白を含められないことには注意が必要でしょうLarry Foxによるとリガチャに空白は含められるとのことです。またKeyamoonによる含められるがChromeで不具合があるということです。

では、アイコン・フォントは失策なのか?

もちろん、そんなことはありません。これまで挙げてきたような危険を鑑みても、アイコン・フォントを利用できます!ただ単純に@font-faceをCSSに組み入れる以上に少しだけ気をつけてやるだけです。

より良い実装についての詳細に入る前に、私達はまず以下の二種類のアイコン・フォントの利用ケースそれぞれに違う手法を用いることを決めました:

  1. 飾りのアイコン: 本当に単なる飾りにすぎず、それに意味や機能がないようなアイコンです。アイコンが見えなくなることそれ自体は構いませんが、フォールバックに当たるものがいかなる場合も画面を専有しないようにし、付近のコンテンツの位置揃えに影響を与えないようにしなければなりません。
  2. 重要なアイコン: つまり、そのアイコンによってウェブサイトの何かを認識できたり、機能が示されたりするもので、アイコンそのものかそのフォールバック内容が必ず表示されるべきものです。このようなアイコンは更に二種類に分かれ、文字列でフォールバックされる(その長さにもよりますが、専有面積を制限しないようにします)か別のグリフにフォールバックされるかします。フォールバックは妥当なユニコード文字(クロス・プラットフォームでクロス・ブラウザーな代替文字は少ないですが、幸運にもJohn Holt Ripleyによりユニコード文字列のサポート状況の表がまとめられています)か、場合によっては画像(互換性の面からPNGになるでしょう)を指定します。

実装はかなりややこしいものなので、簡単に扱えるようにCSSやJavaScriptを簡潔にまとめて、再利用可能なライブラリとしてA Font Gardeを作成しました。GitHubで公開しているので、これを利用して安全でアクセシブルにアイコン・フォントをあなたのウェブサイトでも簡単に利用できるはずです。必要となるModernizrの2つの機能チェックは含まれており、それとafontgarde.css及びafontgarde.js以外には何も必要ありません。

飾りのアイコン

飾りのアイコンの場合は実装は簡単です。単純に@font-faceがサポートされていれば表示し、そうでない場合は隠すようにすれば良いでしょう。

グレードA(訳注: 完全なサポートをしている)の環境ではこのように見えるでしょう:

対してグレードC(訳注: エラーにはならないが機能しない)の環境ではこうなります:

多少のマークアップの変更だけでこのような形で実装することができます。

HTML

<span class="icon icon-twitter" aria-hidden="true"></span>
Share on Twitter (Sibling Text)

まず別にspan要素を追加することによって、スクリーン・リーダーにおけるアクセシビリティ上の問題を解決します。

CSS

そして@font-faceブロックに続けて、アイコン・フォントを利用するためのCSSを書くことになります:

.supports-fontface.icomoon .icon:before {
  font-family: icomoon;
}
.supports-fontface.icomoon .icon-twitter:before {
  content: "\e604";
}

Moderinzrの機能チェックにより追加されるsupports-fontfaceというクラスを利用していることに気をつけてください。icomoonというクラスはafontgarde.js (このライブラリはフォントがちゃんと読み込まれたかどうかをデフォルトのフォントとグリフの幅を比較することによってチェックしています)により追加されます。

JS

AFontGarde('icomoon', '\uE600\uE601\uE602\uE605');

こうやってチェックするフォントのファミリ名とグリフの大きさの比較に使う文字をいくつか指定します。これはフォントごとに一度だけ呼べば結構です。このように呼ぶと追加されることになるicomoonというクラス名を以下のコード例でも使っていきます。

このようにしてアイコン・フォントを使う時にCSS側でクラス名を2つ組み合わせるようにしておくことにより、既に触れた問題を解決することが出来ます。素晴らしい!これだけでこのケースは完璧です。

重要なアイコン

重要なアイコンの場合、アイコン・フォントの表示が失敗した場合に何かしらを表示させたいところです。こういったケースでは取りうる手段は、アイコンの代わりに文字列で置き換えるか、別のアイコンを使うか、この両者のどちらかになるでしょう。

文字列によるフォールバック

グレードA

グレードC

HTML
<span class="icon-fallback-text">
  <span class="icon icon-twitter" aria-hidden="true"></span>
  <span class="text">Twitter</span>
</span>
CSS
.icon-fallback-text .icon {
  display: none;
}
.supports-fontface.supports-generatedcontent.icomoon .icon-fallback-text .icon {
  display: inline-block;
}
.supports-fontface.supports-generatedcontent.icomoon .icon-fallback-text .text {
  /* a generic way to visually hide content while remaining accessible to screen readers (h5bp.com) */
  clip: rect(0 0 0 0);
  overflow: hidden;
  position: absolute;
  height: 1px;
  width: 1px;
}

上記はafontgarde.cssに含まれるコードです

同じように機能チェックを行いますが、supports-generatedcontentというクラス名も併用し:before及び:afterがサポートされているかも確認します。もしサポートされていない場合は文字列が表示される、ということになります。

グリフまたは画像によるフォールバック

グレードA

グレードC (グリフ)

グレードC (画像)

HTML
<span class="icon-fallback-glyph"><!-- or "icon-fallback-img" -->
  <span class="icon icon-hamburger" aria-hidden="true"></span>
  <span class="text">Menu</span>
</span>
CSS

afontgarde.cssに含まれるコードは一部省略されています

/***************************
 * Fallback Glyph
 ***************************/
.icon-fallback-glyph .icon-hamburger:before {
  content: "\2261"; /* Hamburger */
  font-size: 2em;
  line-height: .5;
}
/* A-Grade */
.supports-fontface.icomoon .icon-fallback-glyph .icon-hamburger:before {
  content: "\e601";
}

フォールバックに使うグリフは細心の注意を払って決定しましょう。クロス・ブラウザー及びクロス・プラットフォームであるかはグリフによって違います。John Holt Ripleyの互換性の表を参照してください。

アイコン・フォントとフォールバックに使われるグリフがなるべく同じになるように、font-sizeline-heightプロパティーも同時に調節していることにも気をつけてください。フォントが正常に読み込まれた場合(そのような場合の時にのみ)、CSSのcontentプロパティーでアイコン・フォントのグリフを指定します。

/***************************
 * Fallback Bitmap
 ***************************/
.icon-fallback-img .icon-hamburger {
  width: 1em;
  height: 1em;
  background: url("fonts/png/hamburger.png") no-repeat;
}
/* A-Grade */
.supports-fontface .icon-fallback-img .icon-hamburger:before {
  font-family: icomoon;
  content: "\e601";
}

機能チェックを厳しく実行し、CSSでは背景画像を利用しているにも関わらず、他のアプローチと比べフォールバックを画像で行うのは確実なものとはあまり言えません。もしスクリプトがフォントの読み込みを監視していたとしても(そしてicomoonクラスを追加したとしても)、背景画像へのリクエストはそれよりも早くに実行されるので、フォントの読み込みが成功するか否かは関わらず背景画像は読み込まれます。またこの事情の別の面として、フォントへのHTTPリクエストが失敗した場合、デフォルトのユニコード文字が表示されることになるでしょう。

フォールバックに使う画像をアイコン・フォントのそれとできうる限り同じようにするためにwidthheightプロパティーも調節していることに注意してください。もし背景画像の大きさも調節したい場合は、background-sizeプロパティーのサポートに制限があることにも気をつけましょう。

最後に

これでは冗長すぎると感じる人も多いでしょう。ですから私達は簡単に使えるライブラリとしてパッケージングし、多少の作業でこういった実装パターンを再現できるようにしました。ある機能が実装されているかのチェックがアイコン・フォントにおいては鍵になるので、それらを上手く利用して望まない形でフォールバックされないように気をつけて実装してください。


Translated by Kyo Nagashima.