{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://dta.martinprinz.dev/schemas/dta.config.schema.json",
  "type": "object",
  "properties": {
    "$schema": {
      "description": "URL of this JSON Schema, for editor support in `dta.config.json`. Ignored by DTA.",
      "type": "string"
    },
    "figma": {
      "default": {},
      "type": "object",
      "properties": {
        "fileKey": {
          "description": "Key of the Figma file (from its URL: figma.com/file/<key>/…).",
          "type": "string",
          "minLength": 1
        },
        "personalAccessToken": {
          "description": "Figma personal access token. Prefer the FIGMA_PERSONAL_ACCESS_TOKEN environment variable.",
          "type": "string",
          "minLength": 1
        },
        "source": {
          "description": "How tokens are sourced: `api` (REST API, Enterprise plans only) or `file` (an exported file, `dta pull --from-file`).",
          "type": "string",
          "enum": [
            "api",
            "file"
          ]
        }
      },
      "additionalProperties": false,
      "description": "Where `dta pull` and `dta push` find the Figma file."
    },
    "collections": {
      "description": "Collections to include (default: all of them).",
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1
      }
    },
    "layers": {
      "description": "Deprecated: use `collections`. Kept so v1 configs still load.",
      "type": "object",
      "properties": {
        "primitives": {
          "description": "Deprecated.",
          "type": "string",
          "minLength": 1
        },
        "brand": {
          "description": "Deprecated.",
          "type": "string",
          "minLength": 1
        },
        "dimension": {
          "description": "Deprecated.",
          "type": "string",
          "minLength": 1
        }
      },
      "additionalProperties": false
    },
    "brands": {
      "description": "Modes to build into one theme file each (brands, or themes such as Light and Dark).",
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1
      }
    },
    "breakpoints": {
      "type": "object",
      "properties": {
        "collection": {
          "type": "string",
          "minLength": 1,
          "description": "The collection whose modes are breakpoints, e.g. `ScreenType`."
        },
        "default": {
          "type": "string",
          "minLength": 1,
          "description": "The mode written on `:root` without a media query: the smallest screen for mobile-first queries (`min-width`), the largest for desktop-first (`max-width`)."
        },
        "modes": {
          "type": "object",
          "propertyNames": {
            "type": "string",
            "minLength": 1
          },
          "additionalProperties": {
            "type": "string",
            "minLength": 1
          },
          "description": "Every other mode and its media query, without `@media`: `{ Tablet: '(min-width: 768px)', Desktop: '(min-width: 1024px)' }`. The blocks are written in this order, so a later query wins where several match."
        }
      },
      "required": [
        "collection",
        "default",
        "modes"
      ],
      "additionalProperties": false,
      "description": "With this section, `dta build` writes the tokens of a breakpoint collection to `variables.css`: the default mode on `:root`, every other mode inside its media query, aliases as `var()` references. Without it, such a collection only resolves aliases."
    },
    "tokens": {
      "description": "Where the token files live.",
      "default": {},
      "type": "object",
      "properties": {
        "dir": {
          "default": "tokens",
          "description": "Folder of the token files.",
          "type": "string",
          "minLength": 1
        },
        "stripEmojis": {
          "default": false,
          "description": "Strip emoji characters from collection and mode names in file names.",
          "type": "boolean"
        },
        "resolver": {
          "description": "The DTCG resolver document (`*.resolver.json`) in the tokens folder to read; default: the only one there.",
          "type": "string",
          "minLength": 1
        }
      },
      "additionalProperties": false
    },
    "modifiers": {
      "description": "For a DTCG resolver document: the context each modifier is read in by default, e.g. `{ theme: \"light\" }`.",
      "type": "object",
      "propertyNames": {
        "type": "string",
        "minLength": 1
      },
      "additionalProperties": {
        "type": "string",
        "minLength": 1
      }
    },
    "outputs": {
      "type": "object",
      "properties": {
        "formats": {
          "description": "Which outputs `dta build` writes: the web outputs `css`, `tailwind-v4`, `tailwind-v3`, `js` (the default: all four) and the native ones `ios-swift`, `android-xml`, `android-compose` (only when listed). `dta init` writes it from your answer to \"Which formats should dta generate?\".",
          "minItems": 1,
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "css",
              "tailwind-v4",
              "tailwind-v3",
              "js",
              "ios-swift",
              "android-xml",
              "android-compose"
            ]
          }
        },
        "css": {
          "description": "CSS custom properties: `variables.css`, `base.css` and one theme file per brand (default folder `output/css`).",
          "type": "object",
          "properties": {
            "outDir": {
              "type": "string",
              "minLength": 1,
              "description": "Folder the files are written to, relative to the project."
            },
            "prefix": {
              "description": "Removed; rejected. It never had an effect: delete the line.",
              "not": {}
            }
          },
          "required": [
            "outDir"
          ],
          "additionalProperties": false
        },
        "tailwind": {
          "description": "Tailwind theme files (default folder `output/tailwind`).",
          "type": "object",
          "properties": {
            "outDir": {
              "type": "string",
              "minLength": 1,
              "description": "Folder the files are written to, relative to the project."
            },
            "version": {
              "description": "Removed; rejected. It never had an effect: `formats` chooses the Tailwind versions. Delete the line.",
              "not": {}
            }
          },
          "required": [
            "outDir"
          ],
          "additionalProperties": false
        },
        "ios": {
          "description": "Swift for SwiftUI (`ios-swift` in `formats`): `DesignTokens.swift` and one `BrandTokens+<Brand>.swift` per brand (default folder `output/ios`).",
          "type": "object",
          "properties": {
            "outDir": {
              "type": "string",
              "minLength": 1,
              "description": "Folder the files are written to, relative to the project."
            },
            "lang": {
              "description": "Removed; rejected. `formats` chooses the native outputs. Delete the line.",
              "not": {}
            }
          },
          "additionalProperties": false
        },
        "android": {
          "description": "Android (`android-xml`, `android-compose` in `formats`): resource XML in `main/values/` and `<brand>/values/`, Compose in `compose/DesignTokens.kt` (default folder `output/android`).",
          "type": "object",
          "properties": {
            "outDir": {
              "type": "string",
              "minLength": 1,
              "description": "Folder the files are written to, relative to the project."
            },
            "package": {
              "description": "The Kotlin package of `DesignTokens.kt` (default `designtokens`).",
              "type": "string",
              "pattern": "^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*$"
            },
            "lang": {
              "description": "Removed; rejected. `formats` chooses the native outputs. Delete the line.",
              "not": {}
            }
          },
          "additionalProperties": false
        },
        "docs": {
          "description": "Removed in v2; rejected.",
          "not": {}
        }
      },
      "additionalProperties": false,
      "description": "What `dta build` writes and where. `formats` chooses the outputs (all by default); the other keys move them."
    },
    "lint": {
      "description": "`dta lint` rules (also run by `dta push`).",
      "type": "object",
      "properties": {
        "rules": {
          "description": "Configurable rules by name, with their options. A configured rule is a warning unless its severity says otherwise.",
          "type": "object",
          "properties": {
            "semantic-must-alias": {
              "type": "object",
              "properties": {
                "severity": {
                  "type": "string",
                  "enum": [
                    "error",
                    "warn",
                    "off"
                  ],
                  "description": "`error` fails `dta lint`, `warn` (the default) reports, `off` disables the rule."
                },
                "collections": {
                  "description": "Collections whose tokens must all be aliases.",
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              },
              "additionalProperties": false,
              "description": "Fails when a token in the listed collections holds a raw value instead of an alias."
            },
            "naming-pattern": {
              "type": "object",
              "properties": {
                "severity": {
                  "type": "string",
                  "enum": [
                    "error",
                    "warn",
                    "off"
                  ],
                  "description": "`error` fails `dta lint`, `warn` (the default) reports, `off` disables the rule."
                },
                "patterns": {
                  "description": "Collection name → regular expression every path segment must match.",
                  "type": "object",
                  "propertyNames": {
                    "type": "string"
                  },
                  "additionalProperties": {
                    "type": "string"
                  }
                }
              },
              "additionalProperties": false,
              "description": "Fails when a path segment does not match the pattern of its collection."
            },
            "naming-case": {
              "type": "object",
              "properties": {
                "severity": {
                  "type": "string",
                  "enum": [
                    "error",
                    "warn",
                    "off"
                  ],
                  "description": "`error` fails `dta lint`, `warn` (the default) reports, `off` disables the rule."
                },
                "collections": {
                  "description": "Collection name → the case every path segment uses: `kebab-case`, `camelCase`, `PascalCase` or `snake_case`. Scale steps (`700`, `0-5`, `1.5`, `2xl`) fit every style; `$root` is not checked.",
                  "type": "object",
                  "propertyNames": {
                    "type": "string"
                  },
                  "additionalProperties": {
                    "type": "string",
                    "enum": [
                      "kebab-case",
                      "camelCase",
                      "PascalCase",
                      "snake_case"
                    ]
                  }
                }
              },
              "additionalProperties": false,
              "description": "Fails when a path segment is not in the case style of its collection (TICKET-019)."
            },
            "naming-depth": {
              "type": "object",
              "properties": {
                "severity": {
                  "type": "string",
                  "enum": [
                    "error",
                    "warn",
                    "off"
                  ],
                  "description": "`error` fails `dta lint`, `warn` (the default) reports, `off` disables the rule."
                },
                "collections": {
                  "description": "Collection name → `{ min, max }`: how many path segments a token name has.",
                  "type": "object",
                  "propertyNames": {
                    "type": "string"
                  },
                  "additionalProperties": {
                    "type": "object",
                    "properties": {
                      "min": {
                        "type": "integer",
                        "exclusiveMinimum": 0,
                        "maximum": 9007199254740991
                      },
                      "max": {
                        "type": "integer",
                        "exclusiveMinimum": 0,
                        "maximum": 9007199254740991
                      }
                    },
                    "additionalProperties": false
                  }
                }
              },
              "additionalProperties": false,
              "description": "Fails when a token path has fewer or more segments than its collection allows (TICKET-019)."
            },
            "no-spaces": {
              "type": "object",
              "properties": {
                "severity": {
                  "type": "string",
                  "enum": [
                    "error",
                    "warn",
                    "off"
                  ],
                  "description": "`error` fails `dta lint`, `warn` (the default) reports, `off` disables the rule."
                }
              },
              "additionalProperties": false,
              "description": "Reports spaces in collection names and token path segments; some outputs and tools handle them badly (TICKET-019)."
            },
            "collection-pattern": {
              "type": "object",
              "properties": {
                "severity": {
                  "type": "string",
                  "enum": [
                    "error",
                    "warn",
                    "off"
                  ],
                  "description": "`error` fails `dta lint`, `warn` (the default) reports, `off` disables the rule."
                },
                "pattern": {
                  "description": "Regular expression every collection name must match (not anchored: write `^…$` for the whole name).",
                  "type": "string"
                }
              },
              "additionalProperties": false,
              "description": "Fails when a collection name does not match the pattern (TICKET-019)."
            },
            "color-contrast": {
              "type": "object",
              "properties": {
                "severity": {
                  "type": "string",
                  "enum": [
                    "error",
                    "warn",
                    "off"
                  ],
                  "description": "`error` fails `dta lint`, `warn` (the default) reports, `off` disables the rule."
                },
                "minRatio": {
                  "description": "Minimum WCAG contrast ratio (default 4.5, level AA).",
                  "type": "number",
                  "exclusiveMinimum": 0
                },
                "pairs": {
                  "description": "`[foreground, background]` token names.",
                  "type": "array",
                  "items": {
                    "type": "array",
                    "prefixItems": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "string"
                      }
                    ],
                    "items": false,
                    "minItems": 2,
                    "maxItems": 2
                  }
                }
              },
              "additionalProperties": false,
              "description": "Checks the contrast of text and background pairs, per mode, through aliases."
            },
            "no-duplicate-values": {
              "type": "object",
              "properties": {
                "severity": {
                  "type": "string",
                  "enum": [
                    "error",
                    "warn",
                    "off"
                  ],
                  "description": "`error` fails `dta lint`, `warn` (the default) reports, `off` disables the rule."
                }
              },
              "additionalProperties": false,
              "description": "Reports raw values that appear more than once, across all collections and modes."
            }
          },
          "additionalProperties": false
        }
      },
      "additionalProperties": false
    },
    "diff": {
      "description": "What `dta diff` builds and renders: the project build and its stories.",
      "type": "object",
      "properties": {
        "setup": {
          "description": "Optional command that prepares the fresh checkout of the base, run in its repo root. Without it, dta diff reuses your installed packages when the lockfile did not change, and otherwise runs the lockfile's clean install (npm ci, pnpm install --frozen-lockfile, …).",
          "type": "string",
          "minLength": 1
        },
        "build": {
          "description": "Command that builds the static output, run at the base and the head.",
          "type": "string",
          "minLength": 1
        },
        "outDir": {
          "type": "string",
          "minLength": 1,
          "description": "Folder of the static output, relative to the project."
        },
        "stories": {
          "default": {
            "type": "static-site",
            "manifest": "stories/manifest.json"
          },
          "description": "Where the stories come from: a list of pages (`[\"/\", \"/pricing\"]`), a dta.stories/1 manifest in the static output (`static-site`, the default) or the index of a static Storybook build (`storybook`).",
          "anyOf": [
            {
              "minItems": 1,
              "type": "array",
              "items": {
                "type": "string",
                "format": "starts_with",
                "pattern": "^\\/.*",
                "description": "A page of the static output, e.g. \"/\" or \"/pricing\"."
              },
              "description": "Pages of the static output, rendered as they are: no manifest needed."
            },
            {
              "type": "object",
              "properties": {
                "type": {
                  "default": "static-site",
                  "type": "string",
                  "const": "static-site"
                },
                "manifest": {
                  "default": "stories/manifest.json",
                  "description": "Path of the dta.stories/1 manifest inside `outDir`.",
                  "type": "string",
                  "minLength": 1
                }
              },
              "additionalProperties": false
            },
            {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "const": "storybook"
                },
                "index": {
                  "default": "index.json",
                  "description": "Storybook's index inside `outDir` (the `storybook build` output).",
                  "type": "string",
                  "minLength": 1
                },
                "excludeTags": {
                  "default": [
                    "skip-dta-diff"
                  ],
                  "description": "Stories with one of these tags are not rendered.",
                  "type": "array",
                  "items": {
                    "type": "string",
                    "minLength": 1
                  }
                },
                "exclude": {
                  "default": [],
                  "description": "Regular expressions; stories whose id matches one are not rendered.",
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              },
              "required": [
                "type"
              ],
              "additionalProperties": false
            }
          ]
        },
        "viewports": {
          "default": [
            1280
          ],
          "description": "Viewport widths for stories that do not suggest their own.",
          "minItems": 1,
          "type": "array",
          "items": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 8000
          }
        },
        "threshold": {
          "default": 0,
          "description": "Changed-pixel ratio (0–1) a story may differ by before it counts as changed.",
          "type": "number",
          "minimum": 0,
          "maximum": 1
        },
        "colorThreshold": {
          "default": 0,
          "description": "Per-pixel colour tolerance (0–1); 0 sees every colour change.",
          "type": "number",
          "minimum": 0,
          "maximum": 1
        },
        "thresholds": {
          "description": "Review thresholds by cause, as changed-pixel ratios (0–1).",
          "default": {},
          "type": "object",
          "properties": {
            "explained": {
              "default": 0,
              "description": "A story explained by token changes needs a review above this ratio.",
              "type": "number",
              "minimum": 0,
              "maximum": 1
            },
            "unexplained": {
              "default": 0,
              "description": "A story no token change explains fails above this ratio.",
              "type": "number",
              "minimum": 0,
              "maximum": 1
            }
          },
          "additionalProperties": false
        },
        "publish": {
          "description": "Publish hook: a command run on the report folder (path as argument and DTA_REPORT_DIR) that prints the public base URL as its last stdout line, or `{ command, url }` where `url` is a JSON path (`$.deployment.url`) or a regular expression to find it. `--publish` overrides it.",
          "anyOf": [
            {
              "type": "string",
              "minLength": 1
            },
            {
              "type": "object",
              "properties": {
                "command": {
                  "type": "string",
                  "minLength": 1,
                  "description": "The publish command."
                },
                "url": {
                  "description": "JSON path or regular expression that finds the URL in its output.",
                  "type": "string",
                  "minLength": 1
                }
              },
              "required": [
                "command"
              ],
              "additionalProperties": false
            }
          ]
        },
        "base": {
          "description": "Default base for `dta diff` (a git ref or directory); `--base` overrides it.",
          "type": "string",
          "minLength": 1
        },
        "workflow": {
          "description": "The GitHub workflow `dta init` writes: `single` (one file, for repositories without outside contributors) or `split` (three files, safe for pull requests from forks). Saved so a re-run does not ask again.",
          "type": "string",
          "enum": [
            "single",
            "split"
          ]
        }
      },
      "required": [
        "outDir"
      ],
      "additionalProperties": false
    },
    "audit": {
      "description": "`dta audit`: where to look for hard-coded values that should be tokens.",
      "type": "object",
      "properties": {
        "paths": {
          "description": "Files and folders `dta audit` scans when no paths are given (default `src`).",
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          }
        },
        "ignore": {
          "description": "Globs of files not to scan, relative to the project (`**/*.test.ts`).",
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          }
        },
        "languages": {
          "description": "Languages to scan (default: all). `js` covers JS, TS, JSX and TSX.",
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "css",
              "scss",
              "js",
              "astro",
              "vue",
              "svelte",
              "html"
            ]
          }
        },
        "near": {
          "description": "Also report colours within about 2 % per channel of a token (like `--near`).",
          "type": "boolean"
        },
        "baseline": {
          "default": ".dta/audit-baseline.json",
          "description": "Accepted findings; `dta audit --update-baseline` writes it.",
          "type": "string",
          "minLength": 1
        }
      },
      "additionalProperties": false
    },
    "context": {
      "description": "With this section, `dta build` also writes the context file for coding agents and the pointer in `AGENTS.md`, like `dta context`. Off by default.",
      "type": "object",
      "properties": {
        "out": {
          "default": "DESIGN.md",
          "description": "Context file for coding agents, relative to the project.",
          "type": "string",
          "minLength": 1
        }
      },
      "additionalProperties": false
    },
    "brief": {
      "description": "`dta brief`: what the brief for AI design tools says beyond the tokens.",
      "type": "object",
      "properties": {
        "identity": {
          "description": "One line on the brand's character, which tokens cannot say: \"Calm, trustworthy, institutional.\"",
          "type": "string"
        },
        "rules": {
          "description": "The rules a generator should follow, one line each: \"Never use pure black: use Colors.Text.Default.\"",
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      },
      "additionalProperties": false
    },
    "push": {
      "description": "`dta push` settings.",
      "type": "object",
      "properties": {
        "skipConfirmation": {
          "description": "Skip the typed confirmation before pushing to Figma (CI, automation).",
          "type": "boolean"
        }
      },
      "additionalProperties": false
    }
  },
  "additionalProperties": false
}
