PatientsLikeMe Tech Blog

We’re a team of Ruby on Rails developers and UX practitioners at PatientsLikeMe, where patients share data about their treatments, symptoms, and disease outcomes. We’re classically trained ninjas, pirates, rockstars, and dinosaur hunters. Our keyboards are magic wands — with lots of buttons, so they’re even better than regular magic wands. Meet the Team ↓

Manage Your CSS3 Tricks with Sass Mixins

Shortly after joining PatientsLikeMe last fall, I started working on our family of applications for partners. This was a unique opportunity to start on something essentially from scratch. Despite the fact that we know some major clients are tied to Internet Explorer 6, I took this as an opportunity to use a whole bunch of new CSS3 tricks. I know, IE6’s CSS3 support is beyond spotty. It’s nonexistent. But ask yourself: Do websites need to look exactly the same in every browser?

No, they don’t. They need to work in every browser. They need to be usable and maintain your company’s branding. But the fact that IE6 doesn’t like rounded corners doesn’t mean you need to spend an extra day creating background images with rounded corners. It just means that IE6 users should get straight edges. Let’s face it… IE6 users are used to things looking like crap. A sharp edge on a dropdown menu isn’t going to offend them.

In addition to embracing CSS3, I also continued my lovefest with Sass. A while back on my personal blog, I wrote up my first impressions of Haml & Sass (Haml is a markup language for generating HTML while Sass is a meta-language for generating CSS). Long story short, I’m still in love. In fact, I’ve fallen deeper because CSS3 and Sass are perfect for each other.

Why are they so perfect? Sass Mixins allow you to create a group of CSS rules you can re-use. In addition to reusing the styles, you can also pass variables and arguments into the mixin, making your CSS much more dynamic. Because many CSS3 tricks involve multiple lines that target specific browsers, this can be a big time saver.

Example time!

If you’re new to CSS3, I encourage you to check out css3please.com, a site that allows you to visually try out different CSS3 properties. The first CSS3 property that anyone tries is border-radius. Since border-radius is not officially supported yet, you can only render it through vendor-specific properties. For Firefox, this means adding -moz- to the beginning of the property while for Safari and Chrome that means adding -webkit-. All other browsers, so far, are all out of luck.

This would produce a 12-pixel rounded corner on all corners of a box:

.rounded {
  border-radius: 12px;
  -moz-border-radius: 12px;
  -webkit-border-radius: 12px;
}

Again, -moz- is for Firefox while -webkit- is for Safari, Chrome, and all other Webkit-based browsers. We also include the border-radius property with the assumption (hope?) that all browsers eventually support rounded corners without vendor-specific properties.

There are two ways you can apply those properties over and over:

  1. Keep adding them to every element you want rounded
  2. Add them to a "rounded" class and add that class to all elements you want rounded

Option #1 is obviously tedious and forces you to duplicate the same properties over and over—not ideal. Option #2 is promising, but forces you to include non-semantic class names all over the place (similar to our beloved “clearfix”). Also, what happens if you want the rounding to be 12 pixels in one place, 6 in another, and perhaps a weird mix in a third spot? You need to keep building classes for each of these situations (and those are bound to get non-semantic class names like "rounded12". Again, not ideal.

Using Sass, you can build the mixin once and pass arguments into it, therefore allowing you to modify the rounding values on a case-by-case basis. That, and there’s no pesky class names that muddy up your markup. You simply apply the mixin to the property you want rounded in your Sass file. Then when the Sass file compiles, it creates the correct properties for that element. It’s like magic.

Here’s the mixin:

=border_radius(!radius)
  border-radius = "#{!radius}"
  -moz-border-radius = "#{!radius}"
  -webkit-border-radius = "#{!radius}"

!radius is the argument we use to pass in the value. So, to apply 3-pixel rounded corners to an h1, you would write this:

h1
  +border_radius(3px)

Want 12-pixel rounded corners?

h1
  +border_radius(12px)

Want to round the left side, but not the right side?

=border_radius(!radius)
h1
  +border_radius(12px 0 0 12px)

Let’s take a look at a screenshot from one of the PatientsLikeMe applications and then see how it was put together.

Here’s the basic markup from that image (simplified to just the basic structure):


<div id="categories">
  <h3 class="toggle">
    Modify Categories
  </h3>
  <div id="categories-inner">
    …
  </div>
</div>

Of course, with Haml, that’s just:

#categories
  h3.toggle Modify Categories
  #categories-inner
    …

Much cleaner. So, let’s style the h3 and the inner div. I’m only showing the CSS3 mix-ins in the styling below (there are other styles, but they’re pretty standard). The h3 will get border-radius (but just the top two corners) while the div gets four mixins:

  1. +border_radius on all corners except top-left
  2. +box_shadow: A subtle drop shadow on the box
  3. +box_gradient: A gradient background on the box
  4. +box_gradient_ie67: A hacky solution for when you also want the gradient in IE6 and IE7

Here’s the Sass:

#categories h3
  +border_radius(3px 3px 0 0)
#categories-inner
  +box_shadow(#CCC, 1px 1px 4px)
  +border_radius(0 3px 3px 3px)
  +box_gradient(#FFFFFF,#FCF5DE)
  +box_gradient_ie67(#FFFFFF,#FCF5DE)

You’re already familiar with +border_radius. Here’s the +box_shadow mixin:

=box_shadow(!color, !values)
  box-shadow = “#{!color} #{!values}”
  -webkit-box-shadow = “#{!color} #{!values}”
  -moz-box-shadow = “#{!color} #{!values}”

There are two arguments: one for color of the shadow and one for the values (horizontal offset, vertical offset, and blur radius). Of course, this will only render in Firefox, Safari, and Chrome. But it’s a drop shadow. Those who use nice browsers deserve nice things. Those who don’t have no idea what they’re missing.

+box_gradient is the big daddy of all of these. I borrowed the cross-browser gradient implementation from css3please.com. Here’s how my mixin looks:

=box_gradient(!top,!bottom)
  background-image = “-moz-linear-gradient(top, #{!top}, #{!bottom})”
  background-image = “-webkit-gradient(linear,left top,left bottom,color-stop(0, #{!top}),color-stop(1, #{!bottom}))”
  -ms-filter = “progid:DXImageTransform.Microsoft.gradient(startColorStr=’#{!top}’, EndColorStr=’#{!bottom}’)”

Again, two arguments: one for the top color and one for the bottom color. I’m using all vertical gradients, but you can certainly do horizontal, radial, whatever. You would just need to introduce more arguments into the mixin. I’m just not an overly-argumentative guy. As you’d expect, -moz-linear-gradient works in Firefox while Chrome and Safari get their gradients from -webkit-linear-gradient. The frightening line that starts with -ms-filter targets IE8 (and I assume early betas of IE9).

But there’s actually a vendor-specific property that will work in IE6 and IE7, too. Yeah, I was shocked. I’ve placed that in a separate mixin so I only apply it at certain times (when it is a major visual element of the design). It looks a little something like this:

=box_gradient_ie67(!top,!bottom)
  filter = “progid:DXImageTransform.Microsoft.gradient(startColorStr=’#{!top}’, EndColorStr=’#{!bottom}’)”

I quarantined that one because on occasion it caused conflicts based on the fragility of other IE6-and-IE7-targeted hacks.

Put it all together and what’ve you got?

#categories-inner {
  box-shadow: #cccccc 1px 1px 4px;
  -webkit-box-shadow: #cccccc 1px 1px 4px;
  -moz-box-shadow: #cccccc 1px 1px 4px;
  border-radius: 0 3px 3px 3px;
  -moz-border-radius: 0 3px 3px 3px;
  -webkit-border-radius: 0 3px 3px 3px;
  background-image: -moz-linear-gradient(top, white, #fcf5de);
  background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0, white),color-stop(1, #fcf5de));
  -ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorStr=’white’, EndColorStr=’#fcf5de’);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorStr=’white’, EndColorStr=’#fcf5de’);
  background-color: #FFF;
  position: relative;
  z-index: 1;
  margin-top: -1px;
  border: 1px solid #E4C37C;
  padding: 10px;
}

Huzzah! I loved Sass before. But now that it makes it so much easier to sprinkle in some CSS3 goodies here and there, I’m even more in love.

2 Comments

  1. On May 17th, 2010 at 2:39 pm Tweets that mention Manage Your CSS3 Tricks with Sass Mixins | PatientsLikeMe Tech Blog -- Topsy.com said:

    [...] This post was mentioned on Twitter by Adam Darowski and David Piehler, jeff. jeff said: RT @adarowski: Last week we relaunched the PatientsLikeMe tech blog. I wrote a post on CSS3 & Sass Mixins > http://bit.ly/9OqKU3 [...]

  2. On May 29th, 2010 at 6:57 pm Tedda said:

    Again, two arguments: one for the top color and one for the bottom color. I’m using all vertical gradients, but you can certainly do horizontal, radial, whatever. You would just need to introduce more arguments into the mixin. I’m just not an overly-argumentative guy. As you’d expect, -moz-linear-gradient works in Firefox while Chrome and Safari get their gradients from -webkit-linear-gradient. The frightening line that starts with -ms-filter targets IE8 (and I assume early betas of IE9).
    +1