If you have yet to give Sass a try, don’t worry, this is a good place to start. And if you haven’t already read Andrew’s primer on Sass, give it a read for a better understanding of what we’ll be covering and how to get setup with Sass.
In this article, we’ll start with one of the simplest design elements on a page –– a button. In an earlier post, the talented Mat Helme designed a vector UI button pack, which I’ve recreated in plain CSS. We’ll need to make it themable by creating different color schemes using Sass variables, mixins and functions. Let’s get started!
Contents
What We’ll Cover
We’ll be refactoring the CSS using the SCSS (Sassy CSS) syntax, which is closer to the pure CSS syntax we’re all used to. To start things off, our file structure should look something like this:
project/ index.html css/style.css scss/style.scss
The “style.scss” file will compile into “style.css”, so that’s the only file we’ll need to link to in our HTML. In this demo, we’ll be learning about the following Sass features:
- nesting
- variables
- mixins
- color functions
- partials
Starting with the CSS
Currently, our CSS is straightforward. Check it out in Codepen.
button { display: inline-block; padding: .938em 1.875em; border: .094em solid #743EA5; border-radius: .625em; background-color: #9A68C7; box-shadow: 0 .375em .313em -0.313em rgba(0,0,0,.8), inset 0 .063em rgba(255,255,255,.4), inset 0 -0.188em rgba(0,0,0,.15); color: #FFF; text-decoration: none; text-transform: uppercase; text-shadow: 0 .063em #000; cursor:pointer; font: bold 1.8rem sans-serif; } button:hover { background-color: #8A58B7; box-shadow: 0 .125em rgba(255,255,255,.4), inset 0 .063em rgba(255,255, 255,.4), inset 0 -0.188em #804FAD; } button:active { box-shadow: inset 0 0 1.094em #472566, inset 0 .063em #390668, inset 0 -0.188em #682CA0, 0 .063em rgba(255,255,255,.4); }
All padding and border pixel units have been converted to em
units so they scale relative to the button’s font size, which makes our button modular.
The CSS rules are not modular, however, because they apply to button elements only. Instead, we’ll need to change the button selector to a class.
.btn { ... } .btn:hover { ... } .btn:active { ... }
In the HTML, we’ll add the .btn
class to the element. So far we have one button element to get started with –– this can just as well be an anchor element.
<button class="btn"> Button </button>
Getting Started with Sass
First, we’ll create a variable for each color theme:
$purple: #9A68C7; $blue: #3BA9E4; $green: #75B343; $orange: #FF6A42;
Next, we’ll need to abstract several color properties from the .btn
rule into a Sass mixin since we’ll be reusing most of them for each button theme. We’re only concerned with the theme colors at this point, so let’s create a new mixin that will contain any property that describes a color we might change in a theme.
From our .btn
rule, we’ll abstract the background-color
and border-color
declarations into a mixin called btn-theme
:
@mixin btn-theme { background-color: #9A68C7; border-color: #743EA5; }
We’ll keep the other styles in the .btn
rule, which sets the global theme and structure of our buttons.
Nesting Selectors
Since the :hover
and :active
states are necessary for each button, we can also nest them in our btn-theme
mixin. We’ll need to replace the .btn
class selector with an &
, which will reference the parent selector in their respective rules, while the context still carries through.
@mixin btn-theme { background-color: #9A68C7; border-color: #743EA5; &:hover { background-color: #8A58B7; box-shadow: 0 .125em rgba(255,255,255,.4), inset 0 .063em rgba(255,255,255,.4), inset 0 -0.188em #804FAD; } &:active { box-shadow: inset 0 0 1.094em #472566, inset 0 .063em #390668, inset 0 -0.188em #682CA0, 0 .063em rgba(255,255,255,.4); }
We can also nest properties with matching namespaces. It’s not as common — or as beneficial — as nesting selectors, especially when you can use shorthand properties most of the time. Since there isn’t a “text” shorthand property in CSS, we’ll nest the text
properties in our .btn
rule into one rule:
.btn { ... text: { transform: uppercase; decoration: none; shadow: 0 .063em rgba(0,0,0,.3); } }
I’ll usually check my compiled CSS at this point to make sure things aren’t getting out of hand. That way if I see any red flags (like repeated rules or specificity issues), I can take care of it right away. So far, so good.
Color Values
With Sass we can simplify RGBa values by using a color keyword instead of each RGB value. For example,
rgba(0,0,0,.8); rgba(255,255,255,.4);
can be rewritten as:
rgba(black,.8); rgba(white,.4);
We can also use a hex value in place of the color keywords, as it will work the same way. Go ahead and try it with the remaining rgba values in the stylesheet.
While we’re replacing color values, notice how rgba(white,.4)
is repeated many times throughout the stylesheet. Let’s create a variable for it – that way we’ll only need to declare and maintain it in a single instance.
$off-wht: rgba(white,.4);
Now we can replace the repeating values with our new variable.
box-shadow: 0 .125em $off-wht, inset 0 .063em $off-wht, ...
Color Functions
Sass color functions is where the real theming magic happens. We’ll use the darken
and lighten
functions to modify the shades of our base theme colors. For example,
... &:hover { background-color: #8A58B7; box-shadow: ... ,inset 0 -0.188em #804FAD; }
can be defined simply as:
... &:hover { background-color: darken(#9A68C7, 6%); box-shadow: ... ,inset 0 -.188em lighten(#9A68C7, 2%); }
Both functions return colors with a darker or lighter shade based on the amount we set between 0% and 100%.
Since we are declaring every themeable color property only once, we’ll need to pass an argument in our btn-theme
mixin that will dynamically assign the theme color value to each property, then darken or lighten it as specified –– this will also dynamically set the different color shades we need in our themes.
@mixin btn-theme($btn-color) {
The $btn-color
variable can now be placed anywhere a lighter or darker color is being described in the mixin.
border-color: darken($btn-color, 20%); background-color: $btn-color; &:hover { background-color: darken($btn-color, 6%); box-shadow: 0 .125em $off-wht, inset 0 .063em $off-wht, inset 0 -.188em lighten($btn-color, 2%); } &:active { box-shadow: 0 .063em $off-wht, inset 0 0 1.094em darken($btn-color, 40%), inset 0 .063em darken($btn-color, 20%),inset 0 -0.188em darken($btn-color, 20%); }
Once we define the base colors for our buttons, we no longer need to worry about the other shades –– the color functions handle it for us.
Multiple Arguments
Let’s add a transition for the :active
and :hover
states. The transition
property is supported in most browsers, but we still need to use the vendor prefixes for Chrome, Safari, and older versions of Firefox. Instead of having to write each prefixed declaration every time we want to use a transition, we can create a mixin with the necessary vendor prefixes.
@mixin trans($val...) { -webkit-transition: $val; -moz-transition: $val; -o-transition: $val; transition: $val; }
But sometimes a mixin can have an unknown number of arguments. For example, our mixin above might take any number of transition values as arguments. For this, Sass supports “variable arguments,” which are arguments that get passed as a list of arguments. To create one we need to add “…” after the variable in the argument.
Now our trans
mixin can be reused anywhere in the stylesheet while accepting multiple values, so in our .btn
rule, we’ll include the mixin with the transition values we want to use.
.btn { ... /* other properties */ @include trans(0.2s ease-in-out); }
Creating a Partial
Finally, let’s convert our file into a “partial” by saving it as “_buttons.scss” in our scss folder. The file becomes a chunk (or partial) of SCSS code that can be imported into “style.scss”, but will not compile and output as a separate CSS file. When importing the partial we can leave out the file extension and underscore.
@import 'buttons';
Finishing Up
Now we’re ready to create the color theme rules. Instead of declaring each new color value in every rule, we can simply include the btn-theme
mixin we created earlier and pass the appropriate color variable as its argument.
.purple { @include btn-theme($purple); } .blue { @include btn-theme($blue); } .green { @include btn-theme($green); } .orange { @include btn-theme($orange); }
If we need to change a button’s color theme, we simply replace the element’s class name. To add a new color theme, all we need to do is create the variable and its rule in the partial file.
<button class="purple btn"> Button </button> <button class="blue btn"> Button </button> <button class="green btn"> Button </button> <button class="orange btn"> Button </button>
Hopefully now you have a better understanding of how Sass makes our CSS smarter, cleaner, modular, and above all, fun! You can view the completed working example here.
Some of what we’ve covered here can be simplified even further with the Compass framework – more on that coming soon!
Oh yes, naturally 🙂 Thank you Damon Bauer.