About this book

Who this book is for

The primary target audience for this book is intermediate to advanced CSS developers. By getting the introductory stuff out of the way, we can explore more advanced use cases of modern CSS features and combinations thereof. This, however, means that quite a few assumptions have been made about you, dear reader:

  • I assume you know CSS 2.1 inside out, and have a few years of experience with it. You don’t struggle to understand how positioning works. You’ve used generated content to enhance your designs without extraneous markup or images. You don’t resort to plastering !important all over your code because you actually understand specificity, inheritance, and the cascade. You know what the different parts of the box model are, and you are not fazed by margin collapsing. You are familiar with the different length units and know when it’s best to use each one.

  • You’ve read quite a bit about the most popular CSS3 features, online and/or in books, and have tried them out, even if only in personal projects. Even if you haven’t studied them in depth, you know how to create rounded corners, add a box-shadow, or create a linear gradient. You’ve played with some basic 2D transforms, and have enhanced interactions with basic transitions and animations.

  • You have seen SVG and know what it’s used for, even if you don’t quite know how to write it yourself.

  • You can read and understand basic, vanilla JavaScript, such as creating elements, manipulating their attributes, and adding them to the document.

  • You’ve heard of CSS preprocessors and know what they can do, even if you choose not to use one.

  • You’re familiar with middle school level math, such as square roots, the Pythagorean theorem, sines, cosines, and logarithms.

However, to enable readers that don’t meet all these assumptions to enjoy this book, there is a “Prerequisites” box in the beginning of some secrets, briefly listing any CSS knowledge or previous secrets that need to be known for the secret to make sense (excluding CSS 2.1 features, otherwise the box would get really long). It looks like this:

This way, even if certain things are not already known, one can read up about them and come back to the secret afterward. As long as their prerequisites are met, the secrets can actually be read in any order, though there is value in reading them in the book order, as a lot of thought has been put into what the optimal order is.

Note that I mentioned “CSS developers” and that “design skills” are not in the list of assumptions above. It’s important to note that this is not a design book. While it unavoidably touches on certain design principles and describes a few UX improvements, CSS Secrets is first and foremost a book about solving problems with code. CSS might have a visual output, but it is still code, just like SVG, WebGL/OpenGL, or the JavaScript Canvas API is code, not design. Writing good, flexible CSS requires the same kind of analytical thinking that programming does. Nowadays, most people use preprocessors for their CSS, with variables, math, conditionals, and loops, so it’s almost starting to look like programming!

This is not to imply that designers are discouraged from reading this book. Anybody who has sufficient coding experience with CSS can benefit from it, and there are many talented designers who can also write excellent CSS code. However, it’s important to note that teaching you how to improve the visual design or usability of a website is not among the goals of this book, even if it happens as a side effect.

Format & conventions used

The book consists of 47 “secrets,” grouped by topic in seven chapters. These secrets are more or less independent and—as long as their prerequisites are met—can be read in any order. The demos in every secret are not complete websites, or even parts thereof. They are purposefully as small and simple as possible, in order to facilitate understanding. The assumption is that you already know what you want to implement. The purpose of this book is not to give design ideas, but implementation solutions.

image

FIGURE P.1. This is an example sidebar figure, introducing the great Sir Adam Catlace

Every secret is split into two or more sections. The first section, titled “The problem,” introduces a common CSS challenge that we are going to solve. Sometimes this introduction might describe widely popular solutions that are suboptimal (e.g., solutions that require a lot of markup, hardcoded values, etc.), and usually concludes with variations of the question “Is there a better way to achieve this?”

After introducing the problem, one or more solutions follow. This book was inspired by the CSS talks I have presented at various conferences so I tried to maintain the interactive presentation format as much as a book allows. Therefore, every solution is illustrated by a number of figures, demonstrating the visual output for every step of the solution that results in a visual change. Because figures are not always directly next to the text that describes what they demonstrate, they are numbered and referenced in the text. You can see an example of a figure in Figure P.1 and the current sentence was an example of a reference to it.

Notes, such as this one, provide additional information or explain a term mentioned in the text.

image This is a warning. Its purpose is to warn you (surprising, I know!) about possible false assumptions and certain things that could go wrong.

Inline code is denoted by monospace text and colors often have a small preview next to them as well (e.g., image #f06). Block-level code looks like this:

background: url("adamcatlace.jpg");

or this:

HTML

<figure>
    <img src="adamcatlace.jpg" />
    <figcaption>Sir Adam Catlace</figcaption>
</figure>

As you might have noticed, when the language of a code block is not CSS, it’s noted in the top-right corner. Also, when the example discussed only involves a single element, and no pseudo-classes or pseudo-elements are involved, there is usually no selector or braces ({}) included in the code blocks, for brevity.

All JavaScript examples in the book are vanilla JavaScript, with no frameworks or libraries required. There is only one helper function used, $$(), in order to make it easier to loop over a set of elements that match a certain CSS selector. The function’s definition is:

JS

function $$(selector, context) {
    context = context || document;
    var elements = context.querySelectorAll(selector);
    return Array.prototype.slice.call(elements);
}

Every secret includes one or more live examples that can be accessed with short, memorable URLs in play.csssecrets.io. The references to them look like this:

image PLAY! play.csssecrets.io/polka

It is strongly recommended that you check out the “Play!” examples, especially if you are confused by the techniques described or if you get stuck while following along.

image

Credit where it’s due: When a technique described was first documented by someone else in the community, credit will be given in a “Hat Tip” paragraph like this one, referencing the URL of the source as well. We all know that having to find the “References” section at the end of a book is a hassle, so these essentially provide references in context.

At the end of almost every secret you’ll find a list of related specifications that looks like this:

RELATED
SPECS

This includes references to all the specifications from which features were mentioned. However, just like the “Prerequisites” box, this does not apply to CSS 2.1 (w3.org/TR/CSS21), otherwise it would be listed in the “Related Specs” section of every single secret. This means that the few secrets that only discuss CSS 2.1 features have no “Related Specs” section at all.

Browser support & fallbacks

image

Possibly the biggest peculiarity of this book is the complete lack of browser compatibility tables. This was a conscious decision, as with today’s browser release cycles, such information is bound to get out of date before this book even hits the shelves. I believe that inaccurate browser support information is misleading, and is actually worse than no information.

However, most secrets described either currently have decent browser support and/or degrade gracefully. In cases where a technique described presently has particularly poor browser support, there is a “Limited Support” warning icon next to the relevant solution, like the one next to this paragraph. This should be enough to hint that you should not use the solution without looking up browser support for it and taking extra care for providing good fallbacks.

There are plenty of excellent websites containing up-to-date browser support information. Here are some suggestions:

Sometimes you might find that a certain feature is supported, but slightly differently across browsers. For example, it might need a vendor prefix, or slightly different syntax. Only the standards-compliant, unprefixed syntax will be included in the examples. However, you can almost always use different syntaxes alongside and let the cascade take care of which one wins. For this reason, always place the standard version last. For example, to get a vertical linear gradient from image yellow to image red, the book would only list the standard version:

background: linear-gradient(90deg, yellow, red);

However, if you want to support very old browsers, you might end up having to write something like the following:

background: -moz-linear-gradient(0deg, yellow, red);
background: -o-linear-gradient(0deg, yellow, red);
background: -webkit-linear-gradient(0deg, yellow, red);
background: linear-gradient(90deg, yellow, red);

You can read more on vendor prefixes, why they exist, and how to abstract them away from your code in the A story of ice, fire, and vendor prefixes” section on page 6.

Because the landscape of these differences is just as fluid as browser support, it is expected that things like this are part of your standard research before using a CSS feature and are not discussed further in the solutions presented.

Similarly, most of the time it’s good practice to provide fallbacks, so that your website doesn’t break in older browsers, even if it doesn’t look as fancy in them. These are not discussed extensively when they are obvious, as the assumption is that you know how the cascade works. For example, when specifying a gradient, such as the one just shown, you should also add a solid color version before all of them. A good idea for the solid color might be the average of the two gradient colors (in this case, image rgb(255, 128, 0)):

background: rgb(255, 128, 0);
background: -moz-linear-gradient(0deg, yellow, red);
background: -o-linear-gradient(0deg, yellow, red);
background: -webkit-linear-gradient(0deg, yellow, red);
background: linear-gradient(90deg, yellow, red);

However, sometimes it’s not possible to provide decent fallbacks through the cascade. As a last resort, you could use tools like Modernizr (modernizr.com), which adds classes like textshadow or notextshadow to the root element (<html>), so you can use them to target elements only when certain features are (not) supported, like so:

h1 { color: gray; }

.textshadow h1 {
    color: transparent;
    text-shadow: 0 0 .3em gray;
}

If the feature you are trying to create a fallback for is sufficiently new, you could use the @supports rule, which is the “native” Modernizr. For example, the preceding code would become:

h1 { color: gray; }

@supports (text-shadow: 0 0 .3em gray) {
     h1 {
        color: transparent;

        text-shadow: 0 0 .3em gray;
    }
}

However, for now, be wary of using @supports. By using it here we just limited our effect not only to browsers that support text shadows, but also to browsers that support the @supports rule—a much more limited set.

Last, but not least, there is always the option of using a few lines of home-baked JavaScript to perform feature detection and add classes to the root element in the same fashion as Modernizr. The main way to determine whether a property is supported is to check its existence on the element.style object of any element:

JS

var root = document.documentElement; // <html>

if ('textShadow' in root.style) {
    root.classList.add('textshadow');
}
else {
    root.classList.add('no-textshadow');
}

If we need to test for multiple properties, we can easily turn this into a function:

JS

function testProperty(property) {
    var root = document.documentElement;

    if (property in root.style) {
        root.classList.add(property.toLowerCase());
        return true;
    }


    root.classList.add('no-' + property.toLowerCase());
    return false;
}

If we want to test a value, we need to assign it to the property and check if the browser retains it. Because we are modifying styles here and not just testing for their existence, it makes sense to use a dummy element:

JS

var dummy = document.createElement('p');
dummy.style.backgroundImage = 'linear-gradient(red,tan)';

if (dummy.style.backgroundImage) {
    root.classList.add('lineargradients');
}
else {
    root.classList.add('no-lineargradients');
}

This can easily be converted to a function as well:

JS

function testValue(id, value, property) {
    var dummy = document.createElement('p');
    dummy.style[property] = value;

    if (dummy.style[property]) {
        root.classList.add(id);
        return true;
    }


    root.classList.add('no-' + id);
    return false;
}

Testing selectors and @rules is a bit more complex, but follows the same principle: when it comes to CSS, browsers drop anything they don’t understand, so we can check if a feature is recognized by dynamically applying it and checking if it was retained. Of course, keep in mind that a browser being able to parse a CSS feature offers no guarantee that the feature is correctly implemented, or even that it’s implemented at all.