css: set PRE’s max-width so it doesn’t stretch the viewport
[mina86.com.git] / posts / css-sprites-as-background.en.html
blob90c0a3c9ddde5e62b5c717796492fca231afc323
1 <!-- subject: {CSS} sprites as background -->
2 <!-- date: 2013-05-06 02:46:10 -->
3 <!-- tags: html, css, sprites -->
4 <!-- categories: Articles, Techblog -->
6 <p>CSS sprites aren’t anything new. They have been around for years, and are
7 one of the methods to optimise website’s load time. The idea is to
8 incorporate multiple images into one and in this way decrease number of
9 round trips between the server and the browser.
11 <p>In its traditional use, CSS sprites work as a replacement for images and
12 cannot be used as a background. Alas that is exactly what I wanted to do
13 with a quote and flag icons like the following:
15 <figure><img src="/d/sprites-bg-example.webp"
16 style="max-width: 75%; box-shadow: 0.25em 0.25em 0.2em #042559; border-radius: 2em"
17 alt="Example block quote with a quote icon and two paragraphs with flags" /></figure>
19 <blockquote>Update: This website has evolved slightly since 2013. The flags
20 are no longer used (replaced by content negotiation) and quote sprite icon
21 has been replaced by an SVG. While I no longer use this technique, it is
22 of course still valid.</blockquote>
24 <p>After some playing around I’ve finally figured out how to get this working.
25 Even though there are some caveats, sprites can be used as a top-left
26 no-repeat background image as well.
28 <!-- FULL -->
30 <h2>Basics</h2>
32 <p>Let’s start with the basics. Sprites incorporate many images into a bigger
33 one and use CSS to display only what is needed. Take the below image with
34 a roll-over effect for example:
36 <style>
37 #html5 { width: 450px; }
38 #html5, #html5 span { height: 100px; }
39 #html5 span {
40 display: inline-block;
41 text-indent: 100%;
42 overflow: hidden;
43 background-image: url('/d/html5-sprite.jpg');
45 #spriteh {
46 width: 74px;
48 #spritet {
49 width: 52px;
50 background-position: -74px 0;
52 #spritem {
53 width: 90px;
54 background-position: -126px 0;
56 #spritel {
57 width: 96px;
58 background-position: -216px 0;
60 #sprite5 {
61 width: 138px;
62 background-position: -312px 0;
64 #spriteh:hover {
65 background-position: 0 -110px;
67 #spritet:hover {
68 background-position: -74px -110px;
70 #spritem:hover {
71 background-position: -126px -110px;
73 #spritel:hover {
74 background-position: -216px -110px;
76 #sprite5:hover {
77 background-position: -312px -110px;
79 </style>
80 <div id=html5><span id=spriteh>H</span><span id=spritet>T</span><span id=spritem>M</span><span id=spritel>L</span><span id=sprite5>5</span></div>
82 <p>A naïve approach would require ten separate images for this effect.
83 Sprites in tandem with CSS allows this number to go dawn to one. HTML code
84 is simple and unimpressive:
86 <pre>
87 &lt;div id=&quot;html5&quot;&gt;&lt;span
88 id=&quot;spriteh&quot;&gt;H&lt;/span&gt;&lt;span
89 id=&quot;spritet&quot;&gt;T&lt;/span&gt;&lt;span
90 id=&quot;spritem&quot;&gt;M&lt;/span&gt;&lt;span
91 id=&quot;spritel&quot;&gt;L&lt;/span&gt;&lt;span
92 id=&quot;sprite5&quot;&gt;5&lt;/span&gt;&lt;/div&gt;</pre>
94 <p>In the absence of styles it simply spells ‘HTML5’ so if stylesheet fails to
95 load, user can still sees the message. To make a picture out of those spans
96 their dimension must be set, text content hidden and background image added.
97 The following is would be a good start:
99 <pre>
100 #html5 { width: 500px; }
101 #html5, #html5 span { height: 100px; }
103 #html5 span {
104 display: inline-block;
105 text-indent: 100%;
106 overflow: hidden;
107 background-image: url('html5-sprite.jpg');
110 #spriteh { width: 74px; }
111 #spritet { width: 52px; }
112 #spritem { width: 90px; }
113 #spritel { width: 96px; }
114 #sprite5 { width: 188px; }
115 </pre>
117 <p>So far it’s nothing more than regular ‘replace text with a picture’ CSS
118 trick. Because of that, each span would display the image starting with its
119 top-left corner, end result being a rather repetitive image of the letter
120 ‘H’. The missing ingredient is <code>background-position</code> CSS
121 property which instructs the browser to start drawing the background from
122 a different offset. With a bit more styling, the code is almost complete:
124 <pre>
125 #spritet { background-position: -74px 0; }
126 #spritem { background-position: -126px 0; }
127 #spritel { background-position: -216px 0; }
128 #sprite5 { background-position: -312px 0; }</pre>
130 <p>By default, background image spans an infinite plane repeating indefinitely
131 in all directions. Picture’s top-left corner is aligned with element’s
132 top-left corner producing the backdrop for its contents.
134 <figure><img src=/d/html5-sprite-expl alt=""></figure>
136 <p><code>background-position</code> property specifies the origin point of the
137 background image in element’s coordinate system. In other words, it
138 instructs the browser to shift the image by given amounts and than cut
139 whatever overlaps with the element for the background. By setting position
140 to <code>-126px</code>, the 127th pixel of the background image will be
141 aligned with top-left corner of the element.
143 <p>With that knowledge, getting roll-over effect is now trivial:
145 <pre>
146 #spriteh:hover { background-position: 0 -110px; }
147 #spritet:hover { background-position: -74px -110px; }
148 #spritem:hover { background-position: -126px -110px; }
149 #spritel:hover { background-position: -216px -110px; }
150 #sprite5:hover { background-position: -312px -110px; }</pre>
152 <p>Note that the big image has a ten-pixel gap between sprites. It prevents
153 pixels from adjacent sprites ‘bleeding’ over to the other images.
155 <h2>A bit simpler method</h2>
157 <p>The above is the normal method of using sprites. In the presented example,
158 there’s an easier way. Instead of setting background image for each
159 individual span element, it’s simpler to set background for the wrapping div
160 element and cover it with span’s only when they are hovered. CSS code for
161 that looks as follows:
163 <pre>
164 #html5, #html5 span { height: 100px; }
165 #html5 {
166 width: 500px;
167 background-image: url('html5-sprite.jpg');
169 #html5 span {
170 display: inline-block;
171 text-indent: 100%;
172 overflow: hidden;
174 #spriteh {
175 width: 74px;
176 background-position: 0 -110px;
178 #spritet {
179 width: 52px;
180 background-position: -74px -110px;
182 #spritem {
183 width: 90px;
184 background-position: -126px -110px;
186 #spritel {
187 width: 96px;
188 background-position: -216px -110px;
190 #sprite5 {
191 width: 188px;
192 background-position: -312px -110px;
194 #html5 span:hover {
195 background-image: url('html5-sprite.jpg');
196 }</pre>
198 <h2>Using sprites for background</h2>
200 <p>Finally, we arrive at using sprites as background. The trick here is
201 the <code>no-repeat</code> value which causes the background image to be
202 painted only once, for example:
204 <pre>
205 blockquote {
206 background: url('…') no-repeat;
207 padding: 0 26px;
208 min-height: 20px;
209 }</pre>
211 <p>In regular sprites, the interesting part of the image is cropped thanks to
212 the limited size of the element. Trying to use an image with multiple
213 sprites as a background would result in whatever is on the right or bottom
214 of the icon to be shown as well.
216 <p>However, if there’s nothing on the right or below the icon, if it is the
217 last thing in the sprites image, the above is no longer an issue. And
218 with <code>no-repeat</code> value, we instruct the browser not to wrap
219 around the image thus sprites on the left or top of the image aren’t shown
220 either.
222 <p>It becomes apparent that using a staircase-like image with nothing but
223 transparent background below the diagonal, sprites <em>on</em> the diagonal
224 can be used as a top-left no-repeat background icon.
226 <figure><img src=/d/icon-sprites.webp alt="" id=iconsprites></figure>
227 <script src=/d/icon-sprites.js></script>
229 <p>With that knowledge, the following styles can be constructed:
231 <pre>
232 blockquote, .plFlag, .enFlag {
233 background: url('/d/s.png') no-repeat;
235 blockquote {
236 background-position: -100px 0;
237 min-height: 20px;
239 .plFlag {
240 background-position: -50px -50px;
241 text-indent: 22px;
243 .enFlag {
244 background-position: -75px -25px;
245 text-indent: 22px;
246 }</pre>
248 <p>This technique can be extended in certain situations — either when
249 the height or width of the element is specified. In those
250 situations, more icons can be present either on the right or below
251 the sprite used as a background.
253 <p>Certainly limited, but if there are only a few cases when this
254 method needs to be used, it allows more images to be converted
255 into sprites than previously expected. And it’s what enabled me
256 to convert all but one small images on this site to CSS
257 sprites.