CSSスプライトについてあれこれ。

…配信元を読む

CSS Sprites(CSSスプライト)」についてまとめます。
すでにいろんなサイトで紹介されてるので、検索すれば制作手順から何から何までたくさんヒットしますが、それ故、どの方法が一番ベストなのかってのがいまいち不確かだったので、個人的にまとめ直してみることにしました。

以下は目次です。クリックするとその項目へ移動します。

CSSスプライト概要

TwitterやFacebookのスプライト用画像

CSSスプライトは、サイトの読み込みを高速化する目的で使われているCSSの小技です。TwitterやFacebookなどでは、ちっこいアイコンとかマークを表示するのに使われてますね(2012年8月現在)。
具体的には「サイト内で使用するたくさんの画像(パーツ)をなるべく一枚画像にまとめて、サーバへのリクエスト回数を少なくする」という事をやってます。
画像とCSSだけで高速化が実現できるので大変お手軽なのですが、CSSスプライト用の画像を用意するにはちょっとした工夫が必要なので、ご利用は計画的に行わねばなりません。

メリット

  • サイトの読み込みが速くなる
  • スライス設定が楽ちん
  • HTMLがすっきりする(imgフォルダもすっきりする)

デメリット

  • 更新が面倒(サイズが変わった時とか増えた時とか)
  • スライスするまでにちょっとした工夫が必要
  • CSSがbackground-positionで溢れ返る

技術

画像置換と呼ばれるCSSの小技を応用しています。
※ただし、技術を応用してるってだけで、CSSスプライトと画像置換は別物なので注意です(2013.2.15追記)。
画像置換とは「HTML要素の内容を非表示にする替わりに、backgroundプロパティで背景画像を表示させる」というもので、かなり以前から使われていた技です。
方法はいろいろやり方が公開されてるんですが、基本的には「テキストを非表示にしたところに背景画像を忍ばせる」という事をします。詳しくは後述します。

下記サイトではいろいろな画像置換が、年代別に紹介されてます。ちゃんと名前もあるんですね。

CSS Image Replacement

活用場面

読み込みが速くなるからといって、何でもかんでも一枚画像にしちゃえばいい訳ではありません。
サイトに何度も登場する「アイコン」とか「ボタン」とか「マーク」など、汎用的な部分に使うのが良法です。

商品画像とかサイト内にひとつしかないようなタイトル画像には不向き

目次に戻る

どのくらい速くなるのか

速い速いと言うけれど、一体どのくらい速くなるの?→改めてカンタンな実験をしてみました。

実験内容

ここでは、実験用に以下の3ページを用意して、表示速度を測ってみました。

実験用ページ

キャッシュを消去してからリロード

OS:Mac OS X 10.6.8、ブラウザ:Firefox 14.0.1、回線速度:22.8591Mbps(※こちらで計測)という環境のもと、下記手順で計測します。

  1. キャッシュを今すぐ消去する。
  2. リロード(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)

Phark Method

displayは、要素の表示形式を指定するためのプロパティ。詳しくはこちらを参照のこと。
text-indentはテキストの先頭位置を操作する(インデントする)ためのプロパティです。詳しくはこちらを参照のこと。

<a>などのインライン要素にはwidthheightなどが効かないので、display: block;でブロック要素にします。
text-indentの値を-999emというとんでもない数値にして、画面のはるか左方向へズラし見えなくさせます。画面の左方向ならば、要素がはみ出ててもスクロールバーは出ないのです。

p.btn a {
  display: block;
  width: 200px;
  height: 60px;
  background: url(../img/btn.gif);
  text-indent: -999em;
}

オススメしておいて何ですが、この方法だと「無駄に大きなボックスを生成してしまうため、パフォーマンスに影響を与えてしまう」旨がこちらで指摘されてます。そのため、次に紹介する方法が生み出されたそうです。でもコレが一番シンプルでカンタンなので今でもよく使ってますけどねー;)。

text-indentwhite-spaceoverflowを使った画像置換(Scott Kellum Method)

Scott Kellum Method

overflowは要素の領域からはみ出した部分をどう処理するか指定するプロパティ。詳しくはこちらを参照のこと。
white-spaceは、テキストの改行の仕方を指定するプロパティです。詳しくはこちらを参照のこと。

text-indent100%とすると、インデント量はその要素の幅分となります。しかしそれだけだと、テキストが改行して要素の中に残ってしまうので、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;
}

heightpaddingoverflowを使った画像置換(Leahy / Langridge)

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のグローバルナビは、スプライトの例としてはちょとイマイチなので、下図のようなサイトを用意しました。

サンプルサイト

上図のヘッダー部分にあるグローバルナビを例に、制作手順を見ていきます。大まかな手順は下記の通り。

  1. 画像を作る(→Web用に書き出す)
  2. HTMLを書く
  3. CSSを書く

画像を作る

ここがCSSスプライトの肝です。この段階ですでにCSSの事も考えておく必要があります。
とはいえ、まずはデザインデータの中からグローバルナビ部分を持ってきます(下図)。

スライス用のpsdを用意する

通常時ロールオーバー時クリック時そのページの時の4パターンを作るべく、グローバルナビを4つに複製します。

レイヤーグループを4つに複製する

その際、例えば下図のように、キリのいい間隔で並べると、CSSでの指定が楽になります。

グローバルナビを60px間隔で縦に並べる

それぞれの状態に合わせて、デザインを編集します。

それぞれの状態を作る

スライスは超カンタン。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を書く

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で、下図みたく調べる(計る)と手っ取り早いです。

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を書く

今度は背景じゃなくて<img>をズラす

ボタン部分は下記のように指定します。

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>も用意した事で、前述の懸念はある程度改善されました。

ブラウザのデフォルト設定では背景画像が印刷されない

「背景画像を印刷しない」状況でも下図のように、ボタンは印刷されるようになりました:)。

<img>は印刷される

CSSがONで画像の表示がOFFの環境で、何も表示されない

一方「CSSがONで画像の表示がOFF」の状況では下図の通り。
Firefoxでは、alt属性がテキスト同様に表示されるので、見た目はアレですけど、どこへリンクするのかは分かるようになりました。
しかしIE9では、画像の大きさはそのままに、画像の左上部にalt属性が表示されるため、そのページの時ロールオーバー時などで画像が上にズレると、alt属性ごと見切れてしまいました…:(。
常にalt属性を見せたければ、各々、別の画像で用意した方が良さそうです。

IEだとaltが見切れる

目次に戻る

テキストとデザインは分けて考える

そもそも、テキストとアイコンと背景がごっちゃになってるから、置換しなきゃいけなくなってるんだと思うんです。
テキストとデザインを分けちゃえば万事解決な気がします。

画像を用意する

テキストは、「M+ WEB FONTS」というWebフォントを使って表示させようと思います。
「M+ WEB FONTS」については下記サイトを参照のこと。

M+ OUTLINE FONTS | WEB FONTS

ここでは、Google Fonts 早期アクセスで提供されている日本語フォントを利用します。
「Google Fonts + 日本語 早期アクセス」については下記サイトを参照のこと(2016年12月追記)。

Google Fonts + 日本語 早期アクセス

ボタンの部分はCSSで表示させようと思います。
なので、用意するのはアイコンだけ。

テキスト=Webフォント、背景=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;と指定すれば、インラインなのにブロックな要素になるので、widthheightなど指定できつつ横並びになります。
アイコンの位置は、デザインと見比べながら微調整します。

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;
}

borderを使った妙技

ロールオーバー時などの吹き出しもCSSで付けます。
:aftercontentを使って、<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を生成してくれて便利。

cssarrowplease

クリック時の内側の影は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」の状況では下図の通り。
アイコンは表示されないものの、ボタンはちゃんと表示されてますね。

CSSだからしっかり表示

目次に戻る

まとめ

ということで、いろいろスプライトしてみましたが、画像置換する際の隠しテキストがボトルネックとなりそうです。
別に隠してるつもりが無くても、検索エンジンからは警戒され得るので、そのことを思うと「テキストを隠さない画像置換」をテキストを必要としない画像だけ(2013.2.15訂正)」にすることが賢明のようです。
テキストを隠さない画像置換テキストを必要としない画像というと、マークとかアイコンの類。グローバルナビをテキストごと画像置換するのは避けた方が良さそうですね…。そういやTwitterもFacebookもボタンのテキストには画像は使ってないもんね。

結論:画像置換CSSスプライトするの(2013.2.15訂正)は「アイコン」とか「マーク」だけ!

テキスト部分は極力システムフォントか、可能ならWebフォントを使う。
でもどうしてもって時だけ画像では画像+altにして、置換的にはScott Kellum Methodがよさげです。

以上、最後まで読んで頂きありがとうございました!参考になれば幸いです。

Be the first to comment

Leave a Reply

Your email address will not be published.


*