Useful SASS Snippets

Automating Annoying Systems

One thing I absolutely love about Sass is the mixins and functions. Being able to automate many of the repetitive coding for CSS is both amazing in building and maintaining a base. But I often find many developers creating complex systems for simple tasks, such as managing a font stack or color scheme. These can be tedious to set up and employ. In this article, I will explain how I automate these systems, and remember them.

Colors

A site's color scheme is very important. Typically, I see a lot of the "series of variables" to represent a scheme. Some find this effective, but, and maybe it's just me, I can never seem to kept these kind of systems straight when utilizing them in code. Let's say we have five colors: $red, $red-yellow, $yellow, $tan, $brown. I know what you're thinking: "Hmphry, you seriously can't remember 5 color values?!", but this is a very small color scheme and unlikely in practice. What if there are multiple reds? $red-2? What if there is five different reds? Instead, I like to use a function called color. I know, I am very creative. color typically takes 3 parameter: color, shade, and transparency(for that sweet RGBA action).

$color-stack:
	(group: orange, id: normal, color: #e67835),
	(group: orange, id: pale, color: #f8a878),
	(group: orange, id: dark, color: #ad490c),
	(group: blue, id: normal, color: #426682);

// Color Function
@function color($group, $shade:normal, $transparency:1){
	@each $color in $color-stack{
		$c-group: map-get($color, group);
		$c-shade: map-get($color, id);
		@if($group == map-get($color, group) and $shade == map-get($color, id)){
			@return rgba(map-get($color, color), $transparency);
		}
	}
}

To explain a bit of what is going on here, we have a Sass map called $color-stack. This is the only thing we will ever update to add or remove a color. EVER. The map has three identifiers: group, id, color.

  1. Group: Group will be what you want it called, for instance: orange, blue, or even something silly like Megatron. This field is not unique to the color.
  2. ID: This is the color's unique identifier. So, a pale orange would be 'pale', Megatron's steel would be 'steel'. Normal is the default entry, so it's a good idea to keep the id for your default marked as 'normal'. This field is unique to the group. Do not repeat in the same group.
  3. Color: This is where you put the hex of the color.

Once called, the function will loop through $color-stack until it finds a match for both group and id, then it will adjust for the transparency. Using this is pretty simple:

body{
	color: color(blue, normal,.8);
}
p{
	color: color(orange);
}
blockquote{
	color: color(blue);
	background: color(orange, pale,.4);
	border-color: color(orange, dark);
}

Which compiles to:

body {
	color: rgba(66, 102, 130, 0.8);
}

p {
	color: #e67835;
}

blockquote {
	color: #426682;
	background: rgba(248, 168, 120, 0.4);
	border-color: #ad490c;
}

Update: A common question about this function is "why don't I use $c-*". It's a very good question. The reason why I use color() is because it's the system I am accustomed to. Writing color() is easier for me than $c-*, for whatever reason. I also like the general name and descriptor name being separate. Truthfully, if you use the variable system over mine, you're not going to miss much so long as you keep your color names simple, descriptive, and organized.

Font Stack

The font stack is another one of those problems which are often solved by variables. In this instance, it makes a lot of sense and is easy enough to work with. Weird problems can arise when you use third party services, however. For instance, fonts.com handles their stack weird by default. How wierd? Well, let's say you want want the entire family of Brandon Grotesque for your awesome website! Well, to call weight:200, it'd be font-family:'Brandon Grot W01 Light';. 700? font-family:'Brandon Grot W01 Bold';. See how this can be a problem? Especially when you add more fonts to the mix? Fret not, because I have devised a simple mixin.

$font-stack:
	(group: brandon, id: light, font: ('Brandon Grot W01 Light', san-serif ), weight: 200, style: normal),
	(group: brandon, id: light-italic, font: ('Brandon Grot W01 Light', san-serif ), weight: 200, style: italic),
	(group: brandon, id: regular, font: ('Brandon Grot W01-Regular', san-serif), weight: 400, style: normal),
	(group: brandon, id: regular-italic, font: ('Brandon Grot W01-Regular', san-serif), weight: 400, style: italic),
	(group: brandon, id: bold, font: ('Brandon Grot W01 Black', san-serif), weight: 700, style: normal),
	(group: brandon, id: bold-italic, font: ('Brandon Grot W01-Regular', san-serif), weight: 400, style: italic),
	(group: clarendon, id: regular, font: ('Clarendon LT W01', serif), weight: 200, style: normal),
	(group: code, id: regular, font: (monospace), weight: 400, style: normal);

// Breakpoint Mixin
@mixin font($group, $id:regular){
	@each $font in $font-stack{
		@if($group == map-get($font, group) and $id == map-get($font, id)){
			font-family: map-get($font, font);
			font-weight: map-get($font, weight);
			font-style: map-get($font, style);
		}
	}
}

You're going to notice a map similar to our $color-stack map from above! This code is going to be very similar to the color function, but vary in a few key parts that I'll outline below.

First thing we're going to deal with is our $font-stack map. There are 5 values needed for this map:

  1. Group: This is the group name for the font. This is value is shared by fonts. Examples are brandon, clarendon, or even just serif.
  2. ID: Font unique identifier. This should be unique among the group. Examples are bold, light-italic, regular. Regular is the default value.
  3. Font: This is the actual font you want. Include it's stack in a (map) or a variable!
  4. Weight: CSS font-weight of the font you want.
  5. Style: CSS font-style you want.

Once you have all that defined and call the mixin, the mixin will loop through $color-stack looking for a match to the called group and ID. Once found, it will return the font-family, font-weight, and font-style properties.

h1{
	@include font(brandon, light-italic);
}
p{
	@include font(brandon);
}
p i{
	@include font(brandon, regular-italic);
}
p b{
	@include font(brandon, bold);
}
blockquote{
	@include font(clarendon);
}
code{
	@include font(code);
}

Compiles to:

h1 {
	font-family: "Brandon Grot W01 Light", san-serif;
	font-weight: 200;
	font-style: italic;

p {
	font-family: "Brandon Grot W01-Regular", san-serif;
	font-weight: 400;
	font-style: normal;
}

p i {
	font-family: "Brandon Grot W01-Regular", san-serif;
	font-weight: 400;
	font-style: italic;
}

p b {
	font-family: "Brandon Grot W01 Black", san-serif;
	font-weight: 700;
	font-style: normal;
}

blockquote {
	font-family: "Clarendon LT W01", serif;
	font-weight: 200;
	font-style: normal;
}

code {
	font-family: monospace;
	font-weight: 400;
	font-style: normal;
}

On a personal note, I love handling my fonts this way so much that I always use a version of this mixin, even if I am not using fonts.com for that project.

Media Queries

The final system I wanted to write about is the media mixin. The only thing my media mixin brings to the table is an easier set up. Well, that's what I think, anyways.

$media-stack:
	(group: tablet, id: general, rule: "only screen and (min-device-width: 700px)"),
	(group: small, id: general, rule: "only screen and(min-device-width: 1100px)"),
	(group: small, id: inbetween, rule: "only screen and(min-device-width: 1100px) and (max-device-width: 1400px)"),
	(group: large, id: general, rule: "only screen and(min-device-width: 1400px)"),
	(group: print, id: general, rule: "only print");

@mixin media($group, $id: general){
	@each $media in $media-stack{
		@if($group == map-get($media, group) and $id == map-get($media, id)){
			$rule: map-get($media, rule);
			@media  {@content}
		}
	}
}

Right off the bet, you should notice the variable $media-stack. Much like all the other systems in this article, that is where you write your options. It takes three values:

  1. Group: This is the group name for the query. This is value is shared by many keys. Examples are tablet, small, 1400.
  2. ID: Query unique identifier. This should be unique among the group. Examples are general, inbetween, exclude. Since it might be rare for actual groups, the default is general.
  3. Rule: This is the actual rule you want for the query. The query NEEDS to be in quotations, or Sass will error out.

There are two ways of using this mixin. One is to call it on it's own, the other is to call in nested. I typically choose the nested option to keep my code together, but the former will result in a smaller CSS file.

h1{
	color: #333;
	@include media(tablet){
		font-size: 1rem;
	};
	@include media(small, inbetween){
		font-size: 1.2rem;
	};
	@include media(small){
		color: #000;
	};
}

Compiles to:

h1 {
	color: #333;
}
@media only screen and (min-device-width: 700px) {
	h1 {
		font-size: 1rem;
	}
}
@media only screen and (min-device-width: 1100px) and (max-device-width: 1400px) {
	h1 {
		font-size: 1.2rem;
	}
}
@media only screen and (min-device-width: 1100px) {
	h1 {
		color: #000;
	}
}

Update: Mike Mai made a good point that often times content dictates breakpoints and that there should be a custom option. In order to do this, we're going to make a few changes to our system.

$media-stack:
	(group: tablet, id: general, rule: "only screen and (min-device-width: 700px)"),
	(group: small, id: general, rule: "only screen and(min-device-width: 1100px)"),
	(group: small, id: inbetween, rule: "only screen and(min-device-width: 1100px) and (max-device-width: 1400px)"),
	(group: large, id: general, rule: "only screen and(min-device-width: 1400px)"),
	(group: print, id: general, rule: "only print"),
	(group: custom, id: screen, rule: "only screen and");

@mixin media($group, $id: general, $customRule: ""){
	@each $media in $media-stack{
		@if($group == map-get($media, group) and $id == map-get($media, id)){
			$rule: map-get($media, rule);
			@media   {@content}
		}
	}
}

You'll notice we added a new map called custom. This is the basis of out custom media query! The id is set to screen, because you'll notice I am only targeting screen by default. You'll also find a new parameter called $customRule. $customRule will be where you add your rule. So, let's say you wanted target all devices under 360px width and make their h1s blue, you can write it like so:

h1{
	color: #333;
	@include media(tablet){
		font-size: 1rem;
	};
	@include media(small, inbetween){
		font-size: 1.2rem;
	};
	@include media(small){
		color: #000;
	};
	@include media(custom, screen, " (max-device-width: 360px)"){
		color: blue;
	};
}

And the output would be:

h1 {
	color: #333;
}
@media only screen and (min-device-width: 700px) {
	h1 {
		font-size: 1rem;
	}
}
@media only screen and (min-device-width: 1100px) and (max-device-width: 1400px) {
	h1 {
		font-size: 1.2rem;
	}
}
@media only screen and (min-device-width: 1100px) {
	h1 {
		color: #000;
	}
}
@media only screen and (max-device-width: 360px) {
	h1 {
		color: blue;
	}
}

But why stop there? You can also utilize the $customRule on EVERY option.

h1{
	color: #333;
	@include media(tablet, general, " and (min-device-pixel-ratio: 2)"){
		font-size: 1rem;
	};
	@include media(small, inbetween){
		font-size: 1.2rem;
	};
	@include media(small){
		color: #000;
	};
	@include media(custom, screen, "(max-device-width: 360px)"){
		color: blue;
	};
}

Conclusion

And there you have it. Three Sass systems to keep your code clean and maintainable. Any suggestions, edits, or just want to say hello, be sure to tweet at me. Until next time, happy coding!

Comments