Chapter 4. Large-Scale CSS

In Chapter 3, you saw how a good information architecture based on well-constructed HTML provides a solid foundation on which we can layer other capabilities for modules within a large web application. In this chapter, we explore techniques for adding a layer of presentation.

The purpose of the presentation layer is to enhance usability and provide an aesthetic user experience by applying principles of good visual design. At its best, a good presentation plays a principal role in making a large web application more usable and appealing. At its worst, a poorly executed presentation can render an otherwise useful and meaningful information architecture completely worthless.

In web development, a well-defined presentation layer is implemented using CSS. This, in fact, was the original purpose of CSS when it first came on the scene in the late 1990s: it allowed for clear separation of a web application’s underlying information architecture from its presentation. However, this separation hasn’t necessarily guaranteed modularity. Our goal in developing large web applications is defined in the CSS-related tenet from Chapter 1:

Tenet 4: Large-scale CSS forms a layer of presentation that is separate from the information architecture, applied in a modular fashion, and free of side effects as we reuse modules in various contexts.

In this chapter, we begin by looking at the tradeoffs between the various ways to include CSS. We explore important techniques with selectors, standard module formats, and CSS scoping that help you apply CSS in a modular way. Next, we view some useful methods by which we can position elements outside the normal document flow. These are especially important in the creation of layouts and containers, which are reusable enclosures that allow you to organize modules across a large web application. Finally, we examine a few examples of layouts and containers, and discuss some practices to provide a clean, consistent slate on which to layer a presentation.

Modular CSS

Once you have divided the components of a web page into reusable modules that reflect the information architecture (see Chapter 3), you can focus on how to give the modules the desired presentation through CSS. As mentioned earlier in Tenet 4, the CSS should:

  • Have distinct modules of its own that apply to specific parts of the architecture (in other words, it should be well targeted), and

  • Not produce unexpected consequences as a module is reused in various contexts (in other words, it should be free of side effects).

In practice, you may need to alter your information architecture slightly over time to accommodate highly stylized presentations. But even when making such adaptations, always try to maintain a meaningful HTML structure upon which you can layer modular CSS.

Including CSS

Let’s look at three ways to include CSS in a web application: linking, embedding, or inlining. Although linking is generally the most desirable of these options, it is common to find some combination of these methods in use across a large web application, even within a single page, since each method has some benefits, depending on the specific situation.

Linking

Linking allows you to place CSS in a file, which you then apply by placing a link tag in an HTML file. This has desirable effects: architecturally, multiple pages can share the same CSS, while in terms of performance, the browser can cache the file after downloading it the first time. The following example links a CSS file:

<link href="http://.../sitewide.css" type="text/css" rel="stylesheet" />

Linking also allows you to specify a media type so that you can direct certain CSS rules at specific media (e.g., screens, printers, etc.). For example, when displaying a file for printing, you can remove nonessential items that would consume a lot of printer ink. To achieve this flexibility, specify media="all" as an attribute for the main stylesheets, then conceal certain parts of the page during printing by including specific styles for this purpose in a second stylesheet with the attribute media="print". For example:

<link href="http://.../sitewide.css" type="text/css" rel="stylesheet"
   media="all" />

<link href="http://.../printers.css" type="text/css" rel="stylesheet"
   media="print" />

Once you modify a file that browsers may have already cached, you need to make sure browsers know when to bust the cache and download the new copy. Version numbers help you manage this. Incorporating a date stamp into the CSS file’s name works well, and you can add a sequence number when updating the CSS file more than once a day (or you could use the version number from your source control system instead of the date stamp). For each new copy, simply bump up the version number as shown here:

<link href="http://.../sitewide_20090422.css" type="text/css"
   rel="stylesheet" />

Of course, each page that includes a link to the CSS file must also be updated to refer to the new version. Chapter 7 presents information on managing this in a centralized way.

Embedding

Embedding places CSS directly within a style block on a web page. One benefit of this method is that you can generate and include CSS programmatically. For example, PHP can generate CSS directly as it generates the HTML for the page. It also provides an easy way to override the styles in a linked CSS file on a per-page basis. Because browsers apply CSS rules in order as they encounter them (after taking into account the rules of specificity), the embedded styles must come after the link tags in order to override them. Example 4-1 defines a CSS rule for an element with the ID nwcrev using embedded CSS.

Example 4-1. Embedding CSS
<head>

...

<style>
#nwcrev
{
  ...
}
</style>

...

</head>

You can specify a media type with embedded CSS, just as when linking CSS. Example 4-2 illustrates embedding CSS for different media types.

Example 4-2. Embedding CSS with different media types
<head>

...

<style media="all">
#nwcrev
{
   /* Add styling normally needed for the element. */
   ...
}
</style>
<style media="print">
#nwcrev
{
   /* Avoid displaying this element when printing. */
   display: none;
}
</style>

...

</head>

Inlining

Inlining sets CSS styles for an individual element of an HTML page using its style attribute. You should use inlining very sparingly, because it requires the most effort to override and provides little opportunity for reuse. That said, you can make a good case for inlining in situations where CSS serves more than just a purely presentational purpose. Some examples include pop-up menus or tabbed interfaces in which certain elements need to be concealed by default. Because the CSS in these situations is more tightly coupled to the markup and behavior to begin with, it may make sense to communicate this situation by inlining the CSS. Another benefit in these situations is that inlining CSS also prevents concealed menus or tabs from being revealed briefly in the event that network latency keeps the linked CSS from being downloaded immediately.

Example 4-3 illustrates inlining CSS to conceal a pop-up menu. This technique is useful in that you can hide or show the menu by simply toggling the CSS display property of its enclosing div from none to block via JavaScript rather than reconstructing the menu in the DOM each time.

Example 4-3. Inlining CSS
<div id="msgpop" style="display: none;">
   <ul>
      <li>
         <a href="http://...">Contact via Email</a>
      </li>
      <li>
         <a href="http://...">Contact via IM</a>
      </li>
      <li>
         <a href="http://...">Add to My Contacts</a>
      </li>
   </ul>
</div>

Applying CSS

However you include your CSS, you apply its rules to specific elements in the HTML markup with CSS selectors. A thoughtful use of CSS selectors is essential to the creation of modular CSS. Let’s review the basic, most widely supported selectors so that we can then see how to best use them in large web applications.

IDs

IDs are unique identifiers for elements specified using an element’s id attribute. You select an element based on its ID by preceding the element’s identifier with a pound sign (#). For example, to apply CSS to an element with id="nwcrev", specify the following:

#nwcrev
{
   /* Add styling for the element with the ID nwcrev. */
   ...
}

In large web applications, selection by ID plays a particularly important part in scoping CSS so that it is applied only within certain modules or certain pages (see Scoping with CSS).

Classes

Classes are identifiers for collections of semantically similar elements and are applied to HTML elements using the class attribute. You select an element based on its class by preceding the class identifier with a dot (.). For example, to apply CSS to all elements with class="action", do the following:

.action
{
   /* Add styling for elements with the class action. */
   ...
}

Example 4-4 illustrates how you can use classes to apply different styles to items that appear at various positions within a list. Unlike with IDs, it is possible to assign multiple classes to an HTML element. In addition to beg, mid, and end, the example adds the classes odd and even for styling the list items based on this distinction as well. You could use the odd and even classes to create a striped list.

Example 4-4. Classes within a module
<div id="msgpop" style="display: none;">
   <ul class="menu">
      <li class="beg odd">
         <a href="http://...">Contact via Email</a>
      </li>
      <li class="mid even">
         <a href="http://...">Contact via IM</a>
      </li>
      <li class="end odd">
         <a href="http://...">Add to My Contacts</a>
      </li>
   </ul>
   <ul class="more">
      <li class="beg odd">
         <a href="http://...">View More Options</a>
      </li>
   </ul>
</div>

In large web applications, selection by class plays an especially important role in presentation switching (see Scoping with CSS).

Descendants

The descendants of an element are all the elements it encloses. For instance, a ul element has all its li elements as descendants, as well as all the elements they enclose. You specify descendants in CSS by placing them after the ancestor element, with one or more spaces between them, as shown here:

#msgpop .beg
{
   /* Add styling for elements with class beg enclosed by msgpop. */
   ...
}

In large web applications, a good understanding of selection using descendants is especially important when working with standard module formats (see Standard Module Formats).

Elements

Elements can be selected based just on their type. CSS selects elements this way simply by listing them without any additional punctuation, as shown here:

a
{
   /* Add styling for all links wherever they appear. */
   ...
}

Pseudoclasses, which let you specify a state for the element, are appended to element selectors with a colon:

a:visited
{
   /* Add styling for all links that have been visited. */
   ...
}

You should use element selectors sparingly on their own in a large web application, because they potentially affect such a large number of elements, unless that is truly your intention and all developers are aware of that intent. A better option is to use them as part of a contextual selector that targets a specific pattern of descendants:

#msgpop ul .odd a
{
   /* Only style odd links within unordered lists inside msgpop. */
   ...
}

Appending a class selector to an element selector (using no space between the element and class) lets you achieve even further qualification, as shown here:

#msgpop ul.menu .odd a
{
   /* Only style odd links in uls with the class menu in msgpop. */
   ...
}

In large web applications, selection by element is primarily useful when you have done a good job creating reasonably small, well-constructed modules that each have a clearly defined scope (see Scoping with CSS). You can also qualify an element selector further by requiring the element to have a specific ID (e.g., div#nwcrev).

Grouping

Grouping selectors provides an easy syntax to help you achieve visual consistency in a large web application. To group selectors, separate them with commas:

#nwcrev h3, #nwcrev h4
{
   /* Add styling for h3 and h4 elements enclosed by nwcrev. */
   ...
}

Specificity and Importance

Specificity is a value calculated to determine the winner when multiple selectors in a cascade of styles end up addressing the same element. For example, specifying an element by ID is more specific than using its class; specifying an element by its class is more specific than using an element selector. Because the rules for calculating specificity are often not at the top of a web developer’s mind (although not difficult to learn), it is good to avoid relying on them too heavily in large web applications.

Declaring a style with !important overrides its selector’s specificity. Its use is almost always a bad idea, because it hinders maintainability. In fact, it’s usually a sign that you’re obscuring a mistake that will bite you later. Instead of using !important, interpret the urge as a warning to look for a brittle section of your code. As the lifespan of the application continues, one use of !important inevitably leads to others as developers seek quick fixes in place of understanding the underlying structure of the application. This situation quickly becomes unwieldy.

Scoping with CSS

In a large web application, we expect modules to move around and be used in a variety of contexts over time. To keep your CSS reusable, you must always be aware of the scope in which your CSS applies.

Scoping within a module

In Chapter 3, we discussed the fact that because IDs never should be used on a page more than once, they provide a great way for us to identify modules in a unique way, provided we have a good naming convention. Assuming that a module’s ID is unique, we can use it to create a convenient scope of sorts for any CSS that we want to direct only at that module.

Example 4-5 illustrates defining the CSS for two modules, the New Car Reviews module and the New Car Queries module. Because each selector begins with the module ID, each rule is targeted specifically at the module we expect.

Example 4-5. Scoping CSS within modules
/*
*  New Car Reviews
*/
#nwcrev
{
   ...
}
#nwcrev label
{
   ...
}

/* Add more rules here for the New Car Reviews presentation. */
...

/*
*  New Car Queries
*/
#nwcqry
{
   ...
}
#nwcqry label
{
   ...
}

/* Add more rules here for the New Car Queries presentation. */
...

Scoping at the page level

If you want to apply CSS to a module only where it appears within a specific page, use CSS scoped at the page level. Scoping CSS at the page level is particularly useful when you desire slight differences in the way a module looks on various pages, which is a common issue with reusability in large web applications.

For example, suppose you want a left margin for a module when it appears on the right side of a page and a right margin for the same module when it appears on the left. One solution is to assign the body element of each page a unique page ID, which gives you a way to target CSS for modules on specific pages where they appear. Examples 4-6 and 4-7 illustrate adding top and left margins to the New Car Reviews module and top and right margins to the New Car Queries module on the New Car Search Results page.

Example 4-6. New Car Search Results page
<body id="nwcsrspage">
...
         <div id="nwcrev">
            ...
         </div>

...

         <div id="nwcqry">
            ...
         </div>
...
</body>
Example 4-7. Scoping CSS at the page level
#nwcsrspage #nwcrev
{
   /*
      Only apply these margins to the New Car Reviews module on the New
      Car Search Results page.
   */
   margin: 10px 0 0 10px;
}
#nwcsrspage #nwcqry
{
   /*
      Only apply these margins to the New Car Queries module on the New
      Car Search Results page.
   */
   margin: 10px 10px 0 0;
}

A good guideline is to apply styles that affect a module’s border and everything inside its border at the module level, but leave styles that apply outside a module’s border (e.g., margins, floating, etc.) for each page to style based on how and where the module is used. However, if something within the module requires styling on the outermost div everywhere it is used (e.g., it needs to be floated), style that at the module level.

Presentation switching

When variations in a module’s presentation require multiple rules and when these rules are likely to be reused across a variety of contexts, just relying on a page ID to achieve alternate presentations is cumbersome. In these situations, a better solution is to associate a class with each presentation and switch between presentations by setting the appropriate class on the outermost div for the module.

Figure 4-1 illustrates two presentations for the Popular New Cars module. In Examples 4-8 and 4-9, we use the class default to switch to the CSS for the default (wider) presentation. If we change the class to compact, we end up applying a different set of CSS for the module. CSS that is the same in both presentations is specified without qualifying it further as part of the default or compact classes. The CSS in Example 4-9 assumes that you have first applied the browser reset and font normalization CSS presented at the end of this chapter.

The Popular New Cars module: a) default version, and b) compact version
Figure 4-1. The Popular New Cars module: a) default version, and b) compact version
Example 4-8. The Popular New Cars module
<div id="nwcpop" class="default">
   <div class="hd">
      <div class="tagline">
         <cite>
            View popular cars from Honda
         </cite>
      </div>
      <div class="logo">
         <img src="http://.../logo_honda.gif"/>
      </div>
   </div>
   <div class="bd">
      <ul class="pri">
         <li>
            <a href="http://...">2009 Honda Accord</a>
         </li>
         <li>
            <a href="http://...">2009 Honda Civic</a>
         </li>
         <li>
            <a href="http://...">2009 Honda Civic Hybrid</a>
         </li>
      </ul>
      <ul class="sec">
         <li>
            <a href="http://...">2009 Honda Civic Sedan</a>
         </li>
         <li>
            <a href="http://...">2009 Honda CR-V</a>
         </li>
         <li>
            <a href="http://...">2009 Honda Element</a>
         </li>
      </ul>
   </div>
</div>
Example 4-9. Presentation switching for the Popular New Cars module
/* Apply this CSS to all presentations of the module. */
#nwcpop .hd
{
   position: relative;
}
#nwcpop .hd .tagline
{
   position: absolute;
   padding: 5px 0 0 15px;
}
#nwcpop .hd .tagline cite
{
   font: bold 77% verdana;
   color: #000;
}
#nwcpop ul
{
   list-style-type: disc;
   margin: 0;
   padding: 0;
}
#nwcpop li
{
   margin-left: 15px;
   line-height: 1.3em;
}

/* Apply this CSS to just the default version of the module. */
#nwcpop.default .hd
{
   width: 630px;
   background: url(http://.../top_630.gif) #fff no-repeat;
   padding-top: 8px;
}
#nwcpop.default .tagline
{
   width: 475px;
}
#nwcpop.default .logo
{
   padding: 0 15px 0 0;
   background: url(http://.../mid_630.gif) repeat-y;
   text-align: right;
}
#nwcpop.default .bd
{
   position: relative;
   background: url(http://.../btm_630.gif) no-repeat;
   background-position: bottom left;
   padding: 10px 15px 15px;
}
#nwcpop.default .pri
{
   width: 295px;
}
#nwcpop.default .sec
{
   position: absolute;
   top: 10px;
   right: 15px;
   width: 295px;
}

/* Apply this CSS to just the compact version of the module. */
#nwcpop.compact .hd
{
   width: 300px;
   background: url(http://.../top_300.gif) #fff no-repeat;
   padding-top: 8px;
}
#nwcpop.compact .tagline
{
   width: 145px;
}
#nwcpop.compact .logo
{
   padding: 0 15px 0 0;
   background: url(http://.../mid_300.gif) repeat-y;
   text-align: right;
}
#nwcpop.compact .bd
{
   background: url(http://.../btm_300.gif) no-repeat;
   background-position: bottom left;
   padding: 10px 15px 15px;
}

In Chapter 7, we’ll examine how to manage presentation switching in a nicely encapsulated, programmatic way using PHP. In practice, highly stylized modules often require a combination of presentation switching and programmatic changes to the information architecture.

Standard Module Formats

As a general rule, it may be worthwhile to group parts of modules into some common divisions. Besides adding further elements of structure to your information architecture, they can provide additional hooks for styling. For instance, Examples 4-8 and 4-9 apply background images for the rounded borders in the presentation. These are conveniently placed on the hd division (the header) and bd division (the body). Divisions of this type often contain other divisions. For example, an outer division with presentation for the header or body encapsulates an inner division that contains the content for either section.

Some examples of divisions applicable to most modules (just as they are to entire pages) are a header, a body, and a footer, to which you might give the classes hd, bd, and ft, respectively. This is the case in Example 4-8. Even when one of these sections is empty, it may be useful to add the section for future use, so we could have also added an empty division with the class ft after the bd division in Example 4-8. Undoubtedly, there are other standard divisions that you may choose to apply to certain modules in certain types of applications. Microformats are other examples of standard formats that you might incorporate within some modules. You can read more about microformats at http://microformats.org.

Examples 4-10 and 4-11 illustrate the use of standard header, body, and footer divisions for a module. In these examples, a module with the ID nwclst (New Car Listings) is nested within a module with the ID nwcsrs (New Car Search Results). Examples 4-10 and 4-11 also demonstrate that, although standard formats are useful, you do have to be mindful of side effects when applying them within nested modules (in actuality, the same problem exists when you use any commonly used class within nested modules). In these situations, when you apply CSS to the outer instance of the class, the cascading nature of CSS applies the same styles to any inner instances as well because the inner instances are descendants, too, just deeper ones.

Example 4-10. Nested modules using a standard module format (with side effects)
<div id="nwcsrs">
   <div class="hd">
      ...
   </div>
   <div class="bd">
      <div class="pri">
         <div id="nwclst">
            <div class="hd">
               <!-- This hd gets styling from the outer hd too. -->
               ...
            </div>
            <div class="bd">
               <!-- This bd gets styling from the outer bd too. -->
               ...
            </div>
            <div class="ft">
               <!-- This ft gets styling from the outer ft too. -->
               ...
            </div>
         </div>
      </div>
      <div class="sec">
         ...
      </div>
   </div>
   <div class="ft">
      ...
   </div>
</div>
Example 4-11. Applying CSS for a standard module format (with side effects)
#nwcsrs .hd
{
   /* Add styling for the header section of the nwcsrs module. */
   ...
}
#nwclst .hd
{
   /* The nwclst module hd gets styles from the nwcsrs hd too. */
   ...
}

Fortunately, the solution for guarding against side effects within nested modules is rather simple: use the unique module ID as a prefix for the common classes that may occur within nested modules. Examples 4-12 and 4-13 illustrate how this resolves the issue. Small, self-contained modules generally do not exhibit this issue; therefore, we will leave the shorter classes for the nwclst module (the inner one) intact.

Example 4-12. Nested modules using a standard module format (no side effects)
<div id="nwcsrs">
   <div class="nwcsrshd">
      ...
   </div>
   <div class="nwcsrsbd">
      <div class="nwcsrspri">
         <div id="nwclst">
            <div class="hd">
               <!-- This hd gets only its own stlying now. -->
               ...
            </div>
            <div class="bd">
               <!-- This bd gets only its own styling now. -->
               ...
            </div>
            <div class="ft">
               <!-- This ft gets only its own styling now. -->
               ...
            </div>
         </div>
      </div>
      <div class="nwcsrssec">
         ...
      </div>
   </div>
   <div class="nwcsrsft">
      ...
   </div>
</div>
Example 4-13. Applying CSS for a standard module format (no side effects)
#nwcsrs .nwcsrshd
{
   /* Add styling for the header section of the nwcsrs module. */
   ...
}
#nwclst .hd
{
   /* The hd for the nwclst module gets only its own styles now. */
   ...
}

Positioning Techniques

When you eliminate presentation from your information architecture, a significant part of how you position your modules now must be accomplished within the presentation layer. The document flow that is inherent in your information architecture will dictate to some extent certain aspects of positioning intrinsically—for instance, by default, modules appear on the page in the order specified—but the rest must be accomplished using CSS. As a result, a good understanding of some basic positioning techniques in CSS is essential. These provide the foundation for creating the layouts and containers in which to arrange your modules. The same positioning techniques are useful for positioning elements within modules as well.

CSS Box Model

Because positioning techniques are often sensitive to the size of our elements, it is important at the outset to have a fundamental understanding of the CSS box model and how it affects an element’s size. The parts of an element that figure into an element’s size, working from the outermost edge of an element inward, are its margin, border, padding, and content area (see Figure 4-2).

The CSS box model
Figure 4-2. The CSS box model
Margin

The margin is the buffer zone between the element and others around it. You specify how much space to leave around each edge. Even if your element has a background, this margin is empty and transparent.

Border

The border is the line (or other stylized decoration) that forms a perimeter. You can specify a thickness, style, and color for the border.

Padding

The padding is the buffer zone that appears inside the border of an element. You specify how much space to leave within each edge before the content begins.

Content area

The content area is the innermost region of an element, holding your content. You can set its width and height, among other properties.

The size of an element in either direction (width or height) is the space in that direction (horizontally or vertically) occupied by all four of these properties. If you don’t specify the size of the content area explicitly (using the width or height properties), the content area expands in that direction to fit the space required by the content.

Although browsers used to be inconsistent in how they calculated the size of an element, all popular browsers now handle the calculation consistently as just described. Example 4-14 shows the CSS for a module that yields a width of 320 pixels:

  • 10 pixels for the left margin

  • 2 pixels total for left and right borders of 1 pixel each

  • 18 pixels total for left and right padding of 9 pixels each

  • 290 pixels for the content area

The overall height of the module expands to whatever height is needed for the content since the height is not specified; however, this height will include an additional 20 pixels (2 pixels total for both the top and bottom borders of 1 pixel each, plus 18 pixels total for both the top and bottom padding of 9 pixels each). In the layouts and containers presented later in the chapter, we will fix the widths and let the heights expand to fit the content as needed, which is a common approach. In Example 4-14, we’re setting the margin separately using CSS scoped at the page level to demonstrate margins changing based on where the module is used.

Example 4-14. Setting CSS properties that affect sizing
/* Styles for the module set for wherever the module will be used. */
#nwcqry
{
   width: 290px;
   border: 1px solid #fafafa;
   padding: 9px;
}

...

/* Scoping at the page level to target the margin for just one use. */
#nwcsrspage #nwcqry
{
   margin-left: 10px;
}

Document Flow

Although the goal is to keep information architecture and presentation separate in a large web application, they are, of course, related. The information architecture is largely responsible for the normal flow of elements on a page, and many CSS rules are applied based on the structure of the HTML markup. In the normal document flow, by default, inline elements (e.g., span, strong, img, etc.) flow side by side; block-level elements (e.g., div, p, form, etc.) appear on their own line. However, you can always alter this using CSS. To make an inline element appear as a block-level element, you can set display: block for the element. Similarly, to make a block-level element appear as an inline element, you can set display: inline. If you set display: none, you remove the element from the document flow altogether, and elements around it are closed up as if the element were never there. To conceal an element without removing it from the document flow, use visibility: hidden.

Note

Even though you can change the CSS display property for an element, you must always honor the original HTML-level display of an element within your information architecture. For example, regardless of what you do in CSS, you cannot put block-level HTML elements within inline HTML elements, because it may cause serious bugs in some scenarios.

You can also use CSS to move elements around the page and make them appear out of their normal order. The default, where elements flow down and across the page, is called static positioning. The most useful alternatives are relative positioning, absolute positioning, and floating.

Relative Positioning

You specify relative positioning by setting the CSS property position to relative. Relative positioning lets you position an element relative to where it would have appeared in the normal document flow without disrupting the normal flow of other elements on the page. It’s often used to nudge an element slightly into a desired position without affecting neighboring elements. The following CSS uses relative positioning to position a citation within the nwclst module five pixels higher than it would appear otherwise.

#nwclst cite
{
   position: relative;
   top: -5px;
}

Absolute Positioning

You specify absolute positioning by setting the CSS property position to absolute. Absolute positioning removes an element from the normal document flow and positions it relative to the containing block. Because absolutely positioned elements are removed from the document flow, other elements are superimposed on the same location as if the absolutely positioned elements were not there.

The containing block is the first block-level ancestor of the absolutely positioned element that has something other than static positioning itself; if no ancestor qualifies, the absolutely positioned element is positioned relative to either the html or body element, depending on the browser. This lesser-known nuance of absolute positioning is very important because it has dramatic implications for the actual location where an absolutely positioned element ends up.

The logical choice, then, for establishing the containing block explicitly is to apply relative positioning to a chosen ancestor element. If you avoid specifying coordinates for the ancestor (i.e., using top, left, etc.), the element remains in its original position within the document flow, visually unaffected.

One important use of absolute positioning is to position block-level elements side by side, especially div elements with a set width (you cannot simply set display: inline, because for layout purposes, you usually require other benefits that block-level elements provide related to margins, borders, and padding). As long as you know which of the elements will be the tallest, you can use that element to occupy the necessary vertical space in the document flow while you absolutely position the other element or elements beside it. This approach to positioning elements side by side provides you with the benefit of not having to worry about special measures to get the parent element to size itself appropriately, unlike with floating elements (see Floating). Absolute positioning often works well for positioning small sections of HTML markup side by side.

Examples 4-15 and 4-16 show the HTML markup and CSS relevant to absolutely positioning the tagline and logo blocks for the default presentation of the Popular New Cars module presented in Figure 4-1. Notice that the tagline block, the first division in the module’s normal flow, is the one that we absolutely position. This is required so that the logo block is superimposed at the same vertical position; otherwise, because the logo division follows the tagline division, the tagline would hog its own vertical block and the logo would get the next block down.

Now that we have positioned the tagline and logo at the same position vertically, the text-align attribute, applied to the logo block at the end of Example 4-16, pushes it to the right. Also notice that the hd division for the module has position set to relative so that it becomes the containing block for the tagline block.

Example 4-15. The Popular New Cars divisions for absolute positioning
<div id="nwcpop" class="default">
   <div class="hd">
      <div class="tagline">
         <cite>
            View popular cars from Honda
         </cite>
      </div>
      <div class="logo">
         <img src="http://.../logo_honda.gif" />
      </div>
   </div>

   ...

</div>
Example 4-16. Absolute positioning in the Popular New Cars module
#nwcpop .hd
{
   position: relative;
}
#nwcpop .hd .tagline
{
   position: absolute;
   padding: 5px 0 0 15px;
}
#nwcpop .hd .tagline cite
{
   font: bold 77% verdana;
   color: #000;
}

...

#nwcpop.default .hd
{
   width: 630px;
   background: url(http://.../top_630.gif) #fff no-repeat;
   padding-top: 8px;
}
#nwcpop.default .tagline
{
   width: 475px;
}
#nwcpop.default .logo
{
   padding: 0 15px 0 0;
   background: url(http://.../mid_630.gif) repeat-y;
   text-align: right;
}

...

Floating

To float an element, set the CSS property float to left or right. In some ways, floated elements are removed from the normal flow of the document, but they still affect the document’s layout. In general, when you float an element, other content flows around it. However, if you float a series of elements in sequence, floating provides a powerful means of arranging block-level elements side by side as well.

The rule with floating is that if multiple elements occupy the same vertical location and all of them have float set to left, they appear in order from left to right. Therefore, you can use this technique to place two elements side by side. You could float one to the left and one to the right, but floating everything to the left makes for simpler code if you want to expand the layout to include more than two sections.

Compared with absolute positioning for arranging block-level elements side by side, floating has the benefit of not requiring you to know which of the elements is the tallest. This makes floating especially well suited for creating highly reusable, generic layouts and containers, which we will discuss in a moment. Figure 4-3, presented in the next section, shows an example of floating within a layout.

Block-level elements containing floated elements need to be floated as well to ensure that they expand as needed to fit the floated content. Alternatively, you can set overflow to hidden and zoom to 1 for the parent of the floated elements. The next section provides examples of floating in layouts and containers.

Layouts and Containers

Layouts are generic templates that define the overarching structure of pages. Containers are generic groupings of modules that you can place within layouts to support common organizations. Together, layouts and containers play a vital role in fostering reusability, maintainability, and reliability in large web applications. You can use them to define the standard sections in which to place your modules on a page.

Chapter 3 mentions the importance of exploring the design of web pages to find ways to reuse as many components as possible. Ultimately, building a page as a set of individual components makes it easier to maintain and more reliable throughout its lifetime. In addition to developing reusable modules, you should consider reusable traits within the structure of pages across your entire application, which you can exploit through layouts and containers. Not only does this promote maintainability and reliability within the code, but it also fosters a more consistent user experience across the application and contributes to its overall usability. Furthermore, layouts and containers provide a good opportunity for engineers and designers to work together to establish standard guidelines for designing and building large web applications.

In this section, we explore layouts and containers that are fixed width, variable height, and grid-based. These are some of the most common layouts in large web applications. That said, the principles we will discuss apply equally well for other types of layouts (e.g., fluid layouts where sections grow or shrink as the window is resized, free-form layouts, etc.), despite different implementation details.

Note

No matter what type of layout you choose, it is important to remember the dynamic nature of layout on the Web. Fluid layouts require that you relinquish control to the browser for the sizing and flow of content within any portion of the layout that uses a relative unit (percentage, em, etc.). Fixed-width layouts add a certain amount of control in the horizontal direction, but you still must anticipate the impact of letting content flow as needed in the vertical direction.

Example Layouts

Figure 4-3 presents an example layout well suited to pages containing search results or various types of listings; we’ll call it the Results layout. Examples 4-17 and 4-18 present an implementation for the layout using the techniques discussed in Floating. In Chapter 7, we will explore techniques for inserting modules and containers into the layout dynamically. This way, you can reuse the layout for any page that needs the same overall structure. Let’s look at some examples of the types of content that might appear within each section of the layout.

The Results layout
Figure 4-3. The Results layout
layres (outer wrapper)

Contains no content directly, but provides overarching structure for the other sections in the layout.

layreshdr (header)

Extends across the top of the page and might contain branding, banner advertising, primary navigation, and a query form for narrowing the search results.

layresmaj (major)

Contains no content directly, but provides overarching structure for the major part of the page containing the layrespri, layressec, and layrester sections.

layrespri (primary)

Holds the primary content for the page, which is the set of search results or listings.

layressec (secondary)

Holds the secondary content for the page, such as business partner callouts and additional advertising.

layrester (tertiary)

Holds tertiary content, which, because of its position well down the page, is intended for less frequently used modules.

layresftr1 and layresftr2 (footers)

Sections for footers. We define two sections to provide slightly different styling options (the second footer does not have the border on the left and right). Both might contain helpful cross-linking to other parts of the site.

Within the CSS, we float all these sections to the left. Because layrespri and layressec together fit within the width of their container (layresmaj), the result is that layressec appears to the right of layrespri. Our hardcoded widths ensure that the two layers take up the exact horizontal space we have allocated. We float the other elements on the page to ensure each section expands to fit any floated content within it. Recall our earlier discussion about this in Floating.

The overflow attribute on the inner elements is set to hidden to guard against letting any content that happens to be too wide cause problems when the content is placed within the layout. Setting display to inline for floated elements with margins avoids the infamous “double margin” bug in earlier versions of Internet Explorer that doubled the margins for floated elements.

Example 4-17. HTML for the Results layout
<div id="layres">
   <div class="layreshdr">

      ...

   </div>
   <div class="layresmaj">
      <div class="layrespri">

         ...

      </div>
      <div class="layressec">

         ...

      </div>
      <div class="layrester">

         ...

      </div>
   </div>
   <div class="layresftr1">

      ...

   </div>
   <div class="layresftr2">

      ...

   </div>
</div>
Example 4-18. CSS for the Results layout
#layres
{
   float: left;
   background: #e3e4e8;
}
#layres .layreshdr
{
   float: left;
   width: 950px;
   overflow: hidden;
   border-left: 1px solid #d1d6dc;
   border-right: 1px solid #d1d6dc;
   border-top: 1px solid #fff;
   padding: 10px 9px 0;
}
#layres .layresmaj
{
   float: left;
   width: 968px;
   border-left: 1px solid #d1d6dc;
   border-right: 1px solid #d1d6dc;
   background: #e3e4e6;
}
#layres .layrespri
{
   float: left;
   display: inline;
   width: 750px;
   overflow: hidden;
   margin: 10px 10px 0 9px;
}
#layres .layressec
{
   float: left;
   display: inline;
   width: 190px;
   overflow: hidden;
   margin: 10px 9px 0 0;
}
#layres .layrester
{
   clear: both;
   float: left;
   display: inline;
   width: 950px;
   overflow: hidden;
   margin: 10px 9px;
}
#layres .layresftr1
{
   clear: both;
   float: left;
   width: 968px;
   overflow: hidden;
   border-left: 1px solid #d1d6dc;
   border-right: 1px solid #d1d6dc;
}
#layres .layresftr2
{
   clear: both;
   float: left;
   width: 970px;
   overflow: hidden;
   border-bottom: 1px solid #fff;
}

Figure 4-4 presents an example layout that is well suited to pages containing details about a search result or listing; we’ll call it the Details layout.

The Details layout
Figure 4-4. The Details layout

Let’s look at some examples of the types of content that might appear within each section of the layout:

laydtl (outer wrapper)

Contains no content directly, but provides overarching structure for the other sections in the layout.

laydtlhdr (header)

Extends across the top of the page and might contain branding, banner advertising, primary navigation, and header information.

laydtlmaj (major)

Contains no content directly, but provides overarching structure for the major part of the page containing the laydtlpri, laydtlsec, and laydtlter sections.

laydtlpri (primary)

Contains no content directly, but provides overarching structure for the leader and latter sections.

laydtlldr (leader)

Contains no content directly, but provides overarching structure for the leader content.

laydtllat (latter)

Provides overarching structure for the latter section containing the laydtllatpri and laydtllatsec sections.

laydtllatpri and laydtllatsec (latter primary and secondary)

Holds the latter content, which is important content just beneath the leader section. In our example, the latter content will always be in a two-column format, which the layout reflects.

laydtlsec (secondary)

Placed to the right to hold secondary content, such as business partner callouts and additional advertising.

laydtlter (tertiarty)

Holds tertiary content, which, because of its position well down the page, is intended for less frequently used modules.

laydtlftr1 and laydtlftr2 (footers)

Sections for footers. These serve the same purpose as in the Results layout presented earlier.

Examples 4-19 and 4-20 present the implementation for the Details layout. For the same reasons discussed earlier, this layout sets overflow to hidden and display to inline in certain places.

Example 4-19. HTML for the Details layout
<div id="laydtl">
   <div class="laydtlhdr">

      ...

   </div>
   <div class="laydtlmaj">
      <div class="laydtlpri">
         <div class="laydtlldr">

            ...

         </div>
         <div class="laydtllat">
            <div class="laydtllatpri">

               ...

            </div>
            <div class="laydtllatsec">

               ...

            </div>
         </div>
      </div>
      <div class="laydtlsec">

         ...

      </div>
      <div class="laydtlter">

         ...

      </div>
   </div>
   <div class="laydtlftr1">

      ...

   </div>
   <div class="layresftr2">

      ...

   </div>
</div>
Example 4-20. CSS for the Details layout
#laydtl
{
   float: left;
   background: #e3e4e8;
}
#laydtl .laydtlhdr
{
   float: left;
   width: 950px;
   overflow: hidden;
   border-left: 1px solid #d1d6dc;
   border-right: 1px solid #d1d6dc;
   border-top: 1px solid #fff;
   padding: 10px 9px 0;
}
#laydtl .laydtlmaj
{
   float: left;
   width: 950px;
   border-left: 1px solid #d1d6dc;
   border-right: 1px solid #d1d6dc;
   padding: 10px 9px 0;
}
#laydtl .laydtlpri
{
   float: left;
   display: inline;
   width: 750px;
   overflow: hidden;
   margin-right: 10px;
}
#laydtl .laydtlldr
{
   float: left;
   display: inline;
   width: 750px;
   overflow: hidden;
}
#laydtl .laydtllat
{
   float: left;
   display: inline;
   width: 710px;
   overflow: hidden;
   padding: 20px;
   background: #ccc;
   margin-top: 10px;
}
#laydtl .laydtllatpri
{
   float: left;
   display: inline;
   width: 310px;
   overflow: hidden;
   margin-right: 30px;
}
#laydtl .laydtllatsec
{
   float: left;
   display: inline;
   width: 370px;
   overflow: hidden;
}
#laydtl .laydtlsec
{
   float: left;
   display: inline;
   width: 190px;
   overflow: hidden;
}
#laydtl .laydtlter
{
   clear: both;
   float: left;
   display: inline;
   width: 950px;
   overflow: hidden;
   background: #fff;
   margin-top: 10px;
}
#laydtl .laydtlftr1
{
   clear: both;
   float: left;
   width: 968px;
   overflow: hidden;
   border-left: 1px solid #d1d6dc;
   border-right: 1px solid #d1d6dc;
   margin-top: 10px;
}
#laydtl .laydtlftr2
{
   clear: both;
   float: left;
   width: 970px;
   overflow: hidden;
   border-bottom: 1px solid #fff;
}

Example Containers

Figure 4-5 shows two presentations for an example container. This container holds two modules placed side by side in a single row. Examples 4-21 and 4-22 present an implementation for a 2×1 container using the techniques discussed in Floating. The examples also use the techniques discussed previously for presentation switching to achieve different versions of the container that fit within different sections of various layouts.

The 2×1 container: a) default version, and b) minimum version
Figure 4-5. The 2×1 container: a) default version, and b) minimum version

The presentation selected with the class default consists of two sections with room for 370 pixels of content each. The presentation selected with the class minimum consists of two sections with room for 150 pixels of content each. In Chapter 7, we will explore techniques for managing various aspects of containers in a dynamic fashion. This way, containers can be reused on any page that needs the same arrangement of modules. Other containers might arrange three or four sections of modules.

Example 4-21. HTML for the 2×1 container
<div id="con2x1" class="default">
   <div class="con2x1pri">

      ...

   </div>
   <div class="con2x1sec">

      ...

   </div>
</div>
Example 4-22. CSS for the 2x1 container
#con2x1.default
{
   float: left;
   width: 750px;
   overflow: hidden;
}
#con2x1.default .con2x1pri
{
   float: left;
   display: inline;
   width: 370px;
   overflow: hidden;
   margin-right: 10px;
}
#con2x1.default .con2x1sec
{
   float: left;
   width: 370px;
   overflow: hidden;
}
#con2x1.minimum
{
   float: left;
   width: 310px;
   overflow: hidden;
}
#con2x1.minimum .con2x1pri
{
   float: left;
   display: inline;
   width: 150px;
   overflow: hidden;
   margin-right: 10px;
}
#con2x1.minimum .con2x1sec
{
   float: left;
   width: 150px;
   overflow: hidden;
}

Example 4-23 shows how you could use the default version of the 2x1 container within the primary content section of the Results layout presented earlier. From this example, coupled with what we observed in Chapter 3 about constructing an information architecture to create reusable modules, you can begin to see how a page built from a layout, with containers inserted as needed, and modules inserted into the layout and various containers, helps build modular web applications in which components are reusable, maintainable, and reliable.

Example 4-23. The default version of the 2x1 container within the Results layout
<div id="layres">
   <div class="layreshdr">

      ...

   </div>
   <div class="layresmaj">
      <div class="layrespri">
         <div id="con2x1" class="default">
            <div class="con2x1pri">

               ...

            </div>
            <div class="con2x1sec">

               ...

            </div>
         </div>
      </div>
      <div class="layressec">

         ...

      </div>
      <div class="layrester">

         ...

      </div>
   </div>
   <div class="layresftr1">

      ...

   </div>
   <div class="layresftr2">

      ...

   </div>
</div>

Other Practices

To ensure browsers render your presentations in a pixel-perfect manner, use the following practices to help write reliable CSS as you develop large web applications. The goals of these practices are to establish a consistent platform on which to apply your styles and to address inconsistencies with the way browsers render fonts.

Browser Reset CSS

All browsers provide a default set of styles on which your own styles will be layered. Unfortunately, default styles are inconsistent across various browsers. Without a common starting point on which to layer the application’s own presentation, you end up making many more adjustments in CSS for particular browsers to maintain consistency. Fortunately, Yahoo! makes some CSS rules available in its YUI (Yahoo! User Interface) library to reset modern browsers to a consistent set of neutral styles. You can download the library and read its complete documentation at http://developer.yahoo.com/yui. To apply this CSS, simply add the following link before any CSS of your own:

<link href="http://yui.yahooapis.com/2.7.0/build/reset/reset-min.css"
   rel="stylesheet" type="text/css" media="all" />

Because it is helpful to see which properties must be reset, Example 4-24 lists the CSS. Alternatively, you can place this CSS directly in your own CSS file to avoid the additional server request with the previous link.

Example 4-24. Browser reset CSS from the YUI library
/*
Copyright (c) 2009, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.net/yui/license.txt
version: 2.7.0
*/

html
{
   color: #000;
   background: #FFF;
}
body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code,
form, fieldset, legend, input, button, textarea, p, blockquote, th, td
{
   margin: 0;
   padding: 0;
}
table
{
   border-collapse: collapse;
   border-spacing: 0;
}
fieldset, img
{
   border: 0;
}
address, caption, cite, code, dfn, em, strong, th, var, optgroup
{
   font-style: inherit;
   font-weight: inherit;
}
del, ins
{
   text-decoration: none;
}
li
{
   list-style: none;
}
caption, th
{
   text-align: left;
}
h1, h2, h3, h4, h5, h6
{
   font-size: 100%;
   font-weight: normal;
}
q:before, q:after
{
   content: '';
}
abbr, acronym
{
   border: 0;
   font-variant: normal;
}
sup
{
   vertical-align: baseline;
}
sub
{
   vertical-align: baseline;
}
legend
{
   color: #000;
}
input, button, textarea, select, optgroup, option
{
   font-family: inherit;
   font-size: inherit;
   font-style: inherit;
   font-weight: inherit;
}
input, button, textarea, select
{
   *font-size: 100%;
}

Font Normalization

The ways in which browsers render fonts, especially when it comes to font size, are not consistent. Fortunately, Yahoo! makes CSS available in its YUI library to normalize fonts within the modern browsers. To apply this CSS, add the following link before any CSS of your own:

<link href="http://yui.yahooapis.com/2.7.0/build/fonts/fonts-min.css"
   rel="stylesheet" type="text/css" media="all" />

Example 4-25 lists the CSS for font normalization. As with the browser reset CSS shown earlier, you can place this CSS directly in your own CSS file to avoid the additional server request with the previous link.

Example 4-25. Font normalization CSS from the YUI library
/*
Copyright (c) 2009, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.net/yui/license.txt
version: 2.7.0
*/

body
{
   font: 13px/1.231 arial, helvetica, clean, sans-serif;
   *font-size: small;
   *font: x-small;
}
select, input, button, textarea, button
{
   font: 99% arial, helvetica, clean, sans-serif;
}
table
{
   font-size: inherit;
   font: 100%;
}
pre, code, kbd, samp, tt
{
   font-family: monospace;
   *font-size: 108%;
   line-height: 100%;
}

Assuming you use the CSS from Example 4-25, Table 4-1 specifies the percentage to specify in your CSS to render a font in the number of pixels listed for today’s browsers. Research conducted at Yahoo! across many versions of the most popular browsers has shown that fonts render the most consistently across browsers when you specify font sizes using percentages as opposed to points or ems. This is the reason that we use percentages for fonts throughout this book.

Table 4-1. Font sizing in modern browsers, assuming normalization from the YUI library

To render in pixels

Use this percentage

10

77%

11

85%

12

93%

13

100%

14

108%

15

116%

16

123.1%

17

131%

18

138.5%

19

146.5%

20

153.9%

21

161.6%

22

167%

23

174%

24

182%

25

189%

26

197%

Get Developing Large Web Applications now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.