MOBILE

Build Mobile Websites and Apps for Smart Devices : Style over Substance (part 3) - Images and Pseudo-elements

1/21/2014 1:08:21 AM

3. Images and Pseudo-elements

We’re going to make use of an image sprite for the icons in our application—that is, multiple images combined into a single graphic and then selectively cropped in different contexts. It’s a technique that’s often used in desktop web development, as it means there are fewer images for the browser to download. It may seem counterintuitive, but it’s actually faster for a browser to download a single large image rather than several small images. That’s because browsers can only download a limited number of resources in parallel from any server—so the more images, the longer the queue to download each one.

The usual process of implementing sprites is to create a single image that can then be used as a background image for multiple elements. This background image is then repositioned to show the correct sprite in the correct place. The problem with this approach is that it requires either:

  • large, complex images that have enough padding around each sprite for the space they appear in (to ensure other parts of the sprite don’t bleed in accidentally)

  • unnecessary markup that gives us a hook to crop the sprite tightly in place

We’re going to use a technique that avoids both these pitfalls, instead creating the additional elements we need using content generated in CSS pseudo-elements. This approach also lets us alter the source of the sprite using only CSS, which will come in handy down the track.

Before we tackle the code though, we need an image! The one we’re going to use for our app contains all the icons we need, with extra copies for each additional state we require (in our case we’re only using a selected state for the icons that appear in the tab bar). The image we’ve created is shown in Figure 6, and can be found in the code archive as images/sprite.png.

Figure 6. Our tightly packed sprite image.


We’re going to use the “Stars” tab in our tab bar as the demo for this technique. The first step is to create the pseudo-element that’s going to contain our sprite. Pseudo-elements can be used to insert “fake” elements into our page via CSS, allowing us to apply additional background images without affecting the markup of our page.

There are two pseudo-elements that we can use for the purpose of adding extra images, and the syntax for creating them is similar to what’s used for pseudo-classes like :hover and :visited with which you’re probably already familiar:

  • :before, which can be used to insert content before the selected element

  • :after, which can be used to insert content after the selected element

In the example below, these two selectors would place their respective content before and after each paragraph tag:

p:before {
content: "I'm in front!"
}
p:after {
content: "Bringin' up the rear!"
}

This may look a little odd if you’re unfamiliar with the content property in CSS. It lets us generate additional content—like the text in the example above—using only CSS. Both the above pseudo-elements behave in much the same way, but we’re going to use :after, because the icon will appear after the text in our tab bar element. Let’s create our new element with our sprite image:

listing 11. stylesheets/screen.css (excerpt)
#tab-spots a:after {
content: url(../images/sprite.png);
}

A lesser-known feature of the content property is that it can be used to generate more than just text. If we give it the URL of an image, as we’ve done above, it’ll create an <img> element using that URL as its <src>.

Figure 7 shows what this will look like.

Figure 7. Our sprite image, injected using the content property


“Wonderful,” you say, “but how are we supposed to crop the image?” And again, the answer is with a little CSS magic. We’re going to use the clip property to trim the sprite down to just the bit we want. clip lets us define a rectangle inside the bounds of an element and then crop the content of that element to the rectangle.

Let’s look at a simple example so that you can understand the concept:

.example {
background: #000;
height: 100px;
width: 100px;
}

This code will make a 100×100px box with a black background. By adding a clip property, we can crop its contents without altering the overall size of the element:

.example {
background: #000;
clip: rect(0 50px 50px 0);
height: 100px;
width: 100px;
}

Figure 8 shows a before/after comparison (note the original shape of the div has been included in gray to illustrate the difference).

Figure 8. An example of the clip property in action


The element still thinks it’s 100 pixels wide, but only 50 pixels of its content is visible. You can see how this property can be used to selectively show sections of our sprite image. The four values in the parentheses after clip: rect, in order, are the distance to crop from:

  1. the top edge, measured from the top edge

  2. the right edge, measured from the left edge

  3. the bottom edge, measured from the top edge

  4. the left edge, measured from the left edge

The values are in the same order as you’re probably used to specifying for the margin or padding shorthand properties: top, right, bottom, left. The most important point to remember is that the origin for all crop values is the top-left corner of the element. It’s done this way because the top-left corner is the only anchor point that won’t change relative to the content in the clipped element. At first this will seem counterintuitive, but once you get the hang of the syntax, it does make sense.

For the Spots tab in our tab bar, we want to clip a rectangle that includes only the unselected state for the map pin icon, which works out to be:

  • top: 0 pixels from the top edge

  • right: 18 pixels from the left edge

  • bottom: 33 pixels from the top edge

  • left: 0 pixels from the left edge

So our clip value will be:

listing 12. stylesheets/screen.css (excerpt)
#tab-spots a:after {
content: url(../images/sprite.png);
clip: rect(0 18px 33px 0px);
position: absolute;

}

This gives us the nicely cropped icon we’re after, as Figure 9 shows, though, note that clip only works on elements that are absolutely positioned.

Figure 9. Our sprite image clipped to show only the relevant icon


All that’s left to do now is to wrangle it into the position we want. Pseudo-elements can be manipulated in the exact same way as standard elements in the DOM, so we can position our sprite using regular techniques. In this case, we’re going to use the 50% position and negative margin trick to center the icon in the middle tab:

listing 13. stylesheets/screen.css (excerpt)
#tab-spots a:after {
content: url(../images/sprite.png);
clip: rect(0 18px 33px 0px);
left: 50%;
margin-left: -10px;
top: 1.833em;
position: absolute;
}

Perfect! Figure 10 shows the final appearance of our icon.

Figure 10. Our clipped sprite, now where it should be


One trick with this technique that’s not immediately obvious is that we need to offset our pseudo-element by an additional amount equal to the distance we’re cropping in from the left. This is because clip doesn’t reduce the actual size of the element it’s applied to—so if you’re clipping a rectangle 40px from the left edge of an image, the resulting image will still show up 40px from where you’ve placed the image. To offset this, you’ll need to apply a negative margin to pull the element back and line up the clipped section of the background image.

For the first icon we’ve placed above (the “Spots” icon), this turns out to be unnecessary, since that icon is the first one from the left in our sprite. But if you take a look at the code for the selected state of our “Spots” tab, you can see the additional value. Remember, we’re using the class applied to the containing <ul> to note the selected state:

listing 14. stylesheets/screen.css (excerpt)
.page-spots #tab-spots a:after {
clip: rect(0 37px 33px 18px);
margin-left: −29px;
}

Here we’re cropping an area that’s 19px wide, so we’re using a negative left margin of 18px to wrangle it into the correct position—the last value in the rect function. Because we’re already using a negative margin to center the image (half the total width, 10px), the total negative margin winds up being 28px. Whew!

So that’s it! Now we can roll the same implementation out to all the places we need icons.
Other  
 
Video
PS4 game trailer XBox One game trailer
WiiU game trailer 3ds game trailer
Top 10 Video Game
-   Forza Horizon 2 [X360/XOne] Porsche Expansion
-   NBA Live 16 [PS4/XOne] Cover Athlete
-   Tom Clancy’s The Division | Community Q&A
-   Victor Vran [PC] Race To Release
-   GTA 5 Skyfall Cheat Demo
-   GTA 5 Moon Gravity Cheat Demo
-   GTA 5 Spawn Buzzard Cheat Demo
-   GTA 5 Super Jump Cheat Demo
-   GTA 5 Explosive Melee Attacks Cheat Demo
-   Blood Bowl II [PS4/XOne/PC] Campaign
-   verybody's Gone to the Rapture I Launch Date Announcement
-   Halo 5: Guardians | Master Chief Teaser