A Bulletproof Function to Validate Length Values in Sass

Share this article

Once you start getting comfortable with Sass, playing with some functions and writing your own mixins, soon enough you will want to validate developer input. This means adding some extra checks to your functions and mixins to make sure the given arguments are correct in terms of how they are going to be used within the function or mixin.

After having seen a dozen different functions supposedly aimed at validating a length, I thought I would give it a try and write something on it. So here we are.

Let’s first recall what a length is according to official CSS specifications:

Lengths refer to distance measurements […] A length is a dimension. However, for zero lengths the unit identifier is optional (i.e. can be syntactically represented as the ‘0’). A dimension is a number immediately followed by a unit identifier.

If we sum up what we just read, a length is a number followed by a unit, unless it’s zero. It’s a bit simplistic but we’ll start with that.

Setting Up the Function

If a function returns a Boolean, it is usually good practice to name the function with a leading is-. By the very nature of the question is [this] [that]?, we imply that the answer is either yes (true) or no (false).

In our case, we want to make sure the given value is a length, so I suggest is-length, in all its simplicity.

/**
 * Check whether `$value` is a valid length.
 * @param {*} $value - Value to validate.
 * @return {Bool}
 */
@function is-length($value) {
  // Do some magic and return a Boolean.
}

Returning a Valid Value

Thanks to our refresher we know that a length is a number with a unit. Let’s start with that.

@function is-length($value) {
  @return type-of($value) == "number" and not unitless($value);
}

Before going any further, let me remind you of a couple of things:

  • We return a Boolean, so there is no need to pollute the function’s core with unnecessary @if, @else if and @else directives.
  • We quote strings (using double quotes) because it’s more elegant and because an unquoted string is strictly equal to the same quoted string anyway.

Okay, that’s a good start. Unfortunately, most Sass frameworks that do unit validation will stop there. We cannot stop after such a good start! Also, we failed to implement correctly the CSS specification that if the value is zero, the unit is optional. Our function will return false if $value is 0 because it is unitless. Let’s fix it!

@function is-length($value) {
  @return $value == 0 or type-of($value) == "number" and not unitless($value);
}

The or (||) operator is implemented like this in most languages: Keep parsing the condition until a token evaluates to true. That means if $value is 0, the function stops parsing and directly returns true, otherwise it keeps going.

Note that we could have written our condition like this as well (where parentheses are included for readability):

@function is-length($value) {
  @return type-of($value) == "number" and (not unitless($value) or $value == 0);
}

Our first implementation reads as if $value is 0 or $value is a number with a unit. While this implementation reads as if $value is a number and either has a unit or is 0. You can pick the one you feel the most comfortable with.

Allowing Common Values

You are probably not without knowing that there are a couple of values that can apply to all CSS properties: initial and inherit. We can also add auto to the list because most length-based properties do accept auto as a valid value. So we have three values that we want to allow, four if we count 0 that we already added previously. Let’s start with the ugly way:

@function is-length($value) {
  @return $value == 0 
          or $value == "auto" 
          or $value == "initial" 
          or $value == "inherit" 
          or type-of($value) == "number" 
          and not unitless($value);
}

Wow… Not really elegant, is it? Thankfully, in a previous article here at SitePoint I showed you how we can optimise such a statement using the index function.

The index function returns the index of the second parameter (our value) in the first parameter (a list). If the value isn’t found in the list, it returns null in Sass 3.4 or false in Sass 3.3.

@function is-length($value) {
  @return index(0 "auto" "inherit" "initial", $value) 
          or type-of($value) == "number" 
          and not unitless($value);
}

The thing is, our is-length value can now return an integer. For instance, if $value is auto, then the function returns 2, because auto is the second item in our list of allowed values. While it shouldn’t be a big deal if you use @if is-length($value), there might be cases where you want to have a Boolean at all times.

In order to make sure the function returns a bool type, you can use the not-not trick (seen in the same article as above) to cast the value as a Boolean. While this is not really elegant, it works fine:

@function is-length($value) {
  @return not not index(0 "auto" "inherit" "initial", $value) 
          or type-of($value) == "number" 
          and not unitless($value);
}

If you want to make this more explicit, you can build a little contains function that hides this:

/**
 * Check whether `$list` contains `$value`.
 * @param  {List} $list  - List of values.
 * @param  {*}    $value - Value to check in the list.
 * @return {Bool}
 */
@function contains($list, $value) {
  @return not not index($list, $value);
}

…then:

@function is-length($value) {
  @return contains(0 "auto" "inherit" "initial", $value) 
          or type-of($value) == "number" 
          and not unitless($value);
}

Both clear and eloquent, but needs an extra function. I’ll let you decide which is best for your needs.

Note: remember that auto is an invalid value for padding. Quite an edge case, but you should keep that in mind.

Allowing calc()

Our function is starting to get somewhere, isn’t it? However, we still need to allow calc(), which is often forgotten or omitted from length checkers. The thing with checking for calc() is that it requires Sass 3.3, because we need the str-slice method.

The idea is quite simple. We slice the first four characters from the value. If the result is “calc”, then it (likely) means the value is a calc() function, which is valid:

@function is-length($value) {
  @return not not index(0 "auto" "initial" "inherit", $value) 
          or type-of($value) == "number" and not unitless($value) 
          or str-slice($value + "", 1, 4) == "calc";
}

Two things to note here:

  1. We use $value + "" to cast the value as a string in case it’s a number (e.g. 42px) to avoid an error with the str-slice function expecting a string.
  2. We put this check at the very end to perform it only when needed since it is likely to be both less frequent and more costly than the other two.

The function looks pretty good so far!

Extending to Sizing

At this point we have quite a robust function to validate lengths. We could use it in a sizing mixin like this:

/**
 * Set `width` and `height` in a single statement.
 * @param {Number} $width - Width.
 * @param {Number} $height ($width) - Height.
 * @output `width` and `height`.
 * @requires {function} is-length
 */
@mixin size($width, $height: $width) {
  @if is-length($width) {
    width: $width;
  }

  @else {
    @warn "Invalid length `#{$width}` for `$width` parameter in `size` mixin.";
  }

  @if is-length($height) {
    height: $height;
  }

  @else {
    @warn "Invalid length `#{$height}` for `$height` parameter in `size` mixin.";
  }
}

If you try it, you’ll see it works pretty well. I should note that we might have a problem if we try to use intrinsic sizing. In case you are not familiar with that concept, intrinsic sizing involves new valid values for both width and height properties in order to give more control to developers over the size of their elements.

The thing is, we cannot update our function to include intrinsic values since they are invalid for most properties using lengths (padding, margin, background-size, and so on). What we could do, however, is make another function using the one we already built. What about is-size?

/**
 * Check whether `$value` is a valid size.
 * @param {*} $value - Value to validate.
 * @return {Bool}
 * @requires {function} is-length
 */
@function is-size($value) {
  @return is-length($value)
          or not not index("fill" "fit-content" "min-content" "max-content", $value);
}

…or if you use the contains function as well:

@function is-size($value) {
  @return is-length($value)
          or contains("fill" "fit-content" "min-content" "max-content", $value);
}

Now we can safely use intrinsic sizing values with our brand new is-size function without fearing a falsy return! Pretty neat, right?

Final Thoughts

As I’ve shown in this article, to build a bulletproof function to validate lengths, you have to make sure you cover the most frequent edge cases (calc(), intrinsic sizing, etc).

We could still improve the function by filtering angles (deg, rad, grad, turn) and durations (s, ms) because they are currently considered as lengths by our function since they are basically a number with a unit. But since lengths, angles, and durations are used in very different contexts, there’s very little chance that the function will get passed an angle when it’s expecting a length.

Covering edge cases is nice, but covering everything including the most unlikely scenario is sometimes excessive.

You can find the full code to play with on this Sassmeister.

Frequently Asked Questions (FAQs) about Sass Length Validation

How does the Sass length function work?

The Sass length function is a built-in function that returns the length of a list. It is used to determine the number of items in a list. The syntax is simple: length($list). Here, $list is the list whose length you want to find. For example, if you have a list $colors: red, blue, green; the function length($colors) will return 3, as there are three items in the list.

What is the purpose of validating length values in Sass?

Validating length values in Sass is crucial to ensure that the code behaves as expected. It helps in preventing errors that might occur due to incorrect or unexpected values. For instance, if a function is designed to work with a list of a specific length, validating the length of the list before using it can prevent errors.

How can I use the Sass length function in a loop?

The Sass length function can be used in a loop to iterate over each item in a list. For example, you can use the @for directive with the length function to create a loop that runs for each item in the list. Here’s an example:

$colors: red, blue, green;

@for $i from 1 through length($colors) {
.color-#{$i} {
background-color: nth($colors, $i);
}
}

Can I use the Sass length function with maps?

Yes, you can use the Sass length function with maps. However, it’s important to note that the length function will return the number of pairs in the map, not the total number of individual keys or values. To get the number of keys or values, you would need to use the map-keys or map-values function first to create a list, and then use the length function on that list.

What happens if I use the Sass length function on an empty list?

If you use the Sass length function on an empty list, it will return 0. This is because an empty list has no items, so its length is 0.

Can I use the Sass length function with strings?

Yes, you can use the Sass length function with strings. When used with a string, the length function will return the number of characters in the string.

How can I validate the length of a list in Sass?

You can validate the length of a list in Sass by using the length function in a conditional statement. For example, you can use the @if directive to check if the length of a list is what you expect, and take action if it’s not.

What is the difference between the Sass length function and the JavaScript length property?

The Sass length function and the JavaScript length property both return the length of a list or string. However, they are used in different programming languages and have different syntax. The Sass length function is used in Sass, a CSS preprocessor, while the JavaScript length property is used in JavaScript, a programming language used for web development.

Can I use the Sass length function with nested lists?

Yes, you can use the Sass length function with nested lists. However, it’s important to note that the length function will return the length of the outer list, not the total number of items in all the nested lists. To get the total number of items in all the nested lists, you would need to write a custom function or use a loop to iterate over the nested lists.

What is the return type of the Sass length function?

The Sass length function returns a number. This number represents the length of the list or string that the function was used on.

Kitty GiraudelKitty Giraudel
View Author

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

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