How to Develop with CSS
Many web developers know about CSS, but it turns out that they think a proper application of it is something like:
<p>
<span style="font-weight:bold;font-family:Arial">Welcome!</span>
</p>
or:
<table class="middle grey"><tr>
<td class="darkTopHeader">Item #</td>
<td class="darkTopHeader">Qty</td>
<td class="darkTopHeader">Cost</td>
</tr><tr>
<td class="rowHeaderDrk">1341</td>
<td class="cellFieldDrk">3</td>
<td class="cellFieldDrk">$15</td>
</tr><tr>
<td class="rowHeaderLt">1913</td>
<td class="cellFieldLt">2</td>
<td class="cellFieldLt">$17</td>
</tr><tr>
<td class="rowHeaderDrk">1231</td>
<td class="cellFieldDrk">1</td>
<td class="cellFieldDrk">$5</td>
</tr></table>
While the above is *technically* CSS, there is a great deal of opportunity lost if this is how it is used. (If the above looks fine to you, read on to discover how you can make your life easier, and your code better.)
What Is This Document?
This document does not rely on advanced CSS3 selectors (which are neither standard nor well-supported in browsers as of this writing). This document doesn't even talk about certain advanced CSS2 selectors...which are standard but happen to not be supported in the world's most popular browser.
This document is not a guide on how to format your CSS or HTML, and does not attempt to instruct you on how to use CSS to achieve cutting edge layouts. (For examples of some layout techniques, see Glish/CSS, FormDL, and CSS/Edge.)
This document assumes you know the basics about CSS1 (what a selector is, what a compound selector is versus a descendant selector, how values inherit, etc.), and provides a series of guidelines to help you develop with CSS properly.
Contents
- Make Your Site Easy To Develop
- Separate Style from Content
- Use Semantic Selectors
- Minimize HTML Markup
- Semantic HTML
- Contextual Selectors
- Minimize CSS
- Leverage Style Inheritance
- Group Logically-Related Styles
- Re-Use Styles from Page to Page
- Assorted Tips
- Specify
font-size
in em
or %
- Specify Distances in
em
- Die Spacer GIFs, Die!
- Put your CSS in a Separate File
ID
s Must Be Unique
- The
<br>
Tag is NOT Your Friend
- Tables are for Tabular Data. Period.
Make Your Site Easy To Develop
Separate Style from Content
One of the great benefits of CSS is that it allows you to change the display of multiple items in a web page with a single edit, or change an entire site without having to change every page. Putting styles inline in HTML should be avoided in almost all cases; they will always override styles set in a stylesheet, and require modification of the HTML code directly to change the layout.
<p style="margin-top:1em">
CSS applied through the style tag
should be strongly avoided
</p>
<style type="text/css">
p.introParagraph { margin-top:1em }
</style>
...
<p class="introParagraph">
When it must be applied directly, style should
be specified through class or id attributes
</p>
If you're tempted to hack a layout using style="..."
, consider carefully whether there may a better way to abstract out the style into a selector.
(Inline styles can be useful when testing a layout...for example, putting style="border:1px solid red"
on a particular element to highlight where it is appearing on the page. Just remember to remove them prior to releasing your page.)
Use Semantic Selectors
"Semantic" roughly means "conveying meaning and information".
In the above example, the novice developer might be tempted to call the class something like spaceAbove
. This (somewhat naïve) replacement of inline styles with a class that describes the (current) display value provides no real separation between content and display.
Consider if the developer had done this. Then the designer decides that the first paragraph shouldn't actually be pushed down, but should be indented and italicized. So the developer changes the CSS rule:
<style type="text/css">
p.spaceAbove { margin-left:2em; font-style:italic }
</style>
...
<p class="spaceAbove">
This paragraph doesn't actually have any
space above it at all...but it is italic
and indented!
</p>
The result is a class name which is confusing—it claims to do provide style that it doesn't—and provides no clues as to where in the document it might apply.
Never name a class based on the visual style you suppose it will impart. Always describe the content.
Minimize HTML Markup
The more code you have, the more code you have to maintain. The following guidelines help reduce the amount of work needed to add style to raw content.
Minimize HTML—Semantic HTML
To give the CSS selectors a rich data set to work with, the HTML should be marked up using the most appropriate tags for the content.
<div class="h1">Welcome!</div>
<div>My address is:<br>
<span class="address">
123 Foobar St<br>
Cluelesstown, OH
</span>
</div>
<h1>Welcome!</h1>
<p>My address is:</p>
<address>
123 Foobar St<br>
Semanticville, OH
</address>
Not only is the second example better at describing itself, but no special CSS classes are needed to apply the same level of stylistic control to the page.
(Before you email me, consult the W3C HTML4 specification for the list of legal elements and realize: <address>
is perfectly legal HTML tag, not something special I just made up.)
Not convinced that the class
attribute should be used sparingly? Read what the CSS2 specs have to say on this issue:
CSS gives so much
power to the "class" attribute, that authors could conceivably design
their own "document language" based on elements with almost no
associated presentation (such as DIV and SPAN in HTML) and assigning
style information through the "class" attribute. Authors should avoid
this practice since the structural elements of a document language
often have recognized and accepted meanings and author-defined classes may
not.
Whenever you find yourself wanting to use a <span>
or <div>
, wonder if there is another, more meaningful tag allowed in HTML.
Minimize HTML—Contextual Selectors
Rather than putting a class on every bloody item on the page, use the minimum number of id
and class
attributes to describe the page, and then use descendant selectors to apply your style.
For instance, let's look at the example at the top of this document:
<table class="middle grey"><tr>
<td class="darkTopHeader">Item #</td>
<td class="darkTopHeader">Qty</td>
<td class="darkTopHeader">Cost</td>
</tr><tr>
<td class="rowHeaderDrk">1341</td>
<td class="cellFieldDrk">3</td>
<td class="cellFieldDrk">$15</td>
</tr><tr>
<td class="rowHeaderLt">1913</td>
<td class="cellFieldLt">2</td>
<td class="cellFieldLt">$17</td>
</tr><tr>
<td class="rowHeaderDrk">1231</td>
<td class="cellFieldDrk">1</td>
<td class="cellFieldDrk">$5</td>
</tr></table>
Using semantic markup—adding information about the page—results in HTML that's almost half the bytes, and yet which can be styled just as easily. Note the changes/additions to the HTML in bold:
<style type="text/css">
#cart thead th { ... } /* "darkTopHeader" */
#cart tbody th { ... } /* "rowHeader..." common styles */
#cart tbody td { ... } /* "cellField..." common styles */
#cart tr.rowOdd td, /* "...Drk" common coloring */
#cart tr.rowOdd th { ... }
#cart tr.rowEven td, /* "...Lt" common coloring */
#cart tr.rowEven th { ... }
</style>
...
<table id="cart">
<thead>
<tr>
<th>Item #</th>
<th>Qty</th>
<th>Cost</th>
</tr>
</thead>
<tbody>
<tr class="rowOdd">
<th>1341</th>
<td>3</td>
<td>$15</td>
</tr><tr class="rowEven">
<th>1913</th>
<td>2</td>
<td>$17</td>
</tr>
...
</tbody>
</table>
Let the structure of the document work for you.
...but don't rely on the markup too much. It's possible to style most content on the page using only descendant selectors. For example, in the following HTML, the single CSS rule does apply only to the "see full story" links:
<html>
<head>
<title>We rock!</title>
<style type="text/css">
/* "See full story" links are bold */
div div div p a { font-weight:bold }
</style>
</head>
<body>
<div>
<a href="products.html">Our Products</a>
<a href="news.html">News</a>
<a href="about.html">About Us</a>
<a href="contact.html">Contact Us</a>
</div>
<div>
<h1>Welcome!</h1>
<p>...</p>
<div>
<h2>Recent News</h2>
<div>
<p>Blah blah blah<br>
<a href="news.html#n0312">See full story</a>
</p>
<p>
Blah blah blah<br>
<a href="news.html#n0311">See full story</a>
</p>
</div>
</div>
</body></html>
However, a slight change in the document structure could destroy that selector. Far better to provide some semantics in the document to hook the CSS to, which should fare better if the document is changed:
<html>
<head>
<title>We rock!</title>
<style type="text/css">
#newsindex a { font-weight:bold }
/* or perhaps put a class like "clickForMore" on each link */
/* if this same style is used for certain links outside */
/* of the newsindex, in other areas of the site */
</style>
</head>
<body>
<div id="nav">
<a href="products.html">Our Products</a>
<a href="news.html">News</a>
<a href="about.html">About Us</a>
<a href="contact.html">Contact Us</a>
</div>
<div id="content">
<h1>Welcome!</h1>
<p>...</p>
<div class="section">
<h2>Recent News</h2>
<div id="newsindex" class="section">
<p>Blah blah blah<br>
<a href="news.html#n0312">See full story</a>
</p>
<p>
Blah blah blah<br>
<a href="news.html#n0311">See full story</a>
</p>
</div>
</div>
</div>
</body></html>
Minimize CSS
Leverage Style Inheritance
Just as you should let the hierarchy work for you to reduce the amount of HTML on the page, you should also let it reduce the number of times that you have to specify a style. For example, rather than re-setting the font-family
for every rule where it might apply...
p { font-family:Verdana, Arial, sans-serif }
td { font-family:Verdana, Arial, sans-serif }
li { font-family:Verdana, Arial, sans-serif }
dt { font-family:Verdana, Arial, sans-serif }
dd { font-family:Verdana, Arial, sans-serif }
...just set it once, and let it be inherited throughout the page:
body { font-family:Verdana, Arial, sans-serif }
If you find yourself putting the same style in multiple rules, see if they share a common ancestor which could take the style instead.
Group Logically Related Styles
Continuing the previous concept, when multiple selectors share the same style, but not a common ancestor (or where they are a limited subset of that ancestor's descendants and you don't want to apply the same style to EVERYTHING), use a compound selector to set the value only once.
h1 { font-size:120%; margin-bottom:0; font-family:Verdana,sans-serif }
h2 { font-size:110%; margin-bottom:0; font-family:Verdana,sans-serif }
h3 { font-size:100%; margin-bottom:0; font-family:Verdana,sans-serif }
h4 { font-size:100%; margin-bottom:0; font-family:Verdana,sans-serif }
h1,h2,h3,h4 { margin-bottom:0; font-family:Verdana,sans-serif }
h1 { font-size:120% }
h2 { font-size:110% }
h3,h4 { font-size:100% }
Not only is there less code, but if the designer decides that Arial is a better font, the change needs to be made in only one location.
One caveat to this is that you should not attempt to eradicate all occurrences of style duplication. When two elements happen to share the same color or font size as a consequence of a design decision that has nothing to do with the meaning of those elements, then it is possible that they may later want to have different colors, sizes, etc. While it is not difficult to later split a compound selector into its components, you may save yourself some work by not overzealously merging every style/value pair.
If you find yourself putting the same style in multiple rules, see if it makes sense to merge the commonalities into a compound selector.
Re-Use Styles from Page to Page
CSS is great when you can change a single rule and the entire page updates to match. It's even better when you can change a single rule and an entire site is affected.
Certain CSS is appropriate on a page level; specifically, rules which govern a layout unique to that page, such as a homepage. Such rules belong either in a <style>
block inside the <head>
, or linked in from a CSS file unique to that page.
The greatest visual consistency is kept throughout the site when the styles are stored in a common file, and those (semantic) rules are applied consistently to the markup. Use page-specific rules sparingly.
Assorted Tips
Specific uses of CSS and techniques that make a better developer. (For more tips, see also the CSS Crib Sheet.)
Specify font-size
in em
or %
If you specify font size in pt
or px
, the most popular browsers prevent their users from making the font larger or smaller. This is quite user-hostile. By specifying font-sizes in %
or em
, the entire document remains scalable for the user.
Specify Distances in em
In some cases it's necessary to specify distances (top, left, margin, padding) in px
, to match up with specific pixel-based graphic elements in the design. That's fine. The rest of the time, use em
, which is based off of the current font size. This way, if the user changes the font size (or you put a single font-size
style in a rule applied to the body, scaling the font size for the whole page) the design scales with it.
(For an example, view the source of this page, or change your font size and notice how the spacing between paragraphs, headers, and padding around the code samples scales along with the font size.)
Die Spacer GIFs, Die!
In ages past, it was necessary to use table-based layouts to position content on the page; these layouts often used 'spacer GIFs' (small 1px graphics stretched to various dimensions) to hold content in place.
With CSS, gone is the need for nested tables, and with it the need for any sort of spacer GIFs. The only time you should be using <table>
is when you are including tabular data (rows and columns of information) in the page. Further, you should never need a spacer GIF...use appropriate margin
and padding
styles on neighbor elements to achieve the same effect.
Put your CSS in a Separate File
This goes without saying if you're using the same set of CSS for multiple pages on a site. But even if you're only using the CSS for a single page, it *still* can make sense to put it in a separate file. Why?
By putting your CSS in a single file that's linked in from the HTML page, the browser can cache your CSS information (assuming your server sends appropriate HTTP headers). Even if your 'HTML' page is really a server-side ASP (or perl, or CGI, or ColdFusion, or PHP) file, and hence can't be cached, the CSS code that goes with that page will be.
(Note that it is sometimes helpful to leave your CSS in a <style type="text/css">
tag at the top of your HTML while developing, to prevent caching problems and to enable quick inspection of both CSS and code. Just be sure to pull it out into a common file when you're done.)
(This page does not follow the above suggestion. Doing so requires that two files be downloaded if you want to send the file (and not the url) to someone, rather than just one file. Since I have the need for this file to work as a standalone for those without access to the public net, all styles for this page are inline in the head. Plus, it makes it easier for you to view source on this page and see some of the priniciples in action.)
This tip courtesy of John Skufca.
ID
s Must Be Unique
Although some browsers will let you put the same id
attribute on various tags (and apply CSS selectors to both), this is illegal HTML.
<style type="text/css">
#newsitem { ... }
</style>
...
<p id="newsitem">Hey look, here's a news item.</p>
<p id="newsitem">ILLEGAL HTML ALERT! Multiple identical IDs are bad!</p>
<p id="newsitem">An HTML Validator will yell about this.</p>
<style type="text/css">
.newsitem { ... }
</style>
...
<p class="newsitem">Hey look, here's a news item.</p>
<p class="newsitem">Much better.!</p>
<p class="newsitem">Use class to describe multiple similar items.</p>
Whenever you want to duplicate an id
, use a class
instead.
The <br>
Tag is NOT Your Friend
Using the <br>
tag to introduce vertical spacing is exactly the kind of mixing of content with display that you want to avoid. A single <br>
tag may be appropriate under limited circumstances, but more than one is a sure sign that you should be using CSS instead.
<table>
...
</table>
<br><br> <!-- Danger, danger! -->
<table>
...
</table>
<style type="text/css">
table { margin:2em 0 }
</style>
...
<table>
...
</table>
<table>
...
</table>
Limit your use of <br>
tags. If you have more than one in a row, it's a near-certain sign of trouble.
When you are not directly involved in the content creation, you may have no way of determining the author's intent, and hence the semantics of the content. For example, consider a CMS form where users enter content into a field. If a user enters three newlines in a row, you have no way of knowing if that was the end of a paragraph, logically related information in the same paragraph, or...well, you just don't know. In such cases it's acceptable to introduce a new <br>
tag for each newline. If you don't know the semantics and are merely echoing user-entered content back, preserve the presentation.
Tables are for Tabular Data. Period.
The <table>
tag is not evil. Despite what some people think and say, the point of CSS is not to never use it. However, a corollary to the principle of Semantic HTML is that you should never use tables for layout. Let me say that again, as its own paragraph, for emphasis:
Never use tables for layout. Ever.
If you can't decide whether or not a table is the right choice, ask yourself these questions:
- “Do the rows or columns of information share a common attribute?”
- “If I changed the order of the rows or columns, would it still make sense?”
- “If I transposed the axes of the table (made rows into columns and vice-versa) would it still make sense?”
If the answer to the above is generally “Hrm...I guess not,” then you shouldn't be using a table.
If you need more convincing, see "Why Tables Are Bad (For Layout) Compared to Semantic HTML + CSS".
Comments, criticisms and suggestions for additional tips are welcome.
Copyright ©2004
Gavin Kistner.