CSS Features in Responsive Web Design - Mobile HTML5 (2013)

Mobile HTML5 (2013)

Chapter 11. CSS Features in Responsive Web Design

Your content should be designed to work on any device because it will be viewed on every device, everywhere. The website we’re building today with the goal of displaying it on a desktop, smartphone, and tablet may be viewed on a 52-inch TV screen or 3 × 5-inch GPS LCD screen. By starting with a flexible foundation, your site should be able to grow or shrink gracefully no matter the hardware that loads it.

For your website to adapt to any screen size, you want to make it as flexible as possible. Using percents and rems, instead of pixels for widths and font size, will bring you 90% of the way there. Add in some media queries, and you’re 95% of the way there.

There are several CSS features, other than media queries and other CSS3 features we’ve covered so far, that are helpful in developing responsive websites and will bring you up to the 99% mark. Why 99% and not 100%? There is always more you can do to make a site more responsive, more accessible, prettier, faster, etc., but at some point, you have to say “this is good enough” or “this book is way too long.”

Media Queries, Breakpoints, and Fluid Layouts

I mentioned this before, but it bears repeating: don’t create layouts for specific phone sizes. Rather, slowly expand (or shrink) your site in a browser. When the layout starts looking less than optimal, that is where you should alter your design for the next set of devices. You may need eight layouts for tiny, xx-small, small, medium, large, x-large, xx-large, and huge screens, or you may have a single layout that works well across all devices. You won’t know until you view the layout in varying sizes, but do view your layout in a plethora of sizes to make sure your layout works well everywhere.

As you change the size of your browser and decide that a new layout is needed, the width at which the layout changes is called a breakpoint. Don’t just select 320, 480, 640, and 960 as breakpoints because that is what everyone else is doing. Instead, do what makes sense for your site.

When you determine that you need a breakpoint, use media queries to target a span of viewport sizes with a specific layout. You also don’t have to choose a single breakpoint. You can alter the layout of your header, footer, navigation, and main content at different breakpoints if that makes the most sense. There is no right or wrong, there is just better and not as good.

Once you’ve determined where the breakpoints are for your design or for the usability of your application, you can target the layout changes and feature highlighting with media queries. You can also target high pixel-density displays with larger images, remembering that larger images mean larger file sizes. You can use JavaScript with media queries to send high DPI images only to high pixel-density displays with good bandwidth, and perhaps one day we’ll be able to match media queries to bandwidth—but we’re not there yet.

We covered media queries in Chapters 2 and 7, so you should already understand the syntax.

Multiple Columns

The CSS columns property enables us to create multiple columns for laying out text like a newspaper. The columns property is shorthand for column-count and column-width.

The column-width property is the optimal column width, as if you were declaring a min-column-width, which is not an actual property. The column-count is the maximum number of columns, as if you were declaring max-column-count, which is also not an actual property.

The column-count property has precedence. The browser adjusts the width of the column around that suggested column-width, providing for up to as many columns as listed as the integer value of column-count, as long as each column is at least as wide as the length value provided as the value for column-width. Columns allow for scalable designs that fit different screen widths.

We define the gap between columns with the column-gap property, and whether to include a dividing line between columns by declaring a line with the column-rule rule property. The column-gap property takes as its value a length, with the default value being the key term normal, which is 1em in most browsers.

Using columns will make your very wide areas of content more legible. And, as viewport sizes narrow, the number of columns will shrink. While your 24-inch monitor may see six columns, your HTC One in portrait mode will only have one column if the following style is set:

columns: 240px 6;

The preceding line reads “divide the element’s content into a maximum of six columns, ensuring that no column is narrower than 240 px wide.” The iPhone is 320 px wide, so you can’t fit two columns in portrait mode, but you could in landscape if there was no padding or gap. A 1920 × 1080 display could fit eight columns, with no column gap. However, the browser will not render more than six columns, even if more columns of the declared width could fit in the space provided.

Similar to the border shorthand, the column-rule is shorthand for the column-rule-width, column-rule-style, and column-rule-color properties, and takes the same values as the border shorthand, too. As long as the column-rule-width is narrower than the column-gap, the rule will show. These column properties are animatable, with the exception of column-rule-style:

p {

margin: 0 0 1em;

}

div {

padding: 1em;

margin: 1em;

border: 2px solid #ccc;

columns: 240px 6;

column-gap: 2em;

column-rule: 2px dashed #ccc;

}

This code snippet[76] will create a multicolumn layout in a large device, and display in a single column on a device (or parent) narrower than 480 px + 4 ems, as shown in Figure 11-1, with examples in the online chapter resources.

Columns as seen on narrow and wide screens

Figure 11-1. Columns as seen on narrow and wide screens

An interesting note in the preceding code is the margin set on the paragraphs: one of the reasons developers have been reluctant to use columns in the past is because of the way the columns sometimes leave gaps at the top or bottom of a column. Uneven bottoms can be OK: you want to make sure your gap is not at the top of a column. If a node with a margin or padding top starts a new column, there will be a gap at the top of that column. To ensure no gaps at the top of columns, set padding and margins on the bottom of paragraps and other children of your columns.

The column-span property enables elements to span across all columns when its value is set to all. If an element has column-span: all; set on it, the content above it will be divided among all the columns equally, that element will then cut across the entire parent, and the subsequent content will again be rendered in columns.

By default, all columns will be set to approximately the same height, divided equally across all columns. By setting the height of the parent, and the column-fill property to fill, (rather than the balance default), the columns will be filled sequentially. While the other column properties are well supported, column-span and column-fill are not.

To set an exact column width, the calculation needs to include the width, column-width, column-gap, and column-rule-width properties.

To effectively use columns in a responsive layout, make sure that the parent of the columns is a fluid width. Declare the maximum number of columns you would want displayed in a wide screen and the minimum width you would want to see displayed in any screen, and your content will be responsive.

While no media queries are necessary for this to work, shrink and grow your browser window to the smallest and widest widths possible to assess whether media query break points makes sense.

Border Images

border-image allows for a single image to be used to create decorative borders on any element no matter the size or aspect ratio of that element. We can create decorative borders for elements, beyond simple rounded corners, with a single, very small image file size or even with gradients.

The border-image property virtually slices an image into nine sections, putting the corners of that image in the corners of your element, with the width of your left and right borders and the height of your top and bottom borders, and either repeating or stretching the noncorner components to cover your element. You can take a single, relatively small image and stretch it across a small button or a whole page.

Figure 11-2 shows three border images, and how one would slice them up in an image-editing program before browsers supported the border-image shorthand property, and how we tell the browser to virtually slice up the image now.

Small images that we virtually slice to create border and background effects on elements of varying sizes

Figure 11-2. Small images that we virtually slice to create border and background effects on elements of varying sizes

Native iOS apps have buttons, like the tiny button shown in the center of Figure 11-2. We created those buttons earlier with gradients. We could have created that button look with the sliding door method or the several other hacks we used to make buttons last decade. Or, we could use a single small image for every button, whether that button is 10 × 10 px or 200 × 300 px, by using CSS border-image.

Figure 11-3 shows three examples of elements with a border image set using the three small images from Figure 11-2. Let’s learn how to do it!

Elements with border-image set, with the four-corner slices being in the corners, and the top (T), right (R), bottom (B), and left (L) slices repeated or stretched

Figure 11-3. Elements with border-image set, with the four-corner slices being in the corners, and the top (T), right (R), bottom (B), and left (L) slices repeated or stretched

We’ve used the images from Figure 11-2 as the border images for the elements in Figure 11-3, maintaining the corners while repeating (in the case of the stamp) or stretching the middle section of the border image to cover the entire element.

In the stamp example, we’ve repeated the middle slices (T, R, B, and L) to create the outline of a stamp. To ensure that the image is not broken, the width and height should be multiples of the slice’s width (T and B) and height (R and L). While we’ve repeated the top, bottom, and sides, we’ve maintained the four corners (listed as 1, 2, 3, and 4), creating a stamp-like effect.

The border-image is a shorthand property used to declare border-image-source, border-image-slice, border-image-width, border-image-outset, and border-image-repeat.

The syntax for the shorthand is:

-prefix-border-image: <source>

<slice {1,4}> / <width {1,4}> <outset> <repeat{1,2}>;

Browsers that support border images only support the border-image shorthand property, rather than the separate properties that make up the shorthand. We’ll cover the various properties that make up the shorthand border-image property, but I recommend using the shorthand instead of the longhand properties described next.

Note that the current syntax has changed several times since the first implementation. If you’re reading a blog post on the topic, make sure it’s using the current syntax.

Setting Border Images

Border images don’t work if there is no border. The first step is to declare a border for our elements. As we know from Chapter 9, border-style is the only required property:

.button {

border: solid;

}

.stamp {

border: solid;

}

.arrow {

border: solid;

}

If we do not include a border-style with a value other than none or hidden the border image will fail to display.

border-image-source

The border-image-source is the URL, gradient, or data URI of the image you want to use as your border image. In the Figure 11-3 examples, while the longhand property is not yet fully supported, it is as if we had used border-image-source: url(stamp.gif), but instead we start our three border-image shorthand property declarations with:

.button {

border: solid;

border-image: url(button_bi.png) ...

}

.stamp {

border: solid;

border-image: url(stamp.png) ...

}

.arrow {

border: solid;

border-image: url(arrow.png) ...

}

Just as we can include gradients, base-64, GIF, JPEG, PNG, and even SVG images as background images, you can include all these image types as border images.

border-image-slice

The border-image-slice property defines from one to four lengths that set the distance from each edge of the image marking the area that will be used to cut or slice up our border image, as shown in Figure 11-2. The border-image-slice also defines whether the middle part of theborder-image, labeled M in Figure 11-3, is discarded or fills the background of the element.

The border-image-slice property values represent inward offsets from the top, right, bottom, and left (TRouBLe) edges of the image, respectively. You define four imaginary lines that the browser then uses to divide the one border image into nine regions: four corners, four edges, and a middle, as demonstrated in Figure 11-3. The four corners are placed in their respective corners, scaled to fit the space allotted to them by the border width properties. The four sides are stretched or repeated or a combo of the two (round), depending on the values of the other border-imageproperties.

In addition to the four values, the unprefixed version of the border-image-slice property takes the optional value of fill to preserve the middle part of the border image. If the key term fill is not present, the middle part of the border image file is discarded. Whether that middle component is stretched, repeated, or rounded depends on the value of the border-image-repeat property described .

In our examples, we’ve sliced the image 5 px in from each side for our button; 9 px for the stamp; and 0 px from the top and bottom, 5 px from the left, and 20 px from the right of our arrow. We want the middle section of the image to show for the arrow and button, but not for the stamp. If we were writing shorthand, we would have written border-image-slice: 5px fill;, border-image-slice: 9px;, and border-image-slice: 0 5px 0 10px fill; respectively. Instead, we include them in the shorthand property with no length units, and the fill for the button and arrow:

.button {

border: solid;

border-image: url(button_bi.png) 5 fill...

}

.stamp {

border: solid;

border-image: url(stamp.png) 9 ...

}

.arrow {

border: solid;

border-image: url(arrow.png) 0 5 0 20 fill...

}

Note we’ve used no length units. If you are setting the slice values in length, and the value will be interpreted as pixels, omit the units. If you are using percentage values, include the percent.

border-image-width

The border-image-width property sets the width of the element’s border. If the border-image-width property is declared as part of the border-image shorthand, it takes precedence over the border-width property. If omitted and the border-width is omitted, the width of the borders will be 3 px in most browsers, the value of which is medium, the default value of the border-width property.

Since there are quirks with the value of auto, it is often recommended to include border-width as a separate property or part of the border shorthand, rather than part of the border-image shorthand:

.button {

border: solid 5px;

border-image: url(button_bi.png) 5 fill...

}

.stamp {

border: solid 9px;

border-image: url(stamp.png) 9 / 9px ...

}

.arrow {

border: solid;

border-width: 0 5px 0 20px;

border-image: url(arrow.png) 0 5 0 20 fill / 0 5px 0 20px...

}

The four corners, labeled 1, 2, 3, and 4 in Figure 11-3, will be the width of the left and right borders and the height of the top and bottom borders. Having the border-image-width the same width as the border-image-slice will create the best-looking border image with no distortion. But they don’t need to have the same values. The slice will be stretched (or shrunk) to the width of the border-image-width if the values are not the same.

We add a slash between the border-image-slice values and the border-image-width values. In the unprefixed version, we rely on border-width—a positive, nonpercentage length unit—to define the width of our borders.

Remember the box model! border-width is part of the box model and will affect these elements. As you increase the border-image-width, your element will grow larger, unless prevented from doing so with box-sizing: border-box;.

border-image-outset

The border-image-outset property specifies the amount by which the border image area extends beyond the border box on all four sides. The default value is 0.

Because the stamp is a transparent PNG, and we have not filled it, if we added a background-color, the color would show through the middle and through the transparent parts of the border. There are two ways to resolve this border issue: background-clip: padding-box; or by putting the border image outside the box with the border-image-outset property. The former does not alter the size of the box. The latter does, similar to the box-shadow; it makes the element appear larger, but does not impact the box model:

.stamp {

border: solid 9px;

background-color: #dedeef;

border-image: url(stamp.png) 9 / 9px / 12px ...

}

border-image-repeat

Now if you’ve been playing along, testing each line of code, at this point the button and arrow are looking good, but the stamp not so much. The top, right, bottom, and left slices stretch by default, with a single slice spreading across the entire width or height of the element. That looks fine for our arrow and button, and is in fact what we need the arrow and button to do, but it looks crappy for the stamp. We want the stamp side slices to be repeated not stretched. For this we have the border-image-repeat property.

The border-image-repeat property allows you to delineate how noncorner images (the sides and middle) are repeated and/or scaled in TRouBLe order. The specifications define four possible values, but only two are well supported. stretch means that the image should not be tiled, but rather stretched to fill the area. repeat means the image is tiled (or repeated) to fill the area.

If the area allocated for the repeating image is not exactly divisible by the width of the image, the last tiled image may be cut off. With round the image should be tiled (repeated) to fill the area, with the image being scaled down, possibly losing its aspect ratio, but ensuring that the image is never cropped. The unsupported space value was supposed to repeat the slice as many times as can fully fit in the area provided, with the tiles evenly spaced, showing whitespace between the tiles if the width provided is not an exact multiple of the image size. This value, however, has been (temporarily?) removed from the specifications.

In our examples, we used stretch for the button and round for the stamp. You will always want to stretch gradients, as repeating them creates harsh lines where one tile ends and the next begins. And while it may seem to make sense to use repeat for the stamp, we have no way of knowing if the image is evenly divisible by the width of our design. The round distorts the image ever so slightly, but that is better than having the image cut off.

Since round isn’t fully supported, repeat is the fallback.[77]

The arrow is an interesting case. We definitely don’t want to repeat it. We can stretch it, but only slightly before the image becomes distorted. Because of the shape, we set the top border and bottom slices to zero height so that if we do end up stretching the arrow part, it doesn’t lose its shape:

.button {

border: solid 5px;

border-image: url(button_bi.png) stretch 5 fill;

}

.stamp {

border: solid 9px;

background-color: #dedeef;

border-image: url(stamp.png) round 9 / 9px / 12px;

}

.arrow {

border: solid;

border-width: 0 5px 0 20px;

border-image: url(arrow.png) stretch 0 5 0 20 fill / 0 5px 0 20px;

}

We don’t have to declare stretch on the arrow or button, since it is the default value.

Border-image shorthand

Notice the last code example has semicolons instead of ellipses. That completes the various properties that make up the border-image shorthand property. However, what we have won’t work in alld browsers. We include prefixing for mobile WebKit through Android 4.2 and iOS 5.1 and Opera through 12.1. border-image started being supported in IE (with IE11) with no prefix:

.stamp {

background-color: #ccc;

border: solid 9px transparent;

-webkit-border-image: url(stamp.png) 9 / 9px / 12px round;

-o-border-image: url(stamp.png) 9 round;

border-image: url(stamp.png) round 9 / 9px / 12px;

}

.button {

border: solid 5px transparent;

-webkit-border-image: url(button.png) 5;

-o-border-image: url(button.png) 5;

border-image: url(button.png) 5 fill;

}

.arrow {

border: solid transparent;

border-width: 1px 5px 1px 20px;

-webkit-border-image: url(arrow.png) 1 5 1 20 / 0 5px 0 20px stretch;

-o-border-image: url(arrow.png) 1 5 1 20 / 0 5px 0 20px stretch;

border-image: url(arrow.png) stretch 0 5 0 20 fill / 0 5px 0 20px;

}

At this point, you hopefully have a good understanding of how to create a border image. There are a few tools to help you along. There are links to these tools and the demo of our button, arrow, and stamp in the online chapter resources.

Flexbox

Modern browsers are now supporting what is expected to be the final syntax of the flexbox layout mode, but all mobile browsers are supporting some version of flexbox, so it is worth mentioning—especially since flexbox enables developers to easily create flexible multicolumn layouts (as shown in Figure 11-4).

The flexbox layout mode provides flexibility in laying out web pages. The children of a flexed container can be laid out horizontally, vertically, in source order or not. The children can “flex” their width and height and avoid expanding beyond the size of their parent or empty space.

To work, CSS gives us a few new properties that may still be in flux. The current new flexbox properties include the ordering and orientation of flex-direction, flex-wrap, flex-flow, and order properties, the flexibility properties of flex-grow, flex-shrink, flex-basis, and the flex shorthand, the alignment properties of justify-content, align-items, align-self, and align-content properties—as well as new values for the display property.

The layout and visual order can be altered without touching the underlying HTML

Figure 11-4. The layout and visual order can be altered without touching the underlying HTML

The flexbox specifications add two values to the display property: flex and inline-flex.

Apply the flex or inline-flex (-ms-flexbox in IE10) values to the display property of the parent of the children you want to position.[78] Flex’s default creates even columns out of the flexed item’s children. The additional properties allow us to reverse the order, wrap, change the order, create centered rows instead of columns, etc., all without touching the underlying HTML content. By allowing CSS to provide flexibility in the layout, in conjunction with media queries, we can send different layouts of the same content to different viewport configurations.

The various layouts shown in Figure 11-4 were all based on the same HTML:

<article>

<div>A</div>

<div>B</div>

<div>C</div>

</article>

So, how did we change the layout without touching the markup? Well, it wasn’t easy. We’re still dealing with various syntaxes in different browsers:

article {

display: -webkit-box;

display: -moz-box;

display: -webkit-flex;

display: -moz-flex;

display: -ms-flex;

display: flex;

}

The preceding code is basic, creating columns out of the flex’s children. If you look at the online chapter resources, you’ll notice that the divs are now flowing horizontally, as if we had floated them left, except that no matter how much content you add to each nested <div>, they will all be the same height.

Unfortunately, we are still supporting diverse specifications with and without prefixes. Because display is an old property, when adding vendor prefixes, the prefix is on the value, not the property: we included -webkit-box and -moz-box for older WebKit and Firefox through v17. We then include the prefixed candidate recommendation for Chrome and BB10, IE10, and Firefox 17-19. At the time of this writing, FF 20+, Opera, IE11 beta, and Opera Mobile are prefix-free. IE10 supports the February 2012 “tweener” syntax, which is a bit different from the candidate specification supported by the other browsers and IE11 beta.

The preceding code only created columns. We could have created rows. We could have declared even columns. We could have reverse ordered the presentation of those columns. This can all be done with the other flexbox properties.

NOTE

Note: Absolutely positioned children of a flexbox cannot be a flexbox item, as absolutely positioned elements are taken out of the document flow.

Browsers have implemented the flexible box layout module as the specification evolved. Because of this, different browsers have implemented different syntaxes. The rest of this section uses only the current spec syntax, which may or may not be current when you read this. I am including flexbox even though it is in a state of flux because, mixed with media queries, flexbox is super powerful for mobile development. And even though the syntax I am including only works in beta versions of browsers (IE11, Chrome 29+) and Opera Mobile with Presto (Opera 12.1), the general idea of how it works will not change. Check out the online chapter resources for the more up-to-date property and value syntaxes.

To align the flexed children vertically instead of horizontally, we could employ the flex-direction property that specifies the direction of the flexbox layout. Since we omitted the property, it defaulted to the value row, creating a row out of the children. Other options include row-reverse, column, and column-reverse.

By default, the flex container is a single line. You can explicitly set the flex-wrap property to nowrap to keep that default single-line layout, or set it to wrap or wrap-reverse to allow for a multiline layout.

The flex-flow property is shorthand for flex-direction and flex-wrap properties, which combined define the axis of the flexbox’s layout.

If you want to change the display order of the flexed items, the order property can be used. The order value is set on the child flexbox elements, not the flexbox parent. To reverse the order, we can use flex-direction: row-reverse;:

article {

display: flex;

flex-direction: row-reverse;

}

To relocate a single child, apply order: −1; or otherwise the lowest value, to make the child element on which it is applied come first, or order: 1;, or whatever is the greatest value among the siblings, to make it last:

article {

display: flex;

}

div:nth-of-type(2) {

order: −1;

}

If you have an <article> with three <div>s (A, B, and C), all three columns will be in one row, with B appearing first to sighted users as if the order were B-A-C. Had we set:

div:nth-of-type(2) {

order: −1;

}

the order would have appeared to be A-C-B.

flex

The flex property defines the flex-grow, flex-shrink, and flex-basis features. Use the shorthand flex instead of the three longhand properties.

Flexing is the ability of the container to alter its width or height to fill the available space, allowing us to set sizes for our elements. The flex property is applied on flexbox children, not on the flexbox parent. When set on the flexbox children, the browser sets the size of the elements on which flex is declared on a per line basis, evenly distributing the remaining free space on the elements that don’t have flex set.

The flex property can take up to three values. The flex-grow components determines how much the flex item will grow relative to the siblings within the flexbox parent when free space is distributed. Similarly (or oppositely, but that’s not a word), the flex-shrink factor determines how much the element will shrink relative to its siblings when negative free space is distributed. The flex-basis takes the same values as the width property, specifies the initial main size of the item, before free space is distributed according to the flex-shrink and flex-grow. The default is flex: 1 1 0;.

If we want A to be twice as wide as B, and B to be twice as wide as C, we could use this (see the online chapter resources):

div:nth-of-type(1) {

flex: 4;

}

div:nth-of-type(2) {

flex: 2;

}

div:nth-of-type(3) {

flex: 1;

}

When designing for different viewport sizes, using the flexbox layout properties in conjunction with media queries can ease the development process. For a wide screen, you may want to have three columns across the page, putting the aside on the left, the main content in the middle, and the footer on the right, in one row across the page. For a small phone, without touching the page content, you could put the main content on top, followed by the contents of the aside and the footer on the bottom. Semantically, I would put the content first. Basically, develop mobile first:

<article>

<div>Main Content in center in wide screen, first in narrow screen</div>

<aside>Left side in wide screen, after content in narrow screen</aside>

<footer>Right side in wide screen, button in narrow screen</footer>

</article>

@media screen and (min-width: 600px) {

article {

display: flex;

flex-direction: row;

}

article > * {

flex: 1;

}

aside {

order: −1;

}

article > div {

flex: 3;

}

}

Note that the order is set on the <aside>, making it appear first, as seen in the example in the online chapter resources. All three siblings get flex: 1;, which we overwrite with flex: 3 on the main content. This means the article will be split 20%/60%/20% for aside/div/footer. Note thatdisplay: flex; is on the parent, and the other properties (other than flex-direction) are on the children.

In this case, we don’t have to declare a separate layout for smaller sized screens since the default browser layout looks pretty much the same as flex-direction: column. However, we could have declared:

@media screen and (max-width: 600px) {

article {

display: flex;

flex-direction: column;

}

}

Remember to always view your layout in small and large formats, creating media query breakpoint layout changes where appropriate or necessary.

Feature Detection with @supports

While not yet supported in the mobile space, and only at the candidate recommendation level at the W3C, @supports is already supported in Firefox and Opera. The @supports at-rule will be helpful in enabling us to create separate layouts for browsers that support flexbox, and a different layout for those that don’t, all without resorting to hacks.

When flexbox is supported, it will be a long while before all of your users’ devices support flexbox. We will surely be seeing other new CSS features that, like flexbox, use new values for supported CSS properties—like display: flex;. @supports is similar to @media, but instead of matching browsers based on browser and device metrics, it will match based on browser CSS support:

@supports (display: flex) and (background-color: red) {

h1 {color: green;}

}

This will make all <h1>s green in browsers that support both display: flex; and background-color: red;. It will not make any background colors red. It just tests for support of properties and property values.

In the interim, some browsers have added some feature detection through the @media rules:

@media screen and (-webkit-transform-3d) {

h1 {

-webkit-transform: translateZ(0) rotate(5deg);

-webkit-animation: makemedizzy 1s infinite;

}

}

The preceding code matches all WebKit devices that support 3D transforms, rotating and animating the <h1>s within the document. The feature detection component of this media query is prefixed. This media query will match WebKit browsers that still support the vendor prefixing for the CSS transform property.[79] To match browsers that no longer need prefixing, use the following:

@media screen and (transform-3d) {

h1 {

transform: translateZ(0) rotate(5deg);

animation: makemedizzy 1s infinite;

}

}

Only browsers that support transform-3d will understand this media query.

You can also use the @media query to feature detect support for animation and transitions, with and without prefixes. This feature detection will eventually be replaced by @supports, described earlier. When implemented, @supports will support all properties and values. @media is limited to only three properties, and does not discern between property values.

So, why is @supports exciting, instead of just allowing browsers to ignore features they don’t support? @supports will allow you, for example, to lay out a site using flexbox, if supported, and columns if flexbox is not supported, without inadvertently sending columns to your flexedlayout.

Responsive Media

The flexible layout lets you easily create fluid layouts. Unfortunately, flexbox properties are not fully supported on all browsers. CSS 2.1 did provide all the tools for creating fluid layouts: flexbox just makes it easier.

Until flexbox is fully supported on the overwhelming majority of devices, it will still be easier to work with percentages instead of pixels to create layouts that adapt to your screen size. Creating fluid layouts seems more difficult once you introduce fixed-width elements, but there are a few tricks that simplify what may seem like a challenge.

A common example of needing a flexible image is the header image: you want it to take up the whole width of the screen, no matter the device size, without zooming the page in, making the text illegible on smaller devices. You need your image to be 100% of the width whether you have a 440 px screen or a 640 px screen. The solution is so simple it is actually delineated in the preceding sentence:

header img {

max-width: 100%;

height: auto;

}

You can declare fixed-width media, like images and video, to be of any width relative to the width of their parent container. In the preceding case, instead of displaying in the image’s default width (width: auto;) it will not grow bigger than its parent. Because we defined max-widthinstead of width, it will stop growing once it reaches the media’s actual width.

If you don’t mind showing a low-resolution image, you can use min-width: 100% or simply width: 100%;. Unless you are stretching a gradient or other stretchable image, do not declare an actual value for the height, as it will likely distort the aspect ratio of the image. That is why we include height: auto;.

Serving Images

Growing and shrinking images is not the panacea that solves all our mobile image issues. Mobile devices tend to be very limited when it comes to memory. Yet, we have high DPI devices that look really crisp when we serve them larger images—which use up more bandwidth and more memory. Because of limited memory, latency, and different device resolutions, serving images in our current mobile landscape is no longer cut and dried like it was when we only worried about desktop.

Retina®: high pixel-density displays

The iPhone 4, released in 2009, was the first device with a “Retina Display” of 326 dots per inch (DPI). The third-generation iPad, released in 2012, has double the previous version’s resolution with 264 DPI. The first laptop with high DPI was the Retina Display MacBook.

The original iPhone is 320 × 480 px and the original iPad was 768 × 1024 px. The first Retina versions of these devices were 640 × 960 px and 1536 × 2048 px, respectively. The size of the screens remained the same, but four pixels were displayed in the area that used to require a single pixel, creating a better resolution, denser screen.

A Retina Display is a high-definition display. I have capitalized Retina Display as it is a trademark from Apple meaning twice the resolution. It doesn’t actually mean a specific DPI value. Nor is high resolution limited to Apple devices. In fact, there are devices currently on the market with higher resolution than the iPhone, but their manufacturers have to come up with nontrademarked descriptors. The correct term is the nontrademarked “high resolution.”

With the release of the iPhone 4, web developers had to handle Retina Displays, a.k.a. high-resolution displays.

A device pixel is the smallest point of color displayed by a device, which is not exactly the same as a CSS pixel. Understanding the difference may help make things less confusing.

The pixel density is the number, or ratio, of device pixels per CSS pixel. A device may be able to display more than one (or less than one) device pixel in a CSS pixel. The resolution, on the other hand, is the product of the width and height of the device, in pixels.

The density per inch, or DPI, is the density display. The DPI is the quotient of the pixels displayed by the size in inches by the device. For example, a 4-inch wide device displaying 800 px is:

800 pixels ÷ 4 inches = 200px per inch

A device that has a DPI of 200 pixels per inch or greater is considered high DPI. If we take the iPad as an example, where the standard device was originally 768 × 1024 px, and the high DPI standard size version (not the mini) was 1536 × 2048 px, even though they were both the same size at 7.75 inches tall,[80] they are 132 DPI and 264 DPI, respectively:

1024 pixels ÷ 7.75 inches = 132 pixels per inch

2048 pixels ÷ 7.75 inches = 264 pixels per inch

The higher the DPI, the smaller the device pixel, which allows for higher quality images. “Higher quality” images are generally just larger images. Continuing with our example, the newer iPad has double the pixels and therefore benefits from images with four times the pixels to display the same image as the original iPad.

Images made with image-editing software, the JPEGs and PNGs and GIFs, sent over the wires, all have the same resolution of 72 px per inch. A low-resolution image displayed on a high-resolution screen can seem blurry.

To fill the background of a full browser, we set the height and width of the image in the foreground or background to be the original, low DPI dimensions, but we can serve images four times the size (twice as tall and twice as wide leads to images that are four times the original size) to make them appear beautifully crisp in high resolution devices.

This causes a few issues: you don’t need to send high DPI images to low DPI devices, as larger images use more bandwidth and the larger the file size the more memory the file consumes—and mobile devices are notoriously limited in terms of memory.

While we have no current API to determining how much memory is left, we can determine the bandwidth. You may choose to only send high DPI images only to users on a fast network with a high resolution display that can make use of larger images using a combination of JavaScript and CSS. Media queries based on connection speed have been proposed, but the traction isn’t there (yet?).

Connection speed

You can query the current connection type with JavaScript with navigator.connection.type, which returns the values of UNKNOWN, ETHERNET, WIFI, CELL_2G, CELL_3G, or CELL_4G:

var connection, speed;

var connection = navigator.connection ||

navigator.mozConnection ||

navigator.webkitConnection ||

{'type':'0'};

// set download speed

switch(connection.type) {

case connection.CELL_3G: // 3G

speed = 'medium';

break;

case connection.CELL_2G: // 2G

speed = 'slow';

break;

default:

speed = 'fast';

}

document.body.classList.add(speed);

You can change the class of the body and target whether you import high DPI images based on that class:

@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) {

body.fast {

background-image: url(../hidpi/bgimg.jpg);

}

}

Note that on some devices, images larger than 1024 px will tile in memory.

background-size

When including higher resolution images, you still want them to occupy the same physical space as they would in non-Retina devices. The background-size property, discussed in Chapter 9, enables us to ensure that the site appears the same no matter the resolution.

Indeed, different displays show 72, 96, or 144 DPI. Whether you’re sending the regular image or the Retina display image that is four times the size, you still want the background images to display as if they were the same size, just crisper if the device can handle it.

Use the background-size property to ensure that both your 100 × 100 px and your 200 × 200 px Retina image display as 100 × 100 px:

.icon {

background-size: 100px 100px;

background-image(../lodpi/icon.jpg);

}

@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) {

.fast .icon {

background-image(../hidpi/icon.jpg);

}

}

In the preceding code, even though the high DPI image may be four times the size as the low DPI image, they will occupy the same space.

Data URIs

In some cases, using data URIs for images may be more performant than making additional HTTP requests to serve regular images. Data URIs are string representations of binary image files.

Because data URIs use strings to represent binary data, their size can become fairly large, if not huge. For small images, like avatars and favicons, a data URI will likely improve download performance by reducing the number of HTTP requests (and possibly DNS lookups). For large images, like high DPI fullscreen backgrounds, it might be worth the DNS look up and HTTP request. There is no “right” solution.

A site like Twitter, where they are displaying the avatars of all the people who follow you, all the people you follow, and all the people they retweet, would not be able to successfully create a cacheable sprite (described in the next section, ) of avatars. People are able to change their Twitter avatars whenever they like. So, even if Twitter did create sprites, they would have to be updated with each request. For Twitter’s goals, data URIs for avatars might make sense. Their site requires small, noncacheable, nonspriteable images. A separate HTTP request for each of those images would render much slower than including the images as data URIs in a single text file with a single HTTP request.

Sprites

A sprite is a larger image file containing several smaller images. Sprites are used to reduce the number of HTTP requests and DNS lookups, and increase the download speeds of web pages for background-image images. Sprites can also be used in animation.

For example, if your site uses many colorful icons or displays the favicons of a plethora of rating sites, and you know what the limited number or recurring icons will be, you can put all of those icons in one image. Then, using background-position, show just sections of that image to the user. If you have single color icons, you may be able to use font icons, described . An example of such a sprite is displayed in Figure 11-5.

Sprite of popular application and website icons

Figure 11-5. Sprite of popular application and website icons

Sprites can also be used in animation in conjunction with the steps() animation-timing-function values, as we did with the Lemming .

To create a dancing icon, we can use the sprite in Figure 11-6 with the following CSS:

.psy {

width: 22px;

height: 40px;

background-image: url(sprite.png);

animation:

dance 4s steps(23, start) infinite,

movearound 9s steps (23, start) infinite 45ms;

}

@keyframes dance {

0% {

background-position: 0 0;

}

100% {

background-position: −506px 0;

}

}

@keyframes movearound {

0% {

transform: translatex(−300px);

}

100% {

transform: translatex(300px);

}

}

Sprite for character animation

Figure 11-6. Sprite for character animation

In the dance animation, we change the background position. The character we are animating is only 22 px wide by 40 px tall. Approximately every 45 ms the background image jumps 22 px to the left, showing the icon to the right of the previous icon. In this way we can make the div appear to dance. An animation that jumped three times faster would be less janky, but would have required over 40 frames of artwork.

Small sprites reduce the number of HTTP requests, reduce the occurrence of flickering caused by delay in image loads, plus they can be cached and can be used in animation. Large sprites risk causing issues though, due to memory constraints on mobile devices. Use sprites wisely, and preferably keep them under 1024 px.

image-set()

Safari 6 and Chrome 21 support image-set(), which enables you to serve different background images for different pixel density displays:

body > header {

background-image: url(images/header.png);

background-image: -webkit-image-set(url(images/header.png) 1x,

url(images/header_2x.png) 2x);

height:60px;

}

According to the CSS Working group, this is not ready for implementation. I am including it here just to make you aware. Hopefully this will soon be implementable, as images are easier to manage in a srcset syntax than media query blocks.

Font icons

Many websites and applications use a plethora of small images that are so common they are represented in font files. For example, the flower on the back side of the CubeeDoo cards, and the lighter shapes in the “shapes” theme are actually characters in CubeeDoo’s default font, as displayed in Figure 11-7.

Icons used in CubeeDoo

Figure 11-7. Icons used in CubeeDoo

There are over 10,000 different characters in the default character sets on the majority of devices. Chances are you’ll find the shape you need, be it an envelope, arrow, or other. Since font icons are just characters, you can easily change the size and color of the icon with CSS. Font icons scale without distortion, change color without image-editing software, and, if part of fonts found on most devices, load without requiring additional HTTP requests.

When you find the character you need, you can include it directly in the HTML, as generated content using ::before and/or ::after, or include the character in SVG and include it as a background image or data URI.

Whenever possible, choose font icons over images. Many companies create their own character set for their sites with their own unique iconography.

CSS Masking: Creating Transparent JPEGs

There is one other trick I would like to cover: masking. Sometimes you need to use a PNG because you need transparency. However, a detailed PNG produces a much larger file size than a JPEG. But JPEGs don’t have transparency, so you may feel like you need to use a PNG to provide that transparency. Masking allows you to create transparent JPEGs, so you can serve a JPEG instead of a PNG, saving a lot of bandwidth and memory.

CSS masking enables us to overlay a smaller filesize JPEG with a monotone 8-bit transparent PNG to create transparent sections in the original JPEG at display. By masking a JPEG with a transparent PNG, we can create transparencies based off the alpha of an image, greatly reducing bytes needed:

div {

background-image:url(images/smallerFileThanPNG.jpg);

-webkit-mask: url(images/partToShow.png);

}

While downloading two images—the background image and the mask—may add an additional HTTP request, the savings of bytes can be worth it. In the online chapter resources example, the original high DPI PNG was 551 KB. Converting the PNG to a JPEG with no transparency brought the image to 88 KB, and the monotone luminance mask is only 4 KB, for a total of 92 KB: a huge savings over the transparent PNG.

Masking was originally a WebKit-only property, but is being standardized by the W3C. The original syntax is still prefixed, and only supported in WebKit, with basic support in all WebKit mobile browsers.

Client Hints

Client-Hints are not implemented yet, or even in a draft specification form. But since they may be coming, and if they do, it will be awesome, I am mentioning it here.

Client-Hints are hints that the browser will send to the server along with the request header. When supported, it is expected to pass three values, dpr, dw, and dh, for device pixel ratio, device width, and device height:

Client-Hints: dh=1280, dw=768, dpr=2.0

The browser will be able to inform the server via the request header. The server can then serve the most appropriate image sizes based on the browser specifications.

This may sound like it is similar to user-agent sniffing. Browser vendors copied each other’s UA strings to bypass the incomplete UA sniffing-routines deployed on tons of websites. Because developers did so much UA sniffing, browser vendors include each other’s strings to get some semblance of support. Client-Hints will hopefully not have this problem, as there is likely little reason, other than to reduce bandwidth consumption, to lie about the height or width of your device.


[76] The various column properties are supported in all mobile browsers including Opera Mini, starting with IE10, and must be prefixed for WebKit and Firefox.

[77] WebKits don’t support the round or space value, replacing them with repeat instead (which is better than failing, I guess).

[78] Include the vendor prefix on the value rather than the property, as display is not experimental, but the new values are in some browsers. Use display: -webkit-flex; in WebKit browsers and display: -ms-flexbox; in IE 10. Opera and Firefox 20+ do not require prefixed values.

[79] It may also match some pre-Blink Opera browsers, as Opera Presto has added limited support for some WebKit vendor-prefixed properties and values.

[80] When you see the screen size listed as 9.5 or 9.7 inches, that is the diagonal length of the screen from top-left corner to bottom-right corner.