Functions vs. Extends vs. Mixins

Last Updated March 17, 2017

Last week, Danny Lewandowski, commented on my post My Sass Mixins, Extends, and Functions, asking my thoughts on Sass Extends vs. Mixins.

Let me provide a little bit of context:

The post was about a Sass broilerplate that I’ve created and use with every project. — It’s great because I don’t have to reinvent the wheel every time I start a new project. I always have a “go to” set of code I can use and don’t have to waste my time with dumb project set-up tasks.

At first, it might seem like code bloat: adding extra code that you may or may not use? Oh contraire. My Sass library only compiles what you actually use in your code! Awesome!

So what’s the difference between the three, Sass functions, mixins and extends?

NOTE:

If you already know an extend, mixin, and function is, feel free to jump to the chase. But, who knows, you may learning something along the way. 🙂

An Extend

At first glance, an extend looks like an ordinary class:

.heading {
		font-family: sans-serif;
		font-size: 22px;
		font-weight: bold;
}

But, you can other classes reference it:

h1 {
		@extend .heading;
}

h2 {
		@extend .heading;
		font-size: 18px;
}

h3 {
		@extend .heading;
		font-size: 16px;
}

Suddenly, the same styles that apply to .heading also apply to your h1, h2, and h3.

The rendered CSS looks like this:

.heading, h1, h2, h3 {
	font-family: sans-serif;
	font-size: 22px;
	font-weight: bold;
}

h2 {
	font-size: 18px;
}

h3 {
	font-size: 16px;
}

There are a few things to be aware of when working with mixins. Any change to the .heading class, automatically gets applied to every class extending it.

If you’re not careful, you could inadvertently royally mess up your code. When I’m using extends I like to prepend the class I’m extending with a %. That serves as a visual reminder. Plus, any classes that begin with a % aren’t rendered in your final CSS.

You’ll also notice that with my h2s and h3s, I wanted a different font-size, so I had to override them. That doesn’t exactly make for clean code. — Granted, you could remove font-size property from the .heading definition and then be explicit about it within the h1 definition. But, I was trying to make a point: Extends may cause your code to behave unexpectedly.

Take this Sass code, for example:

%red {
	color: red;
}

.one {
	@extend %red;
}

.two {
	color: green;
}

.three {
	@extend %red;
}

The rendered CSS looks like this:

.one, .three {
	color: red;
}

.two {
	color: green;
}

Now, for the HTML:

<div class="one two">Some text</div>

What color is our text? See the confusion? You may look at this example and said, I would write code like that. It’s a dumb implementation. True. When it is this simplified it’s dumb, but when you start writing more complex classes, it’s incredibly easy to do. — By the way, the answer is green, since two is listed in the HTML last.


A Mixin

Let me start by giving you an example:

@mixin heading() {
		font-family: sans-serif;
	font-size: 18px;
	font-weight: bold;
}

When you want to reference it elsewhere in your code:

h1 {
	@include heading;
}

NOTE:

One thing that I’ve always thought was a little strange is the fact that it’s called a mixin — and when you declare it, you use the mixin keyword. But, when you implement it, you use the keyword @include. Oh well. Nobody asked me.

If you’re familiar with other coding languages, a mixin looks like a function — and, in fact, you can pass parameters into them:

@mixin heading($size) {
		font-family: sans-serif;
	font-size: $size;
	font-weight: bold;
}

Right off, that gives our code a little more flexibility.

h1 {
	@include heading(22px);
}

You can also set a default value, so that you don’t have to pass in a parameter every single time.

@mixin heading($size: 18px) {
		font-family: sans-serif;
	font-size: $size;
	font-weight: bold;
}

Within your rendered CSS, a mixin looks different. Let’s take this Sass, for example:

h1 {
	@include heading(22px);
}

h2 {
	@include heading(18px);
}

h3 {
	@include heading(16px);
}

When rendered it looks like this:

h1 {
	font-family: sans-serif;
 	font-size: 22px;
 	font-weight: bold;
}

h2 {
	font-family: sans-serif;
	font-size: 18px;
  	font-weight: bold;
}

h3 {
	font-family: sans-serif;
  	font-size: 16px;
  	font-weight: bold;
}

There’s a lot more code. It basically takes the places where your include is and copies and pastes, or includes the code from your mixin.


A Function

Moving on, a function may seem kind of like a mixin, but the difference is, it’s handling a snippet of code and not the whole class.

Here’s my most used function.

$browser-context: 16;

@function em($pixels, $context: $browser-context) {
  @if (unitless($pixels)) {
    $pixels: $pixels * 1px;
  }

  @if (unitless($context)) {
    $context: $context * 1px;
  }

  @return $pixels / $context * 1em;
}

And its implementation:

h1 {
	font-size: em(22px);
}

I’m creating the font size in ems by passing in a pixel value. I think in pixels. Pixels make sense to me, even though I know ems are the way of internet type. So, why not make the computer work for me? I can still think and work in pixels and let the computer do the conversion to ems for me.

Let’s look at another example:

@function path($src, $dir: img) {
	$src: unquote($src);
	@return url(unquote("..")/$dir/$src);
}

And its implementation:

.hero {
		background: path('bg.jpg') center top no-repeat;
}

This path function creates the full image path for me. — Otherwise, I’d have to write:

.hero {
	background: path('../img/bg.jpg') center top no-repeat;
}

That might not seem like much of a difference, but add that up over time and consider all the background images paths used throughout the site! — Then, what happens if your file path changes? You have to go back through every single instance in your code and change the path, instead of simply changing the path in one place: your function.

The other point I wanted to make with this snippet: you’ll notice that I call the function path() and pass in the image file name. Because the function only handles a snippet of my code, I can still use the other background properties: center top no-repeat without interfering.


Now what?

We’ve gone through a brief synopsis of how each work. Let’s go back to Danny’s question:

I wonder what your thoughts on the debate over mixins vs extends is? Seems that extends are very unpopular.

I decided to do a little digging. It would be easy for me to answer based on my personal assumptions and biases, but I never want to be that person. One of the values for this site is to celebrate differences. In code, there are multiple ways to do one thing. Sometimes (but not always), there’s an obvious right and wrong answer. Other times, you can make a case for a better answer. But, I want to make sure, I approach topics with an open mind — also understanding that best practices change as technology evolves.

I found two great articles, both by Harry Roberts of CSS Wizardry. Harry is a great thought leader on CSS. He wrote cssguideline.es, which is one of my favorite resources when it comes to naming things in CSS.

  1. When to use @extend; when to use a mixin
  2. Mixins Better for Performance

My biggest take-aways from these two articles were

Related Items Should be Grouped Together

You want related items to be grouped together. — This makes sense because the implications extend beyond programming. Who wants the contents of their kitchen pantry spread across their house? No one, you want them all grouped together. And if you wanted to more specific, you’d want cans together, cereal together, etc.

How does this apply practically to our Sass / CSS? Let’s say you have an h3 style. There’s no need for those styles to be applied to a button because they’re unrelated. It’s makes it harder for you to maintain and update your code in the future.

An @extend would associate those two items, grouping them together.

h3, button {
		font-size: 16px;
		font-weight: bold;
}

But, a @mixin makes complete sense. Not only does that allow you to keep the items separate, it also provides the consistency within your code base that you’re looking for.

h3 {
	font-size: 16px;
	font-weight: bold;
}

button {
	font-size: 16px;
	font-weight: bold;
}

Let’s look at the proper use case for an @extend. Here’s some Sass:

@extend %tag {
	border-radius: 30px;
	font-size: 14px;
	padding: 5px 10px;
	text-transform: uppercase;
}

.tag {
	@extend %tag;
	backgrond: gray;
}

.tag--alert {
	@extend %tag;
	background: red;
}

.tag--caution {
		@extend %tag;
	background: yellow;
}

Our CSS output will look like this:

.tag, .tag--alert, .tag--caution {
	border-radius: 30px;
	font-size: 14px;
	padding: 5px 10px;
	text-transform: uppercase;
}

.tag {
		background: gray;
}

.tag--alert {
		background: red;
}

.tag--caution {
	background: yellow;
}

Yes, the items are all grouped together at the top, but that’s OK, because they’re related! They’re all tags. It makes sense that those items would be together.

D-R-Y

Dry is a popular programming concept that stands for “Don’t Repeat Yourself.” — but the point that Harry makes is: don’t repeat yourself. It’s not about completely avoiding repetition.

Repetition in a compiled system is not a bad thing: repetition in source is a bad thing.

If you think about it, that is true for programming across the board. Take PHP, for example, you’ll have includes for header and footer files. You’re not repeating yourself in source, but the system is. It will compile and include all those pieces, repeating those elements so you don’t have to.

Performance / File Size

Let’s take a look at performance and file size.

Organization and DRY code are great programming principles, but if the end result slows down your site, creating larger files, is it really worth it?

Actually, it was file size and performance that first turned me onto to @extends. It produced less code in my CSS files, so it seemed like a better option. …This is true, sort of.

Mixins are better than @extends when gzipped.

In fact, it’s almost as if @mixins were made for gzipping.

NOTE:

What is gzip exactly? gzip is a type of file compression. It looks for repetitive elements within your code (COUGH like mixins) and compresses it.

If you look at Harry’s post, “Mixins Better for Performance” he ran a few tests to prove his point. To summarize, briefly, he found:

  • mixin.css came in at 108K
  • extend.css came in at 72K

That means that mixin.css was 36K larger, that’s almost 150%!

BUT minified and gzipped:

  • mixin.css came in at 12K
  • extend.css came in at 18K

That’s a difference of 6K. Mixins suddenly went from being 1.5x larger, to 33.333% smaller!

To get the full benefit of mixins, you must minify and gzip your code.

You’ve convinced me. Now, what?

Good. Welcome to light. 😊

  1. Use extends only where appropriate.
  2. Default to mixins.
  3. Minify and gzip your code. Here’s how.

The Conversation