When you write Sass and others use it, it’s quite possible for them to make mistakes with your code. Actually, let’s be honest, when I write Sass and use it days later (or even hours later), I make mistakes with my code. You might too. Fortunately, Sass has a number of functions that help us validate the input that developers put in to the Sass we write.
These techniques are especially useful for teams that share Sass mixins or maintain a starter kit or set of mixins and functions. Developers have two options when using a shared Sass library: either they can email, chat, ping, or otherwise interrupt each other for help with custom mixin, or use detailed documentation including code validation to help each other easily troubleshoot code. (For what it’s worth, this isn’t just a Sass thing: any shared code requires communication through interruption or documentation.) Let’s learn about a few of the most useful Sass validation methods now.
Validating Single Values
Mixins and functions take parameters. If you’re passing code off to another developer at work or releasing an open source library, you’ll want to make sure the arguments match your intentions. These functions are useful for validating variables in arguments.
Making sure variables exist: variable-exists()
If your function or mixin relies on a developer-defined variable, make sure the variable exists by using the aptly-named variable-exists() function. This function returns true
or false
based on whether or not a variable has been created and defined.
@mixin create-font-size() {
@if global-variable-exists(base-font) {
font-size: $base-font;
} @else {
@error "Please define the variable `$base-font`.";
}
@if global-variable-exists(line-height) {
line-height: $line-height;
} @else {
@error "Please define the variable `$line-height`.";
}
}
// developer's code
$base-font: 18px;
$line-height: 1.5;
.element {
@include create-font-size;
}
However, a better option than relying on developers’ correctly setting global variables is to include these default variables in your library:
// Your plugin:
$base-font: 18px !default;
$line-height: 1.5 !default;
@mixin create-font-size() {
// etc...
}
// developer's code:
$base-font: 16px;
p {
@include create-font-size();
}
Checking a value’s data type: type-of()
If you need to know what type of value a variable represents, use type-of()
. This function will return one of the following strings:
- string
- color
- number
- bool
- null
- list
- map
This is useful for validating certain kinds of input. You can ensure that a developer only passes a numerical value to a mixin that creates sizes:
@mixin size($height, $width: $height) {
@if type-of($height) == number {
height: $height;
} @else {
@warn "Make sure that `$height` is a number.";
}
@if type-of($width) == number {
width: $width;
} @else {
@warn "Make sure that `$width` is a number.";
}
}
You can also use type-of()
to ensure that color mixins only deal with colors:
@function color-fade($color) {
@if type-of($color) == 'color' {
@return rgba($color, .8);
} @else {
@warn "Make sure you pass a valid color to the color-fade() function.";
}
}
If you need a developer to create a map of config settings for a theme, you can make sure they have a valid map:
@mixin generate-theme($settings) {
@if type-of($settings) == 'map' {
// output here
} @else {
@warn "Make sure `$settings` is a Sass map.";
}
}
Confirming a number’s unit: unit()
Sometimes the math in a function or mixin requires a certain unit in its arguments. You can use unit()
to confirm that a value has the right unit. For example, you may use a mixin that creates sizes in both pixels and rems. (Note, you may be better off using a task runner package for this, but if you need to keep it in Sass, read on.)
$rem-size: 16px !default;
@mixin px-rem($property, $value) {
@if unit($value) == 'px' {
#{$property}: $value;
#{$property}: $value / $rem-size * 1rem;
} @elseif unit($value) == 'rem' {
#{$property}: $value * $rem-size / 1rem;
#{$property}: $value;
} @else {
@warn "Make sure `$value` is in px or rem.";
}
}
Validating Lists and Maps
We’ve already seen how to use type-of()
to ensure a variable contains a list or a map. We can also test two important things: whether a value is in a list, and whether a key is in a map.
Finding a value in a list: index()
The index() function will tell you if a value is found in a list. Technically, it will return either the value’s position in the list (a number) or null
. It’s not a true Boolean function, but for our purposes here, truthy and falsy are good enough.
The index()
function takes two parameters: the list and the value you want to find in the list. This function is useful for testing certain values in a measurement mixin. If we had a mixin that outputs padding or margin calculations using the CSS top, right, bottom, or left shorthand, we’d want to make sure we don’t try to compute values like initial
, inherit
, or auto
.
$rem-size: 16px !default;
@mixin margin-rem($values...) {
$output: ();
@each $value in $values {
@if index(auto inherit initial 0, $value) {
$output: append($output, $value);
} @else {
$output: append($output, $value / $rem-size * 1rem);
}
}
margin: #{$output};
}
Making sure a map has a key: map-has-key()
If you’re checking a map for a specific key, you can use the map-has-key() function to make sure the key exists in the map you’re checking. This is useful if you use a $breakpoints
map and a media query mixin:
$breakpoints: (
small: 480px,
medium: 720px,
large: 1024px
);
@mixin bp($label) {
@if map-has-key($breakpoints, $label) {
@media screen and (min-width: map-get($breakpoints, $label)) {
@content;
}
} @else {
@warn "`#{$label}` is not a valid key in `$breakpoints`.";
}
}
Validating Mixins and Functions
Occasionally you’ll write a mixin or function that depends on an existing mixin or function or maybe on another Sass library. Let’s update the bp()
mixin from the last example to rely on the Breakpoint Sass library. We could expand it like this:
$breakpoints: (
small: 480px,
medium: 720px,
large: 1024px
);
@mixin bp($label) {
@if map-has-key($breakpoints, $label) {
$width: map-get($breakpoints, $label);
@if mixin-exists(breakpoint) {
@include breakpoint($width) {
@content;
}
} @else {
@media screen and (min-width: $width) {
@content;
}
}
} @else {
@warn "`#{$label}` is not a valid key in `$breakpoints`.";
}
}
Now our bp()
mixin (which is shorter and uses map values) will use the breakpoint()
mixin if it exists. If not, it will fall back on our own media query mixin code.
There’s a matching function called function-exists()
. You can use it to test whether a specific function exists. If you’ve got math that relies on a non-standard function, you can make sure there’s a library included that contains that function. Compass adds a pow()
function for exponential math. If you’re creating a font-size scale that needs that function, test for it:
$base-font-size: 16px;
$scale-interval: 1.2;
@function scale-font($steps, $size: $base-font-size, $scale: $scale-interval) {
@if function-exists(pow) {
@return pow($scale, $steps) * $size;
} @else {
@error "A `pow()` function is required for scale math: please include Compass or another library with a `pow()` function.";
}
}
Reporting problems: @warn
and @error
As you’ve probably noticed in the code samples above, I’ve taken care to provide good feedback to the developer when our validation catches some improper input. Most of the time, I used @warn. This directive sends a message to the developer’s console, but allows the compiler to finish running.
Occasionally, when I need to completely stop the compiler (without a certain input or function, a significant amount of output would be broken), I used @error to send a message to the console and halt the compiler.
For more information on the difference between @warn
and @error
, you might want to check out my previous article on the subject or the appropriate section in SitePoint’s Sass reference.
Conclusion
No one is perfect — not those who use our code and not even us a few hours after we’ve written it! That’s why it’s important to help ourselves and others by validating input in our mixins and functions. These techniques not only help you use your own code more efficiently, they also make it far simpler for teams to share and maintain a Sass library.
How do you prevent errors in your Sass? Let us know in the comments!
Frequently Asked Questions (FAQs) on Validating Input in Sass Mixins and Functions
What is the purpose of validating input in Sass mixins and functions?
Validating input in Sass mixins and functions is crucial for maintaining the integrity and functionality of your code. It helps to ensure that the data being passed into your mixins and functions is of the correct type, and that it adheres to the expected format. This can prevent errors and bugs in your code, making it more robust and reliable. It also makes your code easier to debug and maintain, as you can quickly identify and rectify any issues with the input data.
How can I validate input in Sass mixins and functions?
Sass provides several built-in functions that you can use to validate input in your mixins and functions. These include type-of()
, unit()
, and unitless()
, among others. You can use these functions to check the type and unit of your input data, and throw an error if the input does not meet the expected criteria. For example, you can use the type-of()
function to check if the input is a number, and throw an error if it is not.
Can I create custom validation functions in Sass?
Yes, you can create custom validation functions in Sass. This can be useful if you need to perform more complex validation checks that cannot be achieved with the built-in functions. To create a custom validation function, you simply define a new function using the @function
directive, and then use the @return
directive to return a value based on the validation check.
What happens if the input validation fails in a Sass mixin or function?
If the input validation fails in a Sass mixin or function, an error will be thrown and the compilation of your Sass code will be halted. This can help you to quickly identify and rectify any issues with your input data, preventing bugs and errors in your final CSS output.
How can I handle errors in Sass mixins and functions?
Sass provides the @error
directive, which you can use to throw custom error messages when your input validation fails. This can be particularly useful for debugging, as you can provide detailed information about the nature of the error and how to fix it.
Can I use Sass introspection functions for input validation?
Yes, Sass introspection functions can be used for input validation. These functions allow you to inspect the type, unit, and other properties of your input data, and can be used in conjunction with the @error
directive to throw custom error messages when the input does not meet the expected criteria.
What are some common use cases for input validation in Sass mixins and functions?
Input validation in Sass mixins and functions can be used in a variety of scenarios. For example, you might want to ensure that a color value passed into a mixin is a valid color, or that a number passed into a function has the correct unit. Input validation can also be used to enforce certain constraints or rules in your code, such as ensuring that a certain argument is always provided, or that a certain value is within a specific range.
Can I test if a mixin exists in Sass?
Yes, you can test if a mixin exists in Sass using the mixin-exists()
function. This function returns true
if the mixin exists, and false
otherwise. This can be useful for preventing errors in your code, as you can check if a mixin exists before attempting to include it.
How can I use the unit()
function in Sass for input validation?
The unit()
function in Sass returns the unit of a number. You can use this function in your input validation to check if a number has the correct unit. For example, you might want to ensure that a length value passed into a mixin is in pixels, or that a time value passed into a function is in seconds.
What are some best practices for input validation in Sass mixins and functions?
Some best practices for input validation in Sass mixins and functions include: always validate your input data, use the built-in Sass functions for validation where possible, create custom validation functions for more complex checks, use the @error
directive to throw custom error messages, and test your code thoroughly to ensure that your validation checks are working correctly.
James is a Senior Front-End Developer with almost 10 years total experience in freelance, contract, and agency work. He has spoken at conferences, including local WordPress meet-ups and the online WP Summit. James's favorite parts of web development include creating meaningful animations, presenting unique responsive design solutions, and pushing Sass’s limits to write powerful modular CSS. You can find out more about James at jamessteinbach.com.