terrazzoapp / terrazzo

Use DTCG tokens in CI and code

Home Page:https://terrazzo.app

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

RFC Token Linting

drwpow opened this issue · comments

RFC: Token Linting

This week Cobalt added its first linter, a contrast checker (#218) 🎉! This is an exciting development for this project and design ops in general.

Concept

The idea came from a problem I’ve faced as a frontend/design engineer for over a decade: accessibility testing. Designers and devs have manual a11y tools (e.g. Polychrom for Figma and Firefox’s built-in tools), but manual tools are only for designing, and can’t catch regressions. Further, Figma prototypes usually aren’t the “source of truth” that matters anyway.

Automated a11y tooling is more reliable (e.g. Playwright) but is slow (sometimes several minutes per-test) and expensive. And they are usually run on prod, so not only is the user disrupted; the fix has to be re-slotted in amidst planned work and disrupts the flow (i.e. it’s “an outage”). On top of all that, the tests themselves are time-intensive to set up, and may not catch all combinations.

However, linting can be every bit as accurate and effective as the previous two methods, but is cheapest and fastest, and sitting in CI it doesn’t disrupt the development cycle. It only hasn’t been a thing before because teams haven’t had their design systems codified in statically-analyzable DTCG tokens.json, which contains all the data on which colors can/can’t be used together. And with newer a11y testing methods like APCA that take typography into account, we have all the info right there in tokens.json.

Taking a step back, it’s also worth asking “is static analysis (linting) the right tool for the job here?” And at least for contrast, the answer is yes. All the current respected a11y tests are all just static analysis; the only reason they get run in production is because we had a missing piece before, which was all the design tokens collected in a JSON file.

None of what I’m describing is new, but the exciting development is pairing linting + design tokens. This is a relatively new area I think Cobalt seems poised to solve.

RFC

Down to business: what does this look like? See the lint-a11y plugin as the proof-of-concept:

// tokens.config.js
import a11y from "@cobalt-ui/lint-a11y";

/** @type {import("@cobalt-ui/core").Config} */
export default {
  tokens: "./tokens.json",
  outDir: "./tokens/",
  plugins: [a11y()],
  lint: {
    rules: {
      'foo/bar': 'error',
      'bar/baz': 'warn',
      'baz/bat': 'off',
    },
  },
};

The API is modeled heavily off of ESLint (even internally), and works much the same way. The basic workflow is: a) plugins register rules (with defaults), b) users configure (or disable) rules, and b) those lint checks run on every build.

Currently the checks run on co build and co lint, but can be disabled with lint.build.enabled: false.

So open questions are:

  1. Does the ESLint model work for design token linting, or are there other APIs that work better?
  2. (Keeping in mind 2.0 will support AST improvements) What’s missing from this being a proper linter?
  3. How are all the ways Design Tokens could be linted?

Still open to more ideas! But with the completion of #238 and the new linting docs, this is at least a start for people to start getting ideas & giving feedback. Will accept feedback as it comes in, rather than trying to keep it all in this ticket