Chapter 9. CSS3: Modules, Models, and Images

Unlike CSS2, CSS3 has been divided into a set of modules. By splitting CSS3 into modules, the W3C has been able to work on different modules at different speeds, with some modules already at the Recommendation level, and others moving toward final recommendations at a slower pace.

There are over 20 modules in the CSS3 specifications, with each module deserving its own chapter (or two, or three). Unfortunately, we can’t cover all of them. In this chapter, we will cover the CSS topics that are relevant in creating the look and feel of CubeeDoo. To include a few more properties, we will also look at re-creating the look and feel of the native iPhone settings screen.

The native iPhone application look and feel can be done with simple CSS. With border-radius, background properties, gradients, text-shadow, box-shadow, background-size, text-overflow, and some older, well-known and supported CSS 2.1 and earlier properties, we can create the look of an iPhone. We’ll then apply those features to CubeeDoo.

No images means we can create the native iPhone application look without requiring extra HTTP requests and without needing a graphics program. Because we’re using CSS, should your project manager choose to alter the color scheme of your application, you can do so without having to open up an image-editing program.

CSS3 allows for creating websites with less code and fewer images than you may be accustomed to, and fewer images mean fewer HTTP requests, which improves performance. CSS3 also allows for multiple background images on a single element, which may help reduce the number of DOM nodes required to create the look and feel of your site. Reducing the number of DOM nodes improves performance, especially when it comes to page reflows and memory consumption.

Some of the CSS3 features are new. Others have been supported for years. One thing to note, however, is that just because using some of these features can save on the number of HTTP requests, using CSS3 instead of images is not always the best solution. You have to weigh the pros of maintenance and HTTP requests saved against the time needed for the browser to calculate and draw CSS graphics and the memory limitations of mobile devices. Some CSS effects, like very large radial gradients, use up memory, and can make the browser sluggish if you request a device to draw repeatedly to the memory.

Less code can mean both better performance and easier site maintenance. However, not all CSS features perform well on devices with limited memory. When we cover “risky” CSS topics, I will explain any performance ramifications of that feature, and how to avoid slowing down or crashing the user’s browser.

As some CSS3 and HTML5 features can have performance drawbacks, I won’t leave images out completely. We’ll cover multiple background images and border-image, two features that use images to quickly and easily create buttons and backgrounds in Chapter 11.

We will not cover the speech, paged media, or ruby modules since they are for aural readers, printers, and Asian language sites, respectively.[57] We will cover transforms, transitions, and animations in the next chapter.

Although it predates CSS3, we will spend time on the CSS box model. The CSS box model is the basis of web page layout since CSS’s inception, so it’s important to fully grasp.

CSS Box Model Properties

Before we jump into the new CSS3 properties and values, it is important to understand the CSS box model and the properties that make up the box model. All modern mobile browsers support and have always supported border, margin, and padding properties:

The border-width property sets the width of an element’s four borders. The keyword values of the border-width property include thin, medium, thick, and inherit, with the default value being medium. You can also use any of the length values (px, em, etc.) described in Chapter 8.

To make the border for the triangle in Figure 9-1, we created a 15 px thick border on generated content that has no height or width. We made only the top border visible by declaring the top border green and all others as transparent.

The border-width property is important to the border-image property: if you don’t declare the border-width portion of the border-image shorthand, the border-image will inherit the border-width values.

Border width is also important to understand in terms of shadows and inset shadows. You can create some interesting effects using thick borders and shadows: we’ll cover that later in this chapter.

All HTML elements are drawn to the screen as a rectangular box. The CSS box model defines the rectangle (or box) made up of margins, border, padding, and content that make up every element. The box model allows us to space out our elements across the page, defining the width of the content, the space between the content of an element and its border, and the space between multiple elements.

As shown in Figure 9-2, the components of the box model include content, padding, border, and margin:

Content

The content of the box, where text and images appear.

Padding

The padding is the space between the content and the border. If an element has a background image or color, the padding area will have that color or image as a background by default.[58]

Border

The border surrounds the padding and is inside the margin: the border starts where the padding ends. They do not overlap. The border takes up space in the box model. If the element has a background image or color, and the border is dashed or otherwise fully or partially transparent, by default the background will show through the border.

Margin

The margin is the area on the outside of the border. The margin is transparent. If the element has a background color or image, it will be contained within the border, and will not show through the margin area.

In order to set the width and height of an element, you need to fully understand how the box model works. In the traditional box model, the declared width and height given to an element is for the content area only. The width and height you declare include the padding and borders.

Note in Figure 9-2 that the margin area is colorless. That is because the width is not included in the width and height calculations of the width and height properties. The margin, if greater than 0, does take up space.

The size the element occupies in your layout is the content, the padding, border, and margin.[59]

Note

Total width = left margin + left border + left padding + width + right padding + right border + right margin

Total height = top margin + top border + top padding + height + bottom padding + bottom border + bottom margin

Now that the box model is out of the way, we can jump into some very useful CSS3 features. We’re going to start by creating the look and feel of the native iPhone app with CSS only. The HTML for our examples is:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8"/>
  <title>iPhone Look and feel</title>
  <meta name="viewport" content="width=device-width; initial-scale=1.0;"/>
</head>
<body>
<header>
 <nav>
   <ul>
     <li class="button cancel">Cancel</li>
     <li class="button done">Done</li>
   </ul>
   <h1>Languages</h1>
</nav>
</header>
<article>
   <ul>
       <li lang="en-us">English</li>
       <li lang="fr-fr">Français</li>
       <li lang="es-es">Español</li>
     ...
   </ul>
</article>
</body>
</html>

With this bit of HTML code, and some CSS, we’ll be creating a web page that looks like the languages system preferences on the iPhone, as shown in Figure 9-3.

New in CSS3, but not really new to mobile browsers, is native rounded corners created with the border-radius property. Border radius is a quick, lightweight way to improve your UI without increasing download time.

Prior to being able to create native rounded corners with the CSS border-radius property, web developers created rounded corners by including extra markup. The four-corner method required four added elements, each with background images, absolutely positioned in each of an element’s four corners. The sliding door method, the most popular of the methods, also required images and additional markup.

While all these methods created the look of native rounded corners, they required otherwise unnecessary elements be added to the page as hooks and containers for required background images, and they required the creation and download of images. For performance, we want to minimize the number of HTTP requests, download time, bandwidth usage, and DOM nodes. We also can enjoy saving the effort of creating images, and then re-creating them as border radius length, color scheme, or other design changes are made.

The biggest drawback of the image solutions for developers were that any changes made to border color, background color, containing element background color, roundness, or dimensions would require new images to be made. The drawback for users (and therefore the corporate bottom line) is that image methods slow down the download speed and impact the performance on handheld devices with limited memory. The border-radius property enables developers to include rounded corners natively: no more corner images! Another bonus is no pixelation on zoom!

The border-radius property is shorthand for:

border-top-right-radius:
border-bottom-right-radius:
border-bottom-left-radius:
border-top-left-radius:

... in that order.

The syntax for the border-radius property is:

border-radius: length{1,4} / length{1,4}    /* shorthand */
border-(top|bottom)-(left|right)-radius: length length    /* longhand */

In the shorthand, the values before the slash, if a slash is included, are the horizontal radii, and the values after the slash are the vertical radii, as shown in Figure 9-4. For the longhand values, each can take two values: the first value is the horizontal radius, the second, if present, is the vertical radius. No slash is included in the longhand syntax. If only one value is present, the vertical radius will be the same as the horizontal radius, creating a symmetrical rounded corner with the second value copied from the first. If either length is zero, the corner is square, not round.

When four values are given, the order of the corners is topleft, topright, bottomright, bottomleft. Similar to TRBL of borders, if you only declare two values, then the first is for topleft/bottomright, and the second value is for topright/bottomleft.

If targeting a radius value with the DOM, the values are:

myObj.style.borderTopLeftRadius
myObj.style.borderTopRightRadius
myObj.style.borderBottomRightRadius
myObj.style.borderBottomLeftRadius

Note that the dash (-) has a meaning in JavaScript. We don’t want to subtract radius from left from top from border, which is what border-top-left-radius would do. In JavaScript, the CSS property is camelCased when included as a property of the style property of the DOM node. That being said, to keep presentation and behavior separate, avoid using JavaScript to alter CSS property values. Instead, define your styles in stylesheet style blocks, and use JavaScript to change class or state.

border-radius for native-looking buttons on the iPhone and in CubeeDoo

Let’s put our knowledge to work! Native iPhone applications, as shown earlier in Figure 9-3, have several elements with rounded corners: the Cancel and Done buttons, and the main content of the page. By relying on CSS3, we can create these rounded elements of any size, color, and edge radius without any images. Let’s focus on the CSS for the content area:

1     article ul {
2       border: 1px solid #A8ABAE;
3       border-radius: 10px;
4       background-color: #FFFFFF;
5       width: 300px;
6       margin: 10px auto;
7     }

Line 2 of our CSS creates a 1 px gray border. In line 3, we include the border-radius property to add a 10 px radius to our border. If you are supporting Android 2.1 or iOS 3.2, you can include a -webkit- prefixed property immediately before the prefixless property. You may not want to do this: older versions of Android didn’t handle border radius very well: omitting the prefixed border radius saves a bit of memory in these older, less performant browsers, and avoids the bug where scrolling makes the border-radius temporarily disappear. Your application will look a little different in older versions of Android, with no rounded corners. Your users likely won’t notice the missing rounded corners, but they’ll notice if your application fails. For that reason, I stopped including the prefixes for rounded corners a long time ago.

In line 6, we employed the margin shorthand property to move the unordered list 10 px down from the navigation and center the content area by setting the left and right margins to auto. Since two values for margin were declared, the first value is for Top and Bottom, and the second value is for Left and Right. (Confused? See Avoiding TRouBLe: Shorthand Properties and Value Declarations in Chapter 8.)

We’ve also stylized the list items. The CSS for the list items is:

8     article ul {
9      list-style-type: none;
10     }
11     article li {
12      line-height: 44px;
13      border-bottom: 1px solid #A8ABAE;
14      padding: 0 10px;
15     }
16     article li:last-of-type {
17      border-bottom: none;
18     }

Most of that CSS should be very familiar to you. Note the :last-of-type pseudoclass in line 16. Structural selectors were covered in Chapter 7. With article li:last-of-type {border-bottom: none;}, we are able to tell the browser to not include a bottom border on the last list item in every <ul> that is found in an <article>.

I did not use article li:not(:last-of-type) to set the borders on all of the elements except the last one, but I could have. We have a border bottom on the <ul> itself, if we don’t omit the border bottom from the last <li>, there will be a double border at the bottom of each unordered list.

What else has rounded corners? Feel free to add rounded corners to the Done and Cancel buttons on the iPhone app and the buttons and cards on CubeeDoo. The code for the rounded corners is in the chapter files.

Using the border radius property, we were able to create rounded corners, and create the look and feel of the content area of the native application with no added HTML elements, no images, and very few lines of CSS.

In addition to having rounded corners, the Cancel and Done buttons have gradient backgrounds. We could include a separate background image on the Cancel button, and a background image of a different color for the Done button. With a small border-radius we could create a close approximation of the native iPhone buttons. We could then throw another background image in for the back of the navigation, and another background image for the background of the whole page. In fact, the iPhone has a PNG sprite with the buttons on it that the native iOS applications all use as border images. Yes, we can do that, too. However, with CSS3, this plethora of images, even a single sprite, and their additional HTTP requests, are unnecessary. The native iOS application layout can actually be done completely with CSS, without importing a single .svg, .gif, .webp, .png, or .jpeg.

Gradients are not properties, they are values. Gradients can be used anywhere an image can be, including background-images, list-style-images, and border-images. They are supported in all modern browsers, but with varying prefixed syntaxes in older browsers.

A gradient is a CSS image generated by the browser based on developer-defined colors fading from one color to the next. Browsers support linear and radial, and repeating linear and radial gradients. Conical gradients have been added to CSS Images Level 4, but are not ready for production and aren’t covered here.

We’ll cover the current vendor-prefixed syntax supported in all browsers but only now necessary in WebKit browsers. The prefixless version of CSS gradients has gained support and should be included as the default value in your CSS. We still need to cover the prefixed syntax and the very old WebKit syntax so we can support older mobile devices.

The syntax for the prefixed linear gradient is:

-prefix-linear-gradient(<angle|keyterm>, <colorstop>, [<colorstop>,] <colorstop>)

Where angle is the angle in degrees of the gradient path or a key term combination of top or bottom and/or left or right indicating the starting point of the gradient line and, at minimum, one declared color, with optionally more colors, and optionally the position of said colors along the gradient path.

The syntax for the final linear gradient syntax is:

linear-gradient([<angle>| to <keyword>], <colorstop>, [<colorstop>,] <colorstop>)

The main differences are (1) the to keyword, (2) how the keyword’s gradient angles work, and (3) the value of the angles.

With the prefixed syntax, we indicated where the gradient line came from. The prefixless keyword syntax includes the word to, indicating where the line is heading to, rather than where it came from.

Prefixed, the gradient line starts at the point that would be the end of the hypotenuse of a right triangle whose other two points were the midpoint of the box, and the closest corner.

The simplified unprefixed syntax added the to keyword to differentiate it: now the gradient line goes from one side or corner to the opposite side or corner, passing through the center of the box. Confused? Don’t worry. I’ll explain.

For linear gradients, the direction of the fade is controlled in the CSS by the <angle> or key term. You can include a keyterm or value for the angle in degrees, rads, grads, or turns.

When an angle is given, the gradient path passes through the center point of your background image at the angle specified. There is a difference between prefixed and unprefixed syntax, however. The prefixed syntax angles go in the direction of the angle, and 0 deg is up, with 90 deg to the right. The prefixed angles go counterclockwise, and 0 deg is to the right. See Figure 9-5.

When declaring linear gradients, first include the prefixed versions for older browsers. When prefixed, 0 deg is to the right, and angles go counterclockwise, which may be counterintuitive. The last CSS property/value pair should be the default value without a prefix. When no prefix is present, 0 deg points upward or north, and positive angles increase as you rotate clockwise, with 90 deg pointing to the right. The gradient line passes through the center of the element at the angle specified.

Instead of using angles, you can declare the gradient path using key terms. The keywords are the sides top, bottom, left, or right, or corners such as top left or bottom right.

In the prefixed syntax, the key term is the starting point of the gradient path. The gradient will start in the general area described by the key term, passing through the center of the background-image, to the opposing side or corner area. For example, top will head from the top center point to the bottom center point, passing through the center point of the element box, and top right will start in the top right area, passing through the center point, to the bottom left area, though not necessarily to the corner itself.

The default CSS declaration should include the value, with no prefix, prepended with the word to (when using keywords), indicating where the gradient line should end. The gradient line will start from one side or corner, pass through the midpoint, and end at the side or corner indicated by the key term. For example, to top will head from the bottom center point to the top center point passing through the center point of the element box, and to top right will start in the bottom left corner, passing through the center point, to the top right corner.

The default values are top, or 270 deg for the prefixed version, and to bottom or 180 deg in the final, unprefixed version, which have the exact same appearance, as shown in Figure 9-6. To make the gradient fade from black to white as it goes from top to bottom, you could write any of the following:

/* prefixed */
-webkit-linear-gradient(#000000, #FFFFFF);
-webkit-linear-gradient(top, #000000, #FFFFFF);
-webkit-linear-gradient(270deg, #000000, #FFFFFF);

/* un-prefixed */
linear-gradient(#000000, #FFFFFF);
linear-gradient(to bottom, #000000, #FFFFFF);
linear-gradient(180deg, #000000, #FFFFFF);

To create a gradient that has a path that goes from the top left to the bottom right, as shown in Figure 9-7, you could write the following two lines, which are similar but not identical:

/* top left to bottom right */
-webkit-linear-gradient( top left, #000000, #FFFFFF);
linear-gradient(to bottom right, #000000, #FFFFFF);

Note that while prefixed 315deg or unprefixed 135deg will create the same gradient as each other, those gradients are not the same as top left or to bottom right, which are not identical to each other.

Prefixed 315deg and unprefixed 135deg will go from top left or to bottom right if your element is a square, but only the to bottom right will go from corner to corner if your background image is an elongated rectangle. Why? When an angle is used, the gradient path passes through the center of the defined background image at that angle, no matter the aspect ratio of the box.

With the final syntax, or nonprefixed syntax, the key terms define that the gradient path ends in one corner after passing through the center of the background. With the prefixed or older syntax, when not a square, the gradient path extends beyond the edges of your rectangle, allowing for the start and end of your gradient to be displayed. So when prefixed, it only goes from corner to corner in this case if our box is an actual square, as shown in Figure 9-8:

background-image: -webkit-linear-gradient(top left, #000000, #FFFFFF);
background-image: linear-gradient(bottom right, #000000, #FFFFFF);

In Figure 9-8, the black line shows the gradient path for top left, passing through the center of the background image, represented by a tiny white square in the middle. The gray path is the path the gradient takes when it supports the un-prefixed gradient with to bottom right instead of top left. In the prefixed version, the end points of the gradient path are beyond the boundary of the image, defined as the farthest corners of the box from the center of the background image where a line drawn perpendicular to the gradient-line would intersect the corner of the box in that direction.

Now that we know how to declare the angle of the gradient, we can include the colors of the gradient. We created a linear gradient that faded from black to white along the entire path of the gradient. To continue with our very exciting black to white example, what if we wanted to fade from solid color to solid color, displaying the solid colors on both ends for about 10% of the width of the element? That is easy to do by defining color stops.

To declare a color stop, declare the color and the position of the stop. If your first stop is not at 0%, the first color will be solid from 0% or 0 px until the first color stop, where it will begin to fade into the next color. The same is true if your last color stop is not at 100%.

If you don’t declare the position of the color stop (you just declare the color), the color stops will be evenly distributed across the full width (or height) of the background image, with the first color stop at 0% and the last at 100%.

The position can be declared as a percentage of the width of the background image, or as an absolute length. If our background image is 200 px tall, these four declarations are equivalent (Figure 9-9):

-webkit-linear-gradient(top, #000000 10%, #FFFFFF 90%);
-webkit-linear-gradient(top, #000000 20px, #FFFFFF 180px);
linear-gradient(to bottom, #000000 10%, #FFFFFF 90%);
linear-gradient(to bottom, #000000 20px, #FFFFFF 180px);

Notice that in the Figure 9-9 example, the top and bottom are both solid colors for 10% of the height of the element, before starting to fade from black to white. From the top to the 10% mark the image is solid black. From the 90% mark to the bottom, the image is solid white. It fades from black to white for 80% of the height.

The two arrows indicate the color stops, where the solid color ends and where it starts fading into the next color. We are not limited to two colors. You can add as many color stops as necessary to make the effect you are looking for (but remember, just because you can doesn’t mean you should).

Sometimes you also have more than two colors. For example, if you want to revert back to the look and feel of a 1996 website coded with cool 2013 features of linear gradients, you can create a rainbow background:

1     background-image: linear-gradient(
2       red,
3       orange,
4       yellow,
5       green,
6       blue,
7       purple);

This can also be written as:

1     background-image: linear-gradient(
2       red 0%,
3       orange 20%,
4       yellow 40%,
5       green 60%,
6       blue 80%,
7       purple 100%);

These equivalent gradients will create a rainbow with red on top, fading into orange, yellow, green, blue, then purple equally over the height of the element, as seen in the online chapter resources.

If you want your background to be an ugly striped rainbow instead of an ugly gradient rainbow (those are your only two options), we can create striping with hard color stops. A hard color stop is two color stops at the same point creating a sudden change in color rather than a gradual gradient:

1     background-image: linear-gradient(
2       red 16.7%,
3       orange 16.7%,
4       orange 33.3%,
5       yellow 33.3%,
6       yellow 50%,
7       green 50%,
8       green 66.7%,
9       blue 66.7%,
10      blue 83.3%,
11      purple 83.3%);

This gradient will be red for the first 16.7% since the first color stop is at that mark. Remember, if your first stop is not at 0%, the first color will be solid until the first color stop. At the color stop of 16.7%, it will begin to fade into the next color. The next color stop is also at 16.7%. Our fade into orange is over 0%: this is what creates the striping effect. There is no gradual change from one color to the next. Rather, we have hard color stops. Hard stops are good for striping, and also used in popular effects like candy buttons.

If we want a gradient to be only slightly opaque (or, if you’re a glass half full type of person, slightly transparent), use HSLA or RGBA colors:

linear-gradient(180deg,
      rgba(0,0,0,0.1) 10%,
      rgba(0,0,0,0.9) 90%);
iPhone and CubeeDoo linear gradients

Now that we understand gradients, we can create the navigation bar for our faux iOS app, which is a fairly simple linear gradient.

First we have the iOS header bar. We first define a regular blue bar—everything except the gradient:

1     header {
2       /* the general appearance */
3       padding: 7px 10px;
4       background-color: #6D84A2;
5       display: block;
6       height: 45px;
7       -moz-box-sizing: border-box;
8       box-sizing: border-box;
9       line-height: 30px;
10      border-bottom: 1px solid #2C3542;
11      border-top: 1px solid #CDD5DF;

Lines 3 through 11 define the general appearance. Note in line 4 that we have declared a background color. When using gradients or any type of background image, you always want to declare a background color, in case the background image fails, such as for older browsers that don’t support gradients. Another reason to declare a background color is instead of creating gradients of every color for every template color scheme you create, you can create a few translucent white gradients, and use them over your solid background colors, creating a single, reusable gradient for all of your color schemes.

As we’ve included both height and padding, we’ve declared box-sizing: border-box to use the IE box model. We’ve also included the -moz- prefix, which is still necessary as the property is still considered experimental, though it is supported everywhere.

Let’s first start by declaring solid colors. The header bar is solid in the top half, and then gets darker from the halfway mark to the bottom. We declare a solid color for the top and start fading at the 50% mark:

15       background-image: -webkit-linear-gradient(top,
16                   rgb(176, 188, 205) 50%,
17                   rgb(129, 149, 175) 100%);
18       background-image: linear-gradient(to bottom,
19                   rgb(176, 188, 205) 50%,
20                   rgb(129, 149, 175) 100%);

The gradient’s first color stop is at 50%, therefore the background will be solid blue (no fading) until that point. The background then fades into a different color blue in the bottom half.

If we want our gradient to work in BlackBerry 7, Android 3.0, and other archaic browsers, we also have to include the original prefixed syntax:

12         background-image: -webkit-gradient(linear, 0 0, 0 100%,
13                   color-stop(0.5, rgb(176, 188, 205)),
14                   color-stop(1.0, rgb(129, 149, 175)));

The background-size property specifies the size of your background images. The value can be any absolute or relative length, or the key terms contain, cover, and auto.

The contain value scales the image down if necessary while preserving its aspect ratio, which may leave uncovered space. A rectangular background image in a square element set as background-size: contain will not fill the entire area, but rather will scale the background image down until it fully fits within its container. The area that is left uncovered depends on the values of the background-position property, and on the value of the background-repeat property.

The cover value scales the image so that it covers the whole area, completely covering the element, but perhaps not showing part of the image if the element box and the image do not have the same aspect ratio. A rectangular background image in a square element set as background-size: cover will be cut off. auto leaves the background image the same size as the original. The three keyterm values are illustrated in Figure 9-10.

You can also specify the background size in absolute or relative lengths. If defining in absolute or relative lengths, you can include one or two values. If one value is declared, the width will be that value, and the height will scale up or down so that there is no distortion, as shown in the last example in Figure 9-10. If you prefer to scale based on the height, declare auto for the width.

If you provide two values, they are in the order width and height, and the background image will be stretched (or shrunk) to match your stated values. In Figure 9-10, we see that at 150 px by 150 px (in a 250×250 box), the image is distorted to fit the defined size.

Only in the case when both width and height are defined do we get the possibility of distortion. Contain, cover, auto, and declaring only one value, will always maintain the original aspect ratio of the image. If you need to declare a width or height, you can declare just one to ensure that no distortion occurs, setting the other value to auto.

We can use background-size on the gradient for our buttons, as we want the top to be a gradient, and the bottom to be solid. The difference with the candy buttons is that we don’t want the gradient to fade into the solid color. Rather, we want a hard stop. As noted previously, there are a few ways to create this effect. Here in Example 9-1, we are making use of the background-size property.

We’ve defined a gradient that is similar to the header gradient, but goes from bottom to top instead of top to bottom. We then make it occupy only the top of the button by employing the background-size property: telling the gradient to only occupy 50% height by 100% width.

We don’t want our background gradient to tile, or repeat vertically, so we include background-repeat: no-repeat. We could have also declared:

background-size: 2px 50%;
background-repeat: repeat-x;

Make the background only 2 px wide, and repeat that image horizontally. We use 2 px (or larger) instead of 1 px because of a bug in some versions of Chrome. We declare all background properties separately instead of using the background shorthand so we don’t accidentally overwrite any background property values, resetting them by accident to their default values.

By default, gradient background images fill up the entire background of an element. However, since we are defining the size of the gradient as different than the default 100% by 100%, we need to consider whether we want the background image to repeat. In this case, we didn’t.

Note

When I create a CSS reset, I generally include a blanket statement setting background-repeat: no-repeat on most elements. Although no-repeat is not the browser default, it is most often the designer’s preference. By not using the background shorthand, I don’t overwrite my reset.

DPI and background-size

The background-size property is the property that makes high-resolution images work on high DPI devices.

High-resolution images look nice and crisp on high DPI devices, but all images go across the wires at the same resolution. If you want to display high-resolution images, you display an image that is generally twice as wide and twice as tall as the display size. This is where the background-size property plays a vital role in mobile CSS.

Let’s say your corporate logo is 100 px tall by 300 px wide. For most devices, a 100 px × 300 px PNG would be appropriate. For high DPI devices, a 200 px × 600 px PNG will look even better. The resolution of the images are the exact same: one is just four times larger than the other. To fit that larger image in the original space—the 100 by 300 CSS pixels—and make it a crisp-looking higher resolution image, use background-size.

The default value for background-size is auto, which will make the image appear 200 px × 600 px. If your #logo div is 100 px × 300 px, you can declare:

#logo {
 width: 300px; height: 100px;
 background-image: url(logo.png);
 background-size: contain; /* OR */
 background-size: 300px 100px;
}
@media
 screen and (-webkit-min-device-pixel-ratio: 2),
 screen and (min--moz-device-pixel-ratio: 2),
 screen and (-min-moz-device-pixel-ratio: 2),
 screen and (-o-min-device-pixel-ratio: 2/1),
 screen and (min-device-pixel-ratio: 2),
 screen and (min-resolution: 192dpi),
 screen and (min-resolution: 2dppx) {
    #logo {
        background-image: url(hidpi/logo.png);
  }
}

Declaring background-size: contain; or background-size: 300px 100px; both work, as the width and height of the div are the same size you want the image to be.

There is a downside to the background-size property: images with background-size declared take twice as long to render the image: the time to decode the image and the time to resize. The extra few milliseconds can be an issue if you are animating larger images or are otherwise experiencing lots of reflows and repaints.

We’ve covered most of the background images on our fake iOS application, but not all. How about the striped background of the iOS app? Do we need a GIF for that? Not so fast. As noted earlier, we can make stripes with CSS gradients. The background of the iOS app is a gradient: a striped gradient.

If you declare two color stops at the same position you will not get a color fading into another—a gradient—but rather a hard line. This is an important trick in creating shapes. For example, to make stripes you could define:

background-image:
 -webkit-gradient(linear, 0 0, 100% 0,
   color-stop(0.4, #ffffff),
   color-stop(0.4, #000000),
   color-stop(0.6, #000000),
   color-stop(0.6, #ffffff));
background-image:
 -webkit-linear-gradient(right,
   #ffffff 40%,
   #000000 40%,
   #000000 60%,
   #ffffff 60%);
background-image:
 linear-gradient(to left,
   #ffffff 40%,
   #000000 40%,
   #000000 60%,
   #ffffff 60%);

This gradient will go from right to left, starting white and ending white with a black stripe down the middle from 40% to 60%, as shown in Figure 9-11.

The gradient for our body background image has hard stops. For this effect, we create a hard color stop, ending the first color and starting the next color with no fade. It is a 7 px wide background image, repeated:

1     background-color: #C5CCD4;
2     background-image:
3      -webkit-gradient(linear, 0 0, 100% 0,
4           color-stop(0.7142, #C5CCD4),
5           color-stop(0.7142, #CBD2D8));
6     background-image:
7       -webkit-linear-gradient(left,
8           #C5CCD4 0.7142%,
9           #CBD2D8 0.7142%);
10     background-image:
11       linear-gradient(to right,
12           #C5CCD4 0.7142%,
13           #CBD2D8 0.7142%);
14     background-size: 7px 2px;[62]
15     background-repeat: repeat;

Because the color stops are in the same location, the first color will abruptly “fade” into the next color, creating a hard line. Before the first stop, the color is the color of the first stop. After the last stop, the color is the color of the last stop.

The only thing with the gradient code we created in lines 2–13 is that it created one background image that will, by default, cover 100% of the element on which it is applied. For the background image for the body of our web application, we need our image to tile into columns or pinstripes. For that effect, we need to size the image and tile it both horizontally and vertically, or at least horizontally. We do that with the background-repeat and background-size properties.

Because we’ve declared the background size to be 7 px wide, the background image will have a 5 px stripe of #C5CCD4 followed by a 2 px wide stripe of #CBD2D8. We then repeated that tiny image both vertically and horizontally. We could also have created this gradient using pixels instead of percentages:

background-image:
  -webkit-gradient(linear, 0 0, 100% 0,
      color-stop(5px, #C5CCD4),
      color-stop(5px, #CBD2D8));
background-image:
  -webkit-linear-gradient(left,
      #C5CCD4 5px,
      #CBD2D8 5px);
background-image:
  linear-gradient(to right,
      #C5CCD4 5px,
      #CBD2D8 5px);

Or, instead of using background-size and background-repeat, we could instead have created a repeating gradient with the repeating-linear-gradient() value.

CSS3 provides us with background-size and background-repeat, which enables us to create interesting effects, including the stripey background. However, CSS3 also provides us with repeating-linear-gradient:

background-image:
  -webkit-repeating-linear-gradient(left,
      #C5CCD4 0,
      #C5CCD4 5px,
      #CBD2D8 5px,
      #CBD2D8 7px);
background-image:
  repeating-linear-gradient(to right,
      #C5CCD4 0,
      #C5CCD4 5px,
      #CBD2D8 5px,
      #CBD2D8 7px);
background-size: 7px 7px;
background-repeat: repeat;

There are a few quirks to note about repeating gradients. The width of the gradient will be the value of the last color stop: in our case, 7 px. You must declare the 0 value and the last value. Unlike regular linear gradients, it does not default those values for you.

Also, not all browsers support the native sizing feature of repeating gradients.[63] If you use repeating linear gradients, until this is fixed, check to see if you need to include a background-size and repeat the gradient with background-repeat, as done in lines 14 and 15.

Repeating linear gradients have similar browser support as unprefixed regular linear gradients. They are currently supported in all major browsers, since BlackBerry 10, iOS 5, Safari 5.1, and Chrome 10, with vendor prefixes still required in some WebKit browsers. As noted earlier, they don’t render correctly in Chrome or Chrome for Android. However, I often like the effect of Chrome’s poor handling of repeating linear gradients.

How can we include two gradients? CSS3 allows for multiple background images on a single DOM node. Simply separate the various background images, no matter what type of images, with commas. In the preceding example, you’ll note that in a single background-image declaration there are two repeating linear gradients separated by a comma.

We also included two gradients as the background image for the board, creating a checkerboard effect. The gradient background of the board is so small, it doesn’t fully render in lower DPI devices, but it does create a texture for all devices:

#board {
    color: #fff;
    height: 400px;
    width: 100%;
    float: left;
    text-align: center;
    background-color: #eee;
    background-image:
        -webkit-gradient(linear, 0 0, 100% 100%,
            color-stop(0.5, rgba(255,255,255,0)),
            color-stop(0.5, rgba(255,255,255,0.5))),
        -webkit-gradient(linear, 0 100%, 100% 0,
            color-stop(0.5, rgba(255,255,255,0)),
            color-stop(0.5, rgba(255,255,255,0.5)));
    background-image:
        -webkit-linear-gradient(-135deg,
            rgba(255,255,255,0) 50%,
            rgba(255,255,255,0.5) 50%),
        -webkit-linear-gradient(135deg,
            rgba(255,255,255,0) 50%,
            rgba(255,255,255,0.5) 50%);
    background-image:
        linear-gradient(-135deg,
            rgba(255,255,255,0) 50%,
            rgba(255,255,255,0.5) 50%),
        linear-gradient(135deg,
            rgba(255,255,255,0) 50%,
            rgba(255,255,255,0.5) 50%);
    background-size: 2px;
}

You’ll note that we are again declaring multiple background images on a single node, and only one background-size value. When only one value is declared in background-size, the length value is for both the height and width. When there is only one value for multiple background images, all background images will be the same size. If the background images were of different sizes, you would declare multiple background sizes separated by commas, with the order of the background-size declarations being the same order as the background-image declarations.

I recommend against using the background shorthand property, as it sets all the background properties to their default values, even if you didn’t intend for that to happen. If you do use the shorthand with multiple background images, only declare the background-color on the last background declaration.

Although it is important to understand how to create linear gradients (which is why we went into great detail about it), sometimes it is easier to use a tool. The online chapter resources have links to resources, tools, and gradient libraries, to help you get up and running.

You may note that we’ve included a background color. Always include a background color because your site will be viewed in browsers that don’t support gradients, you may push a typo in your gradient code, or your tile may fail to repeat itself. By including a background color, you can prevent the effect of having missing contrast between text and background in case your background image/gradient fails.

Now that we have a good understanding of linear gradients, background-size, and rounded corners, let’s revisit our buttons to give them more depth. We have most of the newer CSS features down ... except for shadows and perhaps the less commonly known text-overflow property.

The code for our buttons includes the following:

1     .button {
2         background-color: #4A6C9B;
3         background-image:
4             -webkit-gradient(linear, 0 100%, 0 0%,
5                  from(rgba(255,255,255,0.1)),
6                  to(rgba(255,255,255,0.4)));
7         background-image:
8             -webkit-linear-gradient(bottom,
9                  rgba(255, 255, 255, 0.1),
10                 rgba(255, 255, 255, 0.4));
11         background-image:
12             linear-gradient(to top,
13                  rgba(255, 255, 255, 0.1),
14                  rgba(255, 255, 255, 0.4));
15         background-size: 100% 50%;
16         background-repeat: no-repeat;
17         color: #FFFFFF;
18         border: 1px solid #2F353E;
19         border-color: #2F353E #375073 #375073; /* T LR B */
20         border-radius: 4px;
21         text-decoration: none;
22         font-family: Helvetica;
23         font-size: 12px;
24         font-weight: bold;
25         height: 30px;
26         padding: 0 10px;
27         text-shadow: 0 −1px 0 rgba(0, 0, 0, 0.6);
28         overflow: hidden;
29         max-width: 80px;
30         white-space: nowrap;
31         text-overflow: ellipsis;
32         -webkit-box-shadow:
33              0 1px 0 rgba(255,255,255, 0.4);
34         -webkit-box-shadow:
35              0 1px 0 rgba(255,255,255, 0.4),
36              inset 0 1px 0 rgba(255,255,255,0.4);
37         box-shadow:
38              0 1px 0 rgba(255,255,255, 0.4),
39              inset 0 1px 0 rgba(255,255,255,0.4);
40         }

That’s a lot of code! With our great new gradient skills, most of it (through line 26) should be familiar to you, but there may be a few properties that you may not be familiar with, such as such text-shadow, box-shadow, and text-overflow. Fear not! We have those covered.

I created a blue to darker blue button with a background color that shows through our translucent white gradient. Lines 18–20 create a border of different blue hues with a radius of 4 pixels. Line 15 defines the size of our gradient: the top part of the button is a gradient and the bottom has a solid background color showing through, so I defined a gradient image that is full width, but only half the height of our element.

Since the gradient image does not take up 100% of the area of the element, it will tile vertically by default. We don’t want it to. By declaring background-repeat: no-repeat on line 16, the image will not repeat. Had I defined the image to have a size of 2 px width (background-size: 2px 50%;), we would have had to repeat the image horizontally with background-repeat: repeat-x;.

The rest, through line 26, provides for text color, typeface, weight, and size, and removal of link underlining. While the HTML sample code has no links, often links are styled as buttons, and links are underlined by default. Had I used a link instead of a <li>, the links would have displayed as an inline element by default, giving height no meaning. But, as we’re floating our buttons in the nav, whatever element has the class of cancel and done will display as block.

The text-shadow on line 27 is new to us, though not new to the CSS specification! Let’s discuss.

Text Shadow

Text shadows were added to CSS 2, and removed in CSS 2.1. They’re back! But in reverse order.

Apple likes to have their text pop. On Apple.com, they’ve created popping text by including images for their text because they want to ensure their website looks similar in all browsers. Yes, they actually have a sprite image for their navigation copy, and another image for the gray navigation bar behind it. They have us all download over 3.5 MB of images on first page load because they want to ensure that all visitors, even those on IE6 (if people aren’t paying to upgrade their 10-year-old computer, I don’t think they’re switching to a Mac) have the same experience.

In the mobile space, we don’t have to worry about craptastic desktop browsers like older IEs not supporting text-shadows. We don’t have to include sprites for our navigation. We can use text-shadow instead of images—keeping the number of HTTP requests low and making internationalization much less of a hassle.

The text-shadow property specifies a text shadow to be added to the text content of an element. The syntax for text-shadow values is a space-separated list of leftOffset, topOffset, blur, and shadow-color. The two shadow offsets are required. The blur radius and color values are optional:

text-shadow: <leftOffset> <topOffset> <blurRadius> <color>[,
       <leftOffset> <topOffset> <blurRadius> <color>][,
       <leftOffset> <topOffset> <blurRadius> <color>];

There are two offset length values: (1) the horizontal distance to the right of the text if it’s positive, or to the left of the text if the value is negative, and (2) the vertical distance below the text if it is positive, or above the text if it is negative.

The third value defines the width of the blur. The larger the blur value, the bigger the blur, making the text shadow bigger and less opaque. Negative values are not allowed. If not specified, the blur radius value defaults to zero, creating a sharp shadow edge.

The offsets are required, but they can be set to zero. If they are both set to zero, the shadow will be a glow, showing evenly on all sides. This feature, along with currentColor (see Chapter 8) can be used to your benefit.

Some fonts, like Helvetica Neue Light, when viewed in high DPI devices, are too thin to read. By declaring:

text-shadow: 0 0 1px currentColor;

the text can be made more legible. The one line of CSS creates a 1 px glow directly behind the text in the current color of the font. By using currentColor, you can make a single property/value declaration for all your text without worrying about different elements having different color text.

When multiple shadows are included, the shadow effects are applied in the reverse order in which they are specified: the last shadow is drawn first and each preceding text shadow is placed on top of it.

Text shadows can be created to be very, very wide. Even so, a text shadow has no effect on the box model. A text shadow can increase beyond the boundaries of its parent element without increasing the size of that element’s box. Because of this, if you put a very wide negative horizontal offset, you may inadvertently cover the preceding text (there is an example in the online chapter resources).

A trick to good-looking text shadows is to include a blur value and to provide a slightly transparent version of the shadow color, generally rgba(0,0,0,0.4); for a slightly gray shadow. While seemingly gray, the translucent black allows for background colors/image to show through the shadow.

However, I am not a designer. And I am definitely not Apple’s designer. Apple appears to use a one pixel dark-gray top shadow. We add this to page title (the <h1>) and to our buttons on our Languages page to make the text pop:

text-shadow: 0 −1px 0 rgba(0, 0, 0, 0.6);

The preceding code provides for a 1 px text shadow sitting on top of the letters that is slightly transparent.

Lines 28 through 31 of our code example above ensure that the button does not take up too much room. We have very limited space on our mobile devices. In our Languages example, we are trying to fit three elements in that top navigation area: two buttons and the page title. So, we need to ensure not only that the buttons don’t take too much space, but that our h1 isn’t too wide either. “Languages” fits here, but what if our page title were “Supported Languages”?

Generally, declaring a width on an inline element, like <button>, has no effect. However, since we’re floating our buttons, visible floated elements are default displayed as block (instead of list-item, inline, inline-block, etc.), so width works!

text-overflow property

The text-overflow property is part of the Basic UI specification. The text-overflow property allows you to clip text that is too wide for its container. The ellipsis value causes ellipses, or three periods, to be appended to the text at the point where it is clipped:

text-overflow: ellipsis | clip

Employing text-overflow: ellipsis; is not enough to cut off text and include ellipses. If the text wraps or is allowed to extend beyond the boundaries of the containing block, then the words won’t be cut off and the ellipses will not appear.[64] Ellipses will only appear when the containing box has overflow set to something other than visible (visible allows the text to flow out of the box) and white-space set to nowrap or pre. You can also use the clip value, which will clip the text but without putting ellipses.

By using the ellipses key term, the clipping will occur after whatever last letter can completely fit into the parent box. There has been discussion about breaking on specific characters rather than letters. Firefox has included this experimental string value since Firefox 9.

While you really should never clip text, the limited real estate on the navigation bar of a mobile application often requires it. It is generally not good user experience design to hide text in this (or any other) way. If you are going to clip text, use ellipses to improve user experience. The ellipses informs the user that text has been clipped:

h1 {
 white-space: nowrap;
 width: 180px;
 overflow: hidden;
 -o-text-overflow: ellipsis; /* opera mini, mobile */
 text-overflow: ellipsis;
}

The white-space, overflow, and, generally, width properties have to be declared for the text-overflow property to work, which means it doesn’t work on elements that are displayed inline.

Box shadows are most noticeably used to create drop shadows to make elements pop off the screen. In CubeeDoo, we use box-shadow to make the cards pop off the board a bit. In our effort at creating a CSS3 native iPhone application look, we employ the box-shadow property to create two subtle shadow effects on the buttons.

The box-shadow property enables the developer to add drop shadows or inset shadows, or both, to elements. One or more box-shadows can be attached to a box, by which you can create some pretty nifty effects. Shadows are drawn just outside the border, or just inside the border in the case of inset.

In terms of the box-model, shadows are similar to outline and text-shadow, in that they do not influence the layout: they may overlap other boxes. It is supported in all browsers starting with IE9. Include the default vendor-less syntax and the -webkit- vendor prefix version for Android 3.0 and iOS 5.

The syntax and rendering of box-shadow is similar to text-shadow, but with two additional optional features, blur spread and inset:

box-shadow: inset <leftOffset> <topOffset> <blurRadius> <spread> <color>[,
     inset <leftOffset> <topOffset> <blurRadius> <spread> <color>][,
     inset <leftOffset> <topOffset> <blurRadius> <spread> <color>];

The values for box-shadow property include none for no shadow (the default), or a list of space-separated values for left-offset, top-offset, blur radius, spread radius, and color, with the optional inset keyword for inner shadows (versus drop shadows). If more than one box shadow is to be included, separate each shadow with a comma.

The vertical and horizontal offset values are both required. Similar to text-shadow, the first three values define the horizontal position, vertical position, and blur radius in that order. The fourth value—the blur spread—makes the shadow grow bigger if positive, and shrink if negative. The inset key term, if included, makes the shadow an inset shadow instead of a drop shadow. The inset key term, blur radius, blur spread, and color values are optional.

Note that inset is only an option for box-shadow and is not part of text-shadow. Also note that Android up to 3.0 and iOS 3.2 require the -webkit- vendor prefix, and do not support the inset values. iOS 4.x and Blackberry 7 require the -webkit- vendor prefix as well, but support inset shadows.

For example (not from the online chapter resources):

1     -webkit-box-shadow:
2          3px 4px 5px 6px rgba(0,0,0, 0.4);
3     -webkit-box-shadow:[65]
4          3px 4px 5px 6px rgba(0,0,0, 0.4),
5          inset 1px 1px 1px #FFFFFF;
6     box-shadow:
7          3px 4px 5px 6px rgba(0,0,0, 0.4),
8          inset 1px 1px 1px #FFFFFF;

Within each grouping we have two comma-separated box shadows. The values making up each of the two box shadows is a space-separated list, including:

  • The left or horizontal offset of the shadow is the first length value. A negative value will put the shadow on the left.

  • The top or vertical offset is the second length value. A negative value will put the shadow above the box.

  • The optional blur radius is the third length value. Negative values are not allowed. If it is 0, the shadow is solid. The larger the value, the blurrier the shadow will be.

  • The optional blur spread is the fourth length value, if it is present. Positive values make the shadow grow bigger. By default, the shadow is the same size as the element.

  • The optional color. Using alpha transparency is recommended if your blur radius is greater than 0, as the effect is much prettier if you start from a slightly transparent version of black than if you start with an opaque version of gray. However, translucent shadows use more memory and can take longer to paint, which is an issue on devices with limited RAM or if you’re animating and/or repainting the element frequently. If the color is not defined, browsers will by default use currentColor.

  • The optional inset key term, if present, will create an inset shadow instead of a drop shadow.

In our example, the first shadow will be a 5 px wide gray transparent shadow on the bottom right of the element. The second box shadow value in each grouping includes the optional key term inset. This second shadow will be a fully opaque white 1 px outline (or inline?) on the inside of the box. The shadows appear on the outside and inside of the border, if there is one, respectively.

The values of the x-offset, y-offset, and optional blur radius and spread radius need to be written in that order. The order of the last two values in the declaration (inset and color) location aren’t important, but keep the order consistent for ease of reading and maintenance. The order of shadows when including multiple shadow values does make a difference. Similar to text-shadows, box-shadows are drawn in the reverse order of their declarations. Drop shadows are always drawn behind the box, as if it had a lower z-index, and do not show through alpha transparent boxes (unlike alpha transparent text, where text shadows show through), as shown in the online chapter resources.

A thick border with an inset shadow can make for an interesting effect, but animating a large translucent shadow can actually crash a browser, as all shadows, images, and gradients are calculated and painted to the screen from back to front, even if they are never made visible to the user.

Warning

On mobile, all properties are drawn to the page even if not visible. Be warned that if you add 20 shadows and gradients, even if they are overlapping, beyond the confines of the page, under another element, or otherwise not visible, they are still painted. Repainting 20 layers and calculating the added effect of transparencies on each pixel may take too long to animate at 60 fps.

While shadows have been supported since the first iPhone, shadows can use a lot of memory and can hang the browser, especially if the shadows are semitransparent (versus opaque), and even more so when the element on which the shadow is set is animated. The browser must recalculate each pixel on each repaint, even if the shadow is not visible.

Warning

In mobile browsers, large shadows, especially inset shadows, use a lot of memory and can hang the browser.

Lines 37–39 of our CSS for the candy buttons include a double shadow:

    box-shadow:
         0 1px 0 rgba(255,255,255, 0.4),
         inset 0 1px 0 rgba(255,255,255,0.4)

This declaration will include two semitransparent white shadows: a 1 px shadow on the outside bottom of the box and a 1 px shadow inside the button, at the top of the box. We also declared this with the -webkit- prefix with two shadows and also with the inset shadow removed for very old Android browsers.

We have several shadows in CubeeDoo. Here are some excerpts from the CSS. This is not all the code that is in the CSS files. I am only including the selectors, properties, and values that should make sense to you up to this point:

1     #board > div {
2         box-shadow: 1px 1px 1px rgba(0,0,0,0.25);
3     }
4     .back {
5         border: 5px solid white;
6     }
7     .back:after,
8     .face:after {
9          position: absolute;
10         content: "";
11         top: 0;
12         left: 0;
13         right: 0;
14         bottom: 0;
15         border-radius: 3px;
16         pointer-events: none;
17     }
18     .back:after {
19         background-repeat: no-repeat;
20         background-color: #fff;
21         background-size:
22             50% 50%,
23             0 0;
24         background-image:
25             url('data:image/svg+xml;utf8,<svg width="40" height="40" 
                 xmlns="http://www.w3.org/2000/svg"><g><text xml:space="preserve" 
                 text-anchor="middle" font-family="serif" font-size="40" id="svg_1" 
                 y="30" x="20" stroke-width="0" stroke="rgb(119, 160, 215)" 
                 fill="rgb(119, 160, 215)">❀</text></g></svg>'),
26             -webkit-linear-gradient(-15deg, 
                 rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.05));
27         background-image:
28             url('data:image/svg+xml;utf8,<svg width="40" height="40" 
                 xmlns="http://www.w3.org/2000/svg"><g><text xml:space="preserve" 
                 text-anchor="middle" font-family="serif" font-size="40" id="svg_1" 
                 y="30" x="20" stroke-width="0" stroke="rgb(119, 160, 215)" 
                 fill="rgb(119, 160, 215)">❀</text></g></svg>'),
29             linear-gradient(75deg, 
                 rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.05));
30         box-shadow:
31             inset 1px 1px 0 currentcolor,
32             inset −1px −1px 0 currentcolor,
33             1px 1px 1px rgba(0,0,0,0.1);
34             color: rgb(119, 160, 215);
35     }
36     #board > div.flipped {
37         box-shadow: −1px 1px 1px rgba(0,0,0,0.25);
38     }
39     .control {
40         border: 1px solid rgba(0, 0, 0, 0.25);
41         box-shadow: inset 1px 1px 0 rgba(255, 255, 255, 0.5);
42         background-image:
43             -webkit-linear-gradient(-15deg,
44                 rgba(0, 0, 0, 0),
45                 rgba(0, 0, 0, 0.025));
46         background-image:
47             linear-gradient(75deg,
48                 rgba(0, 0, 0, 0),
49                 rgba(0, 0, 0, 0.025));
50         text-shadow: 1px 1px 0px rgba(255, 255, 255, 0.5);
51         border-radius: 5px;
52     }

There are many things to note in this example. Let’s go line by line:

  1. We are targeting <div> elements that are the direct children of the #board using a child selector combinator in line 1. The element we’re targeting is the card container. We give the cards in their default state a slight drop shadow in line 2.

  2. We define the appearance of our generated content by creating pseudoelements in lines 7 through 17. We include the required content attribute in line 10, and position these 0 px from the top, left, bottom, and right, forcing the absolutely positioned[66] generated content to be the exact size and position of their parent node. We also include a border-radius of 3 px to make the corners rounded like their parent nodes.

    Note

    Setting generated content position: absolute, with all 4 sides offset to 0, will make the generated content the same size as the parent.

  3. Lines 17 to 35 style the back of the card. We start off by declaring the background properties, including background-repeat and background-color. We have two background images with different background sizes, so we declare the two values separated by a comma as the value of the background-size property.

  4. The back of the cards have a blue band, a little flower, and a gradient to make it pop. The band effect is done with box-shadow. The flower and popping gradient are done with background images.

  5. We declare two background images in two separate ways. The first background image is SVG, as covered in Chapter 5. The second image is a gradient. They are separated with a comma. We declare this image combination twice (or three times if you want to support old WebKit) because gradient syntax has changed, and SVG is not supported in older Android. You would think that we could simply have included the ❀ in generated content, but because we are flipping the card in later chapters and support for animating generated content is not yet ubiquitous, we create our generated content look with a background SVG.

  6. To create the blue band as shown in Figure 9-12, we use two inset shadows: a top/left inset shadow and a bottom right inset shadow. Note that the blue band actually follows the curves of the rounded corners. The blue band is 5 px from the edge of the card. We were able to create this stripe effect because in line 5 we added a 5 px border. And, as we know, shadows and inset shadows are on the outside and inside of the border respectively.

  7. The last of those three borders, the one on line 33, creates a drop shadow for the card.

  8. On line 34, we declare the color. Seemingly, this would be a moot point since there is no text in this element. Even the ❀ “text” is done with SVG and not generated content. But, by including the color here, the currentColor in our drop-shadow obeys this declaration.

  9. When the user flips a card, the shadow stays with the card. This is likely not the effect you want. Our light source has stayed the same, so our shadow should be in the same location relative to the board, not the card, when we flip it. Line 36 uses the child selector combinator, but really doesn’t have to, since no other div is going to have the flipped class. When the div is flipped, we change the left offset of the card so that visually it is always in the same location. We will learn about transforming (flipping or rotating) cards in Chapter 11.

  10. Lines 39 to 52 control the controls, which are the CubeeDoo buttons. To create the effect of a double border (black and white) for the top left, we include a translucent black border and add a white inset shadow in lines 40 and 41.

  11. We include a translucent gradient as the background of the buttons to give the buttons dimension. Note that the angles are written differently for the prefixed syntax versus the unprefixed syntax, but will appear the same.

  12. We also added translucent white text shadow to the buttons as well as rounded corners in lines 50 and 51.

We’ve covered all the features of creating the look of a native iPhone application without the use of images, and all the features of the default look and feel of CubeeDoo. We succeeded in creating CSS3 only versions of these interfaces without opening up an image editing application or making an extra HTTP request.

With CSS3 and SVG, which we discussed in Chapter 5, you really can create just about any shape without images.

However, sometimes images are the right solution as they’re supported in all browsers, and they’re easy. We will cover some image solutions, including border image and multiple background images, in Chapter 11.



[57] The W3C specifications are http://www.w3.org/TR/css3-speech/, http://www.w3.org/TR/css3-page/, and http://www.w3.org/TR/css3-ruby/, if you would like to delve into the speech, paged media, and ruby CSS modules.

[58] background-clip and background-origin can be employed to change this default behavior.

[59] When two elements are next to each other, their adjacent margins collapse.

[60] The original WebKit gradient syntax was used through Chrome 9, iOS Safari 4.3, Android 3.2, and Blackberry 7.

[61] -moz- and -o- prefixed examples are not included as they are no longer needed in modern browsers. Include these additional prefixes if you are supporting Firefox 4–15 and/or Opera 11.1–12.0

[62] Background size was set to 2 px, instead of 1 px—though any other nonzero positive height should work—because of a bug in Chrome that does not properly repeat gradients smaller than 2 px.

[63] For example, Chrome has a weird bug. It seems to think the background size should be 110 px, repeats oddly below that, and fails at times when the repeating linear gradient is wider than 100 px.

[64] To add ellipses to multiple-line text, there are vendor-specific, nonstandards values, including the property of -webkit-line-clamp and value of text-overflow: -o-ellipsis-lastline;, for WebKit and Opera respectively.

[65] Lines 1–2 are for Android up to 3.0 and iOS 3.2, which do not support the inset property. Lines 3-5 is for older WebKit browsers, including and up to Safari 5, iOS 4.3, Android 3.0, and BB 7. Lines 6–8 are for Safari 5.1, Opera 10.5+, Opera Mobile 11+, Chrome 9+, IE9+, and Firefox 4+. Not supported in Opera Mini.

[66] The parent is set to position relative, so the generated content will be relative to that.