Skip to content

Linting

Dispersa includes a plugin-based linting system to validate design tokens against semantic rules. Linting helps maintain token quality by enforcing naming conventions, requiring descriptions, detecting deprecated usage, and more.

Design tokens are the foundation of your design system. Linting ensures:

  • Consistency — Enforce naming conventions across all tokens
  • Documentation — Require descriptions for better discoverability
  • Maintainability — Detect deprecated tokens
  • Quality — Find duplicate values and path structure issues

Run linting independently of the build:

import { lint } from 'dispersa'
import { dispersaPlugin } from 'dispersa/lint'
const result = await lint({
resolver: './tokens.resolver.json',
plugins: { dispersa: dispersaPlugin },
rules: {
'dispersa/require-description': 'warn',
'dispersa/case-check': ['error', { format: 'kebab-case' }],
},
})
console.log(`Found ${result.errorCount} errors, ${result.warningCount} warnings`)

Enable linting in your build configuration:

import { build, css } from 'dispersa'
import { dispersaPlugin } from 'dispersa/lint'
const result = await build({
resolver: './tokens.resolver.json',
buildPath: './dist',
lint: {
enabled: true,
plugins: { dispersa: dispersaPlugin },
rules: {
'dispersa/case-check': ['error', { format: 'kebab-case' }],
},
},
outputs: [css({ name: 'css', file: 'tokens.css', preset: 'bundle' })],
})

When failOnError is true (default), lint errors will cause the build to fail.

Dispersa ships with three predefined lint configurations:

ConfigDescription
minimalOnly no-deprecated-usage as warning
recommendedBalanced rules for most projects
strictAll rules enabled as errors (or warnings)
import { lint } from 'dispersa'
import { recommendedConfig } from 'dispersa/lint'
const result = await lint({
resolver: './tokens.resolver.json',
...recommendedConfig,
})

Minimal:

  • dispersa/no-deprecated-usage: warn

Recommended:

  • dispersa/require-description: warn
  • dispersa/case-check: error (kebab-case)
  • dispersa/no-deprecated-usage: warn

Strict:

  • dispersa/require-description: error
  • dispersa/case-check: error (kebab-case)
  • dispersa/no-deprecated-usage: error
  • dispersa/no-duplicate-values: error

Linting runs in the pipeline after alias resolution but before filters and transforms:

1. Resolve → 2. Preprocess → 3. Parse → ... → 9. Lint → 10. Filter → 11. Transform → 12. Render

This means linting operates on fully resolved tokens with all references expanded.

Dispersa includes five built-in rules:

RuleDescription
require-descriptionEnforce $description on tokens
case-checkEnforce case format (kebab-case, camelCase, etc.)
no-deprecated-usageDetect references to $deprecated tokens
no-duplicate-valuesFind tokens with identical values
path-schemaValidate token path structure with patterns

Extend linting with your own rules and plugins:

import { createRule } from 'dispersa/lint'
const noPrimaryColors = createRule<'NO_PRIMARY'>({
meta: {
name: 'no-primary-colors',
description: 'Disallow "primary" in token names',
messages: {
NO_PRIMARY: "Token '{{name}}' contains 'primary'",
},
},
create({ tokens, report }) {
for (const token of Object.values(tokens)) {
if (token.name.includes('primary')) {
report({ token, messageId: 'NO_PRIMARY', data: { name: token.name } })
}
}
},
})

Lint results can be formatted in different ways:

import { formatLintJson, formatLintStylish, formatLintCompact } from 'dispersa/lint'
console.log(formatLintStylish(result))
console.log(formatLintJson(result))
console.log(formatLintCompact(result))

Available formats:

  • stylish - Human-readable format (default)
  • json - Machine-parseable JSON
  • compact - Single-line format for CI systems