O'Reilly logo

CSS Refactoring by Steve Lindstrom

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 4. Classifying Different Types of Styles

Code reuse is one of the tenets of good architecture and arguably one of the most important parts of writing high-quality CSS. This chapter discusses the subtle intents that different styles can have when they are applied to HTML elements in logical and deliberate ways. When styles are classified and used in relation to their intent, finding ways to reuse code becomes much more obvious. As you make your way through this chapter, think back to Chapter 2 and you’ll see how classifying different styles parallels how the cascade works.

The Importance of Classifying Styles

At its most basic, a website is a collection of documents that display information. However, on the other end of the spectrum and at its most complicated, a website can more resemble an intricate application that facilitates simple human interactions and enables complex operations. Both of these extremes make use of semantic HTML tags that help describe what is being displayed, and both can benefit from intent-based styling.

Intent-based styling helps create a better architecture because organizing styles into different classifications promotes more predictable code that can be reused more easily. Following are the various classifications of styles that can be applied to any website, despite its intricacies.

Normalizing Styles

Browsers come with a default stylesheet, called the user agent stylesheet, that applies default styles to HTML elements. Because different browsers are made by different companies, there can be discrepancies in which properties and values these stylesheets set.

Normalizing styles are styles whose intent is to provide default values for properties on various elements that might otherwise have different defaults across browsers. For example, Example 4-1 normalizes the styling of <hr> elements in various browsers.

Example 4-1. Normalizing <hr> elements across browsers
/**
 * 1. Add the correct box sizing in Firefox.
 * 2. Show the overflow in Edge and IE.
 */

hr {
  box-sizing: content-box; /* 1 */
  height: 0; /* 1 */
  overflow: visible; /* 2 */
}

It can be difficult to test and keep track of which browsers set which properties and values by default, but luckily the web development community has done much of this work for us already and there are a number of different sets of open source normalizing styles available. One of the most commonly used (and the source of the CSS from Example 4-1) is normalize.css, authored by Nicolas Gallagher and Jonathan Neal; it’s available on GitHub and reprinted in Appendix A.

Many of the styles you might find in a set of open source normalizing styles are most helpful when dealing with legacy browsers, so it’s entirely possible that they might not be necessary for your project (depending on which browsers it supports). You might also find that styles are included for a lot of elements that you’re not using, like <audio>, <canvas>, <kbd>, and more. If you have no plans to use some of the included elements you should consider removing their styles in the interest of having smaller CSS files.

Base Styles

Base styles are styles whose intent is to provide a starting point from which other, more specialized styles can be built up. They can be identified easily because they are applied to HTML elements using single type selectors or very simple combinations of type selectors and combinators (ul ul to target unordered lists within unordered lists, for example) and any pseudoclasses that apply to them. Along with normalizing styles, base styles are the least specific styles that should be found in a stylesheet.

Once a base style has been set on an HTML element it shouldn’t need to be redeclared unless the style being set differs for another intended use case. The general rule of thumb to keep in mind when writing base styles is that as additional styles are applied to elements, they shouldn’t need to override lots of base style declarations to accomplish design goals.

Defining Base Styles

Following are some suggestions for how to approach defining base styles for different types of commonly used elements. Many of the suggested properties are usually set by the user agent stylesheet, but they may not be set with values that are appropriate for every design, and those values might change over time as new browser versions are released. Think about the majority of use cases the elements will be styled for, and that will provide guidance for the values that should be set.

Base styles should only set properties and values for the most generic of use cases. Properties that are commonly set include:

  • color

  • font-family

  • font-size

  • font-weight

  • letter-spacing

  • line-height

  • margin

  • padding

If your website is mostly informational in nature, setting just these styles will likely get you far. However, if you are building a more application-like website with a more intricate design, setting these properties will only get you started; more complex styles might be needed for reusable components, as we’ll see later in this chapter.

The properties in this list should be considered when writing your base styles, but they don’t need to be set 100% of the time, as all are inherited from their ancestors (except margin and padding). If margin and padding values should be inherited, this can be accomplished by using the value inherit for each of those properties. Any additional properties or pseudoclasses that should be considered for inclusion in base styles for a particular type of element are included in the following sections.

Leveraging Inheritance

The color, font-family, font-size, font-weight, letter-spacing, and line-height properties are usually inherited from parent elements by child elements, so these values don’t always need to be set. For a complete list of CSS properties and information on if their values are inherited, visit https://www.w3.org/TR/CSS21/propidx.html. For a complete list of HTML elements to style, visit https://www.w3.org/TR/html-markup/elements.html.

Document Metadata

Document metadata tags include the <head>, <title>, <base>, <link>, and <meta> tags. Since they are not visible, they cannot be styled.

Sectioning Elements

Sectioning elements include the <address>, <article>, <aside>, <body>, <footer>, <header>, <nav>, and <section> elements. These elements usually contain other elements and comprise the various sections of an HTML document.

Consider setting the following properties on sectioning elements:

  • color

  • font-family

  • font-size

  • font-weight

  • letter-spacing

  • line-height

  • padding

Setting the background property might also be helpful for the <body> element. Example 4-2 shows how base styles can be applied to sectioning elements.

Example 4-2. Base styles for sectioning elements
body {
  background: #FFFFFF;
  color: #333333;
  font-family: Helvetica, Arial, sans-serif;
  font-size: 14px;
  line-height: 1.3;
  padding: 5% 20%;
}

article,
footer,
header,
nav {
  padding: 0;
}

article,
nav {
  margin-bottom: 12px;
  margin-top: 12px;
}

footer {
  margin-top: 12px;
}

header {
  margin-bottom: 12px;
}

Heading and Text Elements

Heading elements include the <h1><h6> elements and are intended to define the topic of each different section in an HTML document. Text elements include the <figure>, <figcaption>, <p>, and <pre> elements and are intended to display blocks of text.

Properties that should be considered when defining base styles for heading and text elements include:

  • font-family

  • font-size

  • font-weight

  • letter-spacing

  • line-height

  • margin-bottom

  • margin-top

Example 4-3 shows how base styles can be applied to heading and text elements.

Example 4-3. Base styles for heading and text elements
h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: Georgia, Times, serif;
  font-weight: 100;
  line-height: 1.1;
  margin: 0.5em 0;
}

h1 {
  font-size: 36px;
}

h2 {
  font-size: 24px;
}

h3 {
  font-size: 21px;
}

h4 {
  font-size: 18px;
}

h5 {
  font-size: 16px;
}

h6 {
  font-size: 14px;
}

p,
pre {
  margin-bottom: 12px;
  margin-top: 12px;
}

Anchor Tags

Anchor tags provide links to other HTML documents or other sections of the same HTML document. They can make use of the :link, :visited, :focus, :hover, and :active pseudoclasses that are commonly used to show state, so when defining base styles it’s important to keep these pseudoclasses in mind. Here’s a breakdown of what each pseudoclass is used for:

  • :link styles are applied to elements that have a valid href attribute.

  • :visited styles are applied to links that have a valid href attribute whose location appears in the browser’s history.

  • :focus styles are applied when a link element has received focus. This occurs when the element is clicked or tapped, or when the Tab key is used to navigate to the element.

  • :hover styles are applied when the mouse pointer is placed over a link. On touch devices, since there is no hover state, :hover styles are usually applied when an element is tapped, and the styles are removed when a different element is tapped.

  • :active styles are applied when a link is “activated.” When using a mouse this happens after the link has been clicked but before the mouse button has been released. On touch devices, this occurs when an element is tapped but before the finger is released.

It’s also important to ensure that if they are set, the :link and :visited pseudoclasses are the first two defined. All of these pseudoclasses have the same specificity, so the cascade will apply them based on order. This means that if a link has been visited and :visited is defined after :hover, :focus, or :active, any overlapping styles defined for the :visited pseudoclass will take precedence.

Another thing to keep in mind is that all of these pseudoclasses effectively give links a higher specificity because they are comprised of one type selector and one pseudoclass selector. This means that overriding those styles requires a higher specificity or an equal specificity that appears later in the stylesheet. For this reason, the :link pseudoclass is often ignored and the styles that would be applied are instead set directly using the a type selector.

Common properties that should be considered when defining base styles for anchor tags and their pseudoclasses include:

  • background-color

  • border

  • color

  • font-weight

  • text-decoration

Although the browser’s defaults usually suffice, defining the outline-width, outline-style, and outline-color properties (or the more concise outline shorthand property) can be helpful when styling the :focus pseudoclass.

Warning

Removing a link’s use of the outline property on :focus without replacing it with another visual cue is considered poor form as it hinders usability for those navigating with just a keyboard or another accessibility device.

Anchor tags are commonly inline by default as they are used to modify text or other inline elements. font-family, font-size, and font-weight could also be set, but more than likely values for these properties will be inherited. Example 4-4 shows how base styles can be applied to anchor tag elements.

Example 4-4. Base styles for links
a,
a:visited,
a:focus,
a:hover,
a:active {
  color: inherit;
  text-decoration: underline;
}

a:hover {
  background-color: #FFFF00;
}

Text Semantics

Text semantics are elements that are used to give text more meaning or structure. These elements are usually inline and include the <abbr>, <b>, <cite>, <code>, <data>, <dfn>, <em>, <i>, <kbd>, <s>, <strong>, <sub>, <sup>, <time>, and <u> tags, among others.

As these elements are used to modify text, the following properties should be considered when defining their base styles:

  • color

  • font-family

  • font-size

  • font-weight

For example, styles for the <code> tag might be defined as follows:

code {
  color: #00FF00;
  font-family: monospace;
  font-weight: 500;
  line-height: 1.5;
}

Lists

List elements include the <ol> (ordered list), <ul> (unordered list), and <dl> (definition list) elements. Ordered and unordered lists can only immediately contain <li> (list item) elements and definition lists can only immediately contain <dt> (definition term) and <dd> (definition description) elements.

Because of their versatility, it can be difficult to determine appropriate base styles for lists. If a website is mostly informational it is common to display lists as numbered or bulleted or as appropriately indented definitions, but if a website has a more intricate user interface, lists may be used in a number of different scenarios that require other styling to match a given design. Some of these scenarios might include horizontal navigation, a list of products for sale, details in a social media profile, and so on.

The following properties should be considered when defining base styles for ordered and unordered list elements:

  • font-family

  • font-size

  • list-style-type or list-style-image

  • list-style-position

  • line-height

  • margin-bottom

  • margin-top

  • padding-left

For a purely informational website, it might make sense to set the list-style-type or list-style-image and list-style-position properties to help enumerate the items in the list. However, for a more application-like website it might make even more sense to simply assign the value none to these properties if they will rarely be used in order to prevent having to constantly override them. To prevent child elements from being indented, padding-left should be set to 0 on the <ol> or <ul> element. Child <li> elements inherit font-family, font-size, and line-height properties from their parent <ol> or <ul> element, but not margin or padding properties.

An unordered list might be assigned the following styles:

ul {
  list-style-position: outside;
  list-style-type: disc;
  margin-top: 0;
  margin-bottom: 12px;
}

ul ul {
    margin-bottom: 0;
}

You can style the same properties for definition lists as for ordered and unordered lists, though it’s not very common to use list-style-type, list-style-image, or list-style-position as those styles only apply to elements that have the display property set to list-item. list-item is the default value for the display property of li elements, but block is typically the default for <dt> and <dd> elements.

<dd> and <dt> elements inherit font-family, font-size, and line-height properties from their parent <dl> element, and it is common for <dd> elements to have margin-left set to create an indented effect (to avoid this, assign this property a value of 0).

Grouping Elements

Grouping elements include <div>, <main>, and <span>. Although the <span> tag is technically a text-level semantic, its main use is for grouping text or inline elements.

The <div> and <main> tags are typically block-level elements, whereas the <span> tag is inline. Because these elements are simply used for grouping other tags, they don’t usually warrant any base styles; their styling will vary on a case-by-case basis denoted by classes. If, however, the <main> tag is used as a visual container, it might benefit from having values set for margin and padding.

Tables

As the name suggests, tables display data within a tabular structure. Elements used for displaying tabular data include the <table>, <caption>, <colgroup>, <col> (column), <tbody> (table body), <thead> (table header), <tfoot> (table footer), <tr> (table row), <td> (table cell), and <th> (table header cell) elements. In the late 1990s and early 2000s it was common to use tables to create page layouts, but now that CSS and browsers have matured it makes more sense to leave that to other styled elements and instead use tables to only display tabular data.

The following properties should be considered when defining base styles for <table> elements:

  • border-collapse

  • border-spacing

  • border (border-width, border-color, border-style)

  • empty-cells

  • font-family

  • font-size

  • letter-spacing

  • line-height

The following properties should be considered when defining base styles for <thead>, <tbody>, and <tfoot> elements:

  • background-color

  • color

  • text-align

  • vertical-align

And these properties should be considered when defining base styles for <th> and <td> elements:

  • background-color

  • border (border-width, border-color, border-style)

  • color

  • font-family

  • font-size

  • letter-spacing

  • line-height

  • text-align

  • vertical-align

Given these considerations, base table styles might resemble something like the following:

table {
  border-collapse: collapse;
  border-spacing: 0;
  empty-cells: show;
  border: 1px solid #000000;
}

tfoot,
thead {
  text-align: left;
}

thead {
  background-color: #ACACAC;
  color: #000000;
}

th,
td {
  border-left: 1px solid #000000;
  padding: 0.5em 1em;
}

th:first-child,
td:first-child {
  border-left: none;
}

Forms

Forms are used for gathering information from users and include the <form>, <label>, <input>, <button>, <select>, <datalist>, <optgroup>, <option>, <textarea>, <output>, <progress>, <meter>, <fieldset>, and <legend> elements. As usual, properties that should be considered when declaring base styles include:

  • font-family

  • font-size

  • line-height

  • margin

  • padding

Styles can be set on the <form> element and inherited by its children for basic designs, but more complicated designs may warrant more styling. It’s common for <legend>, <label>, and <input> elements to have differing values set for font-weight, font-size, and font-family, in which case those values need to be set on the element.

Some form elements can be difficult to style because many browsers simply ignore properties that are applied to them. For example, attempting to apply border-color, border-width, background-color, and a number of other properties on checkboxes and radio buttons results in the browser ignoring the desired styles. This can be overcome by creating custom checkbox and radio button components that hide the form control and use other HTML elements to represent the visual metaphor, but this is not the job of base styles.

Form elements might be reasonably assigned the following styles:

fieldset {
  border: 0;
  margin: 0;
  padding: 0;
}

input {
  display: block;
  font-size: inherit;
  padding: 4px;
  width: 100%;
}

label {
  display: block;
  font-weight: 900;
  margin-bottom: 6px;
  padding: 4px;
}

legend {
  border: 0;
  color: #000000;
  display: block;
  font-size: 1.2em;
  margin-bottom: 12px;
  padding: 0 12px;
  width: 100%;
}

Images

Graphics can be displayed using the <img> or <picture> tags. Properties that should be considered when declaring base styles include:

  • border

  • max-width

  • vertical-align

Because <img> elements can be used in inline formatting contexts, baseline is the default value assigned to the vertical-align property, which may or may not be appropriate for the design being implemented. Additionally, when <img> elements are displayed within sized block-level elements, it’s common to prevent the image from overflowing its container by assigning it a max-width equal to 100% of its parent container.

Base styles for the img tag might be defined as follows:

img {
  border: none;
  max-width: 100%;
  vertical-align: middle;
}

Component Styles

Reusable components are elements or groups of elements that are styled to employ visual metaphors that make it easier to interact with a website. Some examples of reusable components include buttons, drop-down menus, modal windows, progress bars, and tabs.

Reusable components are easy to recognize, but they can be difficult to build correctly. Before building a reusable component it’s helpful to answer the following questions:

  • Will this be a standalone component or will there be more than one grouped together?

  • Will this component typically behave like an inline element, a block-level element, or in some other way (i.e., will it be positioned absolutely out of the document flow)?

With the answers to these questions in mind, the process of creating reusable components can be simplified by following a few guidelines:

  1. Before building a component, define the behavior that needs to be built.

  2. Keep component styles granular and set reasonable defaults.

  3. Override visual styles for grouped components as needed by surrounding them with a container element that has a differentiating class.

  4. Delegate the assignment of dimensions to structural containers.

The following example, which explains how to build a simple tab component (Figure 4-1), explores each of these guidelines.

cssr 0401
Figure 4-1. Tabs

Define the Behavior that Needs to Be Built

For this example we’ll be building the tabs depicted in Figure 4-1. The requirements are as follows:

  • There are three tabs in Figure 4-1, but the component should work with two or more tabs.

  • When a tab is active, the bottom border turns blue and the background white.

  • When a tab is inactive, the background color is gray.

We’ll use the HTML in Example 4-5 to build the tabs.

Example 4-5. Markup for a group of three tabs
<nav>
  <ul>
    <li><a href="#">Tab One</a></li>
    <li><a href="#">Tab Two</a></li>
    <li><a href="#">Tab Three</a></li>
  </ul>
</nav>

Keep Component Styles Granular

Keeping component styles granular means that each should be written to style just one element, so they can be reused. In Example 4-5 there are three tabs, so a class could be created for each (as in Example 4-6), but that would just result in a lot of duplicated code.

Example 4-6. Individual classes for each tab
/**
 * Styling each tab individually results in lots of duplicated code.
 */
.tab-1 { /* styles go here */ }
.tab-2 { /* styles go here */ }
.tab-3 { /* styles go here */ }

.tab-1:hover { /* styles go here */ }
.tab-2:hover { /* styles go here */ }
.tab-3:hover { /* styles go here */ }

.tab-1.active { /* styles go here */ }
.tab-2.active { /* styles go here */ }
.tab-3.active { /* styles go here */ }

.tab-1 > a { /* styles go here */ }
.tab-2 > a { /* styles go here */ }
.tab-3 > a { /* styles go here */ }

.tab-group { /* styles go here */ }

This isn’t a very good solution—each class has the same declaration block as the others, which leads to larger CSS files for clients to download and an increased risk of visual inconsistencies because there is more code to maintain. To avoid this the classes could be grouped as in Example 4-7, but there would still be a bunch of classes that do the same thing.

Example 4-7. Individual classes for a tab grouped together
/**
 * Grouping tabs like this results in lots of unnecessary duplicated classes.
 */

.tab-1,
.tab-2,
.tab-3 {
  /* styles go here */
}

.tab-1:hover,
.tab-2:hover,
.tab-3:hover {
  /* styles go here */
}

.tab-1.active,
.tab-2.active,
.tab-3.active {
  /* styles go here */
}

.tab-1 > a,
.tab-2 > a,
.tab-3 > a {
  /* styles go here */
}

.tab-group {
  /* styles go here */
}

The best solution is to abstract the styles into a reusable class (Example 4-8). Note that there is also an .active class that can be conditionally applied to indicate which tab is active, as well as styles for a .tab-group element that contains multiple tabs.

Example 4-8. Styles for a tab
/**
 * Tab Component Styles
 */

.tab {
  background-color: #F2F2F2;
  border-bottom: 1px solid #EEEEEE;
  border-top: 1px solid #EEEEEE;
  bottom: -1px;
  display: inline-block;
  margin-left: 0;
  margin-right: 0;
  margin-top: 4px;
  position: relative;
}

.tab:first-child {
  border-left: 1px solid #EEEEEE;
  border-top-left-radius: 4px;
}

.tab:last-child {
  border-right: 1px solid #EEEEEE;
  border-top-right-radius: 4px;
}

.tab.active {
  background-color: #FFFFFF;
  border-bottom: 1px solid #2196F3;
  color: #000000;
}

.tab:hover {
  background-color: #F9F9F9;
}

.tab > a {
  color: inherit;
  display: block;
  height: 100%;
  padding: 12px;
  text-decoration: none;
  width: 100%;
}

/**
 * Tab Component Containers
 */

.tab-group {
  border-bottom: 1px solid #EEEEEE;
  list-style: none;
  margin: 0;
  padding-left: 0;
}

Now that we have these styles assigned to the appropriate classes we can update the HTML to make use of them, as in Example 4-9.

Example 4-9. Markup for a group of three tabs with new tab class
<nav>
  <ul class="tab-group">
    <li class="tab active"><a href="#">Tab One</a></li>
    <li class="tab"><a href="#">Tab Two</a></li>
    <li class="tab"><a href="#">Tab Three</a></li>
  </ul>
</nav>

Let the Component’s Container Override Visual Styles as Needed

At this point we have a horizontal tab component, but what if we need to also be able to display tabs vertically? When we need to use the tabs in a context in which they are styled differently, we’ll delegate the responsibility of defining those styles to the parent container. To accomplish this, we’ll create a new .tab-group-vertical class that will also contain .tab elements and group it with the .tab-group class:

.tab-group,
.tab-group-vertical {
  list-style: none;
  margin: 0;
  padding-left: 0;
}

Next, we’ll move some of the declarations made in the .tab ruleset to rulesets with more specific selectors so new classes don’t have to always override them. The .tab class will now only contain styles that are applicable to all tabs:

.tab {
  background-color: #F2F2F2;
  margin-left: 0;
  margin-right: 0;
  position: relative;
}

.tab:hover {
  background-color: #F9F9F9;
}

.tab.active {
  background-color: #FFFFFF;
  color: #000000;
}

The border, border-radius, and display styles should now be delegated to appropriately scoped selectors, and the .tab-group and .tab-group-vertical classes should each receive their own differing border styles (Example 4-10).

Example 4-10. Delegating properties to appropriate containers
/**
 * Horizontal Tab Groups
 */

.tab-group {
  border-bottom: 1px solid #EEEEEE;
}

.tab-group .tab {
  border-bottom: 1px solid #EEEEEE;
  border-top: 1px solid #EEEEEE;
  bottom: -1px;
  display: inline-block;
}

.tab-group .tab:first-child {
  border-left: 1px solid #EEEEEE;
  border-top-left-radius: 4px;
}

.tab-group .tab:last-child {
  border-right: 1px solid #EEEEEE;
  border-top-right-radius: 4px;
}

.tab-group .tab.active {
  border-bottom: 1px solid #2196F3;
}


/**
 * Vertical Tab Groups
 */

.tab-group-vertical {
  border-left: 1px solid #EEEEEE;
}

.tab-group-vertical .tab {
  border-left: 1px solid #EEEEEE;
  border-right: 1px solid #EEEEEE;
  left: -1px;
  display: block;
}

.tab-group-vertical .tab:first-child {
  border-top: 1px solid #EEEEEE;
  border-top-right-radius: 4px;
}

.tab-group-vertical .tab:last-child {
  border-bottom: 1px solid #EEEEEE;
  border-bottom-right-radius: 4px;
}

.tab-group-vertical .tab.active {
  border-left: 1px solid #2196F3;
}

One last thing you may have noticed is that 1px solid #EEEEEE appears over and over. We can refactor this by setting the border-color and border-style properties on the .tab class and then appropriately setting the border-width and overriding the border-color property when necessary, like in Example 4-11. This is advantageous because in the event that this color needs to be changed, there will be fewer places that need to be updated.

Example 4-11. Markup for a group of three tabs with new tab class
/**
 * Tab Component Styles
 */

.tab {
  background-color: #F2F2F2;
  margin-left: 0;
  margin-right: 0;
  position: relative;
}

.tab:hover {
  background-color: #F9F9F9;
}

.tab.active {
  background-color: #FFFFFF;
  color: #000000;
}

.tab > a {
  color: inherit;
  display: block;
  height: 100%;
  padding: 12px;
  text-decoration: none;
  width: 100%;
}

/**
 * Tab Component Containers
 */

.tab-group,
.tab-group-vertical {
  list-style: none;
  margin: 0;
  padding-left: 0;
}

.tab,
.tab-group,
.tab-group-vertical {
  border-color: #EEEEEE;
  border-style: solid;
  border-width: 0;
}

/**
 * Horizontal Tab Groups
 */

.tab-group {
  border-bottom-width: 1px;
}

.tab-group .tab {
  border-bottom-width: 1px;
  border-top-width: 1px;
  bottom: -1px;
  display: inline-block;
}

.tab-group .tab:first-child {
  border-left-width: 1px;
  border-top-left-radius: 4px;
}

.tab-group .tab:last-child {
  border-right-width: 1px;
  border-top-right-radius: 4px;
}

.tab-group .tab.active {
  border-bottom-color: #2196F3;
  border-bottom-width: 1px;
}


/**
 * Vertical Tab Groups
 */

.tab-group-vertical {
  border-left-width: 1px;
}

.tab-group-vertical .tab {
  border-left-width: 1px;
  border-right-width: 1px;
  left: -1px;
  display: block;
}

.tab-group-vertical .tab:first-child {
  border-top-width: 1px;
  border-top-right-radius: 4px;
}

.tab-group-vertical .tab:last-child {
  border-bottom-width: 1px;
  border-bottom-right-radius: 4px;
}

.tab-group-vertical .tab.active {
  border-left-color: #2196F3;
  border-left-width: 1px;
}

When a group of tabs present in a .tab-group-vertical element are rendered using the CSS in Example 4-11 you’ll see the tabs in Figure 4-2.

cssr 0402
Figure 4-2. Vertical tabs

These tabs aren’t quite right yet, but we’ll take care of that next by delegating the assignment of dimensions to the component’s structural container.

Delegate the Assignment of Dimensions to Structural Containers

You may have noticed that in the CSS we wrote for the .tab-group and .tab-group-vertical components’ dimensions weren’t specified. This omission was intentional, because the responsibility for assigning dimensions should be delegated to the structure that contains the component or component group. Structural styles, described in the next section, are styles that dictate the basic structure of a page. There are any number of different layouts you can build, including those with sidebars, columns, or whatever you dream up.

Components are intended to be reusable, so it’s difficult to predict all of the different places where they’ll appear. This is why it’s important to delegate the responsibility of setting dimensions to the elements that are actually housing the components.

For example, let’s assume that we want to display two horizontal groups of tabs that each occupy 50% of the available visible area for desktop browsers, as shown in Figure 4-3. We could add a width: 50% declaration to the .tab-group definition, but that wouldn’t be very reusable if we needed to use the tabs at a different width elsewhere. Instead, Example 4-12 delegates the responsibility of setting dimensions to a structural element that has the .tabbed-pane class. You probably wouldn’t want to use this many different sets of tabs on the same page, but the example is merely intended as an illustration of how to delegate the responsibility of setting dimensions.

cssr 0403
Figure 4-3. Delegating component dimensions to a structural container
Example 4-12. Code that delegates component dimensions to a structural container
<!doctype html>
<html>
  <head>
    <title>Example: Tabs</title>
    <style type="text/css">
      *,
      *:after,
      *:before {
        box-sizing: border-box;
      }

      body {
        margin: 0;
        padding: 0;
      }

      /**
       * Tab Component Styles
       */

      .tab {
        background-color: #F2F2F2;
        margin-left: 0;
        margin-right: 0;
        position: relative;
      }

      .tab:hover {
        background-color: #F9F9F9;
      }

      .tab.active {
        background-color: #FFFFFF;
        color: #000000;
      }

      .tab > a {
        color: inherit;
        display: block;
        height: 100%;
        padding: 12px;
        text-decoration: none;
        width: 100%;
      }

      /**
       * Tab Component Containers
       */

      .tab-group,
      .tab-group-vertical {
        list-style: none;
        margin: 0;
        padding-left: 0;
      }

      .tab,
      .tab-group,
      .tab-group-vertical {
        border-color: #EEEEEE;
        border-style: solid;
        border-width: 0;
      }

      /**
       * Horizontal Tab Groups
       */

      .tab-group {
        border-bottom-width: 1px;
      }

      .tab-group .tab {
        border-bottom-width: 1px;
        border-top-width: 1px;
        bottom: -1px;
        display: inline-block;
      }

      .tab-group .tab:first-child {
        border-left-width: 1px;
        border-top-left-radius: 4px;
      }

      .tab-group .tab:last-child {
        border-right-width: 1px;
        border-top-right-radius: 4px;
      }

      .tab-group .tab.active {
        border-bottom-color: #2196F3;
        border-bottom-width: 1px;
      }


      /**
       * Vertical Tab Groups
       */

      .tab-group-vertical {
        border-left-width: 1px;
      }

      .tab-group-vertical .tab {
        border-left-width: 1px;
        border-right-width: 1px;
        left: -1px;
        display: block;
      }

      .tab-group-vertical .tab:first-child {
        border-top-width: 1px;
        border-top-right-radius: 4px;
      }

      .tab-group-vertical .tab:last-child {
        border-bottom-width: 1px;
        border-bottom-right-radius: 4px;
      }

      .tab-group-vertical .tab.active {
        border-left-color: #2196F3;
        border-left-width: 1px;
      }

      /**
       * Tab Component Containers
       */

      .tabbed-pane {
        display: block;
        width: 100%;
      }

      .tabbed-pane .tab-group {
        float: left;
        width: 45%;
      }

      .tabbed-pane .tab-group:first-child {
        margin-right: 5%;
      }

      .tabbed-pane .tab-group:last-child {
        margin-left: 5%;
      }

      /**
       * Structural Styles
       */

      .global-nav {
        float: left;
        padding: 5% 0;
        width: 10%
      }

      .content {
        float: left;
        padding: 5%;
        width: 80%;
      }

    </style>
  </head>
  <body>
    <nav class="global-nav">
      <ul class="tab-group-vertical">
        <li class="tab"><a href="#">Home</a>
        <li class="tab active"><a href="#">Policies &amp; Fees</a>
        <li class="tab"><a href="#">Documents</a>
        <li class="tab"><a href="#">Billing</a>
      </ul>
    </nav>

    <main class="content">
      <nav class="tabbed-pane">
        <ul class="tab-group">
          <li class="tab active"><a href="#">Policy One</a>
          <li class="tab"><a href="#">Policy Two</a>
          <li class="tab"><a href="#">Policy Three</a>
        </ul>
        <ul class="tab-group">
          <li class="tab active"><a href="#">Fee One</a>
          <li class="tab"><a href="#">Fee Two</a>
          <li class="tab"><a href="#">Fee Three</a>
        </ul>
      </nav>
    </main>
  </body>
</html>

Structural Styles

Structural styles contain components and their containers, just like the .tabbed-pane element in Example 4-12. Since layouts need dimensions, it’s easy to delegate setting the dimensions to structural styles and then simply add in components and their containers. Example 4-13 is code for a simple layout that contains a header, sidebar, and content area (Figure 4-4). When the viewport gets too small, the header, sidebar, and content area stack vertically (Figure 4-5).

Example 4-13. Layout with a header, sidebar, and content area
<!doctype html>
<html>
  <head>
    <title>Layout Example</title>
    <style type="text/css">
    *,
    *:after,
    *:before {
      box-sizing: border-box;
    }

    html,
    body,
    main  {
      height: 100%;
      margin: 0;
      width: 100%;
    }

    .layout-header {
      background-color: #DDDD88;
      display: block;
      min-height: 10%;
      width: 100%;
    }

    .layout-header,
    .layout-sidebar,
    .layout-content {
      text-align: center;
    }

    .layout-sidebar,
    .layout-content {
      float: left;
      height: 100%;
    }

    .layout-sidebar {
      background-color: #8888BB;
      width: 20%;
    }
    .layout-content {
      background-color: #EEBB55;
      width: 80%;
    }

    @media all and (max-width: 640px) {
      .layout-header,
      .layout-sidebar,
      .layout-content {
        display: block;
        float: none;
        height: auto;
        min-height: 100px;
        width: 100%;
      }
    }
    </style>
  </head>
  <body>
    <main>
      <header class="layout-header">Header</header>
      <div class="layout-sidebar">Sidebar</div>
      <div class="layout-content">Content</div>
    </main>
  </body>
</html>
cssr 0404
Figure 4-4. Layout with a header, sidebar, and content area
cssr 0405
Figure 4-5. Layout with a stacked header, sidebar, and content area

Utility Styles

We learned in Chapter 2 that the !important declaration overrides the cascade by telling the browser that a declaration should be used for elements that match its containing ruleset’s selectors regardless of the specificity of the declaration block’s selector. Misuse of !important leads to confusing CSS: if it’s used in multiple declaration blocks to style the same element, the order in which those rulesets appear will determine which styles get applied.

One way to avoid confusion is to avoid using !important entirely, but used sparingly it can be very helpful. Utility styles are styles that get applied to elements either by a prudent developer when she is defining the classes that appear on an HTML element, or by JavaScript when a certain condition is met. For example, if pressing a button hides a particular element, this could be achieved by adding the following class:

.hidden {
  display: none !important;
}

Adding !important ensures that the element will continue to be hidden regardless of other classes that are added to or removed from the element, (unless, of course, a conflicting display property augmented by !important is added after).

Browser-Specific Styles

Older browsers have quirks that can often be overcome by using browser-specific CSS hacks that trick the browser into acting the way you expect it to. For example, due to a bug in Internet Explorer 7 the inline-block value does not always work as expected for the display property, but it is still possible to make elements behave like inline-block elements. To do this you’ll have to trick the browser by using the CSS shown in Example 4-14.

Example 4-14. The inline-block hack for IE7
.selector {
  display: inline-block;
  *display: inline;
  zoom: 1;
}

There are two problems with this code: the *display property is not valid because it’s a syntax error (property names cannot begin with an asterisk), and you’re cluttering up your CSS with a hack for an obsolete browser. If you absolutely cannot drop support for an old browser that needs hacks like this, you should keep these hacks separate and be sure to leave behind comments explaining what the code does. For example, if you need to use a CSS hack for older versions of Internet Explorer, you could keep these hacks in their own stylesheet and add them to the page using conditional comments that only load those styles for a particular version of the browser:

<!–-[if IE 7]>
  <link rel="stylesheet" href="ie7.css" type="text/css" />
<![endif]–->

Chapter Summary

This chapter described the various intents that styles can have. Although the ideas in this chapter are fairly simple, they can be very powerful; as we’ll see in Chapter 6, a solid understanding of the different intents as well as the functionality of the cascade makes organizing and reusing CSS much easier. Before we get to that, though, we’ll talk about testing.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required