Introducing CSS3 - HTML5 & CSS3 FOR THE REAL WORLD (2015)

HTML5 & CSS3 FOR THE REAL WORLD (2015)

Chapter 6 Introducing CSS3

The content layer is done. Now it’s time to make it pretty. The next four chapters focus on presentation. In this one, we’ll start by covering some basics: we’ll first do a quick overview of CSS selectors, and see what’s been added to our arsenal in CSS3. Then, we’ll take a look at a few new ways of specifying colors. We’ll dive into rounded corners, drop shadows, and text shadows—tips and tricks enabling us to style pages without having to make dozens of rounded-corner and text images to match our designs.

But first, we need to ensure older browsers recognize the new elements on our page, so that we can style them.

Getting Older Browsers on Board

As we mentioned back in Chapter 2, styling the new HTML5 elements in older versions of Internet Explorer requires a snippet of JavaScript called an HTML5 shiv. If you’re using the Modernizr library detailed in Appendix A you'll have the option to include the shiv, so in that case you'll be fine.

Even with this JavaScript in place, though, you’re not quite ready to roll. IE through version 8 will now be aware of these new elements, but these archaic browsers will still be without any default styles. In fact, this will be the case for previous versions of other browsers as well; while they may allow arbitrary elements, they’ve no way of knowing, for example, that article should be displayed as block-level and mark should be displayed inline. Because elements render as inline by default, it makes sense to tell these browsers which elements should be block-level.

This can be done with the following CSS rule:

article, aside, figure, footer, header, main, nav, section {

display: block;

}

With this CSS and the required JavaScript in place, all browsers will start off on an even footing when it comes to styling HTML5 elements.

CSS3 Selectors

Selectors are at the heart of CSS. Without selectors to target elements on the page, the only way to modify the CSS properties of an element would be to use the element’s style attribute and declare the styles inline, which is awkward and unmaintainable. So we use selectors. Originally, CSS allowed the matching of elements by type, class, and/or ID. This required adding class and ID attributes to our markup to create hooks and differentiate between elements of the same type. CSS2.1 added pseudo-elements, pseudo-classes, and combinators. With CSS3, we can target almost any element on the page with a wide range of selectors.

In the descriptions that follow, we’ll be including the selectors provided to us in earlier versions of CSS. They are included because, while we can use CSS3 selectors, selectors that predate CSS3 are also part of the CSS Selectors Level 3 specification and are still supported, as CSS Selectors Level 3 expands on them. Even for those selectors that have been around for quite some time, it’s worth going over them here, as there are some hidden gems in the old spec that few developers know. Note that all modern browsers, including IE9 and above, support all CSS3 selectors.

Relational Selectors

Relational selectors target elements based on their relationship to another element within the markup. All of these are supported since IE7+, and in all other major browsers:

Descendant combinator (E F)

You should definitely be familiar with this one. The descendant selector targets any element F that is a descendant (child, grandchild, great grandchild, and so on) of an element E. For example, ol li targets li elements that are inside ordered lists. This would include li elements in a ulthat’s nested in an ol, which might not be what you want.

Child combinator (E > F)

This selector matches any element F that is a direct child of element E—any further nested elements will be ignored. Continuing the example, ol > li would only target li elements directly inside the ol, and would omit those nested inside a ul.

Adjacent sibling, or next sibling selector (E + F)

This will match any element F that shares the same parent as E, and comes directly after E in the markup. For example, li + li will target all li elements except the first li in a given container.

General sibling or following sibling selector (E ~ F)

This one’s a little trickier. It will match any element F that shares the same parent as any E and comes after it in the markup. So, h1 ~ h2 will match any h2 that follows an h1, as long as they both share the same direct parent—that is, as long as the h2 is not nested in any other element.

Let’s look at a quick example:

<article>

<header>

<h1>Main title</h1>

<h2>This subtitle is matched </h2>

</header>

<p> blah, blah, blah …</p>

<h2>This is not matched by h1 ~ h2, but is by header ~ h2</h2>

<p> blah, blah, blah …</p>

</article>

The selector string h1 ~ h2 will match the first h2, because both the h1 and h2 are children, or direct descendants, of the header. The next h2 you’ll see in the code snippet doesn’t match, since its parent is article, not header. It would, however, match header ~ h2. Similarly, h2 ~ p only matches the last paragraph, since the first paragraph precedes the h2 with which it shares the parent article.

Note: Why is there no “parent” selector?

You’ll notice that up to this point there has been no “parent” or “ancestor” selector, and there’s also no “preceding sibling” selector. The performance of the browser having to go backwards up the DOM tree, or recurse into sets of nested elements before deciding whether or not to apply a style, prevented the ability to have native "up the DOM tree" selectors.

jQuery included :has() as an ancestral selector. This selector is being considered for CSS Selectors Level 4, but has yet to be implemented in any browser. If and when it is implemented, we will be able to use E:has(F) to find E that has F as a descendant, E:has(> F), to find Ethat has F as a direct child, E:has(+ F), to find E that directly precedes a sibling F, and similar.

Looking through the stylesheet for The HTML5 Herald, you’ll see a number of places where we’ve used these selectors. For example, when determining the overall layout of the site, we want the three-column divs to be floated left. To avoid this style being applied to any other divs nested inside them, we use the child selector:

main > div {

float: left;

overflow: hidden;

}

As we add new styles to the site over the course of the next few chapters, you’ll be seeing a lot of these selector types.

Attribute Selectors

CSS2 introduced several attribute selectors. These allow for matching elements based on their attributes. CSS3 expands upon those attribute selectors, allowing for some targeting based on pattern matching. CSS Selectors Level 4 adds a few more:

E[attr]

Matches any element E that has the attribute attr regardless of the attribute’s value. We made use of this back in Chapter 4 to style required inputs; input:required works in the latest browsers, but input[required] has the same effect and works in IE7 and IE8 as well.

E[attr=val]

Matches any element E that has the attribute attr with the exact value val. While not new, it’s helpful in targeting form input types; for instance, targeting checkboxes with input[type=checkbox].

E[attr|=val]

Matches any element E whose attribute attr either has the value val or begins with val-. This is most commonly used for the lang attribute. For example, p[lang|="en"] would match any paragraph that has been defined as being in English whether it be UK or US English with <p lang="en-uk"> or <p lang="en-us">.

E[attr~=val]

Matches any element E whose attribute attr has within its value the full word val, surrounded by whitespace. For example, .info[title~=more] would match any element with the class info that had a title attribute containing the word “more,” such as “Click here for more information.”

E[attr^=val]

Matches any element E whose attribute attr starts with the value val. In other words, the val matches the beginning of the attribute value.

E[attr$=val]

Matches any element E whose attribute attr ends in val. In other words, the val matches the end of the attribute value.

E[attr*=val]

Matches any element E whose attribute attr matches val anywhere within the attribute. It is similar to E[attr~=val], except the val can be part of a word. Using the same example as before, .fakelink[title~=info] {} would match any element with the class fakelink that has a titleattribute containing the string info, such as “Click here for more information.”

In these attribute selectors, the value of val is case-sensitive for values that are case sensitive in HTML. For example, input[class^="btn"] is case sensitive as class names are case sensitive, but input[type="checkbox"] is not case sensitive, as the type value is case-insensitive in HTML.

The value does not have to be quoted if the value is alphanumeric, with some exceptions. Empty strings, strings that begin with a number, two hyphens, and other quirks need to be quoted. Because of the exceptions, it’s a good idea to make a habit of always including quotes for those times when you do need them.

In CSS Selectors Level 4, we can have case insensitivity by including an i before the closing bracket, E[attr*=val i].

Pseudo-classes

It’s likely that you’re already familiar with some of the user interaction pseudo-classes, namely :link, :visited, :hover, :active, and :focus.

Important: Key Points to Note

· There are security issues the :visited pseudo-class can pose, so browsers do not support all CSS properties on visited links. Without these limitations, malicious sites could apply a style to a visited link, such as a unique background image for each visited link, to check whether popular sites or banks have been visited by the user. This allows the attacker to glimpse the user’s browsing history without their permission. As a result, modern browsers limit the styles that can be applied with :visited.

The spec explicitly condones these changes, saying: “UAs [User Agents] may therefore treat all links as unvisited links, or implement other measures to preserve the user’s privacy while rendering visited and unvisited links differently.”

· For better accessibility, add :focus wherever you include :hover as not all visitors will use a mouse to navigate your site.

· :hover can apply to any element on the page—not just links and form controls.

· :focus and :active are relevant to links, form controls, content editable elements, and any element with a tabindex attribute.

While it’s likely you’ve been using these basic pseudo-classes for some time, there are many others available. Several of these pseudo-classes have been in the specification for years, but weren’t supported (or commonly known) until browsers started supporting the new HTML5 form attributes that made them more relevant.

The following pseudo-classes match elements based on attributes, user interaction, and form control state:

:enabled

A user interface element that’s enabled, which is basically any form control that supports the disabled attribute but doesn’t currently have it applied.

:disabled

Conversely, a user interface element that is disabled: any form control that supports the disabled attribute and currently has it applied.

:checked

For radio buttons or checkboxes that are selected or ticked.

:indeterminate

For form elements that are neither checked nor unchecked. For example, if you tick a check all checkbox to select a group of checkboxes, then deselect some but not all of the checkboxes in the group, the check all could be set to the indeterminate state (with JavaScript) to indicate that it’s neither checked nor unchecked.

:target

This selector singles out the element that is the target of the currently active intrapage anchor. That sounds more complicated than it is: you already know that you can have links to anchors within a page by using the # character with the ID of the target. For example, you may have <a href="#content">Skip to content</a> link in your page that, when clicked, will jump to the element with an ID of content.

This changes the URL in the address bar to thispage.html#content—and the :target selector now matches the element in the document that has content as its ID. It's as if you had included, temporarily, the selector #content. We say temporarily because as soon as the user clicks on a different anchor, :target will match the new target.

:default

Applies to one or more UI elements that are the default among a set of similar elements. For example, the one radio button in a group of same-named radio buttons that was checked on page load will continue to match :default after another radio button in the same-named group is selected. Similarly, checkboxes that are selected on page load will continue to match :default after they are unchecked.

:valid

Applies to elements that are valid, based on the type, pattern, or other input attributes (as we discussed in Chapter 4).

:invalid

Applies to empty required elements and elements failing to match the requirements defined by the type or pattern attributes.

:in-range

Applies to elements with range limitations where the value is within those limitations. This applies, for example, to date/time, number, and range input types with min and max attributes. When the value is null, it is :in-range.

:out-of-range

The opposite of :in-range: elements whose value is outside the limitations of their range. Missing values are not out of range, as they are empty.

:required

Applies to form controls that have the required attribute set.

:optional

Applies to all form controls that do not have the required attribute.

:read-only

Applies to elements whose contents are unable to be altered by the user. This is most elements other than those with the contenteditable attribute set and form fields.

:read-write

Applies to elements whose contents are user-alterable, such as contenteditable components and writable input fields.

Browser support for these attributes is complete in browsers that support the attributes in their form controls; in other words, browsers that support required and pattern also support the associated :valid and :invalid pseudo-classes.

IE8 and earlier lack support for :checked, :enabled, :disabled, and :target. The good news is that IE9 does support these selectors, but not the user-interface selectors. IE10 and IE11 support :indeterminate, :required, and :optional, but not :default, :in-range, :out-of-range, :read-only, or :read-write.

While support is still lacking, JavaScript libraries such as Selectivizr can help in targeting these pseudo-classes in Internet Explorer.

Structural Pseudo-classes

So far, we’ve seen how we can target elements based on their attributes and states. CSS3 also enables us to target elements based simply on their location in the markup. These selectors are grouped under the heading structural pseudo-classes.

These might seem complicated right now, but they’ll make more sense as we look at ways to apply them later on. These selectors are supported in IE9 and newer, as well as current and older versions of all the other browsers—but not in IE8 and below:

:root

The root element, which is the html element in our HTML files.

E:nth-child(n)

The element E that is the nth child of its parent. The n parameter is explained in the note below.

E:nth-last-child(n)

The element F that is the nth child of its parent E, counting backwards from the last one. li:nth-last-child(1) would match the last item in any list—this is the same as li:last-child (see the note below).

E:nth-of-type(n)

The element that is the nth element of its type in a given parent element.The difference between :nth-child and :nth-of-type is explained in the note below.

E:nth-last-of-type(n)

Like nth-of-type(n), except counting backwards from the last element in a parent.

Note: Parameters of Structural Selectors

There are four pseudo-classes that take the equation an+b as a parameter in parentheses, or the keywords odd and even. The structural pseudo-classes include :nth-child(an + b), :nth-last-child(an + b), :nth-of-type(an + b), and :nth-last-of-type(an + b). In the equation an+b, a is the multiplier as an integer, b is the offset as an integer, and n is always the variable n.

In the simplest case, you can pass an integer. For example, E:nth-of-type(3) will target the third E element child of a single parent element. You can pass one of the two keywords odd or even, targeting every other element. You can also, more powerfully, pass a number expression such as E:nth-of-type(3n+1). 3n means every third element, defining the frequency, and +1 is the offset. The default offset is zero, so where :nth-of-type(3n) would match the 3rd, 6th, and 9th elements in a series, :nth-of-type(3n+1) would match the 1st, 4th, 7th, and so on.

Negative offsets are also allowed. CSS is based on linguistic languages, not programming languages, so the count starts at 1 not 0. There can be no space between the multiplier a and the variable n, and the offset must come last.

With these numeric pseudo-classes, you can pinpoint which elements you want to target without adding classes to the markup. The most common example is a table where every other row is a slightly darker color to make it easier to read. We used to have to add odd or even classes to every tr to accomplish this. Now, we can simply declare tr:nth-of-type(odd) to target every odd line without touching the markup. You can even take it a step further with three-colored striped tables: target tr:nth-of-type(3n), tr:nth-of-type(3n+1), and tr:nth-of-type(3n+2) and apply a different color to each.

E:first-child

The element E if E is the first child of its parent. This is the same as E:nth-child(1).

E:last-child

The element E if E is the last child of its parent, same as E:nth-last-child(1).

E:first-of-type

The same as :nth-of-type(1).

E:last-of-type

The same as :nth-last-of-type(1).

E:only-child

Element E if E is the only child of its parent.

E:only-of-type

Element E if E is the only element of type E that is a direct child of its parent element.

Note: Child versus Type

In employing the structural selectors of nth-of-type and nth-child, it’s important to understand what “child” and “type” mean in this case. “Child” looks at all the child elements that match the count and check if the precursor is a match. “Type” looks at all the elements that match the precursor first, then matches based on the count.

In the case of p:nth-child(3n), the browser looks at every third child of a parent. If that child is a p, there is a match; if not, no match. In the case of p:nth-of-type(3n), the browser looks at all the p children of the parent, and matches every third p.

Structural pseudo-classes are based on the parent, and restart counting for each new parent. They only look at elements that are the direct children of the parent. Text nodes are not part of the equation.

E:empty

An element that has no children; this includes text nodes, so <p>hello</p> and <p> </p> will not be matched by p:empty, but <p></p> and <p><!-- comment --></p> will be. This selector also matches empty or void elements, such as <br> and <input>. In CSS Selectors Level 4, we’ll getp:blank that will match <p> </p>.

E:lang(en)

An element in the language denoted by the two-letter abbreviation, such as en. Unlike E:[lang|=en], where the lang attribute must be present as an attribute of element E, E:lang(en) will match E if the language was declared on the element itself or any ancestor.

E:not(exception)

This is a particularly useful one: it will select elements that don’t match the selector in the parentheses.

Selectors with the :not pseudo-class match everything to the left of the colon, and then exclude from that matched group the elements that also match what’s to the right of the colon. The left-hand side matching goes first. For example, p:not(.copyright) will match all the paragraphs in a document first, and then exclude all the paragraphs from the set that also have the class of copyright. You can string several :not pseudo-classes together. input:not([type=checkbox]):not([type=radio]) will match all input elements on a page except those that are of typecheckbox or radio.

Pseudo-elements and Generated Content

In addition to pseudo-classes, CSS gives us access to pseudo-elements. Pseudo-elements allow you to target text that is part of the document, but not otherwise targetable in the document tree. Pseudo-classes enable us to target existing elements. Pseudo-elements, on the other hand, enable us to target content based on the structure of the document when there are no actual elements or DOM nodes to target.

For example, all text nodes have a first letter and a first line, but how can you target them without wrapping them in a span, especially when you don’t know exactly where the first line will wrap? CSS provides the ::first-letter and ::first-line pseudo-elements that match the first letter and first line of a text node respectively. These can alternatively be written with just a single colon: :first-line and :first-letter.

Note: Why bother with the double colon?

The double colon is the correct syntax, but the single colon is what IE supported through IE8. All other browsers support both. Even though :first-letter, :first-line, :first-child, :before, and :after have been around since CSS2, these pseudo-elements in CSS3 have been redefined using double colons to differentiate them from pseudo-classes.

Generated Content

The ::before and ::after pseudo-elements don’t refer to content that exists in the markup, but rather to a location where you can insert additional content, generated right there in your CSS. While this generated content won’t become part of the DOM, it can be styled.

To generate content for a pseudo-element, use the content property. For example, let’s say when printing a document that you wanted all external links on your page to be followed by the link’s URL in parentheses, enabling users to know where the links led even when they’re unclickable as they’re looking at a printed page. Rather than hardcoding the URLs into your markup, you can use the combination of an attribute selector and the ::after pseudo-element:

a[href^=http]:after {

content: " (" attr(href) ")";

}

attr() allows you to access any attribute of the selected element, coming in handy here for displaying the link’s target. And you’ll remember from the attribute selectors section that a[href^=http] means “any a element whose href attribute begins with http”; in other words, external links. To be more precise, we could have written a[href^="http://"], a[href^="https://"].

Here’s another example:

a[href$=".pdf"] {

background: transparent url(pdficon.gif) 0 50% no-repeat;

padding-left: 20px;

}

a[href$=".pdf"]:after {

content: " (PDF)";

}

Those styles will add a PDF icon and the text “ (PDF)” after links to PDFs. Remember that the [attr$=val] selector matches the end of an attribute—so document.pdf will match but pdf.html won't.

::selection

The ::selection pseudo-element matches user-selected or highlighted text.

This is supported in all browsers since IE9, but requires the -moz vendor prefix for Firefox. Let’s use it on The HTML5 Herald to bring the selection background and text color in line with the monochrome style of the rest of the site:

::-moz-selection {

background: #484848;

color: #fff;

}

::selection {

background: #484848;

color: #fff;

}

CSS3 Colors

We know you’re probably champing at the bit to put the really cool stuff from CSS3 into practice, but before we do there’s one more detour we need to take. CSS3 brings with it support for some new ways of describing colors on the page. Since we’ll be using these in examples over the next few chapters, it’s important we cover them now.

Prior to CSS3, we almost always declared colors using the hexadecimal format (#FFF, or #FFFFFF for white). It was also possible to declare colors using the rgb() notation, providing either integers (0–255) or percentages. For example, white is rgb(255, 255, 255) or rgb(100%, 100%, 100%). In addition, we had access to several named colors such as purple, lime, aqua, red, and the like. While the color keyword list has been extended in the CSS3 color module to include 147 additional keyword colors (that are generally well supported), CSS3 also provides us with a number of other options: HSL, HSLA, and RGBA. The most notable change with these new color types is the ability to declare semitransparent colors.

RGBA

RGBA works just like RGB, except that it adds a fourth value: alpha, the opacity level or alpha transparency level. The first three values still represent red, green, and blue. For the alpha value, 1 means fully opaque, 0 is fully transparent, and 0.5 is 50% opaque. You can use any number between 0 and 1 inclusively.

Unlike RGB, which can also be represented with hexadecimal notation as #RRGGBB, there is no hexadecimal notation for RGBA. An eight-character hexadecimal value for RGBA as #RRGGBBAA has been proposed and added to the draft CSS Color Module Level 4, but is yet to be supported.

For example, let’s look at our registration form. We want the form to be a darker color, while still preserving the grainy texture of the site’s background. To accomplish this, we’ll use an RGBA color of 0,0,0,0.2—in other words, solid black that’s 80% transparent:

form {

background: url(../images/bg-form.png) no-repeat bottom center rgba(0, 0, 0, 0.2);

}

Since Internet Explorer 8 and below lack support for RGBA, if you declare an RGBA color, make sure you precede the property value pair with the same property that includes as its value a color IE can understand. IE will render the last color it can make sense of, so it will just skip the RGBA color. Other browsers will understand both colors, but thanks to the CSS cascade, they’ll overwrite the IE color with the RGBA color as it comes later.

In the aforementioned example, we’re actually fine with older versions of IE having no background color, because the color we’re using is mostly transparent anyway.

HSL and HSLA

HSL stands for hue, saturation, and lightness. Unlike RGB, where you need to manipulate the saturation or brightness of a color by changing all three color values in concert, with HSL you can tweak either just the saturation or the lightness while keeping the same base hue. The syntax for HSL comprises an integer value for hue, and percentage values for saturation and lightness.[9]

Although monitors display colors as RGB, the browser simply converts the HSL value you give it into one the monitor can display.

The hsl() declaration accepts three values:

· The hue in degrees from 0 to 359. Some examples are: 0 = red, 60 = yellow, 120 = green, 180 = cyan, 240 = blue, and 300 = magenta. Of course, feel free to use everything in between.

· The saturation as a percentage with 100% being the norm. Saturation of 100% will be the full hue, and saturation of 0 will give you a shade of gray—essentially causing the hue value to be ignored.

· A percentage for lightness with 50% being the norm. Lightness of 100% will be white, 50% will be the actual hue, and 0% will be black.

HSL also allows for an opacity value. For example, hsla(300, 100%, 50%, 0.5) is magenta with full saturation and normal lightness, which is 50% opaque.

HSL mimics the way the human eye perceives color, so it can be more intuitive for designers to understand and, as mentioned, make adjustments a bit quicker and easier. Feel free to use whatever syntax you’re most comfortable with—but remember that if you need to support IE8 or below, you’ll generally want to limit yourself to hexadecimal notation.

Let’s sum up with a review of all the ways to write colors in CSS. A shade of dark red can be written as:

· #800000

· maroon

· rgb(128,0,0)

· rgba(128,0,0,1.0)

· hsl(0,100%,13%)

· hsla(0,100%,13%,1.0)

Last, but certainly not least, CSS Color Module Level 3 provides us with currentColor. The currentColor keyword is equal to the current element’s color property.

In CSS Colors Level 4, we get four- and eight-digit hex colors with the last digit(s) specifying transparency, hwb() and hwba() to specify colors by hue, whiteness and blackness, plus a gray() function to enable a shorter method for declaring shades of gray.

Opacity

In addition to specifying transparency with HSLA and RGBA colors (and soon, eight-digit hexadecimal values), CSS3 provides us with the opacity property. opacity sets the opaqueness of the element on which it’s declared. Similar to alpha transparency, the opacity value is a floating point number between (and including) 0 and 1. An opacity value of 0 defines the element as fully transparent, whereas an opacity value of 1 means the element is fully opaque.

Let’s look at an example:

div.halfopaque {

background-color: rgb(0, 0, 0);

opacity: 0.5;

color: #000000;

}

div.halfalpha {

background-color: rgba(0, 0, 0, 0.5);

color: #000000;

}

Though the two declaration blocks may seem identical at first glance, there’s actually a key difference. While opacity sets the opacity value for an element and all of its children, a semitransparent RGBA or HSLA color has no impact on the element’s other CSS properties or descendants, other than inherited properties such as text color.

Looking at the example, any text in the halfopaque div will be 50% opaque (most likely making it difficult to read!). The text on the halfalpha div, though, will still be black and 100% opaque. Only the background color of that single element will be slightly opaque, as background-color is not an inherited property.

While the opacity property is a quick and easy solution for creating semitransparent elements, you should be aware of this consequence.

Putting It into Practice

Now that we’ve been through all the available CSS selectors and new color types, we’re ready to really start styling.

For the rest of the chapter, we’ll style a small section of The HTML5 Herald front page; this will demonstrate how to add rounded corners, text shadow, and box shadow.

In the right-hand sidebar of The HTML5 Herald’s front page are a series of whimsical advertisements—we marked them up as article elements within an aside way back in Chapter 2. The first of these is an old “Wanted” poster-style ad, advising readers to be on the lookout for the armed and dangerous HTML5 and CSS3. The ad’s final appearance is depicted in Figure 6.1.

Our “Wanted” ad

Figure 6.1. Our “Wanted” ad

You’ll notice that the dark gray box in the center of the ad has a double border with rounded corners, as well as a three-dimensional “pop” to it. The text that reads “<HTML5> & {CSS3}” also has a shadow that offsets it from the background. Thanks to CSS3, all these effects can be achieved with some simple code and with no reliance on images or JavaScript. Let’s learn how it’s done.

The markup for the box is simply <a href="…"><HTML5> & {CSS3}</a>. Other than the HTML entities, it’s as straightforward as it gets!

Before we can apply any styles to it, we need to select it. Of course, we could just add a class attribute to the markup, but where’s the fun in that? We’re here to learn CSS3, so we should try and use some fancy new selectors instead.

Our box isn’t the only a element on the page, but it might be the only a immediately following a paragraph in the sidebar. In this case, that’s good enough to single out the box. We also know how to add some pre-CSS3 styling for the basics, so let’s do that:

aside p + a {

display: block;

text-decoration: none;

border: 5px double;

color: #ffffff;

background-color: #484848;

text-align: center;

font-size: 1.75rem;

margin: 5px 5px 9px 5px;

padding: 15px 0;

position: relative;

}

Not bad! As Figure 6.2 shows, we’re well on our way to the desired appearance. This will also be the appearance shown to IE8 and below except for the font styling, which we’ll be adding in Chapter 9.

The basic appearance of our ad link, which will be seen by older browsers

Figure 6.2. The basic appearance of our ad link, which will be seen by older browsers

IE6 lacks support for the adjacent sibling selector—so if you really need to provide support to that browser, you can add an id or class to the HTML and select it that way.

This presentation is fine and should be acceptable—there’s no need for web pages to look identical in all browsers. Users with Internet Explorer 9 and older will be unaware that they’re missing anything. But we can still provide treats to better browsers. Let’s go ahead and add a bit of polish.

Rounded Corners: border-radius

The border-radius property lets you create rounded corners without the need for images or additional markup. To add rounded corners to our box, we simply add:

border-radius: 25px;

Safari, Chrome, Opera, IE9+, and Firefox all support rounded corners without a vendor prefix (just border-radius).

Figure 6.3 shows what our link looks like with the addition of these properties.

Adding rounded corners to our link

Figure 6.3. Adding rounded corners to our link

The border-radius property is actually a shorthand. For our a element, the corners are all the same size and symmetrical. If we had wanted different-sized corners, we could declare up to four unique values—border-radius: 5px 10px 15px 20px;, for example. Just like padding, margin, andborder, you can adjust each value individually:

border-top-left-radius: 5px;

border-top-right-radius: 10px;

border-bottom-right-radius: 15px;

border-bottom-left-radius: 40px;

The resulting off-kilter box is shown in Figure 6.4.

It’s possible to set the radius of each corner independently

Figure 6.4. It’s possible to set the radius of each corner independently

When using the shorthand border-radius, the order of the corners is top-left, top-right, bottom-right, and bottom-left. You can also declare only two values, in which case the first is for top-left and bottom-right, and the second is for top-right and bottom-left. If you declare three values, the first refers to top-left, the second sets both the top-right and bottom-left, and the third is bottom-right.

We recommend using the shorthand—because it’s much shorter.

You can also create asymmetrical corners with a different radius on each side. Rather than being circular, these will appear elliptical. If two values are supplied to any of the four longhand values, you’ll be defining the horizontal and vertical radii of a quarter ellipse respectively. For example,border-bottom-left-radius: 20px 10px; will create an elliptical bottom-left corner.

When using the shorthand for elliptical corners, separate the value of the horizontal and vertical radii with a slash. border-radius: 20px / 10px; will create four equal elliptical corners, and border-radius: 5px 10px 15px 20px / 10px 20px 30px 40px; will create four unequal elliptical corners. That last example will create corners seen in Figure 6.5. Interesting? Yes. Aesthetically pleasing? Not so much.

Four interesting unequal elliptical corners

Figure 6.5. Four interesting unequal elliptical corners

There’s only one other element on The HTML5 Herald that uses rounded corners: the registration form’s submit button. Let’s round those corners now:

input[type=submit] {

border-radius: 10%;

background-clip: padding-box;

}

You’ll note two things about this CSS: we’ve used an attribute selector to target the submit input type, and we’ve used percentages instead of pixel values for the rounded corners. This will come in handy if we need to add more forms to the site later; other submit buttons might be smaller than the one on the registration page, and by using percentages, rounded corners will scale in proportion to the size of the button.

Note that if a border-radius declaration is larger than 50% of the width or height of the box, the browser will round the corners proportionally based on the values provided. We also included background-clip: padding-box; to ensure that the background color doesn’t bleed through the rounded border.

The border-radius property can be applied to all elements except the table element when the border-collapse property is set to collapse.

Note: Older Browsers

Generally speaking, there’s no need to provide an identical look in older browsers, but sometimes a client may insist on it. There are some JavaScript solutions such as CSS3 PIE that provide CSS3 decorations to older versions of IE without requiring additional images or markup. For Android 2.3, you can prefix the property as -webkit-border-radius. For performance reasons, we recommend against providing rounded corners to Android 2.3, IE8, and earlier versions of those browsers.

Drop Shadows

CSS3 provides the ability to add drop shadows to elements using the box-shadow property. This property lets you specify the color, height, width, blur, and offset of one or multiple inner and/or outer drop shadows on your elements.

We usually think of drop shadows as an effect that makes an element look like it’s hovering over the page and leaving a shadow; however, with such fine-grained control over all those variables, you can be quite creative. For our advertisement link, we can use a box-shadow with no blur to create the appearance of a 3D box.

The box-shadow property takes a comma-separated list of shadows as its value. Each shadow is defined by two to four size values, a color, and the key term inset for inset—or internal—shadows. If you fail to specify inset, the default is for the shadow to be drawn outside of the element:

Let’s look at the shadow we’re using on our element, so that we can break down what each value is doing:

box-shadow: 2px 5px 0 0 rgba(72,72,72,1);

The first value is the horizontal offset. A positive value will create a shadow to the right of the element, a negative value to the left. In our case, our shadow is two pixels to the right of the a.

The second value is the vertical offset. A positive value pushes the shadow down, creating a shadow on the bottom of the element. A negative value pushes the shadow up. In our case, the shadow is five pixels below the a.

The third value, if included, is the blur distance of the shadow. The greater the value, the more the shadow is blurred. Only positive values are allowed. Our shadow is not blurred ― being opaque throughout ― so we can either include a value of zero (0), or omit the value altogether.

The fourth value determines the spread distance of the shadow. A positive value will cause the shadow shape to expand in all directions. A negative value contracts the shadow. Our shadow has no spread, so again we can either include a value of zero (0), or omit the value altogether.

The fifth value is the shadow’s color. You will generally want to declare the color of the shadow. If it’s omitted, the spec states that it should default to the same as the color property of the element, or currentColor. In the example, we used an RGBA color. In this particular design the shadow is a solid color, so we could just have used the hex value. Most of the time, though, shadows will be partially transparent, so you’ll typically be using RGBA or HSLA.

The drop shadow created by these declarations is shown in Figure 6.6.

Adding a drop shadow to our box gives it the illusion of depth

Figure 6.6. Adding a drop shadow to our box gives it the illusion of depth

By default, the shadow is a drop shadow—occurring on the outside of the box. You can create an inset shadow by adding the word inset to the start or end of your shadow declaration.

The box-shadow property is well supported, with IE support starting at IE9.

Warning: Drop Shadows on Transparent Images

Drop shadows look good on rectangular elements, following the curve of the corners. Keep in mind, though, that the shadow follows the edges of your element, rather than the pixels of your content. So, if you try to use drop shadows on semitransparent images, you’ll receive an ugly surprise: the shadow follows the rectangular borders of the image box instead of the contour of the image’s content. To create drop shadows that work for alpha transparent images, use the filter property’s drop-shadow() function—filter: drop-shadow(2px 5px rgb(72,72,72))—which works in Firefox 35, with the -webkit- prefix in Chrome 31+, Safari 7+, Opera, Android 4.4+, and iOS6+. The filter property is significantly different from and incompatible with Microsoft’s older filter property, and is without support in IE 11.

To include more than one box shadow on an element, define a comma-separated list of shadows. When more than one shadow is specified, the shadows are layered front to back as if the browser drew the last shadow first and the previous shadow on top of that.

Like an element’s outline, box shadows are supposed to be invisible in terms of the box model. In other words, they should have no impact on the layout of a page—they’ll overlap other boxes and their shadows if necessary. We say “supposed to,” because there are bugs in some browsers, though these are few and will likely be fixed fairly quickly.

Inset and Multiple Shadows

The registration form for The HTML5 Herald has what looks like a gradient background around the edges, but it’s actually a few inset box shadows.

To create an inset box shadow, add the inset key term to your declaration. In our case, we have to include two shadows so that we cover all four sides: one shadow for the top left, and one for the bottom right:

form {

box-shadow:

inset 1px 1px 84px rgba(0,0,0,0.24),

inset -1px -1px 84px rgba(0,0,0,0.24);

}

As you can see, to add multiple shadows to an element repeat the same syntax again, separated with a comma.

Warning: WebKit and Inset Shadows

Older versions of WebKit-based browsers that are still found on many mobile devices suffer from very slow performance when rendering inset box shadows with a large blur value, such as the one we’re using on The HTML5 Herald’s registration form. For this reason, we recommend against using the vendor-prefixed property. It’s very simple to not give older mobile browser users a bad experience: avoid adding the -webkit- prefix on shadow and border radii.

Text Shadow

Where box-shadow lets us add shadows to boxes, text-shadow adds shadows to individual characters in text nodes. Added in CSS2, text-shadow has been supported in Safari since version 1 and finally received support in IE10, unprefixed in all browsers.

The syntax of the text-shadow property is very similar to box-shadow, including prefixes, offsets, and the ability to add multiple shadows; the exceptions are that there’s no spread, and inset shadows aren’t permitted:

/* single shadow */

text-shadow: topOffset leftOffset blurRadius color;

/* multiple shadows */

text-shadow: topOffset1 leftOffset1 blurRadius1 color1,

topOffset2 leftOffset2 blurRadius2 color2,

topOffset3 leftOffset3 blurRadius3 color3;

Like box-shadow, when multiple shadows are declared, they’re painted from front to back with the first shadow being the topmost. Text shadows appear behind the text itself. This is different from box-shadow, which starts on the outside of the box (or is displayed only on the inside in the case of inset). If a shadow is so large that it touches another letter, it will continue behind that character.

Our text has a semi-opaque shadow to the bottom right:

text-shadow: 3px 3px 1px rgba(0, 0, 0, 0.5);

This states that the shadow extends three pixels below the text, three pixels to the right of the text, is slightly blurred (one pixel), and has a base color of black at 50% opacity.

With that style in place our ad link is nearly complete, as Figure 6.7 shows. The finishing touch—a custom font—will be added in Chapter 9.

Our ad link is looking quite snazzy!

Figure 6.7. Our ad link is looking quite snazzy!

Tip: Color-matching Your Shadow

Use the keyword currentColor if you want your text’s shadow to match the color of your text. A single-pixel text shadow can make very thin fonts, such as Helvetica Neue Light, more legible:

text-shadow: 0 0 1px currentColor;

More Shadows

We now know how to create drop shadows on both block-level elements and text nodes. But so far, we’ve only styled a fraction of our page—only one link in one advertisement, in fact. Let’s do the rest of the shadows before moving on.

Looking back at the site design, we can see that all the h1 elements on the page are uppercase and have drop shadows. The text is dark gray with a very subtle solid-white drop shadow on the bottom right, providing a bit of depth.[10] The tagline in the site header also has a drop shadow, but is all lowercase. The taglines for the articles, meanwhile, have no drop shadow.

We know that we can target all these elements without using classes. Let’s do that without any additional markup:

h1, h2 {

text-transform: uppercase;

text-shadow: 1px 1px #FFFFFF;

}

:not(article) > header h2 {

text-transform: lowercase;

text-shadow: 1px 1px #FFFFFF;

}

The first declaration targets all the h1 and h2 elements on the page. The second targets all the h2 elements that are in a header, but only if that header is not nested in an article element.

Our text shadows are a solid white, so there’s no need to use alpha transparent colors or a blur radius.

Up Next

Now that we have shadows and rounded corners under our belt, it’s time to have some more fun with CSS3. In the next chapter, we’ll be looking at CSS3 gradients and multiple background images.


[9] A full exploration of color theory—along with what is meant by the terms “saturation” and “lightness”—is beyond the scope of this book. If you want to read more, The Principles of Beautiful Web Design (SitePoint: Melbourne, 2014) includes a great primer on color.

[10] See http://twitter.com/#!/themaninblue/status/27210719975964673.