My Favorite CSS Techniques

I’ve been creating websites for 12 years. I started using CSS many years ago, but for a long time (like many designers) I only used it for text formatting. It wasn’t until 2006 that I took the plunge into pure CSS for layout. Since then, I’ve developed some favorite techniques that I use over and over again. Here, I’m finally putting them in one place.

These techniques don’t require any acrobatic wizardry or anything. They mirror my design approach—simple, clear, minimal, and (hopefully) easy to follow. It is because of this approach that my cross-browser fixes are usually quite minimal. That’s why you won’t see many IE tweaks here.

Before I start…

How did I learn CSS?

I’ve only picked up a few CSS books over the years, but I’ve leaned on those books heavily. They are:

What? No Zeldman? I know, I know. I started with the local protégé, Mr. Cederholm. He’s taken me far.

Of course, I also read a ton of web design blogs and online publications. The key ones (for me) are:

Now that you know how I learned to do this, here’s what I find myself using over and over again:

Image Replacement on Headers

Headers (h1, h2, etc.) are very, very important. For starters, search engines love them. When Google crawls your page, it first asks “Okay, where’s the h1? That will tell me what this page is.” If you don’t include headers, then you’re making Google guess. Why make Google guess?

This is what the h1 of Darowski.com looks like:

<h1><a href="http://www.darowski.com/tracesofinspiration">Adam Darowski’s Traces of Inspiration</a></h1>

And here’s how it renders with no CSS:

h1 with no styling

Of course, that just ain’t gonna do. Here’s how it renders with the rest of the design:

h1 styled and integrated with design

So, how do you do that? With image replacement. We’re taking the h1, then giving it a background image and height & weight (using the dimensions from the background image). For the text itself, we give it a negative indent so that the text doesn’t appear on the page. It’s still there for screen readers and search engines, but the typical user won’t see it.

Here’s the CSS:

h1 {
  margin: 0;
}
h1 a {
  display: block;
  background: url(darowski.png) no-repeat 0 0;
  height: 103px;
  width: 449px;
  text-indent: -9999px;
}

Why is most of the code applied to the link inside the h1? Because if you apply the negative indent to the text inside the h1, the clickable link actually appears off the page. If you indent the text inside the clickable link, the link itself still appears on the page—only the text inside is hidden. I know, weird. But it works!

Implementing types and values using Microformats

Using Microformats to mark up an email address is pretty easy.

<a href="mailto:adarowski@gmail.com">adarowski@gmail.com</a>

becomes:

<a class="email" href="mailto:adarowski@gmail.com">adarowski@gmail.com</a>

And that’s it.

But what if you want to add more information to that email address, like if it is a home or work email address? In that case, inside of the object with the class of “email”, you need two more objects: one with the class name of “type” and another with the class name of “value”. The “type” is where you define home or work while “value” is the actual email address.

Here’s an example from my About page:

<p>Work Info:</p>
<ul>
  <li>Website: <a href="http://batchblue.com/" class="url">http://batchblue.com/</a></li>
  <li>Blog: <a href="http://darowski.com/" class="url">http://blog.batchblue.com/</a></li>
  <li class="email"><span class="type hidetext">Work</span> Email: <a href="mailto:adarowski@batchblue.com" class="value">adarowski@batchblue.com</a></li>
  <li class="tel"><span class="type hidetext">Work</span> Phone: <span class="value">(888) 402-2824</span></li>
</ul>

So, inside the li (which has the “email” class) lives two spans—one “type” and one “value”. The HTML renders like so with no CSS:

markup with microformats and no CSS

But you’ll notice that in this case, I have a “hidetext” style on the “type”. That’s because I only want to display it as “Email”. I simply add this style:

span.hidetext {
  display: none;
}

and I’m done. It renders like so:

Microformatted markup with CSS to hide type

I don’t always hide the types, though. On BatchBook’s contact detail pages, the type works very well into the layout:

Microformat types for email addresses works into BatchBook's UI nicely

Custom icons on list items

Here’s one I use A LOT. Unordered lists are wonderful. I use them all the time. But that bullet that denotes an unordered list by default can be a bit boring. Here’s what I did to spruce up the bloggers list on the BatchBlue Blog:

BatchBlue Blog blogger list with icons or each list item

And here’s the markup:

<h2>Bloggers</h2>
<ul id="bloggers">
  <li id="calhoun"><a href="http://blog.batchblue.com/?author=6" title="Archive for Keri Calhoun">Keri Calhoun</a></li>
  <li id="darowski"><a href="http://blog.batchblue.com/?author=5" title="Archive for Adam Darowski">Adam Darowski</a></li>
  <li id="larson"><a href="http://blog.batchblue.com/?author=8" title="Archive for Will Larson">Will Larson</a></li>
  <li id="ohara"><a href="http://blog.batchblue.com//?author=4" title="Archive for Pamela O'Hara">Pamela O'Hara</a></li>
  <li id="ransom"><a href="http://blog.batchblue.com/?author=1" title="Archive for Sean Ransom">Sean Ransom</a></li>
  <li id="riggen"><a href="http://blog.batchblue.com/?author=2" title="Archive for Michelle Riggen-Ransom">Michelle Riggen-Ransom</a></li>
  <li id="sweeney"><a href="http://blog.batchblue.com/?author=7" title="Archive for Stephanie Sweeney">Stephanie Sweeney</a></li>
</ul>

As you can see, we gave the list an id of “bloggers” and each list item an id of the blogger’s last name. That allows us to apply a different icon to each blogger’s list item.

But first, here’s the CSS to get the list looking the way we want (correct padding, left indenting, removing the default bullet):

ul#bloggers {
  margin: 0;
  list-style: none;
}
ul#bloggers li {
  padding: 4px 0;
  margin: 1px;
  padding-left: 24px;
}
ul#bloggers li a {
  text-decoration: none;
}

Next, here is the CSS for each of the individual line items:

#calhoun { background: url(../images/keri-calhoun-icon.jpg) no-repeat left 2px; }
#darowski { background: url(../images/adam-darowski-icon.jpg) no-repeat left 2px; }
#larson { background: url(../images/will-larson-icon.jpg) no-repeat left 2px; }
#ohara { background: url(../images/pamela-ohara-icon.jpg) no-repeat left 2px; }
#ransom { background: url(../images/sean-ransom-icon.jpg) no-repeat left 2px; }
#riggen { background: url(../images/michelle-riggen-ransom-icon.jpg) no-repeat left 2px; }
#sweeney { background: url(../images/stephanie-sweeney-icon.jpg) no-repeat left 2px; }

If you look around any site I’ve created, you’ll probably see a lot of these. The footer at Darowski.com uses it:

Darowski.com footer with custom icons on list items

Creative use of definition lists

As much as I love unordered lists, there’s one thing I love more. Definition lists!

I admit, I had never even used one before reading Web Standards Solutions, but now I use them all the time. All contact info within BatchBook is laid out in definition lists. Think about it… what better defines the home email than the email address itself? The definition list is an unordered list with even more semantic meaning.

When we launched the new personas section of BatchBlue.com, I went with a definition list. The names of the personas are the definition terms (dt) and the person used in the persona as well as the information that person is likely to track help define it (dd).

Here’s the complete markup:

<h3 class="persona-header">Who needs a CRM anyway?</h3>
<div id="personas" class="clearfix">
  <dl class="sales">
    <dt>Sales professionals</dt>
    <dd class="photo"><a title="Peter Caputa" href="http://www.linkedin.com/in/pc4media" target="_blank">PC4Media.net</a></dd>
    <dd class="track">Customers, <span>leads,</span> conversions…</dd>
  </dl>
  <dl class="freelancers">
    <dt>Freelancers</dt>
    <dd class="photo"><a title="Alex Taylor" href="http://bigringdesign.com/" target="_blank">Big Ring Design</a></dd>
    <dd class="track">Clients, <span>partnerships,</span> invoices …</dd>
  </dl>
  <dl class="virtalassistants">
    <dt>Virtual assistants</dt>
    <dd class="photo"><a title="Michelle Wolverton" href="http://chelpixie.com/" target="_blank">ChelPixie</a></dd>
    <dd class="track">To-dos, <span>schedules,</span> executives…</dd>
  </dl>
  <dl class="entrepreneurs">
    <dt>Entrepreneurs</dt>
    <dd class="photo"><a title="Jack Templin" href="http://thoughtcap.com/" target="_blank">ThoughtCap</a></dd>
    <dd class="track">Opportunities, <span>partners,</span> VCs…</dd>
  </dl>
  <dl class="editors">
    <dt>Editors</dt>
    <dd class="photo"><a title="Anisa Raoof" href="http://kidoinfo.com" target="_blank">KidoInfo</a></dd>
    <dd class="track">Writers, <span>deadlines,</span> articles…</dd>
  </dl>
  <dl class="consultants">
    <dt>Consultants</dt>
    <dd class="photo"><a title="Brent Leary" href="http://www.brentleary.com/" target="_blank">CRM Essentials</a></dd>
    <dd class="track">Clients, <span>vendors,</span> proposals…</dd>
  </dl>
  <dl class="eventorganizers">
    <dt>Event organizers</dt>
    <dd class="photo"><a title="Chris Brogan" href="http://chrisbrogan.com/" target="_blank">Chris Brogan</a></dd>
    <dd class="track">Speakers, <span>attendees,</span> sponsors…</dd>
  </dl>
  <dl class="nonprofits">
    <dt>Non-Profits</dt>
    <dd class="photo"><a title="Danielle Brigida" href="http://www.nwf.org/wildlife/" target="_blank">National Wildlife Federation</a></dd>
    <dd class="track">Donors, <span>fundraisers,</span> volunteers…</dd>
  </dl>
  <dl class="realestateagents">
    <dt>Real estate agents</dt>
    <dd class="photo"><a title="Gerry Bourgeois" href="http://realtyman.com/" target="_blank">RealtyMan.com</a></dd>
    <dd class="track">Buyers, <span>sellers,</span> referrals…</dd>
    <dd class="custom"><a href="realestate/">Custom version available</a></dd>
  </dl>
  <dl class="designers">
    <dt>Designers</dt>
    <dd class="photo"><a title="Mikey Hougland" href="http://lamikey.com/" target="_blank">lamikey.com</a></dd>
    <dd class="track">Projects, <span>clients,</span> deliverables…</dd>
    <dd class="custom"><a href="designer/">Custom version available</a></dd>
  </dl>
  <dl class="marketers">
    <dt>Marketers</dt>
    <dd class="photo"><a title="Saul Colt" href="http://saulcolt.com" target="_blank">Saul Colt</a></dd>
    <dd class="track">Press, <span>bloggers,</span> social media…</dd>
  </dl>
  <dl class="superheroes">
    <dt>Super Heroes</dt>
    <dd class="photo"><a title="Small Business Super Heroes" href="super-heroes.html">Super Heroes</a></dd>
    <dd class="track">We know <span>you’re out</span> there!</dd>
  </dl>
</div> <!-- close #personas -->

An abbreviated version of how that looks unstyled:

Unstyled personas box

And here’s how I got that to look:

Styled personas box

I’ll admit… I’m a bit proud of that one. :)

So, the CSS. There’s a lot of it. Let’s start with the basic definition list:

#personas {
  border-top: 1px solid #CACACA;
  border-left: 1px solid #CACACA;
  margin-bottom: 10px;
  width: 688px;
}
#personas dl {
  float: left;
  width: 163px;
  padding: 4px;
  border-bottom: 1px solid #CACACA;
  border-right: 1px solid #CACACA;
  margin: 0;
}
#personas dt {
  font-family: Georgia,serif;
  font-size: 120%;
  color: #004A8D;
  padding-bottom: 2px;
}
#personas dd {
  margin: 0;
}

So, the whole thing is in a div (with an id of “personas”) that has a top and left 1-pixel border. The reason I did this is the individual definition lists inside the div will have bottom and right borders. That means we don’t have any double borders on any side.

Above, I’ve also set the width for each dl and floated it. The dt simply has some text formatting while the dd has its margin eliminated.

Here’s some more styling done to the definitions:

#personas dd.track {
  color: #666;
  font-family: Georgia, serif;
  font-style: italic;
  font-size: 110%;
  line-height: 120%;
}
#personas dd.track span {
  display: block;
}
#personas dd.custom {
  font-size: 80%;
  margin-top: 3px;
  padding-left: 22px;
  background-position: left 50%;
}
#personas dd.custom a:link,
#personas dd.custom a:visited,
#personas dd.custom a:hover,
#personas dd.custom a:active {
  color: #F88C17;
  text-decoration: none;
}
#personas dd.custom a:hover {
  color: #F88C17;
  text-decoration: underline;
}
#personas dl.realestateagents dd.custom {
  background: url(../images/icon-realestate.png) no-repeat;
}
#personas dl.designers dd.custom {
  background: url(../images/icon-designer.png) no-repeat;
}
#personas dl.designers,
#personas dl.realestateagents,
#personas dl.marketers,
#personas dl.superheroes {
  min-height: 95px;
}

First, we are styling the text of the “track” definition (that’s the one we use to explain what each persona will track). I threw a very non-semantic span on the middle item in each of those. The reason I did that is so I could make it display as block (force it to a new line) to make sure they all line up the same.

A couple items in the bottom row have another definition with the class “custom”. This is to denote we have a custom version of BatchBook for this persona. First, I adjust the font size, margin, and padding. Then, I set the background position of the icons (in the same manner as explained above). Next, I set the link styles. Then, I set the background images for the Real Estate and Designers version. If you’re a designer, you just may find BatchBook for Designers handy. ;)

Finally, I set the minimum height of the bottom row to make room for that “custom” style, even if it isn’t there. That way the boxes all line up on the bottom. (I realize this isn’t completely bulletproof, but these boxes will all have the exact same text in them so box height is easier to plan for.)

Finally, we have the thumbnail photos, which I’m actually going to cover in the next technique.

CSS sprites

I’ve recently been turned on to the wonder that is CSS Sprites. What are they? Well, for examples like the personas box above, I’d need to call 12 graphics. Instead of creating twelve graphics, I’ve created one that looks like this:

Personas CSS sprites file

Now, I just call the same graphic and only expose part of it, depending which persona it is. Sure, the file is bigger, but calling one image and caching it is a LOT easier on the server than fetching twelve smaller images.

And this is only a small example. Every button in BatchBook is now in one single graphic file (it’s pretty darn big). I just call that one graphic and adjust the background position.

So, here’s how the CSS works for the personas table. First, here’s how we style each photo:

#personas dd.photo a {
  float: left;
  display: block;
  height: 45px;
  width: 45px;
  text-indent: -9999px;
  background: url(../images/personas.jpg) no-repeat;
  margin-top: 3px;
  margin-right: 8px;
}

So, we float it, set a height & width, indent the text (like covered earlier), set a background image, and give it some margins. Right now, we’re calling the same image and position for each. Here’s how we adjust the positioning for each photo:

#personas dl.sales dd.photo a { background-position: 0 0; }
#personas dl.freelancers dd.photo a { background-position: -45px 0; }
#personas dl.virtalassistants dd.photo a { background-position: -90px 0; }
#personas dl.entrepreneurs dd.photo a { background-position: -135px 0; }
#personas dl.editors dd.photo a { background-position: -180px 0; }
#personas dl.consultants dd.photo a { background-position: -225px 0; }
#personas dl.eventorganizers dd.photo a { background-position: -270px 0; }
#personas dl.nonprofits dd.photo a { background-position: -315px 0; }
#personas dl.designers dd.photo a { background-position: -360px 0; }
#personas dl.realestateagents dd.photo a { background-position: -405px 0; }
#personas dl.marketers dd.photo a { background-position: -450px 0; }
#personas dl.superheroes dd.photo a { background-position: -495px 0; }

So, with each dl, we change the horizontal position by 45 pixels (which makes sense, since the photos are 45 pixels wide!). If I need to add more personas, I just add them to the image, save it again, and add a few lines to the CSS. Done!

Auto-clearing with .clearfix

I use this one all the time, too—including in the personas example above. Say you’ve got a div and you put two elements in it. You float one left and float the other right. You need to clear that div so everything lines up correctly under it and nothing crashes together.

You could make sure the element after it uses a clear: both; style. But, you’d have to remember to put that in every single time. And what if you insert something in between them? It breaks. It is much cleaner to get the divs to clear themselves.

You can do with using the clearfix class. Here’s how it looks:

.clearfix:after {
  content: ".";
  display: block;
  height: 0;
  clear: both;
  visibility: hidden;
}

Now, “after” the element with the class “clearfix”, it will render a “.”, display it as block (forcing a new line), set the height to “0″ (I believe this is to make Firefox behave), hide it, and—of course—clear both!

In your IE6 and IE7 stylesheets, you’ll just have to add this:

.clearfix { height: 1%; }

That will trigger “hasLayout”. I’m not even going to try to explain hasLayout. I’ll leave it to this guy.

Font sizing: keywords & percentages

One of the first things you need to decide when you design a site with CSS is how you’re going to size your text. You’ve got (essentially) three options:

Pixels are fine for sizing text. It’s probably what you’re used to. But, there’s a problem.

It is quite common for users to increase the size of text on web pages. Let’s face it, a lot of sites are built by 20somethings with perfect eyesight. I wouldn’t be able to read many sites I created in college. I’m among those that will bump up the font size on sites occasionally.

But, as always, the problem is Internet Explorer. IE won’t adjust the size of text rendered in pixels. Kind of kills the #1 accessibility feature right there.

Ems are a perfectly usable workaround. But as you can see, they can be a bit difficult to grasp. I’ll be honest… I never bothered. I always thought it would be neat to create a completely em-based design so that the entire site zoomed in and out along with the text size. But now that modern browsers are replacing the text sizing option with a full-page zoom, that type of functionality is being handled by the browser. So, it’s a lot of extra work that, quite honestly, I’m glad I didn’t do.

So, what do I use? I use those silly keywords that come with HTML. You know… small, medium, large, etc. That’s me. Well, I actually only really use small. Because I use that as the baseline for my entire site. I then use percentages to adjust sizes accordingly.

First, what does it look like? I start off by establishing small as my baseline.

body { font-size: small; }

Then, in BatchBook I drop that down to 95% right off the bat to shrink it ever so slightly.

div#content { font-size: 95%; }

I actually leave this step out on BatchBlue.com. I guess that’s because when you’re actually inside the app, every pixel is roughly 5% more valuable. :)

This becomes the default font size. I then use percentages to scale headings up and supplemental text down. So, for example, if I’m adding a little note to a section that I want to appear a bit smaller, I’d mark it up as:

p.note { font-size: 90%; }

Then, to style my headings I scale it up:

h1 { font-size: 180%; }
h2 { font-size: 140%; }
h3 {font-size: 110%; }

With the increase in prominence of full-page zoom, I’ve considered going back to pixels. I still wouldn’t do a completely em-based design, though. I just don’t have the urge to show off my math skills that much.

Override iPhone text adjustment

I’ll finish with a very simple one. I like to make sure things look good on my iPhone, so I was delighted to find this on the Apple site:

Safari on iPhone adjusts the text size so it’s more readable after the user double taps. After a double-tap, Safari on iPhone gets the width of the block of text and determines the width after a double-tap. Safari on iPhone then determines an appropriate multiplier that is applied to the layout.

Sometimes you might find that automatic text size adjustment doesn’t produce ideal results. For example, text in absolute-positioned elements might overflow the viewport after adjustment. Other pages might need a few minor adjustments to make them look their best. In these cases, you can override the text size adjustment.

Use the -webkit-text-size-adjust CSS property to override Safari’s default text size adjustment. The values for -webkit-text-size-adjust are:

  • none: <body style=”-webkit-text-size-adjust:none”>
  • auto: <table style=”-webkit-text-size-adjust:auto”>
  • multiplier percentages: <div style=”-webkit-text-size-adjust:200%”>

Only thing is… I find the results from -webkit-text-size-adjust can make things look weird. You just never know. Sometimes a paragraph will appear half the size of an unordered list for no apparent reason. So, on BatchBlue.com and BatchBook, I’ve added this:

html {-webkit-text-size-adjust: none}

And we’re in business! Everything looks like it does on regular ol’ Safari.

Dassit!

So, there you have it. That’s certainly not everything I use. And I’ve certainly used more complex solutions than those. But, the more complex something is, the less likely I am to use it again.

So, tell me dear reader, what are your favorite CSS techniques?

Or, what are you having a hard time wrapping your brain around (in regards to CSS)?

15 Comments

  1. On November 14th, 2008 at 1:17 pm Adam’s Favorite CSS Techniques | BatchBlue: Blog said:

    [...] Designing with CSS can be a bit tricky, and I’ve helped quite a few people get started over the past couple of years. I finally decided to collect all of my favorite CSS techniques and record them in one place—which I’ve done in a post on my personal blog appropriately called “My Favorite CSS Techniques“. [...]

  2. On November 14th, 2008 at 1:39 pm Brian Christiansen said:

    Adam, thanks for this post. Some excellent examples.

  3. On November 14th, 2008 at 1:45 pm Marc Amos said:

    This should be required reading for everyone. Yes, I said EVERYONE.

  4. On November 14th, 2008 at 1:48 pm Scott McCracken said:

    Excellent breakdown and well-written, lots of good tips in here — long live the definition list!

  5. On November 14th, 2008 at 2:33 pm Lorin Rivers said:

    One of the places I like to use DLs is for addresses and the like. I use DT for the label (e.g., “Phone:”) and DD for the value. Semantic markup FTW!

    Thanks for the pointers here!

  6. On November 14th, 2008 at 2:40 pm Adam Darowski said:

    Thanks Lorin!

    Yes, in BatchBook definition lists are for contact info. They work nicely with Microformats. So, the dl gets the “email” class, the dt (populated with Home/Work/etc.) gets the “type” class and the dd (populated with the email address) gets the “value” class. Like a charm!

    Brian, Mark, Scott… you guys rock. Thanks!

  7. On November 18th, 2008 at 7:01 pm WebWorkerDaily » Archive Clearing The Cache « said:

    [...] BatchBlue designer Adam Darowski shares his Favorite CSS Techniques [...]

  8. On November 22nd, 2008 at 2:22 am Dean Landolt said:

    I hate to be pedantic (sometimes I just can’t help it), but what you’re using are not microformats as much as microformat design patterns (which are plenty good in and of themselves). But you should probably wrap your contact microformats in a vcard class per the hcard microformat.

  9. On November 22nd, 2008 at 8:48 am Adam Darowski said:

    Hi Dean… Oh yes, I certainly use full Microformats. That example didn’t focus on creating the entire Microformat, though… just showing how to use the less-used “type” class with some CSS. Perhaps a full Microformats post can come in the future, though. Thanks!

  10. On November 25th, 2008 at 10:49 am Barry Menard said:

    Great post, very informative!

  11. On January 26th, 2009 at 11:41 am Danny Chapman » Blog Archive » Top Five Web Design Tools I Can’t Live Without said:

    [...] by Adam Darowski’s Top 10 CSS tricks, here are my top five design tools I can’t live without. Hope you find it [...]

  12. On February 16th, 2009 at 1:19 pm Chris Bloom said:

    Some very cool examples. Nice work.

  13. On May 16th, 2009 at 5:33 pm Adam Luikart said:

    Hey Adam!

    I found this post while researching that Mobile Safari text size adjust issue, but my comment is related to clearing float containers.

    I used to use that exact same clearfix class all the time, but it turns out there’s a way easier, no-hacks-needed way to do it: by setting the overflow property on the container to auto (or hidden, for the edge cases where auto gives you unwanted scrollbars).

    For more info, check out the great writeup on PPK’s site:
    http://www.quirksmode.org/css/clearing.html

    Changed my life.

  14. On May 22nd, 2009 at 4:00 pm Adam Darowski said:

    Hey Adam. Thanks for the tip. I’ve gradually started putting this in. Because of the width involved and the varying overflow attributes, tough to do it all at once. But it’s certainly a start. Thanks!

  15. On April 17th, 2010 at 9:13 pm David said:

    Great post!

    Just thought I’d point out that, as an SEO consultant, it seems that and other tags are not nearly as important an SEO ranking factor as was thought. The tag, of course, is absolutely vital.