Dealing With Constants In Sass

Kitty Giraudel
Share

Sass, like JavaScript, does not have a native way to implement constants. But before going any further, let’s have a quick reminder for those of you who are not very aware of this programming jargon.

In computer programming, a constant is an identifier whose associated value cannot typically be altered by the program during its execution (though in some cases this can be circumvented, e.g. using self-modifying code). Many programming languages make an explicit syntactic distinction between constant and variable symbols.
Wikipedia

So a constant is basically an immutable variable. However since a variable is editable by its very own nature, we needed a new name to describe a variable that is not… you know, variable. Hence, constant.

So as I was saying, Sass has no way to define constants. And if you ask me, it’s probably for the best. In a declarative, styling related language such as CSS, needs for immutable values is very unlikely. But as you may know, I’m the guy who does crazy things with Sass so…

Let’s go.

How did it all started?

There is a convention in computer programming that wants authors to define constants name as snakerized uppercased strings, like this: LOOK_AT_ME_I_AM_A_CONSTANT.

So my first thought was to rely on naming convention in order to define constants in Sass. Basically, all snakerized uppercased variables should be considered as constants, thus should never ever be muted.

For instance:

$MAX_Z_INDEX: 2147483647;
$PI: 3.1415926535897932384626433832795028841971693993751;
$E: 2.71828182845904523536028747135266249775724709369995;

If you ask me, you should probably stop there in most cases. But if you like adding some extra fun/useful stuff to your code, you might want to keep reading.

Keeping it simple

So I thought (and obviously wasn’t alone) about gathering all constants in a $CONSTANTS map. This way, even for an inexperienced developer, it should be quite clear that those values should not be edited.

/// Constants map
/// @type Map
/// @prop {Number} MAX_Z_INDEX - 2147483647
/// @prop {Number} PI - 3.1415926535897932384626433832795028841971693993751
/// @prop {Number} E - 2.71828182845904523536028747135266249775724709369995

$CONSTANTS: (
  'MAX_Z_INDEX': 2147483647,
  'PI': 3.1415926535897932384626433832795028841971693993751,
  'E': 2.71828182845904523536028747135266249775724709369995
);

Although, since it is not very convenient to use map-get() to fetch a value in this map, let’s build a little function to get them, called const().

/// Constant getter
/// @param {String} $name - Name of constant to get
/// @return {*} Constant value
/// @require $CONSTANTS
/// @throw 'Unknown constant `#{$name}`.'

@function const($name) {
  @if not map-has-key($CONSTANTS, $name) {
    @error 'Unknown constant `#{$name}`.';
  }

  @return map-get($CONSTANTS, $name);
}

This function could hardly be any simpler: it only needs a constant name. If it doesn’t exist in $CONSTANTS map, it throws an error. Feel free to get rid of this statement if you feel like returning null (which is what map-get does if a key doesn’t exist) is okay. As far as I’m concerned, I feel like if you try to fetch an unknown constant, it should fail.

Then, you would use it like this:

.test {
  z-index: const('MAX_Z_INDEX');
}

It’s looking nice, isn’t it? I like how you can see right away from the name of the function that the value of z-index is a constant and not some magic number.

Pushing things further

You may have noticed that our current version only deals with reading constants, not creating them. If you want to add a new constant, you have to manually write it to the $CONSTANTS map.

So, what if we had a way to set constants dynamically? This would also allow us to throw an error if we try to override an existing constant. This is what we are going to do in this last section.

The idea is quite simple as well, don’t worry:

  • build a const mixin to set a constant;
  • build a const function to get a constant.

We’ll keep our $CONSTANTS map idea, but we’ll initialize it as an empty map. Then the const mixin will place new constants within it.

$CONSTANTS: () !global;

Let’s get started with our const mixin. It should be quite straightforward to understand: if the constant we are trying to create already exists, we blow up. That’s the whole point. If not, we will add it to the map.

/// Constant setter
/// @param   {String} $name  - Name of constant to get
/// @param   {*}      $value - Constant value
/// @return  {*}             - Constant value
/// @require $CONSTANTS
/// @throw   'Constant `#{$name}` already defined.'
/// @output  Nothing

@mixin const($name, $value) {
  @if map-has-key($CONSTANTS, $name) {
    @error 'Constant `#{$name}` already defined.';
  }

  $CONSTANTS: map-merge($CONSTANTS, ($name: $value)) !global;
}

Our const function should be quite similar in length:

/// Constant getter
/// @param   {String} $name - Name of constant to get
/// @return  {*}            - Constant value
/// @require $CONSTANTS
/// @throw   'Unknown constant `#{$name}`.'

@function const($name) {
  @if not map-has-key($CONSTANTS, $name) {
    @error 'Unknown constant `#{$name}`.';
  }

  @return map-get($CONSTANTS, $name);
}

So we have our ‘getter’ and our ‘setter’. We are now ready to use our little system:

// Initializing new constants

@include const('MAX_Z_INDEX', 2147483647);
@include const('PI', 3.1415926535897932384626433832795028841971693993751);
@include const('E', 2.71828182845904523536028747135266249775724709369995);

// Using a constant;
// same API as before

.test {
  z-index: const('MAX_Z_INDEX');
}

Final thoughts

So you have three solutions:

  1. Name your constants in a specific way to distinguish them from variables.
  2. Use a constants map to store them all and build a little function to get them.
  3. Build ‘getters’ and ‘setters’ to have a simple yet powerful system.

As far as I’m concerned, I’d probably go with the first option. Snakerized uppercased variables. But, whatever floats your boat right? Feel free to play with the code on SassMeister.

Play with the simple version on SassMeister. Then go ahead and check out the the complete version, also on Sassmeister.