CSSスプライト:今も役立つ場面と作り方
CSSスプライトのチュートリアルの大半は、今も2009年と同じ説明から始まります。スプライトはHTTPリクエストを減らすので、サイトを速くするというものです。その議論は、最新サーバーにはもうないHTTP/1.1の制約に基づきます。実際に今も使う根拠になるもの、ならないもの、そして正確な background-position 動くものを作るRetinaの計算。
CSSスプライトの実体
CSSスプライトは、多数の独立した画像を含む1つの画像ファイルと、そのうち1つだけを表示するCSSで構成されます。切り抜き処理もJavaScriptも使いません。要素には固定の width および height、シート全体が次の項目に設定されます background-image、そして background-position 要素の背後でシートを動かし、目的のアイコンを可視ボックス内へ置きます。ボックスは窓、スプライトシートは背後でドラッグする大きな紙です。
初めて使う人がつまずく1つの点は、オフセットが 負の値。窓をアイコンへ動かすのではなく、シートを動かしてアイコンを窓の位置へ合わせます。左上がシートの左端から96ピクセルにあるアイコンを表示するには、シートを96ピクセル左へ動かします。その値は background-position: -96px 0.
具体例として、24×24のアイコン5個を横1列に並べ、アイコン間に8ピクセルの隙間を設け、左から8ピクセル、上から8ピクセルの位置から始めるとします。シートは160×32になります。3番目のアイコンの左端は8 + 24 + 8 + 24 + 8 = 72ピクセル、上端は8ピクセルです。ルールは次のとおりです。
| 宣言 | 機能 |
|---|---|
| background-image: url("/sprite.png") | 要素の背後にシート全体を読み込みます |
| background-repeat: no-repeat | シートの繰り返し表示と、端に他のアイコンが現れるのを防ぐ |
| background-position: -72px -8px | シートを左に72px、上に8px移動し、3番目のアイコンを原点に置く |
| width: 24px; height: 24px | 表示枠をちょうど1つのアイコンに限定し、残りを隠します |
| display: inline-block | spanのようなインライン要素で、幅と高さを適用できるようにします |
次の2つの数値以外は変更しないでください background-position 同じファイルから別のアイコンが得られます。仕組みはそれだけです。

率直に言うと、HTTP/2で計算が変わった
CSSスプライトは、特定の制限を解消するために考案されました。HTTP/1.1では、ブラウザーはオリジンごとに少数、通常6本のTCP接続を開き、各接続では一度に1つのリクエストしか扱えませんでした。40個のアイコンファイルは、6本の経路を通る40件のリクエスト待ちを意味し、それぞれに接続の確立や往復の待ち時間がかかりました。1ファイルにまとめると、順次処理する40件のリクエストを1件にできました。遅延の大きい接続では、これは微小な最適化ではなく、ページで得られる最大の改善であることがよくありました。
HTTP/2は多重化します。多数のリクエストが1つの接続を同時に共有し、HTTP層での先頭待ちによるブロックがなく、ヘッダーもリクエスト間で圧縮されます。スプライトが解決するために考案された特定の問題は、ほぼ消えました。2026年のチュートリアルが、リクエスト数を減らすからスプライトは速いとだけ説明するなら、それはもう存在しないウェブを前提に書かれています。
では、なぜこの記事があるのでしょうか。「元の理由がなくなった」と「理由がまったくない」は同じではないからです。多重化しても残る実際の利点がいくつかあり、判断すべきなのは次の点です:
- リクエストごとの負荷はゼロにはなりません。 多重化したリクエストは低コストですが、無料ではありません。それぞれヘッダー、キャッシュ検索、ブラウザーのリソーススケジューラーの枠を使います。5アイコンなら誤差程度ですが、200個の小さいアイコンなら測定でき、スプライトはそれを1項目へまとめます。
- キャッシュ管理が簡単になります。 1ファイルならキャッシュ項目1つ、1つの Cache-Control 方針と、無効化するハッシュ付きファイル名は1つです。個別に版管理する200アイコンは、管理が必要なビルド・デプロイ対象になりますが、通常は管理されていません。
- 一括のバージョン管理。 すべてのアイコンが一緒に配信されるため、3個だけが古いデザインのキャッシュから読み込まれ、残りは新しいといった、半端に更新されたアイコンセットにはなりません。デザインシステムの導入では、単なる整理上の利点ではなく、正しさを保証する実質的な性質です。
- アイコン欠落のちらつきがない。 シートを読み込むと、すべてのアイコンが瞬時に使えます。個別読み込みではリクエストの完了ごとに1つずつ現れ、初期表示領域のツールバーやアイコングリッドという、最も邪魔な場所で目立ちます。
- 遅延のない状態切り替え。 同じシートの別の場所にあるホバー状態やアクティブ状態の画像は、すでにダウンロードされています。個別のファイルでは、ホバー画像は最初のホバーまで取得されないことが多く、初めてボタンに触れた際に空白がちらつきます。これは以前からスプライトを使う強い理由の1つであり、HTTP/2でもこの利点は弱まりません。
CSSスプライトを使わない方がよい場合
信頼できる推奨には、適さない場合も含める必要があります。CSSスプライトには、そのような場合がいくつかあります。
- 単色で拡大縮小するアイコンにはSVGが適しています。 インラインの <svg> または次から作るSVGスプライト <symbol> および <use> Retinaの計算なしで任意の寸法へ拡大縮小し、色を次から継承: currentColor。一般的なUIアイコンセットには、こちらのほうが適しており、スプライトシートがアイコンシステムでのシェアの大半を徐々に失ったのも、このためです。
- アイコン1つにスプライトは不要です。 画像が1つだけなら、その画像を使ってください。スプライトは、何の利点もなく、座標管理とビルド手順を増やします。
- 個別に変わるアイコン。 一括のバージョン管理には両面があります。1つのアイコンが変わるだけでシート全体のキャッシュが無効になり、全ユーザーがすべてを再ダウンロードします。頻繁に変わるアイコンセットはスプライトに適しません。
- 色変更やテーマ対応が必要なもの。 ラスター形式のスプライトには色が固定されています。ダークモード、ブランド別テーマ、状態ごとの色には、別のシートか壊れやすいフィルターの工夫が必要です。これはSVGの最も明確な構造上の利点です。
- フォントサイズに応じて変わる必要があるアイコン。 スプライトはピクセル寸法に固定されます。アイコンを次に合わせて拡大縮小する必要があるなら em 文字と一緒に変えるなら、ベクター方式は対応できますが、スプライトでは苦労します。
スプライト、SVGスプライト、インラインSVG、アイコンフォント
| 方法 | きれいに拡大縮小 | CSSで色変更 | リクエスト | キャッシュ | アクセシビリティ |
|---|---|---|---|---|---|
| CSSスプライト(ラスター) | いいえ — 固定ピクセルなので2xのシートが必要 | いいえ、フィルターによる工夫のみ | 全アイコンに1つ | 優秀、長期間使う1ファイル | 背景画像は支援技術から見えないため、テキストラベルが必要 |
| SVGスプライト(symbol + use) | はい、任意のサイズ | はい、currentColorで指定 | 全アイコンに1つ | 優秀、1ファイル | DOM内にあり、titleとARIAに対応 |
| アイコンごとのインラインSVG | はい、任意のサイズ | はい、CSSで完全に制御 | ゼロ。HTMLに埋め込み | なし — 各ページで再送信 | 最良、完全にDOM内に存在 |
| アイコンフォント | はい、font-sizeに追従 | はい、colorで指定 | 1つのフォントファイル | 良好 | 低い — 字形が読み上げられたり、代替フォントに置き換わる |
表は点数表でなく判断として読んでください。旗、ロゴ、イラストのバッジ、アプリアイコンのスクリーンショットなど、多色のラスター画像は合理的にSVGスプライトにはできません。その領域ではCSSスプライトは古い方法でなく、今も正しい答えです。
1つを作成する
手順は4段階で、手作業で細かく調整する必要があるのは3段階目だけです。
- アイコンを集めてください。 最終表示サイズ、またはその2倍で書き出してください。Retinaの節を参照できます。できるだけサイズを揃えると、均一なグリッドによって座標計算が簡単になり、CSSも規則的になります。
- 1枚のシートへ配置してください。 単純な1列配置や固定グリッドは把握しやすく、サイズが異なる場合はパッカーのほうが空間を効率よく使えます。アイコン間には隙間を残してください。
- 座標を読み取ってください。 各アイコンには、シートのピクセル単位でx、y、幅、高さが必要です。画像エディターで手作業で測るのが、スプライト作業で問題が起こるところです。1ピクセルの読み違いで隣のアイコンの細い一部が現れ、理由を教えてくれるものはありません。
- CSSを書く。 共有の基本ルール1つと、各アイコンの小さいルール1つ。
共通の基本ルールは見た目以上に重要です。すべてのアイコンには同じものが必要です: background-image, background-repeat: no-repeat、そして display: inline-block。次を繰り返すと background-image 50個のルールにURLがあっても、ブラウザーは1回だけ取得するため性能上の問題ではありません。しかし保守の問題です。キャッシュ更新でシート名を変えると、1か所ではなく50か所の編集が必要になります。そこで、共有事項は基本クラス、位置とサイズだけをアイコン別クラスに置きます:
| ルール | 目次 |
|---|---|
| .sprite | background-image, background-repeat: no-repeat, display: inline-block |
| .sprite-search | background-position: -8px -8px; width: 24px; height: 24px |
| .sprite-settings | background-position: -40px -8px; width: 24px; height: 24px |
マークアップでは、次のようになります <span class="sprite sprite-search"></span>。Sassでは、同じ考え方を通常はプレースホルダーとして表現します。 %sprite-base、各アイコンのルールには次を使って取り込みます @extend — コンパイル出力では宣言を繰り返さず1つのグループ化セレクターになり、マークアップはアイコンごとに1クラスで済みます。
RetinaとHiDPI:background-sizeの工夫
大半のガイドが省略または誤解する部分です。1xで描いたラスタースプライトは2x画面でぼやけます。対策はオフセット変更ではなく、2倍解像度で詰め、CSSに実寸の半分のシートサイズを伝えることです。
先ほどの例で、書き出し時にすべてを2倍にします。アイコンは実ピクセルで48×48、隙間は16、シートは実ピクセルで320×64になります。次に設定するのは background-size: 160px 32px — 実際のシート寸法の正確な半分。ブラウザーは全シートを160×32 CSSピクセルの座標空間へ縮小し、HiDPI画面では追加の細部を端末の物理ピクセルへ対応づけます。
利点は、CSSのほかの数値すべてを元の1x座標系のままにできることです。3番目のアイコンは引き続き background-position: -72px -8px と width: 24px; height: 24px、変更せず使えます。ファイル内の実際のピクセル位置が144, 16であっても同様です。オフセットはCSSピクセル単位で1回だけ計算し、1つの background-size 基本クラスの宣言で、全アイコンの密度の対応づけを一度に行います。
ここから導かれる2点を明示しておきます。まず、 background-size アイコンごとに繰り返さず共通の基本ルールに置きます。次に、強い理由がなければメディアクエリで2シートを配信しないでください。2xシートを半分で表示すると1x画面でも正しく見えるため、ファイルが大きくなる代わりに、1シート・1ルールで両方をカバーできます。十分圧縮したアイコンシートは通常小さいので、トレードオフに価値があります。また1.5や2.5などの小数の端末ピクセル比という中間領域も回避できます。メディアクエリの切り替えはどちらかを選ぶ必要があり、一方が不適切になるからです。
ホバーと状態のバリエーション
対話的な要素の典型的なスプライト配置は、列ごとにアイコン、行ごとに状態を置くグリッドです。1行目が通常、2行目がホバー、3行目が押下または無効です。列は移動しないので、状態変化はY方向だけの移動になり、Xオフセットはそのままです。
24pxアイコンと8pxの隙間なら、1行目はy = 8、2行目はy = 40です。3番目のアイコンの通常ルールは background-position: -72px -8px ホバーのルールは background-position: -72px -40px。縦方向の間隔を変数として設定すると、例えば --sprite-row: 32px — 基本クラス上でホバーを一度だけ表すには、 calc() アイコンごとのX変数と組み合わせ、各アイコンに2つ目のルールを書かずに済みます。
ここではスプライトが実際に個別ファイルより優れています。通常状態とともにホバーのピクセルも届いているので、初回ホバーが即座です。状態を別々にせず同じシートに置くべき理由でもあります。別のホバーシートにすると、初回ホバー時のリクエストによるちらつきという、配置が防ごうとした問題が戻ります。
余白と、ゲームよりにじみが弱い理由
画像を隣接して詰めると、サンプラーがアイコンの境界を越え、隣の色を取り込む可能性があります。ゲームエンジンでは、縮小、ミップマップ、フィルタリング、任意のサブピクセル変形で描くため、常にある既知の問題です。
CSSスプライトが使われる環境は、より穏やかです。ミップマップはなく、背景は通常、整数の位置で合成され、変形なしの正確な1:1なら、サンプリングは境界をまったく越えません。ただし「穏やか」は「起こらない」ではなく、問題が起こる場所は予測できます。端末のピクセル比が1.5、2.25、3のような整数でない値の場合、ブラウザーのページズームが半端な割合の場合、そして任意の transform: scale() 親要素に設定し、 background-size Retinaの節の縮小などです。いずれもアイコンの縁を端末ピクセルの非整数境界へ置くため、合成処理が補間する必要があります。
対策は同じで、低コストです。すべてのアイコンの間とシートの外周に、1xで2〜4ピクセル、2xシートなら4〜8ピクセルの小さな透明な隙間を残します。ファイルサイズの増加はわずかで、「このアイコンの左に薄い線があり、自分のノートPCでだけ出る」という種類の不具合報告を一掃できます。押し出し、ミップマップのにじみ、2の累乗の寸法という、より深い問題のゲームエンジン版はこちら: スプライトアトラスのパッキング:余白、2の累乗、色のにじみ.
よくある失敗のパターン
- 端に隣のアイコンが見える。 要素のボックスがアイコンより大きく、窓からその先のシート内容が見えています。幅・高さが間違っているか、要素のpaddingがボックスを広げています。次を確認してください: box-sizing: border-box 使われているか確認してください。宣言した幅に含まれるものが変わるからです。
- background-repeat: no-repeatの指定忘れ。 標準設定は repeat、そのためシートが繰り返され、他のアイコンの断片が枠内に現れます。アイコンがシートの原点近くにあり、ほぼ正しく見える場合は、この問題を見落としがちです。
- 寸法のないspan。 インライン要素は次を無視します width および height 完全に。そのため空の <span> スプライトクラス付きでは寸法がゼロになり、何も表示されません。設定するのは display: inline-block (または block、あるいはflexアイテムにする)を基本クラスに指定します。
- 再構築後も古いシートが残る。 スプライトを詰め直すと座標が変わりますが、再訪問者は新しいCSSに対して古いシートをキャッシュしている場合があり、自分には完璧でも相手には全アイコンが微妙に違います。再構築ごとにファイル名を変更してキャッシュを更新します(内容のハッシュ、 sprite.a1b2c3.png)を使い、クエリ文字列やユーザーによる強制再読み込みには頼らないようにします。
- 半ピクセルのオフセット。 2xシートのアイコンサイズ、隙間、開始余白が奇数なら、半分にした後のオフセットが端数になり、それが端のにじみを起こします。2xシートのすべての寸法を偶数にしてください。
よくある質問
2026年にCSSスプライトは時代遅れですか?
元の根拠は時代遅れですが、技術はそうではありません。HTTP/2とHTTP/3では、リクエスト数の削減だけでは有力な理由になりません。残るのは、多くの小さなラスター画像を持つアイコンセットの用途です。1つのキャッシュ項目、不可分なバージョン管理、個別アイコンの読み込み時の出現を防げること、即座のホバー状態が今も利点になります。ただし、単色UIアイコンではSVGが実際にラスタースプライトを置き換えています。2026年にその用途で使うのは、より悪いツールを選ぶことです。
CSSスプライトとSVGスプライト、どちらを使うべきですか?
配信方法ではなく、画像の性質で決めてください。アイコンがフラット、幾何学的、単色または2色なら、SVGスプライトを使います。2xのシートなしで拡大縮小でき、次で色を変えられます currentColor、支援技術からアクセスできるDOM内に存在します。画像が写真、グラデーションの多いもの、または旗・商品のサムネイル・プラットフォームのロゴ・ピクセルアートのような実際に多色のラスター画像なら、SVGに利点はなく、ラスター形式のCSSスプライトが適切です。
シートはどれほど大きくなると問題になりますか?
2つの別の上限があります。実用上、シートは全アイコンの描画を妨げるため、初回描画を遅らせるほど大きいなら利点がなくなっています。数百KBの前半程度に抑え、管理画面や設定パネルなど使用頻度の低いアイコンは、必要な場所だけで読む2枚目に分けてください。技術上、ブラウザーやモバイル端末にはデコード画像寸法とキャンバスメモリの上限があり、巨大シートは低メモリのスマートフォンで警告なく縮小されたりデコードに失敗したりします。各辺が約2000ピクセルを超えるなら、それだけでも分ける価値があります。2xシートはすでに設計寸法の2倍であることも忘れないでください。
CSSでスプライトアイコンの色を変えられますか?
本当の意味ではできません。便利な答えではなく正直な答えです。色はラスターのファイルに固定されています。次で近似はできます filter — 連鎖 invert, sepia, saturate、そして hue-rotate 黒アイコンを目標の色相へ近づけます。ただし文字どおりの場当たり的な方法です。値は試行や計算で見つけ、正確なブランド色にはならず、多色の絵では壊れ、次の担当者には読めません。別色の2枚目のシートの方がフィルター連鎖より素直です。再着色が本当の要件なら、それはSVGを使うべきということです。
アイコンをスプライトシートへ配置
Sprite Genはドロップした画像を指定した余白で1枚のシートに詰め、座標を書き出します。キャンバスからオフセットを手で読み取る必要はありません。CSSを選ぶと、共通の基本ルールに次のものが含まれます: background-image, background-repeat: no-repeat および display: inline-block、さらに各スプライトの負の値を指定するルールを1つずつ追加します background-position ピクセル幅・高さも含み、上記の構造そのものです。SCSSを選ぶと同じ出力が次として得られます: %sprite-base プレースホルダーを、各アイコンのルールが次から取り込みます: @extend。JSONを選ぶと、独自のツールに渡すためのフレーム座標の生データが得られます。処理はすべてブラウザー内で行われ、どこにもアップロードされません。
スプライトシート生成を開く