Lint rules
dta lint checks the token files in tokens.dir. A rule reports errors (exit code 2) or warnings (exit code 0, printed). dta push runs the same check before it changes Figma (--skip-lint skips it).
Always on
Section titled “Always on”| Rule | Severity | Finds |
|---|---|---|
no-dangling-aliases |
error | An alias whose target does not exist |
no-circular-aliases |
error | Aliases that point at each other in a circle |
no-default-mode-names |
error | Modes still called Mode 1, Mode 2 (Figma’s default) |
One finding of each, on the brand-ab-problems fixture (the brand-ab tokens with three flaws; it is the playground’s “Example with problems”, which shows the same findings in your browser):
| Brand(Alias).BrandB.json|| error Alias {Colors.Foundation.Sky.550} references non-existent token "Colors.Foundation.Sky.550" [no-dangling-aliases]|| (unknown file)|| error Circular alias chain: Brand(Alias).BrandA/Colors/Surface/Muted → Brand(Alias).BrandA/Colors/Text/Muted → Brand(Alias).BrandA/Colors/Surface/Muted [no-circular-aliases]|| Primitives.Mode 1.json|| error Mode name "Mode 1" is a Figma default — rename to something meaningful [no-default-mode-names]||| 3 errors found.|— Lint failed.Broken aliases fail the build
Section titled “Broken aliases fail the build”A dangling or a circular alias has no value, so dta build fails on the first two rules too: it prints the same message as dta lint (one error per alias and per cycle), writes no file at all and exits with 1. Files from an earlier build stay as they were, so the output folder is never half rebuilt. Fix the alias, then build again. On the same fixture:
x error [dangling-alias] Alias {Colors.Foundation.Sky.550} references non-existent token "Colors.Foundation.Sky.550". See site/src/content/docs/reference/lint-rules.mdx. (Brand(Alias).BrandB.json)|x error [circular-alias] Circular alias chain: Brand(Alias).BrandA/Colors/Surface/Muted → Brand(Alias).BrandA/Colors/Text/Muted → Brand(Alias).BrandA/Colors/Surface/Muted. See site/src/content/docs/reference/lint-rules.mdx. (Brand(Alias).BrandA.json)|| Nothing was written: fix the errors above and build again.|— Build failed.The playground’s Outputs view shows these errors instead of files. A default mode name does not stop the build. Its Graph view draws them where they are: BrandB’s dangling alias ends in a dashed “missing” row, BrandA’s circular pair gets two error arcs, and the default mode name sits on the primitives’ header; “Show in graph” on a finding leads there.
Configurable
Section titled “Configurable”Configured under lint.rules. A configured rule is a warning unless you set severity: 'error'; 'off' disables it.
lint: { rules: { 'semantic-must-alias': { severity: 'error', collections: ['Brand(Alias)', 'ScreenType'] }, 'naming-pattern': { severity: 'warn', patterns: { 'Brand(Alias)': '^[A-Z][A-Za-z ]*$' } }, 'color-contrast': { severity: 'warn', minRatio: 4.5, pairs: [ ['Colors.Text.Default', 'Colors.Surface.Default'], ['Colors.Text.On Primary', 'Colors.Brand.Primary'], ['Colors.Text.Muted', 'Colors.Surface.Muted'], ], }, 'no-duplicate-values': { severity: 'warn' }, },},semantic-must-alias
Section titled “semantic-must-alias”Every token in the listed collections must be an alias. Catches a semantic or brand token that holds a raw value and would no longer follow the palette.
Token has a raw value "#ffffff" but should be an alias in collection "Brand(Alias)"naming-pattern
Section titled “naming-pattern”patterns maps a collection name to a regular expression; every segment of every token path in that collection must match it. An invalid expression is a config error.
warn Token path segment "overlay" does not match pattern /^[A-Z][A-Za-z ]*$/ for collection "Brand(Alias)" [naming-pattern]naming-case
Section titled “naming-case”collections maps a collection name to a case style: kebab-case, camelCase, PascalCase or snake_case. Every segment of every token path in that collection must be in it; a scale step (700, 0-5, 1.5, 2xl) fits every style, and $root is not checked. A token in several modes is reported once. For other conventions, use naming-pattern.
'naming-case': { collections: { Primitives: 'camelCase' } },warn Token path segment "Colors" is not camelCase (collection "Primitives") [naming-case]naming-depth
Section titled “naming-depth”collections maps a collection name to { min, max } (one of them is enough): how many segments a token path has, without $root. Colors.Brand.Primary has 3.
'naming-depth': { collections: { 'Brand(Alias)': { max: 2 } } },warn Token path has 3 segments, expected at most 2 (collection "Brand(Alias)") [naming-depth]no-spaces
Section titled “no-spaces”Reports spaces in collection names and token path segments. CSS variable names replace them, but other tools and future outputs may not.
warn Token path segment "Primary Dark" contains a space [no-spaces]collection-pattern
Section titled “collection-pattern”pattern is a regular expression every collection name must match; it is not anchored, so write ^…$ to match the whole name. A collection’s problem is reported on its first mode file. An invalid expression is a config error.
'collection-pattern': { pattern: '^[A-Za-z]+$' },warn Collection name "Brand(Alias)" does not match pattern /^[A-Za-z]+$/ [collection-pattern]color-contrast
Section titled “color-contrast”For each [foreground, background] pair, the WCAG contrast ratio must reach minRatio (default 4.5, level AA). Token names are written with dots or slashes. Colours in any DTCG colour space are converted to sRGB first; one outside sRGB is checked clipped to it, with a contrast-clipped warning that names the sRGB colour used.
Each pair is checked in every mode it can differ in: both tokens are resolved through their aliases, and every multi-mode collection either alias chain passes through (each brand, light and dark, …) adds its modes. A brand text colour over a single-mode background is checked once per brand; a pair of primitives once, without a mode. The finding names the mode and sits under the file of that mode:
| Brand(Alias).BrandA.json|| warn Contrast ratio 4.39:1 between "Colors.Text.Muted" (#6b7280) and "Colors.Surface.Muted" (#f3f4f6) is below 4.5:1 in BrandA [color-contrast]|| Brand(Alias).BrandB.json|| warn Contrast ratio 4.39:1 between "Colors.Text.Muted" (#6b7280) and "Colors.Surface.Muted" (#f3f4f6) is below 4.5:1 in BrandB [color-contrast]When several multi-mode collections are involved, the finding lists one mode of each (in BrandA · Dark). A pair that cannot be read (a name that is not a token, an alias that breaks in one mode, a value that is not a colour) is skipped where it fails, with a warning; the other modes are still checked. With a pair whose foreground does not exist:
! warning [contrast-unresolved] color-contrast: "Colors.Text.Link" does not resolve: There is no token "Colors.Text.Link". Pair "Colors.Text.Link" / "Colors.Surface.Default" skipped. See site/src/content/docs/reference/lint-rules.mdx.The playground runs the default rules only, so it shows no contrast findings.
no-duplicate-values
Section titled “no-duplicate-values”Reports raw values that appear more than once, across all collections and modes: a hint that one of them could be an alias.
warn Duplicate value "8" — also defined in: Primitives.Value/Dimensions/spacing/2 [no-duplicate-values]dta lint --fix is accepted but has no fixes yet; it says so and lints as usual.