Overview

Why do we care if the CSS is well-written? For the same reasons why we like good code:

  • making it easier to reuse
  • making it as easy as possible for your team members to understand and manipulate it
  • efficiency (speed and size)
  • good css patterns tend to repeat and provide consistency to both the code and visual appearance
  • well designed sites can be styled very differently with the same markup
  • it is best practice, and we should follow the industry standard

Writing Strong Markup

Here are some guidelines that help you write good markup

  • Use the right markup elements
  • Use <span> to indicate non-semantic markup
  • try to avoid <br/> when you can use <p>
  • use <ul> and <ol> instead of nested divs
  • Try to stick to valid markup
  
<h1><a>Text</a></h1> <!-- VALID --!>
<a><h1>Text</h1></a> <!-- NOT VALID --!>
  

Doctypes

Using XHTML is recommended over HTML because XHTML is valid XML, and can be parsed by browsers, XSLT, etc.

As referenced from W3Schools

  • XHTML elements must be properly nested
  • XHTML elements must always be closed
  • XHTML elements must be in lowercase
  • XHTML documents must have one root element

Strict or Transitional?

  • most of our pages use transitional, harder to adhere to strict
  • most code out there is transitional, for ease of development transitional is recommended
  • Here's a good article that compares these doctypes in further detail

IDs and Classes

The more you add ids and classes, the more complexity you are injecting into the system. Creating more potential for useless or confusing css code.

  • try to add as few as possible
  • more complex CSS selectors help with this
  • that said, the strength of IDs is readily apparent when you need to target a specific leaf in the DOM for things like selenium or javascript manipulation
  • a good use of id's is on a leaf of the DOM, if you need to target a specific dom element for things like selenium or javascript manipulation
  • another good use of id's is at application-layout levels (such as header, footer, etc) to ensure those css definitions aren't used in other places on the site, even if the styles may be equivalent at that time
  • if you find that your css is nesting lots of dom elements & classes to get to your specific element you're trying to style (and its happening consistently), an ID at a strategically placed level in your dom can make it easier to understand and work with. (give example)

When do we use classes? Classes are great for denoting important parent nodes in the DOM (body), or describing contents of tags (users). IE6 has some issues with multiple classes (more on that later)

Naming conventions: Check this naming conventions table, which shows what popular sites are using for their markup names.

Know your CSS Shorthand

CSS shorthand is more compact and can make your code easier to scan quickly, when maintaining your code. Here's a link to shorthand properties

  
margin: x y;      /* top/bottom  left/right */
margin: x y z;    /* top  left/right  bottom */
margin: w x y z;  /* top  left  right  bottom */
  
   
border: width style color;
  
  
background: color image repeat attachment position;

/* verbose */
background-color: #f00;
background-image: url(background.gif);
background-repeat: no-repeat;
background-attachment: fixed;
background-position: 0 0;

/* compact (order can matter in old versions of safari) */
background: #f00 url(background.gif) no-repeat fixed 0 0;
  
  
/* verbose */
font-style:italic;
font-variant:small-caps;
font-weight:bold;
font-size:1em;
line-height:140%;
font-family:"Lucida Grande",sans-serif;

/* compact */
font:italic small-caps bold 1em/140% "Lucida Grande",sans-serif;
  

Content-Driven CSS Design

The idea behind content-driven CSS design is to use an iterative development process that emphasizes creating semantically accurate markup, while maintaining consideration for other factors that will determine the your implementation strategy.

  • Simple and Semantically-accurate markup
  • Creating/Editing the CSS easily and flexibly
  • Re-usability if required
  • Context-specific requirements

Developing your CSS iteratively helps you keep your code simple, designing only as needed.

  1. Design the markup

    What's the simplest thing you can do to make it work? Design the markup as semantically and simply as possible. This will not only aid in search engine optimization, but also lends itself to be more easily interpretable and editable. This also forces you to determine up front what the content will be if no mockup exists for the feature you're working on.

  2. Choose your strategy

    Whether you are trying to decide how to implement your site's overall layout from your mockups, coming up with your site's rounded corners strategy or styling page-specific content, its always good to have a plan of attack before going head-first into the code.

    The nature of CSS allows for many different solutions to solve the same problem, for better or worse. In the latter case, its important to understand the advantages and disadvantages of each technique you choose.

    Understand your constraints. Using rounded corners as an example, do you need to support gradients in your rounded corners? Can the background behind your rounded corner container be different colors? Do you need to have borders and drop shadows?

    In our Strategies & Solutions section, we try to help guide you towards finding the appropriate implementation technique for common CSS challenges by describing advantages and disadvantages of each.

  3. Iterate.

CSS Selectors & Pseudo-Elements

CSS selectors and pseudo-elements are very handy tools to streamline both your markup and your CSS. Its possible to get by without using them, but they can make life so much easier.

IE Compatibility. Not all selectors and pseudo-elements are supported by IE 6/7, but there is at least one javascript extensions that give IE the capability to use them. IE7-JS supports not only selectors/pseudo-elements, but also supports provides IE the ability to utilize other functionalities such as PNG support.

Selectors

Especially useful selectors: >, *, +, E[ ]

*
Matches any element.
E[foo]
Matches any E element with the "foo" attribute set (whatever the value).
E + F
Matches any F element immediately preceded by an element E.
E > F
Matches any F element that is a child of an element E.

Pseudo-Elements

E:link E:visited
Matches element E if E is the source anchor of a hyperlink of which the target is not yet visited (:link) or already visited (:visited).
E:active
Matches E during certain user actions.
E:hover
Matches E during certain user actions.
E:focus
Matches E during certain user actions.
E:first-child
Matches element E when E is the first child of its parent.

Pseudo-Elements with a content property

E:before
Inserts specified content before E
E:after
Inserts specified content after E
<ul> <li>one</li> <li>two</li> <li>three</li> </ul>
    
  ul li:before {
    content: ' | ';
  }

  ul li:first-child:before {
    content: ' ';
  }
  
  • one
  • two
  • three

Width Control for Block Elements

My rule of thumb is, if I set a width, I don't set margin or padding. Likewise, if I'm setting a margin or padding, I don't set a width. Dealing with the box model can be such a pain, especially if you're dealing with percentages. Therefore, I set the width on the containers and then set margin and padding on the elements within them. Everything usually turns out swimmingly.
Jonathan Snook

If you set a width, you should probably avoid setting padding on that same item, as padding increases the width of the specified container. Instead, add a child element and specify it as padding on that element. It does add markup to your page, but it can greatly improve flexibility and control working within the box model.

See the code below this example for more details.

50% width, no padding. 50% width, no padding. 50% width, no padding. 50% width, no padding. 50% width, no padding. 50% width, no padding. 50% width, no padding. 50% width, no padding. True 50% width, no padding. 50% width, no padding. 50% width, no padding. 50% width, no padding. 50% width, no padding. 50% width, no padding. 50% width, no padding. 50% width, no padding.
50% width, with an inner child element with padding: 15px. 50% width, with an inner child element with padding: 15px. True 50% width, with an inner child element with padding: 15px. 50% width, with an inner child element with padding: 15px.
50% width, padding: 15px. 50% width, padding: 15px. 50% width, padding: 15px. 50% width, padding: 15px. (total width > 50% width. not what we wanted when we specified its width to 50%) 50% width, padding: 15px. 50% width, padding: 15px. 50% width, padding: 15px.

Example 1: Baseline box with 50% width, and no padding.

  
<div id="box">
  50% width, no padding.
</div>
  
  
#box {
  width: 50%;
}
  

Example 2 (our preferred method): Baseline box with 50% width, and no padding.

  
<div id="box">
  Box 50% width, with an inner child element giving us the padding we want (15px in this case)
</div>
  
  
#box {
  width: 50%;
}

#box > span.inner {
  padding: 15px;
}
  

Example 3 (typical implementation): Baseline box with 50% width and padding applied to it. Notice that even though you gave it a width of 50%, the actual width is larger than 50% because of the padding. Not idea. There's no point in giving it a 50% width if it isn't going to actually be 50%. Notice that in this scenario, even our fixed height of 100px is no longer true. The padding has increased the height to 130px.

  
<div id="box">
  Box with 50% width and padding: 15px applied to it.
</div>
  
  
#box {
  width: 50%;
  padding: 15px;
}
  

Nested Positioning

This is an essential technique that can be applied to different CSS strategies including tooltips, vertical centering and equal-height columns. The essence to this technique is that, when you have a relatively-positioned parent element, you can take the child element out of flow and position it relative to the parent's box.

  
<div class="nested_positioning_example">
  <div class="abs">Absolutely Positioned Div</div>
</div>

  
  
.nested_positioning_example {
  position: relative;
}

.abs {
  position: absolute;
  top: 20%;
  left: 90%;
}
  
Relatively Positioned Parent Div
Absolutely Positioned Child Div

Vertical Centering

Three popular ways to vertically center are using

  • line-height
  • absolute positioning
  • display-table-cell

Markup to vertically center

  
<div id="vertically_center_example">
  <span>Center me!</span>
</div>
  

Using line-height

Set the line-height to be the same as the height of the container.

Advantages: Very simple to use.

Disadvantages: This only works if you only have 1 line of text to center, and you know the height of its container.

  
#vertically_center_example {
  height: 5em;
  line-height: 5em;
}
  
Vertically Center me!

Using Absolute Positioning

The vertically positioned element needs to be position: absolute, 50% from the top of its parent container with a negative margin of half its height.

Advantages: Works for any type of element, including multiple lines of text.

Disadvantages: This only works if you know the element you want centered has a fixed height.

  
#vertically_center_example2 {
  position: relative;
}

#vertically_center_example2 span {
  display: block;
  position: absolute;
  height: 1em;
  top: 50%;
  left: 0;
  margin-top: -0.5em;
}
  
Vertically Center me!

Display-table-cell

Doesn't work in IE 6/7/8 (at least not yet for IE8).

Full-height Columns

Here are a couple popular ways to style adjacent columns so they share the same height, which is determined by the size of content in the main div.

Absolute Columns

24 Ways introduced this method they call Absolute Columns to solve this common problem.

Advantages: This method is very simple to implement and understand. It is very easy to adjust column properties, such as color and width.

Disadvantages: You have to know which column will have the greater amount of content. In most cases this is the case, so its generally not an issue. To make it work perfectly in IE, you may need to add an additional line of CSS.

The idea that drives this technique is that

  • There is a parent div wrapping the columns. This parent div is relatively positioned.
  • The column that will be the tallest in height has default positioning (static).
  • Any additional columns are absolutely positioned within the parent div. The key to making each column 100% in height is to set its top and bottom parameters to 0px each. Its horizontal positioning is set via left/right properties.

Our Absolute Columns Live Demo example

  
<div id="container">
  <div id="column-left">
    <h2>#left</h2>
  </div>
  <div id="column-mid">
    <h2>#mid</h2>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit...</p>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit...</p>
  </div>
  <div id="column-right">
    <h2>#right</h2>
  </div>
</div>
  
  
#container {
  position: relative;
  background-color: #DDD;
}

#column-left {
  width: 80%;
  background-color: #990000;
}

#column-right {
  position: absolute;
  top: 10px;
  right: 0px;
  bottom: 10px;
  width: 20%;
  overflow: auto;
}
  

Faux Columns

A List Apart describes another method known as Faux Columns, aptly named because the method gives the appearance of equal-height columns despite the fact, each column container is only as tall in height as its content.

Advantages: This method is also very simple to implement and understand.

Disadvantages: Fixed width solution only. Requires a background image to provide the colors of each column. It is also not very flexible, as any changes to the width and color of any column requires a new image be created.

The idea that drives this technique

  • There is a parent div wrapping the columns. Place a background image on the wrapping parent DIV that houses the columns.
  • This background image is the aesthetic indicator of each column's width/height, and is repeated vertically to the full height of the wrapping container (and not applied to each column). Thus, the background of each column will stretch the entire height of the container, even though each column's actual height may not.
  • Position the actual columns so they match the widths drawn by the background image. In our live demo, we use floats to position the columns, but because the columns' properties area actually independent of the background image on the wrapping container, you can choose any positioning method you'd like.

Our Faux Columns Live Demo example

  
<div id="container">
  <div id="column-left">
    <h2>#left</h2>
  </div>
  <div id="column-mid">
    <h2>#mid</h2>
  </div>
  <div id="column-right">
    <h2>#right</h2>
  </div>
</div>
  
  
#container {
  width: 800px;
  overflow: hidden;
  background: url(/images/faux_columns/bg.png) repeat-y top left;
}

#column-left {
  float: left;
  width: 160px;
}

#column-mid {
  float: left;
  width: 480px;
}

#column-right {
  float: right;
  width: 160px;
}
  

Sticky Footer

This is a common technique using CSS to create a footer that sticks to the bottom of the window or page content, when you don't have enough content to fill your browser window. Read more about sticky footers

Advantages: Works in all browsers and is relatively simple to implement for most layouts.

Disadvantages: This technique is hard to work with if you are mixing 100% width layout elements and fixed width layout elements, but not unworkable.

The idea that drives this technique is that

  • There are two primary divs in the layout: the footer div and the wrapper div, which wraps everything other than the footer.
  • The wrapper's height is 100% of the browser. The last element in the wrapper div is the push div.
  • The footer and push div share the same height. The footer doesn't require any positioning properties, but the wrapper requires a margin that is the negative value of the height of the footer/push div. (example: If the footer is 50px high, set the wrapper's margin-bottom to -50px.)
  • The negative margin on the wrapper makes sure the footer shows up within the 100% height of the browser without scroll bars, when the content is less than the full browser height. When the content is greater than the full browser height, the push div keeps the footer below the content, preventing them from overlapping due to the margin-bottom from the wrapper div.

Our simple sticky footer example

  
<div class="wrapper">
  <div class="push"></div>
</div>
<div class="footer">
  <span>I'm the sticky footer</span>
</div>
  
  
html, body {
  height: 100%;
}

.wrapper {
  min-height: 100%;
  height: auto !important;
  height: 100%;
  margin: 0 auto -5em;
}

.footer, .push {
  height: 5em;
}
  

Rounded Corners

There are 48392743289 different ways to do rounded corners, but there's no one perfect method.

Here are some examples

  • border radius
  • absolute positioning
  • sliding doors
  • javascript solutions

Border Radius

Use browser-specific CSS.

Advantages: Very simple to use.

Disadvantages: Does not work in IE and is not anti-aliased.

  
-moz-border-radius: 0.2em;
-webkit-border-radius: 0.2em;
  

Absolute Positioning

A simple method that requires an image for each corner (or a single image through CSS Sprites). Transparency is optional, but if you have transparency you can make the inside portion of the rounded corner images transparent, which allows you to change the background color of the rounded element via CSS. Additionally, this method supports font-scaling in all browsers, as rounded images stay at the corners of the rounded container in all situations.

Advantages: Simple CSS, works gracefully with font-scaling. Very easy to modify corners you want rounded.

Disadvantages: Additional markup is required.

The idea that drives this technique is that

  • There is a container that requires rounded corners. This container is set to position: relative.
  • A div for each corner is required. Each of these divs are set to position: absolute. Additionally, they are positioned through the top/bottom/left/right properties by setting the appropriate ones to zero.
  
<div class="absolute_rounded_corners_example example">
  <div class="tl"></div>
  <div class="tr"></div>
  <div class="bl"></div>
  <div class="br"></div>
</div>
  
  
.absolute_rounded_corners_example {
  position: relative;
  height: 6em;
  width: 15em;
  margin: 2em;
  background-color: #004400;
}

.tl, .tr, .bl, .br {
  position: absolute;
  height: 10px; width: 10px;
  background-image: url(/images/absolute_roundies.png);
  background-repeat: no-repeat;
}

.tl {
  top: 0; left: 0;
  background-position: top left;
}

.tr {
  top: 0; right: 0;
  background-position: top right;
}

.bl {
  bottom: 0; left: 0;
  background-position: bottom left;
}

.br {
  bottom: 0; right: 0;
  background-position: bottom right;
}
  

Sliding Doors

The sliding doors technique involves using two adjacent markup elements to create the rounded corner aesthetic. In our example, we will use a list tag and an anchor tag. These two adjacent elements display the left and right sides of the rounded corner container, with one side expanding horizontally in a "sliding door" fashion, based on the length of the content within it.

Advantages: Potentially no additionally markup required. Works with vertical gradients.

Disadvantages: Images are required. Basic implementation does not font-scale. Does not support transparency behind the container.

The idea that drives this technique is that

  • A background image that is horizontally as wide as the longest potential container is required.
  • The two adjacent elements used to render the rounded container in our example a list tag and an anchor tag.
  • The list tag is given a background of the image, positioned to start from the left. A padding-left is given to this list item so that the nested anchor tag is positioned correctly.
  • The anchor tag is given the same background image, but positioned to start from the right. A padding top/bottom is used to vertically center the text, while also giving the container the appropriate height required to show the background image properly. A padding-right is given that is equal to the padding-left given to the list item, to horizontally center the content and display the right side of the rounded container correctly.
    
  <div class="example_container">
    <ul class="sliding_doors_example">
      <li><a href="#rounded_corners" name="">Tab 1</a></li>
      <li><a href="#rounded_corners" name="">Tab Two</a></li>
      <li><a href="#rounded_corners" name="">Another Tab</a></li>
    </ul>
  </div>
    
  
    
  ul.sliding_doors_example {
    overflow: hidden;
  }

  ul.sliding_doors_example li {
    list-style-type: none;
    float: left;
    margin-right: 0.3em;
    width: auto;
    padding-left: 25px;
    background: url(/images/sliding_doors.png) no-repeat top left;
  }

  ul.sliding_doors_example li a {
    display: block;
    padding: 10px 25px 10px 0;
    background: url(/images/sliding_doors.png) no-repeat top right;
  }
    
  

Javascript Solutions

Use javascript to add necessary DOM elements, can be image or non-image based.

Advantages: Many different solutions already available. Template markup is cleaner.

Disadvantages: Usually inserts many DOM nodes, different solutions have varying support for the visual effect you are trying to achieve.

Miscellaneous Tricks

Make font-size ~= 10px

  
font-size: 62.5%;
  

Image replacement

  
h1 {
  text-indent: -9999em;
  width: 182px;
  height: 28px;
  background: url(../images/overview.png) no-repeat top left;
}
  
  
<h1>Image replacement</h1>
  

Forcing anchors not to wrap

  
a {
    white-space:nowrap;
}
  

Removing active anchor outlines

  
a:active, a:focus {
  outline:none;
  -moz-outline: none; /* for firefox */
}
  

Fixing rails form error indicators

  
form .fieldWithErrors {
  display: inline;
}
  

Other Interesting Articles

CSS 3

CSS3 is still going through a lot of changes. Read up more on its future