Creating Utility Classes with Design Tokens using Sass

At the start of a Design System, there’s a chance that things can move pretty fast.

There may be more than one or two people working on creating components (if you’re lucky). You might want to prototype things quickly with “throwaway code”. When it matures there may be certain use cases where you need a slight adjustment here and there for a particular component to work, or you may not be able to include the components you need as you would hope to.

After creating a set of Design Tokens for my clients Design System I looked at how best we can quickly create some additional utility classes for the various “exceptions to the rule” mentioned above. We needed something that would work automatically, programatically, and with as little human interaction as possible once it was set up.

Using Sass in 2019.

The project was already using Sass (a decision that was made after some research (that might be another article, whenever I find the time)) and I have previously used loops and interpolation so I knew we could create these classes with a pre-processor language that is already in place without resorting to something else, an extra dependency.

As we’re using Sass already we are outputting the Design Tokens using Theo to generate files of Sass variables and maps that are needed.

To create our utility classes we would need a Sass @mixin that would use the class selector to inform the initial naming of the created rule sets. An @each rule to loop through the Sass maps, and an additional $prefix variable to allow us to create classnames.

Design Tokens and Maps.

Theo takes your Design Tokens that can be authored in a .yml or .json format and then can convert them into a Sass map, amongst a lot of other formats.

An example Sass map that we are going to use could look something like this:

$ds-color-map: (
  green-100: #c8e6c9,
  green-200: #a5d6a7,
  green-300: #81c784,
  green-400: #66bb6a,
  green-500: #4caf50,
  green-600: #43a047,
  green-700: #388e3c,
  green-800: #2e7d32,
  green-900: #1b5e20
);

Creating the @each loop.

In the above map with have the key/value pair of the name of the colour and the hex code that represents it. To create an @each rule we would need to write something like:

.u-color {
  @each $name, $hex in $ds-colors-map {
    #{$name}: $hex;
  }
}

Using the @each rule we go through key/value pairs of $ds-colors-map and get the key: $name and the value: $hex. We then use these variables to create the declaration inside the rule. This @each rule makes use of interpolation to turn the $name variable into property inside the declaration.

But this would only create a list of CSS colour rules within the ruleset like:

.u-color {
  green-100: #c8e6c9;
  green-200: #a5d6a7;
  green-300: #81c784;
  green-400: #66bb6a;
  green-500: #4caf50;
  green-600: #43a047;
  green-700: #388e3c;
  green-800: #2e7d32;
  green-900: #1b5e20;
}

What we want is to generate individual class selectors to hold each key/value pair.

Making the @mixin.

To get Sass to generate the utility classes for our Design Tokens using the Sass maps. We need to create a @mixin and slightly change the @each rule. Before we look into that we should think about the structure of the class selectors we are wanting to create. Using colour as the example I want to create a set of CSS rules that look like this:

.u-color-text-green-100 {
  color: #c8e6c9;
}

With a @mixin we need to be able to create part of the selector of a rule so that it include --. We also need the correct key: $name as part of the selector, and value: $hex as the declarations value.

We can make use of Sass’ interpolation to help us with this, so that we can generate the correct naming conventions for the class selectors. Inside the @mixin we can add our working @each rule like this:

@mixin color-utilities {
  @each $name, $hex in $ds-colors-map {
    &-#{$name} {
      color: $hex;
    }
  }
}

When called this @mixin will go through the $ds-colors-map we have and generate a class selector using the parent selector name using the &, we add the -- to then separate the parent selector from the key: $name of the pair in the map and then print the value: $hex as the value in the declaration. When invoked in a class like:

.u-color-text {
  @include color-utilties();
}

We get an compiled output like:

.u-color-text-green-100 {
  color: #c8e6c9;
}
.u-color-text-green-200 {
  color: #a5d6a7;
}
.u-color-text-green-300 {
  color: #81c784;
}
.u-color-text-green-400 {
  color: #66bb6a;
}
.u-color-text-green-500 {
  color: #4caf50;
}
.u-color-text-green-600 {
  color: #43a047;
}
.u-color-text-green-700 {
  color: #388e3c;
}
.u-color-text-green-800 {
  color: #2e7d32;
}
.u-color-text-green-900 {
  color: #1b5e20;
}

Extending the @mixin

That works great but it’s not very flexible. I want to generate rules for each way colour can be used in CSS. Not only for text but for borders and backgrounds like this:

// text colours
.u-color-text-green-100 {
  color: #c8e6c9;
}
// background colours
.u-color-background-green-100 {
  background-color: #c8e6c9;
}
// border colours
.u-color-border-green-100 {
  border-color: #c8e6c9;
}

To do this we can modify the @mixin we have created to allow us to set the property that would be inside the declaration. We need to add an argument to our @mixin that will set this property and then use that argument where needed. As it’s the property of the declaration that is needs to be changed we can add a it to our @mixin along with a default so the first line is like:

@mixin color-utilities($property: 'color') {

We then need to use that $property argument as our property in the declaration using interpolation, so that the whole @mixin looks like this:

@mixin color-utilities($property: 'color') {

  @each $name, $hex in $vf-colors-map {
    
    &-#{$name} {
      #{$property}: $hex;
    }
  }
}

Doing this allows us to include the @mixin in the 3 types of colour classes we want to create:

.u-color-text {
  @include color-modifiers();
}
.u-color-background {
  @include color-modifiers(background-color);
}
.u-color-border {
  @include color-modifiers(border-color);
}

Which will generate the a list of rules like I have mentioned above.

.u-color-text-green-100 {
  color: #c8e6c9;
}

.u-color-background-green-100 {
  background-color: #c8e6c9;
}

.u-color-border-green-100 {
  border-color: #c8e6c9;
}

What next?

Now we can create an individual rule for each of the colours that our Design Tokens has for text, background and border but Design Tokens are more than colour. With a few modifications to the @mixin created here we can then do this with any typographic styles and spacing values we have.

I created a CodePen that includes the @mixins that can be used to create those additional utility classes.

See the Pen Utility Class Sass Generators by Stuart Robson (@sturobson) on CodePen.