As mentioned at the start of this chapter, two main methodologies, known as adaptive and responsive, are used for modern website development. Both share the concept of using breakpoints, which are the limits created by using media queries, at which point a change to the site layout is imposed.
The difference between adaptive and responsive methodologies comes down to how the site changes between breakpoints; adaptive is essentially a series of fixed-width layouts, whereas responsive uses flexible dimensions so even between breakpoints sites have fluidity.
My opinion is that, given the enormous variability in device screen sizes, making a series of fixed-width pages to accommodate even the most common configurations is probably a fool’s errand. The better approach, and the key to building responsively, is to use what’s known as fluid design, which involves using percentages for length values to make the dimensions of the elements on the page relative to the size of the viewport.
Making a page that’s a fixed 960px wide was acceptable when you knew your users were mostly using desktops or laptops with monitors at least 1024px wide, but those days are over, and designing that way nowadays means users with mobile devices see a scaled-down screen that they have to zoom in and out of and scroll around to see. Not the end of the world, but also not ideal.
Using percentages instead of fixed values means your page elements scale up or down along with the size of the viewport, making the content flow inside the boundaries of the screen—hence the name, fluid. Combine this with media queries for content or devices, and you have the very core of responsive design and a tailored, sympathetic experience for your user, regardless of his or her device.
Working with percentages is not without its problems, however, one of which is the difficulty of mixing length units. Consider, for example, a page with three columns: a central column that’s 50 percent wide, and a column on either side that’s 25 percent wide:
E, G { width: 25%; } F { width: 50%; }
Now say you want to add 20px of padding to the left and right of the central column, to act as a gutter, but you don’t want this width to be variable—it must always be exactly 20px:
F {
padding: 0 20px;
width: 50%;
}
Maybe you can already see the problem. As you know, the CSS box model makes padding, borders, and margins extra to the content’s dimensions, so now these elements have a combined width of 100 percent, plus 40px, making them wider than their container.
A further problem when using percentage widths is that nesting elements leads to some pretty weird numbers. For example, imagine you have an element that you want to be 85 percent of the viewport, and inside that there’s a child element you want to be 55 percent of the viewport. Unfortunately, as percentages are relative, you can’t just set the width to be 55 percent; you have to calculate 55 percent of 85 percent, which gives you this:
E { width: 85%; } F { width: 64.70588%; }
Note that I stopped after the fifth decimal place. So working in this way can involve a lot of calculations, and what’s even worse is the various browsers round these numbers up or down in different ways, so pixel perfection can’t be guaranteed.
This drawback might be enough to put you off of working responsively, but a few features in CSS3 should help eliminate these problems.
You can, to some degree, work around the unit mixing problem that I just mentioned by using the CSS box-sizing property. To explain, let me recap the current code example: I have three columns with a 20px gap between them, meaning the total width is greater than that of their parent, causing them to overflow:
E, G { width: 25%; } F { padding: 0 20px; width: 50%; }
With box-sizing, you can change the boundary from where the width is measured—so the stated width includes the border and the padding. In this example, I can change element F so its width includes the padding, using the border-box value for box-sizing:
F {
box-sizing: border-box;
padding: 0 20px;
width: 50%;
}
Now the entire element F, including the padding, is 50 percent wide, so the combined width of the elements is 100 percent again, making them fit neatly inside their container.
Some people find box-sizing so handy that they advocate applying it to every element, using the universal selector:
* { box-sizing: border-box; }
I think this is overkill and could cause unwanted difficulties, so my preferred approach is to apply it only where it’s actually required:
div, .etc { box-sizing: border-box; }
Using box-sizing does have its limitations, however; for one, it doesn’t affect margins at all. To accommodate that, having some way to perform dynamic calculations on length values instead would be better.
When mixing units, a new value function eliminates all of the problems mentioned in the previous section. This function is called calc(), and with it, you can perform simple calculations using any numbered length units. At its very simplest, the argument supplied to the function would be two figures and a mathematical operator:
E { height: calc(10px + 10px); }
For a more practical illustration of what it can do, let’s return once again to the simple three-column example. Say you now want to have a 4px border on the left and right sides of the central column as well as the 10px padding and 20px margin. You could use box-sizing to cover the border and padding, as before, but this time also use the calc() function to subtract the left and right margins from the width:
F {
border: 4px solid black;
border-width: 0 4px;
box-sizing: border-box;
margin: 0 20px;
padding: 0 10px;
width: calc(50% - 40px);
}
You could, instead of box-sizing, use further calc() values to set the width and even mix up the values a little more by using different length units. In the following example, I use em units for the margin and then an extra calc() function to calculate the combined border, padding, and margin values, before subtracting them all from the total:
F {
border: 4px solid black;
border-width: 0 4px;
margin: 0 20px;
padding: 0 1em;
width: calc(50% - calc(48px + 2em));
}
You’re not limited to using calc() only for width or height properties, however; you can use it on any property or function where length values are permitted.