My new ebook  Design Systems for Developers  is here! Start reading

Design

Linting Design Tokens With Stylelint

Last Updated: 2021-03-15

Table of Contents

1 | Introduction to Design Tokens
2 | Managing and Exporting Design Tokens With Style Dictionary
3 | Exporting Design Tokens From Figma With Style Dictionary
4 | Consuming Design Tokens From Style Dictionary Across Platform-Specific Applications
5 | Generating Design Token Theme Shades With Style Dictionary
6 | Documenting Design Tokens With Docusaurus
7 | Integrating Design Tokens With Tailwind
8 | Transferring High Fidelity From a Design File to Style Dictionary
9 | Scoring Design Tokens Adoption With OCLIF and PostCSS
10 | Bootstrap UI Components With Design Tokens And Headless UI
11 | Linting Design Tokens With Stylelint
12 | Stitching Styles to a Headless UI Using Design Tokens and Twind

What You’re Getting Into

In the previous article, I wrote about my process to create a CLI that generates a “scorecard” rating an application’s adoption of design tokens.

This tooling is useful to make sure that applications within a company are actually using the design tokens that are available to them.

A scorecard is a nice tool for acute, or short-term, testing. It would work well when applications in a company adopt new design tokens following an initiative.

It can be useful for chronic, long-term testing (i.e. manual testing that a developer and/or QA tester does before releasing new code).

However, catching the implementation of unofficial design tokens during local development is a better experience for the developer and more likely to prevent the release of unofficial design specifications.

Just as an EsLint plugin works well to catch improper syntax before a manual code review, so too, linting to catch the implementation of unofficial design tokens can lighten the load of manual testing.

A tool called Stylelint offers the ability to lint your CSS.

What if there was a Stylelint plugin that could detect the implementation of unofficial design tokens/specs when given the official set of tokens?

In this brief article, I’ll show how I ventured to build such a plugin.

The Process

To start, I read through the developer’s guide on writing Stylelint plugins.

Then, I looked through the source code of existing plugins that I found on the awesome-stylelint repo.

This helped generate the boilerplate code for a Stylelint plugin.

Ultimately, the Stylelint plugin consists of rules that can be enabled by a user.

Each rule is a function that takes in the user’s options for a rule which they configure. You have to validate these options in the code using a utils.validateOptions util that the Stylelint package provides for you. Also, you should document these options in your documentation.

I wrote the function for an official-specs rule that expects the user to configure/provide a set of official design tokens/specs:

import scorecard from '@tempera/postcss-scorecard';
import { utils } from 'stylelint';

import { namespace, validateSpecs } from '../utils';

export default {
  'official-specs': function rule(specs = {}) {
    return (root, result) => {
      const validOptions = utils.validateOptions(result, ruleName, {
        actual: specs,
        // validateSpecs is a function
        // that validates that
        // the provided specs is a flat
        // object consisting of key-value pairs
        possible: [validateSpecs]
      });

      if (!validOptions) {
        return null;
      }
    };
};

Next, I used the @tempera/postscss-scorecard plugin which I created for a “scorecard” reporting tool as an API.

This plugin exposes hooks into the processing of CSS to do something when a valid or invalid specification is found:

const scorecard = require("@tempera/postcss-scorecard");

const specs = require("./tokens");

await postcss()
  .use(
    scorecard({
      onInvalid: (score) => {
        // do something when CSS property is not an official spec
      },
      onValid: (score) => {
        // do something when CSS property is an official spec
      },
      onFinished: () => {
        // do something after validation finishes
      },
      specs, // the official design tokens
    })
  )
  .process(css, { from: undefined });

It also provides a score context which contains the prop, value, as well as the predicted nearestValue (the closest official value to the unofficial value).

Using this as an API in the Stylelint rule, messages can be reported when a CSS declaration is using an invalid/unofficial value by using the utils.report method.

The utils.report method expects a rule message that can be generated using the utils.ruleMessages method.

I exposed a rule message that receives the actual/unofficial spec and the nearest official spec from the score context.

import scorecard from '@tempera/postcss-scorecard';
import { utils } from 'stylelint';

import { namespace, validateSpecs } from '../utils';

export const ruleName = namespace('official-specs');
export const messages = utils.ruleMessages(ruleName, {
  'official-specs': ({ spec, nearestSpec }) => {
    const prefix = `An unofficial spec was detected: "${spec}"`;
    if (!nearestSpec) {
      return prefix;
    }

    return `${prefix}. The nearest official spec is ${nearestSpec}`;
  }
});

export default {
  'official-specs': function rule(specs = {}) {
    return (root, result) => {
    const validOptions = utils.validateOptions(result, ruleName, {
      actual: specs,
      possible: [validateSpecs]
    });

    if (!validOptions) {
      return null;
    }

    const { Once } = scorecard({
      onInvalid: (score) => {
        const spec = score.value;
        const nearestSpec = score.nearestValue;
        const node = score.context;

        utils.report({ message: messages['official-specs']({ spec, nearestSpec }), node, result, ruleName })
      },
      specs,
    });
    Once(root);
  };
};

Just like that, we can provide linting around the usage of unofficial design specifications in CSS:

linting your design tokens with StyleLint

🎉 Cool!

Final plugin: https://github.com/michaelmang/tempera/tree/master/packages/stylelint

Conclusion

I hope this experimental plugin helps to highlight the potential to enforce the usage design tokens.

I hope it also helps to stimulate creativity for future tooling to unleash the power and success of adopting design tokens.

Design Systems for Developers

Read my latest ebook on how to use design tokens to code production-ready design system assets.

Design Systems for Developers - Use Design Tokens To Launch Design Systems Into Production | Product Hunt

Michael Mangialardi is a software developer specializing in UI development with React and fluent in UI/UX design. As a survivor of impostor syndrome, he loves to make learning technical skills digestible and practical. Formerly, he published articles, ebooks, and coding challenges under his brand "Coding Artist." Today, he looks forward to using his mature experience to give back to the web development community. He lives in beautiful, historic Virginia with his wife.