Building a Linear-gradient Mixin in Sass

Share this article

First things first: you should probably not use a preprocessor, here Sass, to add vendor prefixes to CSS properties. This is not the kind of thing you want to bring to your code base. It should typically be the kind of thing you want to treat with something like Autoprefixer.

Not only will Autoprefixer do a far better job than you at printing the accurate prefixes (I am looking at you, developers who write -ms-linear-gradient and -moz-border-radius), but it’s also a breeze to update, maintain and scale. Everything is done from a configuration file, not from within the stylesheets.

Anyway, I’m done with the disclaimer. In this article, I would like to show you how to write a mixin that prints a linear-gradient as well as a fallback color and the old version for WebKit (still using the old angle notation). While you might not necessarily need it, I believe you could learn quite a few things from this code.

Note: please let me take this article as an opportunity to remind you that linear gradients only need the WebKit prefix, and none of the others. Please stop printing obsolete and absent prefixed gradients in your stylesheets. :)

Building the mixin

Building a mixin to create a gradient from arguments, convert an angle, prefix the linear-gradient property and everything else may sound like a heavy task but you’ll see that it is actually very easy to achieve as long as you endeavor to keep things simple.

It has occurred to me that people writing gradient prefixes often try to parse and control everything. This is certainly not a good idea. Not only is it quite tedious to do, but it also ends up with crowded complex code that is absolutely not necessary.

Let’s recap what we need to do:

  • Output the first color as a fallback in case CSS gradients are not supported.
  • Output the -webkit- prefixed value while converting the angle to old syntax.
  • Output the standard declaration as given.

Then our mixin needs two things: the direction, and any number of extra arguments that will be printed the way they are being passed.

/// Mixin printing a linear-gradient
/// as well as a plain color fallback
/// and the `-webkit-` prefixed declaration
/// @access public
/// @param {Keyword | Angle} $direction - Linear gradient direction
/// @param {Arglist} $color-stops - List of color-stops composing the gradient
@mixin linear-gradient($direction, $color-stops...) {
  background: nth(nth($color-stops, 1), 1);
  background: -webkit-linear-gradient(legacy-direction($direction), $color-stops);
  background: linear-gradient($direction, $color-stops);
}

Don’t worry too much about the legacy-direction function and the fallback for now. We will come back to those later. Now, the direction is optional in linear-gradient notation. For instance, the following declaration is perfectly valid:

.selector {
  background-image: linear-gradient(hotpink, tomato);
}

This will use 180deg as a default value for direction, producing a vertical gradient (horizontal for old WebKit, due to the change in the syntax). In order to keep our mixin fully compliant with the usual notation from CSS, we need to allow this. Let’s see what’s going on if we rewrite our previous example using our mixin:

.selector {
  @include linear-gradient(hotpink, tomato);
}

In this case, $direction is hotpink, and $color-stops is an arglist of a single value: tomato. Because of this, we need our mixin to define a default value for $direction if it’s not a valid direction (either an angle or a directional keyword). In this case, the current $direction, a color, needs to be prepended to the $color-stops list.

It may sound a bit complicated but trust me everything is going to be clear with the following code snippet:

@mixin linear-gradient($direction, $color-stops...) {
  // Direction has been omitted and happens to be a color-stop
  @if is-direction($direction) == false {
    $color-stops: $direction, $color-stops;
    $direction: 180deg;
  }

  background: nth(nth($color-stops, 1), 1);
  background: -webkit-linear-gradient(legacy-direction($direction), $color-stops);
  background: linear-gradient($direction, $color-stops);
}

Let’s recap: if the $direction argument happens not to be a direction (we’ll inspect the is-direction function later on), we prepend it to the $color-stops list (because it’s a color-stop), and we redefine $direction to 180deg, the default value. Easy, right?

We are almost done, let me just explain how the fallback works because it might look weird. We want the first color value of the $color-stops list to be the fallback color in case linear gradients are not supported.

One might think nth($color-stops, 1) would be enough, but what if the first value happens to be a 2-items long list containing a color and a color stop (e.g. hotpink 42%)? Because of this, we make sure always to grab the color value, using nth(nth($color-stops, 1), 1), also known as the first value of the first value of $color-stops.

Testing if a value is a direction

The first line of our mixin tests whether the $direction argument is a valid direction as per CSS specifications. There is no built-in function for this, so we need to make one. It is not very difficult per se, but it turns out there are quite a few ways to define a direction in CSS. If we stick to linear gradients, here are the different options:

  • a keyword value among to top, to top right (or to right top), to right, to bottom right (or to right bottom), to bottom, to bottom left (or to left bottom), to left, to left top (or to top left);
  • an angle either in deg, rad, grad or turn.

So checking whether a value is a direction is as simple as testing if it either a valid keyword, or a number with a valid unit.

/// Test if `$value` is a valid direction
/// @param {*} $value - Value to test
/// @return {Bool}
@function is-direction($value) {
  $is-keyword: index((to top, to top right, to right top, to right, to bottom right, to right bottom, to bottom, to bottom left, to left bottom, to left, to left top, to top left), $value);
  $is-angle: type-of($value) == 'number' and index('deg' 'grad' 'turn' 'rad', unit($value));

  @return $is-keyword or $is-angle;
}

Converting angle to legacy syntax

The WebKit version relies on the old syntax, where:

So we need our legacy-direction function to:

  • return the accurate keyword if the input is a keyword;
  • return the accurate angle if the input is an angle;
  • throw an error if the input is neither a keyword nor an angle.
/// Convert a direction to legacy syntax
/// @param {Keyword | Angle} $value - Value to convert
/// @require {function} is-direction
/// @require {function} convert-angle
/// @throw Cannot convert `#{$value}` to legacy syntax because it doesn't seem to be a direction.;
@function legacy-direction($value) {
  @if is-direction($value) == false {
    @error "Cannot convert `#{$value}` to legacy syntax because it doesn't seem to be a direction.";
  }

  $conversion-map: (
    to top          : bottom,
    to top right    : bottom left,
    to right top    : left bottom,
    to right        : left,
    to bottom right : top left,
    to right bottom : left top,
    to bottom       : top,
    to bottom left  : top right,
    to left bottom  : right top,
    to left         : right,
    to left top     : right bottom,
    to top left     : bottom right
  );

  @if map-has-key($conversion-map, $value) {
    @return map-get($conversion-map, $value);
  }

  @return 90deg - $value;
}

Note: as of writing, LibSass 3 does not support @error yet. If you want to increase support for LibSass, feel free to replace @error with @warn. I currently maintain a compatibility table between the different versions of Sass (Ruby, and LibSass)

Using it

Alright! We’re done with the code. All that’s left is using our mixin to make sure it works.

.selector-1 {
  @include linear-gradient(#31B7D7, #EDAC7D);
}

.selector-2 {
  @include linear-gradient(to right, #E47D7D 0%, #C195D3 50%, #4FB4E8 100%);
}

.selector-3 {
  @include linear-gradient(42deg, #B58234 0%, #D2B545 50%, #D7C04D 50.01%, #FFFFFF 100%);
}
.selector-1 {
  background: #31B7D7;
  background: -webkit-linear-gradient(-90deg, #31B7D7, #EDAC7D);
  background: linear-gradient(180deg, #31B7D7, #EDAC7D);
}

.selector-2 {
  background: #E47D7D;
  background: -webkit-linear-gradient(left, #E47D7D 0%, #C195D3 50%, #4FB4E8 100%);
  background: linear-gradient(to right, #E47D7D 0%, #C195D3 50%, #4FB4E8 100%);
}

.selector-3 {
  background: #B58234;
  background: -webkit-linear-gradient(48deg, #B58234 0%, #D2B545 50%, #D7C04D 50.01%, #FFFFFF 100%);
  background: linear-gradient(42deg, #B58234 0%, #D2B545 50%, #D7C04D 50.01%, #FFFFFF 100%);
}

Final thoughts

That’s it folks. As you can see, it was not that hard to build and did not involve some weird parsing and detection. You can play with the code on SassMeister.

Frequently Asked Questions on Building Linear Gradient Mixin with SASS

What is a Linear Gradient Mixin in SASS?

A Linear Gradient Mixin in SASS is a reusable block of code that generates a linear gradient. It allows developers to create a smooth transition between two or more specified colors along a straight line. This is particularly useful in web design for creating visually appealing backgrounds and effects. The Mixin is written once and can be included in other SASS files, making it a powerful tool for maintaining clean and DRY (Don’t Repeat Yourself) code.

How do I create a basic Linear Gradient Mixin in SASS?

Creating a basic Linear Gradient Mixin in SASS involves defining the Mixin with the @mixin directive, followed by the name you want to give to the Mixin. Inside the Mixin, you specify the properties for the linear gradient. Here’s a simple example:

@mixin linear-gradient($color-start, $color-end) {
background: $color-start;
background: linear-gradient(to right, $color-start, $color-end);
}
In this example, the Mixin takes two parameters: $color-start and $color-end, which represent the starting and ending colors of the gradient.

How do I use the Linear Gradient Mixin in my SASS files?

To use the Linear Gradient Mixin in your SASS files, you include it with the @include directive followed by the name of the Mixin and the arguments for the parameters. For example:

.my-element {
@include linear-gradient(red, blue);
}
In this example, the .my-element will have a background gradient that transitions from red to blue.

Can I create a Linear Gradient Mixin with more than two colors?

Yes, you can create a Linear Gradient Mixin with more than two colors. You can do this by passing a list of colors as an argument to the Mixin. Here’s an example:

@mixin multi-linear-gradient($colors) {
background: nth($colors, 1);
background: linear-gradient(to right, $colors...);
}

.my-element {
@include multi-linear-gradient(red, blue, green);
}
In this example, the .my-element will have a background gradient that transitions from red to blue to green.

How can I control the direction of the gradient in the Mixin?

You can control the direction of the gradient by adding a parameter for the direction in the Mixin. The direction can be specified as a degree, or using keywords like ‘to right’, ‘to left’, ‘to top’, ‘to bottom’. Here’s an example:

@mixin linear-gradient($color-start, $color-end, $direction: to right) {
background: $color-start;
background: linear-gradient($direction, $color-start, $color-end);
}
In this example, the default direction is ‘to right’, but it can be overridden when including the Mixin.

Can I use a Linear Gradient Mixin to create a radial gradient?

No, a Linear Gradient Mixin is specifically for creating linear gradients. If you want to create a radial gradient, you would need to create a separate Radial Gradient Mixin. However, the concept is similar, and you would define and include the Mixin in a similar way.

How can I make the gradient transition smoother in the Mixin?

You can make the gradient transition smoother by adding more color stops in the gradient. A color stop is where a new color starts in the gradient. By adding more color stops, you can create a smoother transition between colors.

Can I use a Linear Gradient Mixin in a CSS file?

No, Mixins are a feature of SASS and cannot be used in a CSS file. However, once your SASS is compiled, it will generate the corresponding CSS with the linear gradient.

How can I create a diagonal gradient with the Linear Gradient Mixin?

You can create a diagonal gradient by specifying a degree for the direction in the Mixin. For example, a 45 degree angle will create a diagonal gradient from bottom left to top right.

Can I use variables in the Linear Gradient Mixin?

Yes, you can use variables in the Linear Gradient Mixin. This can be useful if you want to use the same colors in multiple gradients, or if you want to easily change the colors later. You can define the variables at the top of your SASS file and then use them as arguments when including the Mixin.

Kitty GiraudelKitty Giraudel
View Author

Non-binary trans accessibility & diversity advocate, frontend developer, author. Real life cat. She/they.

AdvancedCSScss3 gradientssasssass functionssass mixinsStuR
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week