Translation of: Bulletproof Accessible Icon Fonts by Filament Group
アイコン・フォントを利用する場合、あらゆるユーザーに適切にアイコンを提供しようとすると、かなり気をつける必要があります。そのフォントが読み込まれなかった時にどうなりますか?@font-face
がまだサポートされていないブラウザーでは? どうすれば安全に(bulletproofに)アイコン・フォントを利用できるかをこれから解説したいと思います。
効率的で機能的なウェブサイトを制作するという、この終わることのない探求において、ベクター形式のアイコンを提供する手段として、簡便であるフォントを利用することが何度も提案されてきました。対して私達は通常ベクター形式のアイコンとしてSVGをIan 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」と読み上げてくれます。
注: ChromeとFirefoxには<button>
要素の子にaria-hidden
のcontent
がある場合に既知の問題があります。ですがそれでもaria-hidden
を使うのがスクリーン・リーダーからcontent
を隠すにはもっとも良い方法でしょう。
次は@font-face
をサポートしていないブラウザーやフォント・ファイルの読み込みに失敗した時にどうなるかについて考えてみましょう。
アイコン・フォントからアイコンを利用するための方法として、ユニコード文字にアイコンを割り当てる方法と、特定の文字列のリガチャに割り当てるという、2つの手法が一般的です。
ユニコードにはその仕様ではいかなる文字も規定されていないセクションが3つあります。それらは私用領域(PUA)と呼ばれており、フォントがこれらの領域にユニコード仕様には存在しないグリフを持つことを許可しています。最初の私用領域(BMP PUAと呼ばれることがあります)はUTF-8エンコーディングで扱うことが出来ます。
多くの有名なアイコン・フォントはそのアイコンを私用領域の範囲に割り当てることにより、既存のユニコード定義との衝突を回避しています。例えば、アイコン・フォントを利用するとユニコードで定義されているBlack Starが虹で上書きされてしまうなどということはありません(もしかしたらあなたは上書きして欲しいかもしれませんが、そうなるべきではありません)。
私用領域を利用することによりセマンティクス(訳注: ユニコード定義におけるセマンティクス)での衝突は回避できますが、その見た目についての衝突は解決されません。例えば、あるOSに含まれるフォントは私用領域に独自の文字を定義しています。アイコンをこれら独自の文字列の位置にマッピングしていて、かつフォントのロードに失敗した場合、デフォルトのそれら独自のグリフが表示されることになるでしょう。例えばiOS 7では私用領域に絵文字を定義しています:
ここで先述のサンプルに戻ってみましょう。独自の黒い星のアイコンをユニコード私用領域にマップしたとすると、マークアップは以下のようになります:
<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
の処理中にフォールバックされてしまうグリフを隠すために別の方法を考えださないとならないでしょう。
限られた文字を使ったフォールバックに頼らないようにするため、フォールバックにリガチャを使うという素晴らしいアイディアを考えだした人がいました。リガチャとは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に組み入れる以上に少しだけ気をつけてやるだけです。
より良い実装についての詳細に入る前に、私達はまず以下の二種類のアイコン・フォントの利用ケースそれぞれに違う手法を用いることを決めました:
実装はかなりややこしいものなので、簡単に扱えるようにCSSやJavaScriptを簡潔にまとめて、再利用可能なライブラリとしてA Font Gardeを作成しました。GitHubで公開しているので、これを利用して安全でアクセシブルにアイコン・フォントをあなたのウェブサイトでも簡単に利用できるはずです。必要となるModernizrの2つの機能チェックは含まれており、それとafontgarde.css
及びafontgarde.js
以外には何も必要ありません。
飾りのアイコンの場合は実装は簡単です。単純に@font-face
がサポートされていれば表示し、そうでない場合は隠すようにすれば良いでしょう。
グレードA(訳注: 完全なサポートをしている)の環境ではこのように見えるでしょう:
対してグレードC(訳注: エラーにはならないが機能しない)の環境ではこうなります:
多少のマークアップの変更だけでこのような形で実装することができます。
<span class="icon icon-twitter" aria-hidden="true"></span>
Share on Twitter (Sibling Text)
まず別にspan
要素を追加することによって、スクリーン・リーダーにおけるアクセシビリティ上の問題を解決します。
そして@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
(このライブラリはフォントがちゃんと読み込まれたかどうかをデフォルトのフォントとグリフの幅を比較することによってチェックしています)により追加されます。
AFontGarde('icomoon', '\uE600\uE601\uE602\uE605');
こうやってチェックするフォントのファミリ名とグリフの大きさの比較に使う文字をいくつか指定します。これはフォントごとに一度だけ呼べば結構です。このように呼ぶと追加されることになるicomoon
というクラス名を以下のコード例でも使っていきます。
このようにしてアイコン・フォントを使う時にCSS側でクラス名を2つ組み合わせるようにしておくことにより、既に触れた問題を解決することが出来ます。素晴らしい! これだけでこのケースは完璧です。
重要なアイコンの場合、アイコン・フォントの表示が失敗した場合に何かしらを表示させたいところです。こういったケースでは取りうる手段は、アイコンの代わりに文字列で置き換えるか、別のアイコンを使うか、この両者のどちらかになるでしょう。
<span class="icon-fallback-text">
<span class="icon icon-twitter" aria-hidden="true"></span>
<span class="text">Twitter</span>
</span>
.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
がサポートされているかも確認します。もしサポートされていない場合は文字列が表示される、ということになります。
<span class="icon-fallback-glyph"><!-- or "icon-fallback-img" -->
<span class="icon icon-hamburger" aria-hidden="true"></span>
<span class="text">Menu</span>
</span>
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-size
やline-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リクエストが失敗した場合、デフォルトのユニコード文字が表示されることになるでしょう。
フォールバックに使う画像をアイコン・フォントのそれとできうる限り同じようにするためにwidth
とheight
プロパティーも調節していることに注意してください。もし背景画像の大きさも調節したい場合は、background-size
プロパティーのサポートに制限があることにも気をつけましょう。
これでは冗長すぎると感じる人も多いでしょう。ですから私達は簡単に使えるライブラリとしてパッケージングし、多少の作業でこういった実装パターンを再現できるようにしました。ある機能が実装されているかのチェックがアイコン・フォントにおいては鍵になるので、それらを上手く利用して望まない形でフォールバックされないように気をつけて実装してください。