CSSスプライトについてあれこれ。
「CSS Sprites(CSSスプライト)」についてまとめます。
すでにいろんなサイトで紹介されてるので、検索すれば制作手順から何から何までたくさんヒットしますが、それ故、どの方法が一番ベストなのかってのがいまいち不確かだったので、個人的にまとめ直してみることにしました。
以下は目次です。クリックするとその項目へ移動します。
CSSスプライト概要
CSSスプライトは、サイトの読み込みを高速化する目的で使われているCSSの小技です。TwitterやFacebookなどでは、ちっこいアイコンとかマークを表示するのに使われてますね(2012年8月現在)。
具体的には「サイト内で使用するたくさんの画像(パーツ)をなるべく一枚画像にまとめて、サーバへのリクエスト回数を少なくする」という事をやってます。
画像とCSSだけで高速化が実現できるので大変お手軽なのですが、CSSスプライト用の画像を用意するにはちょっとした工夫が必要なので、ご利用は計画的に行わねばなりません。
メリット
- サイトの読み込みが速くなる
- スライス設定が楽ちん
- HTMLがすっきりする(imgフォルダもすっきりする)
デメリット
- 更新が面倒(サイズが変わった時とか増えた時とか)
- スライスするまでにちょっとした工夫が必要
- CSSが
background-position
で溢れ返る
技術
画像置換と呼ばれるCSSの小技を応用しています。
※ただし、技術を応用してるってだけで、CSSスプライトと画像置換は別物なので注意です(2013.2.15追記)。
画像置換とは「HTML要素の内容を非表示にする替わりに、background
プロパティで背景画像を表示させる」というもので、かなり以前から使われていた技です。
方法はいろいろやり方が公開されてるんですが、基本的には「テキストを非表示にしたところに背景画像を忍ばせる」という事をします。詳しくは後述します。
下記サイトではいろいろな画像置換が、年代別に紹介されてます。ちゃんと名前もあるんですね。
活用場面
読み込みが速くなるからといって、何でもかんでも一枚画像にしちゃえばいい訳ではありません。
サイトに何度も登場する「アイコン」とか「ボタン」とか「マーク」など、汎用的な部分に使うのが良法です。
どのくらい速くなるのか
速い速いと言うけれど、一体どのくらい速くなるの?→改めてカンタンな実験をしてみました。
実験内容
ここでは、実験用に以下の3ページを用意して、表示速度を測ってみました。
- アイコンを
<img>
を使って直接表示させたページ(画像で表示) - バラバラのアイコン画像を使って画像置換したページ(スプライトしてない)
- アイコンをひとまとめにした画像を使って画像置換したページ(スプライトしてる)
OS:Mac OS X 10.6.8、ブラウザ:Firefox 14.0.1、回線速度:22.8591Mbps(※こちらで計測)という環境のもと、下記手順で計測します。
- キャッシュを今すぐ消去する。
- リロード(F5)する。
上記を10回繰り返す。
で、以下のような結果になりました。
結果
回 | 画像で表示 | スプライトしてない | スプライトしてる |
---|---|---|---|
1 | 95ms (onload: 539ms) | 88ms (onload: 559ms) | 32ms (onload: 474ms) |
2 | 97ms (onload: 534ms) | 94ms (onload: 697ms) | 33ms (onload: 504ms) |
3 | 89ms (onload: 628ms) | 85ms (onload: 548ms) | 36ms (onload: 468ms) |
4 | 92ms (onload: 611ms) | 86ms (onload: 544ms) | 36ms (onload: 477ms) |
5 | 98ms (onload: 535ms) | 85ms (onload: 558ms) | 32ms (onload: 480ms) |
6 | 97ms (onload: 484ms) | 89ms (onload: 563ms) | 38ms (onload: 485ms) |
7 | 95ms (onload: 571ms) | 85ms (onload: 659ms) | 32ms (onload: 479ms) |
8 | 97ms (onload: 548ms) | 83ms (onload: 690ms) | 39ms (onload: 545ms) |
9 | 93ms (onload: 532ms) | 89ms (onload: 558ms) | 36ms (onload: 483ms) |
10 | 91ms (onload: 532ms) | 89ms (onload: 487ms) | 33ms (onload: 484ms) |
平均 | 94.4ms (onload: 551.4ms) | 87.3ms (onload: 586.3ms) | 34.7ms (onload: 487.9ms) |
サイト全体の読み込み時間で見ると微々たるものですが、画像の読み込みだけ見ると圧倒的ですね。ガッテン!
画像置換
画像置換はもともと、「システムフォントで書かれたテキストを、かっこいいフォントの画像に置き換える」目的と、「ロールオーバー時の画像の切り替えをJavaScriptを使わずに実装する」目的で普及した技です。一方CSSスプライトは、「複数の画像をひとつにまとめて、HTTPリクエスト回数を減らして表示速度を速くしよう」という目的で普及してます:)。
目的は違えど、手法や、使うCSSとかは一緒なので、ここでは「<img>
の代わりにbackground
プロパティを使って画像を表示させる方法」として、画像置換を紹介します。(2013.2.15追記)
以下は、個人的にオススメな(使いやすいと思う)画像置換の方法です。括弧内の名前は「CSS Image Replacement」に倣ってます。
すべては下記HTMLに対するCSSとなります。
<p class="btn"><a href="#">画像置換用のテキスト</a></p>
text-indent
を使った画像置換(Phark Method)
display
は、要素の表示形式を指定するためのプロパティ。詳しくはこちらを参照のこと。
text-indent
はテキストの先頭位置を操作する(インデントする)ためのプロパティです。詳しくはこちらを参照のこと。
<a>
などのインライン要素にはwidth
、height
などが効かないので、display: block;
でブロック要素にします。
text-indent
の値を-999em
というとんでもない数値にして、画面のはるか左方向へズラし見えなくさせます。画面の左方向ならば、要素がはみ出ててもスクロールバーは出ないのです。
p.btn a { display: block; width: 200px; height: 60px; background: url(../img/btn.gif); text-indent: -999em; }
オススメしておいて何ですが、この方法だと「無駄に大きなボックスを生成してしまうため、パフォーマンスに影響を与えてしまう」旨がこちらで指摘されてます。そのため、次に紹介する方法が生み出されたそうです。でもコレが一番シンプルでカンタンなので今でもよく使ってますけどねー;)。
text-indent
とwhite-space
とoverflow
を使った画像置換(Scott Kellum Method)
overflow
は要素の領域からはみ出した部分をどう処理するか指定するプロパティ。詳しくはこちらを参照のこと。
white-space
は、テキストの改行の仕方を指定するプロパティです。詳しくはこちらを参照のこと。
text-indent
を100%
とすると、インデント量はその要素の幅分となります。しかしそれだけだと、テキストが改行して要素の中に残ってしまうので、white-space: nowrap;
で改行を禁止して完全にはみ出させます。overflow: hidden;
ではみ出た部分を非表示にします。
p.btn a { display: block; width: 200px; height: 60px; background: url(../img/btn.gif); white-space: nowrap; text-indent: 100%; overflow: hidden; }
height
とpadding
とoverflow
を使った画像置換(Leahy / Langridge)
要素の高さを0px
にして、padding-top
でアイコンの高さ分、テキストを下方向へズラします。
overflow: hidden;
ではみ出た部分を非表示にします。
最近これをやっているサイトを見かけて「なるほどー」と感心したんですが、CSS Image Replacementでは2003年の方法として紹介されてました…X)。
p.btn a { display: block; width: 200px; height: 0; padding: 60px; background: url(../img/btn.gif); overflow: hidden; }
CSSスプライトを使ってみる
グローバルナビのボタンを、CSSスプライトを使って作ってみます。
Lopan cafeのグローバルナビは、スプライトの例としてはちょとイマイチなので、下図のようなサイトを用意しました。
上図のヘッダー部分にあるグローバルナビを例に、制作手順を見ていきます。大まかな手順は下記の通り。
- 画像を作る(→Web用に書き出す)
- HTMLを書く
- CSSを書く
画像を作る
ここがCSSスプライトの肝です。この段階ですでにCSSの事も考えておく必要があります。
とはいえ、まずはデザインデータの中からグローバルナビ部分を持ってきます(下図)。
通常時、ロールオーバー時、クリック時、そのページの時の4パターンを作るべく、グローバルナビを4つに複製します。
その際、例えば下図のように、キリのいい間隔で並べると、CSSでの指定が楽になります。
それぞれの状態に合わせて、デザインを編集します。
スライスは超カンタン。PNG-8で書き出します。※PNG-8の他、画像形式についてはこちらが詳しいです。
HTMLを書く
HTMLは下記のように、CSSで指定しやすいように<li>
に固有のclass名を付けますが、その他は割と普通に書きます。(該当箇所のみ抜粋)
<nav> <ul> <li class="boy"><a href="/#!boy">男の子</a></li> <li class="girl"><a href="/#!girl">女の子</a></li> <li class="pickup"><a href="/#!pickup">限定商品</a></li> <li class="shop"><a href="/shop/">店舗検索</a></li> <li class="login"><a href="/login/">ログイン</a></li> </ul> </nav>
CSSを書く
<a>
に対して、画像置換で背景画像を表示させ、ボタンがひとつずつ表示されるようにズラして調整します(上図は「限定商品」ボタンの例)。
下記のように、「共通するスタイルを書いてから、固有のスタイルを書く」という順序で書くと、要領の良い書き方になると思います。
nav ul li a { display: block; width: 137px; height: 47px; overflow: hidden; background: url(../img/nav.png) 0 0; text-indent: 100%; white-space: nowrap; } nav ul li.login a { width: 132px; } nav ul li.girl a { background-position: -137px 0; } nav ul li.pickup a { background-position: -274px 0; } nav ul li.shop a { background-position: -411px 0; } nav ul li.login a { background-position: -548px 0; }
背景画像をズラす量は、かけ算(137×2=274、137×3=411…)です。
ボタンのサイズがバラバラな時には足し算、もしくはPhotoshopで、下図みたく調べる(計る)と手っ取り早いです。
引き続き、ロールオーバー時、クリック時、そのページの時のスタイルを書きます。縦へズラす量は画像を作る時に60pxにして作ったので計る必要なしです:)。
nav ul li a:hover { height: 52px; } nav ul li.boy a:hover { background-position: 0 -60px; } nav ul li.girl a:hover { background-position: -137px -60px; } nav ul li.pickup a:hover { background-position: -274px -60px; } nav ul li.shop a:hover { background-position: -411px -60px; } nav ul li.login a:hover { background-position: -548px -60px; } nav ul li.boy a:active { background-position: 0 -120px; } nav ul li.girl a:active { background-position: -137px -120px; } nav ul li.pickup a:active { background-position: -274px -120px; } nav ul li.shop a:active { background-position: -411px -120px; } nav ul li.login a:active { background-position: -548px -120px; } #boy nav ul li.boy a { height: 52px; background-position: 0 -180px; } #girl nav ul li.girl a { height: 52px; background-position: -137px -180px; } #pickup nav ul li.pickup a { height: 52px; background-position: -274px -180px; } #shop nav ul li.shop a { height: 52px; background-position: -411px -180px; } #login nav ul li.login a { height: 52px; background-position: -548px -180px; }
出来上がり+ちょっとアレンジ
完成するとこうなります。
※モダンブラウザ(Firefox 14.0.1、Safari 5.1.7、Chrome 21.0.1180.79、Internet Explorer 9)で表示確認済み。
- HTML
- CSS
- PREVIEW
transition
プロパティを使ってみたり、<li>
にも背景画像を指定してopacity
プロパティで表示を切り替えてみたり、工夫次第でおもしろい効果を付けることもできます(※transition
に対応したブラウザのみ)。
transition
は、CSSに時間的変化(トランジション効果)を与えられるプロパティ。詳しくはこちらを参照のこと。
opacity
は、要素の透明度を操作するプロパティです。詳しくはこちらを参照のこと。
- HTML
- CSS
- PREVIEW
画像置換の懸念
画像置換(テキストを非表示にして背景画像だけ表示する事(2013.2.15追記))には、「更新が面倒」とか「工夫が必要」とか以前に、もっと重要な懸念があります。
- スパムと判断されがち
- CSSがONで画像の表示がOFFの環境で、何も表示されない
- ブラウザのデフォルト設定では背景画像が印刷されない
スパムと判断されがち
この件については、こちらの記事とか、こちらの記事とか、様々なブログで「別に使ったっていい」ことが謳われてます。僕も使ってもいいと思ってます。
ただ、こちらの記事にあるように、Googleのスタンスは一貫して“NG”
。いかなる場合であっても隠しテキストである事が見受けられればスパムの疑いがあるとして要注意マークを付けられやす
くなるんだそうです。Googleにとってはdisplay: none;
でもtext-indent: 100%;
でも、テキストを隠してる事に変わりないんですね…。
画像置換を使用する際は、それ相応の覚悟が必要のようです。…けど、いくら疑われたって、真摯な気持ちで制作したものなら、いつかは信じてもらえる気がするんですけど…、いつかじゃ遅いのか…X(。
あとのふたつ
ブラウザの初期設定を、設定し直す事が出来ずに「背景画像を印刷しない」ままになってしまっているユーザーがいるかも知れません。
それとは逆に、画像は重いからと言って、あえて「画像を自動的に読み込まない」設定にしてるユーザーもいるかも知れません。
こんな記事もありました。
僕がCSSによる “画像置換” を使わなくなった3つの理由
もちろん背景画像を印刷する設定にすればよいわけすが、「ツール(T)」→「インターネットオプション(O)」→「詳細設定」という手順で背景画像を印刷するよう設定するなんてことは、PCリテラシーの低いユーザーに対して安易に求められる要求ではない気がします。
そう考えるとやはり、テキストを含む画像は<img>
を使って、alt属性で代替テキストを記述した方が良い気がしてきます…。
<img>も併用したCSSスプライト
上記懸念をふまえて、<img>
も併用してCSSスプライトしてみます。
画像を用意する
画像はスパムを警戒して、画像内のテキストとalt属性のテキストが違わないようにするため、ボタンごとにバラバラに用意します。と言ってもロールオーバー時、クリック時、そのページの時の状態が繋がったものになります。
HTMLを書く
HTMLに画像を<img>
を使って配置します。alt属性には、それぞれのボタン名を入れます。(該当箇所のみ抜粋)
<nav> <ul> <li class="boy"><a href="#"><img src="img/nav1.png" alt="男の子" width="137" height="232"></a></li> <li class="girl"><a href="#"><img src="img/nav2.png" alt="女の子" width="137" height="232"></a></li> <li class="pickup"><a href="#"><img src="img/nav3.png" alt="限定商品" width="137" height="232"></a></li> <li class="shop"><a href="#"><img src="img/nav4.png" alt="店舗検索" width="137" height="232"></a></li> <li class="login"><a href="#"><img src="img/nav5.png" alt="ログイン" width="132" height="232"></a></li> </ul> </nav>
CSSを書く
ボタン部分は下記のように指定します。
nav ul li a { display: block; width: 137px; height: 47px; overflow: hidden; } nav ul li.login a { width: 132px; }
引き続き、ロールオーバー時、クリック時、そのページの時のスタイルを書きます。
<a>
の中の<img>
を60px間隔で上下にズラすことで、それぞれの状態の表示・非表示を切り替えます。
普通のCSSスプライトに比べて、background-position
がない分、かなりまとめて書けました。
nav ul li a:hover, #boy nav ul li.boy a, #girl nav ul li.girl a, #about nav ul li.about a, #shop nav ul li.shop a, #login nav ul li.login a { height: 52px; } nav ul li a:hover img { margin-top: -60px; } nav ul li a:active img { margin-top: -120px; } #boy nav ul li.boy a img, #girl nav ul li.girl a img, #about nav ul li.about a img, #shop nav ul li.shop a img, #login nav ul li.login a img { margin-top: -180px; }
出来上がり+ちょっとアレンジ
完成するとこうなります。
※モダンブラウザ(Firefox 14.0.1、Safari 5.1.7、Chrome 21.0.1180.79、Internet Explorer 9)で表示確認済み。
- HTML
- CSS
- PREVIEW
この方法でも、工夫次第でおもしろい効果を付けることができます(※transition
に対応したブラウザのみ)。
- HTML
- CSS
- PREVIEW
効果
HTMLに<img>
も用意した事で、前述の懸念はある程度改善されました。
ブラウザのデフォルト設定では背景画像が印刷されない
「背景画像を印刷しない」状況でも下図のように、ボタンは印刷されるようになりました:)。
CSSがONで画像の表示がOFFの環境で、何も表示されない
一方「CSSがONで画像の表示がOFF」の状況では下図の通り。
Firefoxでは、alt属性がテキスト同様に表示されるので、見た目はアレですけど、どこへリンクするのかは分かるようになりました。
しかしIE9では、画像の大きさはそのままに、画像の左上部にalt属性が表示されるため、そのページの時やロールオーバー時などで画像が上にズレると、alt属性ごと見切れてしまいました…:(。
常にalt属性を見せたければ、各々、別の画像で用意した方が良さそうです。
テキストとデザインは分けて考える
そもそも、テキストとアイコンと背景がごっちゃになってるから、置換しなきゃいけなくなってるんだと思うんです。
テキストとデザインを分けちゃえば万事解決な気がします。
画像を用意する
テキストは、「M+ WEB FONTS」というWebフォントを使って表示させようと思います。
「M+ WEB FONTS」については下記サイトを参照のこと。
ここでは、Google Fonts 早期アクセスで提供されている日本語フォントを利用します。
「Google Fonts + 日本語 早期アクセス」については下記サイトを参照のこと(2016年12月追記)。
ボタンの部分はCSSで表示させようと思います。
なので、用意するのはアイコンだけ。
ひとまとめでスライスします。
アイコンはそれぞれサイズがバラバラですが、表示させる範囲を同じにして、規則正しく並べることで、CSSで指定しやすくします。
HTMLを書く
ナビ部分には、アイコンを表示する用に、ボタン名の手前に<i></i>
を用意しときます。
<nav> <ul> <li class="boy"><a href="#"><i></i>男の子</a></li> <li class="girl"><a href="#"><i></i>女の子</a></li> <li class="pickup"><a href="#"><i></i>限定商品</a></li> <li class="shop"><a href="#"><i></i>店舗検索</a></li> <li class="login"><a href="#"><i></i>ログイン</a></li> </ul> </nav>
CSSを書く
<li>
に対して、Webフォントのための指定を書きます。詳しくは「M+ WEB FONTS」のサイトを参照のこと。
nav ul li { font-size: 15px; font-family: mplus-2c-medium; line-height: 1.2; }
<i>
に、アイコンを画像置換でbackground
プロパティを使って背景画像として(2013.2.15訂正)表示。
display: inline-block;
と指定すれば、インラインなのにブロックな要素になるので、width
、height
など指定できつつ横並びになります。
アイコンの位置は、デザインと見比べながら微調整します。
nav ul li i { display: inline-block; width: 26px; height: 26px; margin: -6px 3px 0 0; background: url(../img/nav_ico.png) 0 0 no-repeat; }
ロールオーバー時などの吹き出しもCSSで付けます。
:after
とcontent
を使って、<li>
の後に擬似的に空の要素を作り、その要素にborder
を上手く使って吹き出しっぽい三角形を表示させます。
nav ul li:after { content: " "; position: absolute; top: 100%; left: 50%; width: 0; height: 0; margin: 2px 0 0 -6px; border: 6px transparent solid; } nav ul li:hover:after, #boy nav ul li.boy:after, #girl nav ul li.girl:after, #about nav ul li.pickup:after, #shop nav ul li.shop:after, #login nav ul li.login:after { border-top-color: white; }
下記サイトではブラウザ上で、吹き出しを付けるためのCSSを生成してくれて便利。
クリック時の内側の影はbox-shadow
で。
nav ul li a:active { padding: 13px 0 9px; box-shadow: 0 1px 3px rgba(0,0,0,0.15) inset; }
出来上がり+ちょっとアレンジ
完成するとこうなります。「M+ WEB FONTS」で「舗」の字が出ないのがちょっと残念…。「舗」の字出てます!ごめんなさい!(2012.9.20)
※モダンブラウザ(Firefox 14.0.1、Safari 5.1.7、Chrome 21.0.1180.79、Internet Explorer 9)で表示確認済み。
- HTML
- CSS
- PREVIEW
この方法でも、工夫次第でおもしろい効果を付けることができます。
※Firefox以外では疑似要素へのtransition
が効かないみたいです…(2012年8月現在)。
※Chromeでも効くようになりました(2012年6月現在追記)。
- HTML
- CSS
- PREVIEW
懸念に対しては
「背景画像を印刷しない」状況では下図の通り。
ボタン名がしっかり印刷されるので良しとします。
「CSSがONで画像の表示がOFF」の状況では下図の通り。
アイコンは表示されないものの、ボタンはちゃんと表示されてますね。
まとめ
ということで、いろいろスプライトしてみましたが、画像置換する際の隠しテキストがボトルネックとなりそうです。
別に隠してるつもりが無くても、検索エンジンからは警戒され得るので、そのことを思うと「テキストを隠さない画像置換」を「テキストを必要としない画像だけ(2013.2.15訂正)」にすることが賢明のようです。
テキストを隠さない画像置換テキストを必要としない画像というと、マークとかアイコンの類。グローバルナビをテキストごと画像置換するのは避けた方が良さそうですね…。そういやTwitterもFacebookもボタンのテキストには画像は使ってないもんね。
結論:画像置換CSSスプライトするの(2013.2.15訂正)は「アイコン」とか「マーク」だけ!
テキスト部分は極力システムフォントか、可能ならWebフォントを使う。
でもどうしてもって時だけ画像では画像+altにして、置換的にはScott Kellum Methodがよさげです。
以上、最後まで読んで頂きありがとうございました!参考になれば幸いです。
コメントはまだありません