CSS Gradients have pretty messy syntax, made even worse once you add vendor prefixes. So, in this tutorial, we’re going to make Gradient color generation more intuitive with a LESS Mixin.
Our Mixin will cater for three types of CSS gradients, namely linear
, radial
, and repeating
. We will have a couple of settings to manipulate the gradient output, including the gradient type, the color combination and the gradient orientation.
Here’s a demo of something we’ll be able to create with the resultant mixin:
Now let’s see how we build it!
CSS Gradient Syntax
To begin with, we’ll examine the syntax that forms the three gradient types, eventually helping us to drive our Mixin.
// 1. linear gradient background-image: linear-gradient(90deg, #ED4264, #FFEDBC); // 2. radial gradient background-image: radial-gradient(circle, #ED4264, #FFEDBC); // 3. repeating (linear) gradient repeating-linear-gradient(90deg, #ED4264, #FFEDBC);
These are the CSS gradient syntax forms, as per W3C standard. Vendor prefixes would be required to support older browsers.
Important note:
Bear in mind there is a discrepancy with how older browsers (when using the prefixed syntax) render the linear gradient orientation.
It used to be the case that the 90deg
(which we specified to form a linear gradient) would span the gradient from bottom to top, starting from the #ED4264
to #FFEDBC
. 0deg
was interpreted as being from left to right. The final specification, on the other hand, states that 0deg
is pointing from bottom to top, hence 90deg
spans the gradient from right to the left instead.
Quirks also occur with the keyword values used to define the gradient orientation. Earlier specifications used the keywords top
, left
, right
and bottom
, whereas the final version adds to
, hence to top
, to left
, to right
, and to bottom
.
The following demo shows two equally built gradients, each using the values 90deg, #ED4264, #FFEDBC
. The one on the left uses prefixes and the one on the right uses modern syntax.
Angle values are more reliable for our case. We can easily convert the old angle behavior, despite the discrepancy, to behave the same as in the final specification with a simple subtraction.
x = 90 - y
The y
is the angle we specified for the standard syntax, while the x
is the value to use in the prefixed syntax.
Creating the Mixin
Right then, let’s create the Mixin. We’ll name our Mixin simply .gradient()
, as follows:
.gradient(@variables) when (@conditions) { }
The Mixin will pass a number of variables to specify the gradient type, the color combinations, the vendor prefixes for backward compatibility, and the index
, as well as the conditions to iterate the Mixin.
We will set default values for the direction and the vendor prefixes. Therefore, unless we want to change the gradient direction, we’ll only have to specify the type and the colors.
Here’s a breakdown of the values we need:
.gradient(@type; @colors; @dir: 0deg; @prefixes: webkit, moz, ms, o; @index: length(@prefixes)) when (@index > 0) { .gradient(@type; @colors; @dir; @prefixes; (@index - 1)); // style goes here }
Prefixed Values
It is worth noting that most of LESS features such as Loop are rooted in Mixins. So, although we are technically creating a Loop here, we can still call it a Mixin. The reason we’re using a Loop is because we need to generate multiple instances of the background
or background-image
property to declare the gradient, like so:
background-image: -webkit-linear-gradient(90deg, red, blue); background-image: -moz-linear-gradient(90deg, red, blue); background-image: -ms-linear-gradient(90deg, red, blue); background-image: -o-linear-gradient(90deg, red, blue); background-image: linear-gradient(0deg, red, blue);
This is the complete syntax to generate a CSS gradient, providing a fallback for some earlier browsers with prefixed syntax.
To generate all these rules, we would firstly need to retrieve each prefix defined in the @prefixes
variable and convert the angle value set in the @dir
. Then, we form the gradient syntax with so-called Escaping and Interpolation.
.gradient(@type; @colors; @dir: 0deg; @prefixes: webkit, moz, ms, o; @index: length(@prefixes)) when (@index > 0) { .gradient(@type; @colors; @dir; @prefixes; (@index - 1)); @prefix : extract(@prefixes, @index); @dir-old : 90 - (@dir); background-image: ~"-@{prefix}-@{type}-gradient(@{dir-old}, @{colors})"; }
Escaping is useful when it comes to generating non-standard CSS syntax, or an arbitrary string that may not be recognized by LESS. LESS will output the code exactly as it finds it, with the only exception being interpolated variables. LESS will still replace these with their respective values.
Standard Syntax
Lastly, we will generate the standard syntax, which must be output after all the prefixed values. This instructs supporting browsers to pick it up in place of the prefixed syntax. To do so, we wrap the standard syntax within a Mixin Guard, as follows:
.gradient(@type; @colors; @dir: 0deg; @prefixes: webkit, moz, ms, o; @index: length(@prefixes)) when (@index > 0) { .gradient(@type; @colors; @dir; @prefixes; (@index - 1)); @prefix : extract(@prefixes, @index); @dir-old : 90 - (@dir); background-image: ~"-@{prefix}-@{type}-gradient(@{dir-old}, @{colors})"; & when ( @index = length(@prefixes) ) { background-image: ~"@{type}-gradient(@{dir}, @{colors})"; } }
We set a condition that will output the syntax when the index
of the current iteration is equal to the number of the prefixes set in @prefixes
. As the loop iteration starts from the greatest number and then goes down to 0
, the standard syntax will be generated along with the first prefixed syntax–which in this case is -o-
.
Our gradient Mixin is all set, we can use it now!
Using the Mixin
As mentioned, we’re only required to specify the gradient type and the colors, for example:
.gradient(linear; #2BC0E4, #EAECC6);
Notice that each parameter should be separated with a ;
. We use a comma to list colors and vendor prefixes.
If you would like to customize the color stop you could write:
.gradient(linear; #2BC0E4, #EAECC6 30%);
Changing the gradient direction should be done with an angle value instead of its keyword counterpart:
.gradient(linear; #2BC0E4, #EAECC6 30%; 180deg);
The following is an example in which we create a radial gradient:
.gradient(circle; #2BC0E4, #EAECC6);
Generating a repeating gradient? No problemo:
.gradient(repeating-linear; #085078, #2BC0E4 25px, #EAECC6 50px);
In this case, make sure you adjust the background-size
accordingly to see the desired result.
Conclusion
In this tutorial, we have created a Mixin to make CSS gradient generation more conscise. We've also learnt about escaping and interpolation along the way.
Additionally, our gradient Mixin is a good example of using Loop. Instead of listing all prefixed syntax this way:
.radial( @direction: 50% 50%, @origin: #fff, @end: #000 ) { background-color: #e9b90f; background-image: -webkit-gradient(radial, @direction,0,@direction,200, from(@end), to(@origin)); background-image: -webkit-radial-gradient(@direction, @origin, @end); background-image: -moz-radial-gradient(@direction, @origin, @end); background-image: -o-radial-gradient(@direction, @origin, @end); background-image: -ms-radial-gradient(@direction, @origin, @end); background-image: radial-gradient(@direction, @origin, @end); }
...we iterate through a list of prefixes from a variable, outputting each of them as we go. We can combine multiple colors in combination, as well as specify the stops without restriction. This Mixin is really quite flexible.
The only missing piece is the Internet Explorer proprietary syntax DXImageTransform.Microsoft.gradient
, though I would encourage everyone to look to the future and Microsoft Edge instead!