See the Pen bcjyH by Jesse Shawl (@jshawl) on CodePen

I’m a huge fan of the button styles on CSS Tricks, and wanted to create my own.

When I inspected the element, I saw there was a ridiculous amount of box-shadows with alternating hex values:

My first attempt of recreating these buttons involved me writing out the box shadows by hand. After about 10 minutes of guesswork and less than desirable results, I knew there had to be a better way.

Looking for a pattern

Since the box shadow values are growing incrementally in a systematic way, we can loop through some arbitrary number with Sass.

.button{
 @for $i from 0 through 10{
    //make some box shadows maybe like:
    box-shadow: $i+px $i+px 0px #bada55;
 }
}

I first thought we could just embed a forloop in a declaration, but doing this will just keep adding box-shadow properties to the rule, overwriting each previous one as we loop through. Additionally, we’ve got no way to handle the alternating colors.

I decided the best way to handle this was to use Sass to build a string of values, such that the desired CSS output was something like:

  box-shadow: 1px 0px color1, /* first arbitrary number */
              0px 1px color2, /* first arbitrary number */
              2px 1px color1, /* second arbitrary number */
              1px 2px color2; /* second arbitrary number */  

Ah, yes. A pattern!

What we’re trying to achieve is for each arbitrary number, we want one box shadow 1 pixel visible to the right in one color, and one box shadow 1 pixel visible on the bottom in another color.

That is, for each $i in the loop, we need to do two things:

  1. Handle the box shadow visible on the right
  2. Handle the box shadow visible on the bottom

Once we have each of these things, we can build up a string of values and add them to our box-shadow property. In order to build a string, we’ll need a Sass function.

  @function boxShadowString($offset, $color) {
  // this function takes two arguments: an offset (how 3d the button will be)
  // and a color: we'll use Sass to darken the color to get alternating
  // box shadow colors.

  // create an empty variable: this will be the string to which we
  // append our box-shadow values
  $box-shadow:();

  //loop through however many numbers ($offset)
  @for $i from 0 through $offset{

    // create the value for the box-shadow visible to the right
    $first: $i+1+px $i+px 0px darken($color, 15%);

    // create the value for the box-shadow visible to the bottom
    $second: $i+px $i+1+px 0px darken($color, 18%);

    // add the box shadow visible to the right string to our $box-shadow variable
    $box-shadow: append($box-shadow, $first, comma);

    // add the box shadow visible to the bottom string to our $box-shadow variable
    $box-shadow: append($box-shadow, $second, comma);
  }
  // return the string of all of these values
  @return $box-shadow;
}

Note: the append method in sass takes two arguments, and an optional third.

They are: append(THING_APPENDED_TO, THING_APPENDING, SEPARATOR). We’re using ‘comma’ as the third argument to get a comma separated list of each of these box-shadow values. You can read more about the append() method here http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#append-instance_method.

3d-ifiyng the button

Now, we can add this function to our button:

.button{
  box-shadow:boxShadowString(10, #bada55);
}

It looks super 3D, but there’s no interactivity. Let’s add a hover state that moves the button up and to the left 2 pixels, and makes the box shadow 2 pixels longer.

.button{
  box-shadow:boxShadowString(10, #bada55);
  &:hover{
    transform:translate(-2px, -2px); /* move the button up and to the left */
    box-shadow:boxShadowString(12, #bada55); /* make the box shadow 2px longer */
  }
}

And finally, we can add an :active state to change the button style on click.

.button{
  box-shadow:boxShadowString(10, #bada55);
  &:hover{
    transform:translate(-2px, -2px); /* move the button up and to the left */
    box-shadow:boxShadowString(12, #bada55); /* make the box shadow 2px longer */
  }
  &:active{
    transform:translate(9px, 9px); /* move the button all the way down, except for a 1 pixel offset */
    box-shadow:boxShadowString(1, #bada55);
  }
}

At the moment this works, but there’s a huge amount of repeated and tightly coupled code here. The box-shadow in the :hover state should just be 2px greater than whatever the $offset is and the :active state’s transform property should be the $offset minus 1 pixel.

I know! We can use a mixin!

  @mixin threeDify($offset, $color){
    /*do some base stuff*/ 
    box-shadow:boxShadowString($offset, $color);
    &:hover{
      box-shadow:boxShadowString($offset+2, $color);
      transform:translate(-2px,-2px);
    }
    &:active{
      box-shadow:boxShadowString(1, $color);
      transform:translate($offset - 1+px, $offset - 1+px);
    }
  }

And finally, we can make our button super 3D by @include‘ing the mixin:

  .button{
    background:#bada55;
    @include threeDify(10, #bada55);
  }



See the Pen bcjyH by Jesse Shawl (@jshawl) on CodePen

Edit this Post