Sass Mixins to Kickstart Your Project
Mixins are great, aren’t they? They are like functions that output CSS. According to the Sass docs:
Mixins allow you to define styles that can be re-used throughout the stylesheet without needing to resort to non-semantic classes like
.float-left
. Mixins can also contain full CSS rules, and anything else allowed elsewhere in a Sass document. They can even take arguments which allows you to produce a wide variety of styles with very few mixins.
Mixins are really convenient, especially when working on a large project. How many times have you had to define width and height values in your last project? How many times have you Googled how to do a triangle in CSS? Or haven’t you ever wished there was a shorthand for top
, right
, bottom
, left
when using CSS positioning?
You can solve all those problems with mixins. Even better: you won’t have to write them since I’ve already done that for you. Here we go guys, a couple of Sass mixins to kickstart your projects!
Sizing Mixin
I bet you already know this one. I use it as a mixin 101 in all my projects: a size()
mixin defining both the width and the height. First argument is the width, second one is the height. If height is omitted, it falls back to the width value.
@mixin size($width, $height: $width) {
width: $width;
height: $height;
}
Simple enough.
Example Usage
Sass:
.element {
@include size(100%);
}
.other-element {
@include size(100%, 1px);
}
CSS output:
.element {
width: 100%;
height: 100%;
}
.other-element {
width: 100%;
height: 1px;
}
Positioning Mixin
If there is one shorthand I really think CSS misses, it’s a shortcut for offsets: top
, right
, bottom
and left
. We have shorthands for everything: padding
, margin
, background
, even text-decoration
! But we still don’t have one for offsets… So I built my own.
It was inspired by Stylus’ syntax:
absolute: top 0 left 1em
Unfortunately, Sass doesn’t provide a transparent mixin feature so, at best, we can come up with something like this:
@include absolute(top 0 left 1em);
… which is not that bad, you have to admit. Obviously we’ll also have relative()
and fixed()
which work the same way.
I dedicated a whole blog post to the making of this mixin so I won’t get too deep into details here, but the main idea is to provide a list of values to the mixin. Whenever a keyword is found (top
, right
, bottom
, left
), the value directly after the keyword gets applied to it, as long as it is a valid value.
@mixin position($position, $args) {
@each $o in top right bottom left {
$i: index($args, $o);
@if $i and $i + 1< = length($args) and type-of(nth($args, $i + 1)) == number {
#{$o}: nth($args, $i + 1);
}
}
position: $position;
}
@mixin absolute($args) {
@include position("absolute", $args);
}
@mixin fixed($args) {
@include position("fixed", $args);
}
@mixin relative($args) {
@include position("relative", $args);
}
Example Usage
Sass:
.element {
@include absolute(top 0 left 1em);
}
.other-element {
@include fixed(top 1em left "WAT? A STRING?!" right 10% bottom)
}
CSS output:
.element {
position: absolute;
top: 0;
left: 1em;
}
.other-element {
position: fixed;
top: 1em;
right: 10%;
}
Vendor Prefix Mixin
You know how you have to prefix some properties with -webkit-
or -moz-
(among other prefixes) for them to work across browsers because they are non-standard properties? Then you also know how annoying that can be. This is precisely the type of thinking Compass and Bourbon try to fix by providing a collection of mixins dealing with prefixes for you (e.g. @include box-sizing()
).
I have been using Compass for long now but I have recently decided to ditch it. The main reason is I wasn’t using it enough to keep it in projects; in the end, this means faster compilation times. That being said, I found myself in need of vendor prefix mixins. So I built one (and updated it when moving to Sass 3.3).
Sass 3.2 Version
The idea is very simple. The first argument is the property. The second argument is the value for that property. Third and optional argument is the list of prefixes to use. Default value dumps all prefixes.
@mixin prefix($property, $value, $vendors: webkit moz ms o) {
@if $vendors {
@each $vendor in $vendors {
#{"-" + $vendor + "-" + $property}: #{$value};
}
}
#{$property}: #{$value};
}
Example Usage
Sass:
.element {
@include prefix(transform, rotate(42deg), webkit ms);
}
Ouput:
.element {
-webkit-transform: rotate(42deg);
-ms-transform: rotate(42deg);
transform: rotate(42deg);
}
Sass 3.3 Version
My Sass 3.3 version is even better because it allows you to prefix multiple properties at once, and to have a nicer syntax. Basically, instead of having two distinct arguments for property and value, you have only one: A map of declarations. Keys are properties, values are values.
@mixin prefix($map, $vendors: webkit moz ms o) {
@each $prop, $value in $map {
@if $vendors {
@each $vendor in $vendors {
#{"-" + $vendor + "-" + $prop}: #{$value};
}
}
// Dump regular property anyway
#{$prop}: #{$value};
}
}
Example Usage
Sass:
.element {
@include prefix((transform: translate(-50%, -50%)), webkit ms);
}
.other-element {
@include prefix((
column-count: 3,
column-gap: 1em,
column-rule: 1px solid silver,
column-width: 20em
)), webkit moz);
}
CSS output:
.element {
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.other-element {
-webkit-column-count: 3;
-moz-column-count: 3;
column-count: 3;
-webkit-column-gap: 1em;
-moz-column-gap: 1em;
column-gap: 1em;
-webkit-column-rule: 1px solid silver;
-moz-column-rule: 1px solid silver;
column-rule: 1px solid silver;
-webkit-column-width: 20em;
-moz-column-width: 20em;
column-width: 20em;
}
Pushing Things Further
What’s cool with this mixin is it can easily be used in other mixins to avoid having to type prefix()
and the vendor prefixes every time. Think of something like this:
@mixin transform($value) {
@include prefix(transform, $value, webkit ms);
}
@mixin column-count($value) {
@include prefix(column-count, $value, webkit moz);
}
And here’s the example usage:
.element {
@include transform(rotate(42deg));
}
And the CSS output:
.element {
-webkit-transform: rotate(42deg);
-ms-transform: rotate(42deg);
transform: rotate(42deg);
}
Opposite Direction Mixin
If you’re a user of Compass, you may be familiar with the opposite-direction
function (yes, this one is a function and not a mixin, but whatever). The one from Compass is built in Ruby but it’s quite easy to make a Sass one. The one I made relies on Sass 3.3 but it can easily be tweaked to be available to Sass 3.2.
What I like in my function is it allows you to pass a list of directions, so that you get bottom left
from opposite-direction(top right)
. It can be useful when dealing with background-position
, for instance.
@function opposite-direction($directions) {
$opposite-directions: ();
$direction-map: (
'top': 'bottom',
'right': 'left',
'bottom': 'top',
'left': 'right',
'ltr': 'rtl',
'rtl': 'ltr'
);
@each $direction in $directions {
$opposite-direction: map-get($direction-map, $direction);
@if $opposite-direction != null {
$opposite-directions: append($opposite-directions, #{$opposite-direction});
}
@else {
@warn "No opposite direction can be found for `#{$direction}`.";
}
}
@return $opposite-directions;
}
Example Usage
$direction: opposite-direction(top);
// bottom
$other-direction: opposite-direction(bottom left);
// top right
Breakpoint Handler Mixin
If you’ve ever had to do some responsive design, you know working with several different media queries can be hard. It’s often a good idea to store the various breakpoints in variables so they can easily be retrieved without having to be typed all over again every single time.
If you want to take it a step further, you might want to name your breakpoints — which is a very good idea if you want my opinion. This basically means a media query is mapped to a name so you only have to give your breakpoint handler mixin a name to make it dump a media query. Many smart developers including Chris Coyier have made the idea quite popular.
Now if you want to go even further, you can store all those breakpoints in a global map, then make the mixin retrieve the breakpoint based on the name you pass it. Please consider the following code:
$breakpoints: (
'tiny': ( max-width: 767px ),
'small': ( min-width: 768px ),
'medium': ( min-width: 992px ),
'large': ( min-width: 1200px ),
'custom': ( min-height: 40em )
);
@mixin breakpoint($name) {
@if map-has-key($breakpoints, $name) {
@media #{inspect(map-get($breakpoints, $name))} {
@content;
}
}
@else {
@warn "Couldn't find a breakpoint named `#{$name}`.";
}
}
If the string passed to the breakpoint
mixin matches a key in the $breakpoints
map, the mixin opens a @media
directive, using the inspect
function (from Sass 3.3 as well) to literally dump a map. When doing inspect((key: value))
, (key: value)
gets printed as is in the stylesheet.
If the string doesn’t match an existing breakpoint, then the user gets warned through the @warn
directive, printing an error message in his console.
Example Usage
Sass:
.element {
color: red;
@include breakpoint(medium) {
color: blue;
}
}
CSS output:
.element {
color: red;
}
@media (min-width: 992px) {
.element {
color: blue;
}
}
Final Thoughts
I think that’s a good start! Those tools should help get rid of some time-consuming tasks when writing the CSS for your next project. Be sure to use them, tweak them and give your feedback!