Advertisement

Stir up Your Own Delicious Sass Mixins

by
Student iconAre you a student? Get a yearly Tuts+ subscription for $45 →

Sass is designed to help write CSS faster; mixins are of the features that make it possible. Several heavyweight libraries exist with complex formulas for every situation, but today I’m going to show how to stir up your own mixins that complement existing workflows.

Thumbnail: hand mix egg on PhotoDune.

Note: The completed document files can be downloaded ahead of time to view what we’ll be creating, and get an idea of what raw Sass files look like.


I've been using Sass in my front-end development for more than a year, and mixins were the feature I latched onto right away. I saw an instant reduction in the time I spent writing stylesheets, no matter how large or small the project. But what are mixins? From the Sass Language website:

Mixins are one of the most powerful Sass features. They allow re-use of styles—properties or even selectors—without having to copy and paste them or move them into a non-semantic class.

Put another way, mixins are code generators. You create a mixin in your Sass stylesheet with the @mixin directive, define the CSS rules it should output and call it anytime you need those rules included in a style declaration. I use mixins exclusively for CSS3 features like box-shadows, text-shadows, and border-radiuses. Many of these features are not fully supported, and require a vendor prefix like -webkit-, -moz-, -ms-, -o- to work as expected. Instead of writing five rules each time I want to use a CSS3 feature, I can call a one-line mixin and have it generate proper CSS for me.


Step 1: Verify Ruby on Your Local Machine

These first two steps require us to open the Terminal app (Command Prompt in Windows) to verify the Ruby language, and to install the Sass gem. Look for the Terminal in the following places:

  • Mac OS X: Applications > Utilities > Terminal
  • Linux: Applications > Accessories > Terminal
  • Windows: Taskbar Start Button > Command Prompt

Once you have the terminal open, type the following command to check for a Ruby install. Don’t type the dollar sign—it is your placeholder.

$ which ruby

You should see some path information like /usr/local/bin or an error message if Ruby is not installed. Here is the output from my OS X Terminal:

Terminal output for which ruby command

If you receive an error, or happen to know Ruby is not installed on your machine, here are some resources to get up and running with minimal effort. The process for installing Ruby is beyond the scope of this article and is well-documented on the sites below.


Step 2: Install the Sass Gem

Once you have verified or installed the Ruby language, Sass is a snap. From the Terminal, enter this command: $ gem install sass. If your user doesn't have the correct permissions, you may need to run the command with sudo (Super User Do) like this: $ sudo gem install sass. In a few seconds, you should see output similar to the screen below.

Terminal output for Sass gem installation


Step 3: Download and Install LiveReload

While you don’t need a compiler app like LiveReload, Compass or CodeKit to compile Sass into vanilla CSS, it makes that first trip around the block a lot smoother. CodeKit is OS X only, LiveReload is primarily for OS X, with a Windows beta; Compass App works on OS X, Windows, and Linux. Licenses range from $10 USD to $25 USD at the time of writing, but are worth it for the time saved as you get deeper into Sass development.

Now would be a good time to mention that Sass comes in two flavors, .sass and .scss. Without going into specifics, I stick with the .scss syntax because it closely resembles vanilla CSS and compiles without any hiccups.

After you have set up your compiler, you’ll want to set up a working directory with your HTML and CSS files. For this tutorial, I have created a sample called Write Your Own Sass Mixins. Below is the structure I will be referring to throughout the tutorial. If you haven’t downloaded the document files yet, please do so and add the Museo Sans and Droid Serif files to your /fonts directory. These fonts are required for some of the mixins we’ll be creating later in the tutorial.

Write Your Own Sass Mixins
index.html
/css (directory)
/fonts (directory)
/scss (directory)
|-- main.scss

Since I’ve opted for LiveReload, we'll tell it to watch for changes that should be compiled into CSS. When you fire up LiveReload, you should be greeted with the screen shown below. Drag your working directory into the Monitored Folders sidebar on the LiveReload window.

Drag your working folder into LiveReload to start watching for changes

Next you’ll want to check the Compile SASS, LESS, Stylus, CoffeeScript and others box. Click Options to select your output path next.

Check the Compile SASS, LESS, Stylus, CoffeeScript and Others box

You should see a blank table with Output Paths highlighted and scss/main.scss checked on the first line. Click the Set Output Folder... button here. The CSS folder should be selected by default. Click Set Output Folder and Apply.

Selecting your output folder for compiled Sass
Selecting your output folder for compiled Sass
Selecting your output folder for compiled Sass

Update your index.html file with this code, and refresh your browser. Your index should look like the screen below.

	<!DOCTYPE>
	<html>  
		<head>
			<meta http-equiv="Content-type" content="text/html; charset=utf-8">

			<title>Write Your Own Sass Mixins</title>

			<link rel="stylesheet" href="css/main.css" type="text/css" media="screen" title="main" charset="utf-8">
		</head>

		<body>
			<div id="wrapper">
				Some text goes here.
			</div>
		</body>
	</html>
A basic index.html file

LiveReload offers browser extensions for Firefox and Chrome, but the quickest way to get up and running is to copy the following Javascript snippet (from the LiveReload window) into your index.html file, just above the closing body tag. Once this snippet is in place, LiveReload will listen for changes to your HTML and Sass files, and will automatically update the browser—no more Command + R every few minutes.

<script>document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1"></' + 'script>')</script>

Now that we’re through with the housekeeping, we can start creating mixins and let our compiler do the hard work.


Step 4: Border Radius—Your First Utility Mixin

As stated earlier, I use a lot of CSS3 features in my page layouts. The trouble with these leading–edge features is that they require a lot of vendor prefixes and I don’t have a lot of patience. If I can write a mixin once, instead of five heavily duplicated lines of CSS every time I want rounded corners, I am going to do it.

Mixins are identified in Sass files by the @mixin() {} declaration. Anytime a processor like LiveReload comes upon this syntax, it looks for arguments inside the parentheses, and CSS-like instructions inside the curly braces. The compiler will then interpret those instructions and output pure CSS to your stylesheet, main.css in this case.

Open your main.scss file and copy the following mixin code. When you click save, your browser window with index.html should reload automatically and will have a centered gray div with 6px rounded corners.

	/* Main.scss */
	@mixin border-radius($topLeft, $topRight, $bottomRight, $bottomLeft) {
		-webkit-border-radius: $topLeft $topRight $bottomRight $bottomLeft;
		-moz-border-radius: $topLeft $topRight $bottomRight $bottomLeft;
		-ms-border-radius: $topLeft $topRight $bottomRight $bottomLeft;
		-o-border-radius: $topLeft $topRight $bottomRight $bottomLeft;
		border-radius: $topLeft $topRight $bottomRight $bottomLeft;
	}

	#wrapper {
		@include border-radius(6px, 6px, 6px, 6px);
		width: 750px;
		height: 250px;
		background: #eee;
		margin: 0 auto;
	}
Index.html with a proper border-radius mixin


Step 5: Extend the Border Radius Mixin

So we’re off to a good start with our first mixin. But the border-radius mixin has a weak spot: If we apply a border with an opacity less than 100%, the background will show through. Not exactly a great design choice in many instances.

Luckily we have an easy fix in the background-clip property. If you’re not familiar, Chris Coyier of CSS-Tricks has a great write-up on the property.

Sass mixins can also be extended with other mixins. This allows us to write the background-clip separately, and create a third mixin that calls it and our border-radius rules from the previous step. Add the following code to your main.scss file and save. After refreshing your browser, you should see a blue background, and semi-transparent lighter blue radius border.

	/* Main.scss */
	@mixin border-radius($topLeft, $topRight, $bottomRight, $bottomLeft) {
		-webkit-border-radius: $topLeft $topRight $bottomRight $bottomLeft;
		-moz-border-radius: $topLeft $topRight $bottomRight $bottomLeft;
		-ms-border-radius: $topLeft $topRight $bottomRight $bottomLeft;
		-o-border-radius: $topLeft $topRight $bottomRight $bottomLeft;
		border-radius: $topLeft $topRight $bottomRight $bottomLeft;
	}

	@mixin background-clip {
		background-clip: padding-box;
	}

	@mixin combined-radius($topLeft, $topRight, $bottomRight, $bottomLeft) {
		@include background-clip;
		@include border-radius($topLeft, $topRight, $bottomRight, $bottomLeft);
	}

	body {
		background: lightblue;
	}

	#wrapper {
		width: 750px;
		height: 250px;
		background: #eee;
		margin: 0 auto;
		border: 15px solid rgba(255, 255, 255, 0.3);
		@include combined-radius(6px, 6px, 6px, 6px);
	}

Although we duplicated a number of lines initially, we now have a reusable mixin that can be called anytime by providing the border-radius values. And it will render properly.


Step 6: Text Shadow

Text shadows are a great way to punch up typographic treatments and buttons. I often use them for selected navigation states, tabs, or labels. They are also one of the most infuriating items, because IE9 does not support them, and there are no good pure CSS workarounds.

Despite this unfortunate fact, I’m including mixin code for the other modern browsers and IE 10 beta. I have also come across a jQuery script for text-shadow rendering in IE9 or lower.

For modern browsers, the text-shadow property is structured much like the box-shadow, with a horizontal and vertical offset, blur, and color parameters. Add the following code to your main.scss and index.html files, and you should see a 3px blurred shadow behind blue text.

	/* Main.scss */
	
	@mixin text-shadow($horizOffset, $vertOffset, $blur, $color) {
		-webkit-text-shadow: $horizOffset $vertOffset $blur $color;
		-moz-text-shadow: $horizOffset $vertOffset $blur $color;
		-ms-text-shadow: $horizOffset $vertOffset $blur $color; /* IE 10+ */
		-o-text-shadow: $horizOffset $vertOffset $blur $color;
		text-shadow: $horizOffset $vertOffset $blur $color;
		
		#wrapper,
		#wrapper-text-shadow {
			width: 750px;
			height: 250px;
			background: #eee;
			margin: 0 auto 20px;
			padding: 15px 0 0 15px;
			border: 15px solid rgba(255, 255, 255, 0.3);
			@include combined-radius(6px, 6px, 6px, 6px);
		}

		#wrapper-text-shadow > p {
			font-size: 36px;
			color: blue;
			@include text-shadow(0, 3px, 3px, #333);
		}
	}

Index.html, add just after div#wrapper closing tag

	
	<div id="wrapper-text-shadow">
		<p>Text shadow test.</p>
	</div>

Step 7: More Utility Mixins

For the last step, I’ve created mixins for box shadows, background gradients, @font-face declarations, and improved type rendering. These mixins have been tested in modern browsers (Firefox, Chrome, Safari, Opera, IE9) and include all vendor-specific prefixes.

I won’t go into detail about each one; detailed comments are included in the main.scss file. Mixins follow a basic format and can be built upon as your requirements change. If you have a CSS need, a mixin can be written to serve it.

Update your index.html and main.scss files with the following code. After refreshing, you should see a browser window containing a number of boxes, each showing a different mixin test.

	<!DOCTYPE>
	<html>  
		<head>
			<meta http-equiv="Content-type" content="text/html; charset=utf-8">

			<title>Just another Textmate snippet</title>

			<link rel="stylesheet" href="css/main.css" type="text/css" media="screen" title="main" charset="utf-8">
		</head>

		<body>
			<div id="wrapper">
				Box shadow test.
			</div>

			<div id="wrapper-inset-shadow">
				Inset box shadow test.
			</div>

			<div id="wrapper-text-shadow">
				Text shadow test.
			</div>

			<div id="wrapper-background-gradient">
				Background gradient test.
			</div>

			<div id="wrapper-font-family">
				@font-face test in Museo 700 sans.
			</div>

			<div id="wrapper-improve-legibility">
				<h1>Improve text legibility and kerning test.</h1>
			</div>

			<script>document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1"></' + 'script>')</script>
		</body>
	</html>
	
	@mixin border-radius($topLeft, $topRight, $bottomRight, $bottomLeft) {
		-webkit-border-radius: $topLeft $topRight $bottomRight $bottomLeft;
		-moz-border-radius: $topLeft $topRight $bottomRight $bottomLeft;
		-ms-border-radius: $topLeft $topRight $bottomRight $bottomLeft;
		-o-border-radius: $topLeft $topRight $bottomRight $bottomLeft;
		border-radius: $topLeft $topRight $bottomRight $bottomLeft;
	}

	@mixin background-clip {
		background-clip: padding-box;
	}

	@mixin combined-radius($topLeft, $topRight, $bottomRight, $bottomLeft) {
		@include background-clip;
		@include border-radius($topLeft, $topRight, $bottomRight, $bottomLeft);
	}

	@mixin box-shadow($horizOffset, $vertOffset, $blur, $color) {
		/* 
			Blur is an optional parameter, but helps soften the shadow.
			Call the mixin by applying pixel values for $horizOffset, 
			$vertOffset, and $blur, and a hex value for $color. The
			border-collapse rule is included to render shadows properly
			in IE9.
		*/
		-webkit-box-shadow: $horizOffset $vertOffset $blur $color;
		-moz-box-shadow: $horizOffset $vertOffset $blur $color;
		-ms-box-shadow: $horizOffset $vertOffset $blur $color;
		-o-box-shadow: $horizOffset $vertOffset $blur $color;
		box-shadow: $horizOffset $vertOffset $blur $color;
		border-collapse: separate;
	}

	@mixin box-shadow-inset($horizOffset, $vertOffset, $blur, $color) {
		/*
			Same parameters as @mixin box-shadow, but creates an inner shadow
			for pressed or recessed effects.
		*/
		-webkit-box-shadow: $horizOffset $vertOffset $blur $color inset;
		-moz-box-shadow: $horizOffset $vertOffset $blur $color inset;
		-ms-box-shadow: $horizOffset $vertOffset $blur $color inset;
		-o-box-shadow: $horizOffset $vertOffset $blur $color inset;
		box-shadow: $horizOffset $vertOffset $blur $color inset;
	}

	@mixin background-gradient($direction, $first-color, $second-color) {
		background: $first-color;
		background-image: -webkit-linear-gradient($direction, $first-color, $second-color);
		background-image: -moz-linear-gradient($direction, $first-color, $second-color);
		background-image: -ms-linear-gradient($direction, $first-color, $second-color);
		background-image: -o-linear-gradient($direction, $first-color, $second-color);
		background-image: linear-gradient($direction, $first-color, $second-color);
		@include background-clip;
	}

	@mixin typography($font-name, $font-longname) {
		/*
			Make sure to create a /fonts directory at the same level as your /css directory
			to ensure the url strings below work properly. Webfonts will include the short
			name to reference in font-family declarations, and the long name needed for
			url references. The mixin will need to be invoked with the @include declaration
			immediately below to load fonts properly.
		*/
		@font-face {
			font-family: $font-name;
			src: url("../fonts/" + $font-longname + ".eot");
			src: local('☺'), url("../fonts/" + $font-longname + ".woff") format('woff'), 
				url("../fonts/" + $font-longname + ".ttf") format('truetype'), 
				url("../fonts/" + $font-longname + ".svg#webfontjVVPrHqE") format('svg');
			font-weight: normal;
			font-style: normal;
		}
	}

	@include typography("Museo700", "Museo700-Regular-webfont");

	@include typography("DroidSerifRegular", "DroidSerif-Regular-webfont");

	@mixin improve-legibility {
		/*
			The improve-legibility mixin is recommended for headers and smaller amounts of text
			due to the more resource-intensive font-rendering. It is not recommended for mobile
			devices. For more information, refer to the MDN article ( https://developer.mozilla.org/en-US/docs/CSS/text-rendering ),
			or Gigaom ( http://gigaom.com/2010/08/12/optimizelegibility-for-clearer-text-in-your-browser/ ).
			Inspect in Chrome or Safari and toggle the text-rendering: optimizeLegibility rule on and off to
			see the effect on kerning. It will also activate ligatures on fonts that support extended sets.
		*/
		text-rendering: optimizeLegibility;
		@include text-shadow(0, 0, 1px, transparent);
	}



	body {
		background: lightblue;
		font-family: Helvetica, Arial, sans-serif;
		font-weight: normal;
	}

	#wrapper,
	#wrapper-inset-shadow,
	#wrapper-text-shadow,
	#wrapper-background-gradient,
	#wrapper-font-family,
	#wrapper-improve-legibility {
		width: 750px;
		height: 250px;
		background: #eee;
		margin: 0 auto 20px;
		padding: 15px 0 0 15px;
		border: 15px solid rgba(255, 255, 255, 0.3);
		@include combined-radius(6px, 6px, 6px, 6px);
	}

	#wrapper {
		@include box-shadow(0, 3px, 3px, #333);
	}

	#wrapper-inset-shadow {
		@include box-shadow-inset(0, 3px, 3px, #333);
	}

	#wrapper-text-shadow {
		@include text-shadow(0, 3px, 3px, #333);
		font-size: 36px;
		color: blue;
	}

	#wrapper-background-gradient {
		@include background-gradient(top, #999, #fff);
	}

	#wrapper-font-family {
		font-family: "Museo700", Helvetica, Arial;
	}

	#wrapper-improve-legibility > h1 {
		font-family: "DroidSerifRegular", Georgia, "Times New Roman", serif;
		font-weight: normal;
		font-size: 48px;
		@include improve-legibility;
	}

Conclusion

Sass mixins are a great way to get up and running quickly with CSS3. Whether you’re wireraming in code, or building an entire site from scratch, mixins will improve your workflow by reducing lines typed and time spent reviewing syntax.

Because these features are CSS3-centric, you should have a backup plan for older browers. For some sites, rounded corners and shadows are not critical, and it is perfectly acceptable to show a degraded visual layout. For others, a polyfill like Modernizr can provide feature-specific classes to write fallback CSS rules.

I hope you liked this tutorial, thanks for reading!

Advertisement