Hashrocket.com / blog

Large buttons

Sass Button Mixins for Fun & Profit

posted on and written by Cameron Daigle in

Image 100x100 cameron daigle

Here's a SASS pattern for constructing mixins that I've been enjoying lately – and has saved me from a number of CSS-related headaches.

We'll be looking at strategies for styling buttons (and by buttons, I mean links, submits, actual button tags – anything that'll look like a styled action in your design). Buttons are fantastic place to use Sass mixins – there are a number of ways that button mixins can not only clean up our code, but also avoid unhealthy patterns and unexpected consequences.

From Classes to Mixins

Suppose I needed to make a general button style. Without mixins, I'd probably make a class definition of button, as coders have been doing since the dawn of time ...

a.button
  // green background
  background: #00aa00
  // with a slightly lighter green on hover:
  &:hover
    background: #33cc33

... and suppose I need to add a delete button that uses the same button style, but is red.

a.button
  // base button styles
  &.delete
    // red background
    background: #ff0000
    &:hover
      // slightly lighter red
      background: #ff3333

So each additional class modification needs a class with adjustments for the regular state as well as others (hover, active, etc). However, Sass lets us implement the same styles without all of the repetition and classes. We can define a button mixin, and then use that mixin for a second button – now we won't need to add two classes to our 'delete' button.

= button
  // button styles

a.button
  +button

a.delete
  +button
  background: #ff0000
  &:hover
    background: #ff3333

Better, but we're still having to rewrite the hover state color when we modify our button. We can streamline this one step further by moving our background color into the mixin, and creating the :hover state programmatically using Sass' powerful color math functions:

= button($bg: blue)
  background: $bg
  color: #fff
  &:hover
    background: lighten($bg, 10%)

a
  &.button
    +button
  &.delete
    +button(#ff0000)

Burned By Inheritance

Now, we've constructed a nice mixin pattern for any future buttons, there's one other potential problem. In the great class inheritance rulebook, a single class is the weakest selector – that is, it's overridden by literally anything else that might accidentally point to your button. For example, you might not intend for this code to override the font size on your button, but it will:

.bully_container
  %a(href='#' class='button') I'm not supposed to be 30px
a.button
  font-size: 12px

.bully_container
  a
    font-size: 30px

There's a fundamental issue here that you might have noticed: our buttons are global classes with the shallowest possible selector, yet the button itself will always be deep within your DOM structure – the deepest, in fact (given that there will probably not be any elements within your button). A button is essentially a CSS inheritance endpoint.

Well, this is great news for mixins. One of the dangers of mixins is that they can inject styles anywhere and disregard the actual cascade, so sloppy usage can result in unexpected style inheritance. However, since the button is the end of the chain, we don't have to worry about its nonexistent children.

Try not using global classes (no, really)

What I've started doing lately takes this system to its logical conclusion: avoiding global button classes and instead scoping mixins to specific buttons (or containers that have buttons).

For example, our forms typically have a set of actions at the end (to submit, cancel, etc). We'll put those actions in a fieldset.actions and scope mixins to that area. So here's how that would look for a submit button, a grey 'cancel' button, and a red 'delete' button:

fieldset.actions
  input[type='submit']
    +button
  a
    &.cancel
      +button(#888)
    &.delete
      +button(#ff0000)

I typically even abstract out the buttons into a set of mixins, which allows us to DRY our button colors even further. The mixins end up looking something like this:

= button-base($color)
  // button styles

= primary-action
  +button-base(#000)
= secondary-action
  +button-base(#888)
= destroy-action
  +button-base(#ff0000)

And, for our styles:

fieldset.actions
  input[type='submit']
    +primary-action
  a
    &.cancel
      +secondary-action
    &.delete
      +destroy-action

So there you have it. We've written refreshingly minimal Sass, we're inheriting any state changes (hover, active, etc.) automatically, and we're avoiding any unexpected inheritance from the rest of the DOM chain. As a plus (I consider it a plus, anyway), our class names are now more semantic – they indicate the action taken – and we don't need to throw a global 'button' class on everything.

This class-light and mixin-heavy philosophy might be a bit out of the norm, but it's been serving me well as of late. Moving specific button styles from a class inheritance structure to a mixin inheritance structure results in fewer classes and less markup overall, and also results in much more flexibility in terms of semantic class naming. Give it a shot – it might just work for you.

photo credit: Breibeest on Flickr

Posted in Design and tagged with SASS