Buttons: A Look at Module Mixins, Compass Color-Contrast, and Sass Boolean Functions

November 6, 2013

Recently, I’ve been coding websites via a modular approach — meaning, focusing on styling distinct (repeated) elements (like sidebars, buttons, entry fields, etc.), and reusing them throughout the page. Modules ensure consistency throughout your project, and increase efficiency since you’re reusing as much code as possible.

Right now I want to focus on buttons.

Screen Shot 2013-10-31 at 9.25.27 AM.png

(click the buttons to open their Codepen)

Mixing Modules

When I began creating the buttons for the website that inspired this post, I originally styled them individually, but quickly realized, I can do better than that! Why not create a module mixin to make the code more universal and efficient? With this mixin, the only thing I’d need to do to create new buttons is specify their color and size (big or small): much more human and semantic. .button1 { @include button($toad-blue, big); }

I’m using @if statements and color functions to do the rest of the work — change element padding/sizing, darken the border color by a variable percentage, and determine the font color based on the darkness of the background.

There’s a lot going on here, so let’s break it down.

Setting up Compass Color-Contrast

Compass’ Color Functions are one of the reasons why its SO FREAKING AWESOME. Seriously, color contrast is AMAZING. All you need to do is

@import "compass/utilities/color/contrast"

and then, a simple statement:

@include contrasted($color)

gives you a background-color of $color while simultaneously determining what text-color to assign.

The default is a 30% threshold, and the font-color will be black on light backgrounds (white on dark backgrounds), but you can configure these settings with this setup:

@import "compass/utilities/color/contrast";

$contrasted-dark-default: #333333; //text color on light bgs
$contrasted-light-default: #e7e7e7; //text color on dark bgs
$contrasted-lightness-threshold: 50%; //contrast threshold

And then you just include the @include contrasted() statement, and bam! It’s like magic.

The Mixin

@mixin button($color, $size) {
  $push-height: 2px;
  $border-color: darken($color, 40%);
  $font-size: 1em;
  // a larger button has a bigger area of movement and font-size
  @if $size == "big" {
    $push-height: 6px;
    $font-size: 1.5em;

  //the following determine how dark to make the bottom-border of the button depending on the lightness of the actual background color of the button
  @if lightness($color)  80%{
    $border-color: darken($color, 10%);

  @include contrasted($color); //this is magical
    border: none; //as a reset
  border-bottom: $push-height*2 $border-color solid;
    font-size: $font-size;
    margin-top: 0; //must start at 0 bc movement illusion achieved via margin-top
    padding: 0.4em 2em;
  position: absolute;
    &:hover {
        margin-top: $push-height;
    border-bottom: $push-height $border-color solid;
    &:active, &:focus {
    outline-color: transparent; //reset styling
    outline-style: none; //reset styling
        border-bottom: none; //reset styling
        margin-top: $push-height*2;

Checking Lightness

The mixin reads the lightness of $color and darkens the bottom part of the button accordingly (because 10% darkening is really dramatic on light colors, but barely shows up on darker colors). Unlike other languages, there are no characters encapsulating the statements (which can be a little confusing), just write them straight out on the same line.

In this example, I first set a default (this is important!) $border-color: darken($color, 40%) and then checked the actual lightness of $color to see if any of the following rules apply:

@if lightness($color)  80%{
    $border-color: darken($color, 10%);

Sass Boolean Functions

@if statements like the one above (and other control directives) are really fun to use in mixins. You can even extend the use of these with “and,” “or,” and “not” statements. The full list of Sass functions is here and I’d highly recommend checking it out.


In the end, this little line of code: .button1 { @include button($toad-blue, big); }, compiles to this:

.button1 {
  background-color: #93d0ff;
  color: #333333;
  border: none;
  border-bottom: 12px #0070c6 solid;
  font-size: 1.5em;
  margin-top: 0;
  padding: 0.4em 2em;
  position: absolute;
.button1:hover {
  margin-top: 6px;
  border-bottom: 6px #0070c6 solid;
.button1:active, .button1:focus {
  outline-color: transparent;
  outline-style: none;
  border-bottom: none;
  margin-top: 12px;
  margin-bottom: -2px;

Grab a cleaned-up version of the mixin here: https://gist.github.com/una/7249770#file-button-mixin-clean