{
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "$id": "https://signalxjs.github.io/zero/schemas/tokens.schema.json",
    "title": "SignalX Zero design-system tokens (TokensInput)",
    "description": "The argument to @sigx/zero-kit's defineTokens(), as plain JSON data: the design system's color-role vocabulary, size-axis ramp, non-color token categories, breakpoints, custom tokens and themes. An AI (or any tool) can emit this JSON, wrap it in defineTokens(...) and run `sigx zero:validate` \u2014 this schema catches structural mistakes before the validator's deeper semantic checks (completeness, WCAG contrast, undeclared references) take over. Values are CSS text and deliberately unconstrained here: the compiler accepts any value the browser would.",
    "type": "object",
    "additionalProperties": false,
    "required": [
        "themes",
        "defaultLight"
    ],
    "properties": {
        "roles": {
            "description": "The design system's color role vocabulary. Every role emits --color-<role>, plus -content / -soft per its declaration. Omitted \u2192 the recommended eight (primary, secondary, accent, neutral, info, success, warning, error). Role names must be bare kebab-case identifiers starting with a letter \u2014 they become --color-<role>.",
            "type": "object",
            "propertyNames": {
                "pattern": "^[a-z][a-z0-9]*(-[a-z0-9]+)*$"
            },
            "additionalProperties": {
                "$ref": "#/$defs/roleDecl"
            }
        },
        "sizes": {
            "description": "The design system's `size` axis vocabulary \u2014 the values recipes may key variants.size on. Omitted \u2192 the recommended ramp (xs, sm, md, lg, xl); [] \u2192 this design system has no size axis at all. Entries become [data-size=\"\u2026\"] selector values, hence the kebab-case grammar.",
            "type": "array",
            "items": {
                "$ref": "#/$defs/tokenKey"
            }
        },
        "variants": {
            "description": "The `variant` axis vocabulary — the fill/chrome styles this design system offers (solid, outline, …). Once declared, the validator closes the set: a recipe variants.variant value outside it is an error. Omitted → the axis stays undeclared and recipe values are unchecked. Entries become [data-variant=\"…\"] selector values, hence the kebab-case grammar.",
            "type": "array",
            "items": {
                "$ref": "#/$defs/tokenKey"
            }
        },
        "modifiers": {
            "description": "Presence-only modifiers this design system offers — daisyUI's block/wide, Radix's high-contrast, HeroUI's icon-only. Rendered as data-mod-<name>, set from zero's `mods` prop. An axis answers \"which one\" and always carries a value; a modifier answers \"is it on\" and carries none. Once declared, a recipe wiring a modifier outside the list is an error. The mod- prefix keeps these disjoint from zero's own presence-only flag vocabulary, which is versioned.",
            "type": "array",
            "items": {
                "$ref": "#/$defs/tokenKey"
            }
        },
        "axes": {
            "description": "Additional variant axes: axis name → its value vocabulary. Axis names must not re-declare an axis with a named prop (color, size, variant) and must not take a name the anatomy contract reserves — the validator rejects exactly what the zero runtime refuses to render. Once declared, wiring an undeclared axis or an undeclared value is an error.",
            "type": "object",
            "propertyNames": {
                "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$"
            },
            "additionalProperties": {
                "type": "array",
                "items": {
                    "$ref": "#/$defs/tokenKey"
                }
            }
        },
        "scopes": {
            "description": "Per-scope axis vocabularies: component scope → the part of the design-system-wide vocabulary that scope offers (docs/architecture.md, \"Declared vocabulary\"). Every list NARROWS the top-level declaration for that axis — which makes the top-level list the UNION of every scope's vocabulary rather than one vocabulary every scope shares. An absent key means unrestricted; an empty list means this scope has no such axis at all, the same grammar sizes: [] uses design-system-wide. Scope keys are checked against zero's anatomy manifest. `parts` is reserved inside an entry and rejected by name: the restriction unit is the scope, because zero carries one attribute per axis on the scope's carrier part — two vocabularies on two parts are two axes, not one axis restricted twice.",
            "type": "object",
            "propertyNames": {
                "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$"
            },
            "additionalProperties": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                    "colors": {
                        "description": "The `color` values this scope offers — a subset of the declared role names.",
                        "type": "array",
                        "items": {
                            "$ref": "#/$defs/tokenKey"
                        }
                    },
                    "sizes": {
                        "description": "The `size` ramp this scope offers — a subset of the resolved sizes.",
                        "type": "array",
                        "items": {
                            "$ref": "#/$defs/tokenKey"
                        }
                    },
                    "variants": {
                        "description": "The `variant` vocabulary this scope offers — a subset of tokens.variants.",
                        "type": "array",
                        "items": {
                            "$ref": "#/$defs/tokenKey"
                        }
                    },
                    "axes": {
                        "description": "Custom axes this scope offers — each a subset of the matching tokens.axes entry.",
                        "type": "object",
                        "propertyNames": {
                            "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$"
                        },
                        "additionalProperties": {
                            "type": "array",
                            "items": {
                                "$ref": "#/$defs/tokenKey"
                            }
                        }
                    },
                    "modifiers": {
                        "description": "The presence-only modifiers this scope offers — a subset of tokens.modifiers.",
                        "type": "array",
                        "items": {
                            "$ref": "#/$defs/tokenKey"
                        }
                    }
                }
            }
        },
        "swatch": {
            "description": "Role / base-surface token names sampled into theme-picker swatches. Default: the first four declared roles plus base-100 and base-content.",
            "type": "array",
            "items": {
                "$ref": "#/$defs/tokenKey"
            }
        },
        "custom": {
            "description": "DS-declared custom tokens: name \u2192 metadata. Values live per-theme in each theme's `custom`. Names may be spelled with or without the leading `--`.",
            "type": "object",
            "additionalProperties": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                    "description": {
                        "type": "string"
                    },
                    "syntax": {
                        "description": "CSS @property syntax string (e.g. \"<color>\", \"<length>\") \u2192 typed registration in the compiled CSS.",
                        "type": "string"
                    }
                }
            }
        },
        "breakpoints": {
            "description": "Mobile-first min-width breakpoints, in ascending order (declaration order is emission order \u2014 the validator enforces kebab-case names, px/rem/em values and the ordering).",
            "type": "object",
            "propertyNames": {
                "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$"
            },
            "additionalProperties": {
                "type": "string",
                "minLength": 1
            }
        },
        "system": {
            "description": "Design-system-level values for every non-color token category, declared ONCE rather than per theme. The category set is closed (the validator rejects unknown keys); the keys inside each category are yours.",
            "$ref": "#/$defs/systemTokens"
        },
        "systemDark": {
            "description": "Overrides applied to every colorScheme: \"dark\" theme and to :root under prefers-color-scheme: dark \u2014 how a NON-color token differs per scheme (light-dark() only accepts <color>).",
            "$ref": "#/$defs/themeSystem"
        },
        "themes": {
            "description": "Theme name \u2192 theme. Names become [data-theme=\"\u2026\"] values.",
            "type": "object",
            "minProperties": 1,
            "additionalProperties": {
                "$ref": "#/$defs/theme"
            },
            "propertyNames": {
                "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
                "description": "Interpolated into the [data-theme=\"<name>\"] selector \u2014 kebab-case only, or a quote in a name would break out of the attribute."
            }
        },
        "defaultLight": {
            "description": "Theme used for :root (system light). Must name a key of `themes`.",
            "type": "string",
            "minLength": 1
        },
        "defaultDark": {
            "description": "Theme paired with defaultLight for system dark. Must name a key of `themes`.",
            "type": "string",
            "minLength": 1
        }
    },
    "$defs": {
        "tokenKey": {
            "description": "A kebab-case identifier that may start with a digit (--text-2xl is legal) \u2014 the grammar for token keys, size names and swatch entries.",
            "type": "string",
            "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$"
        },
        "tokenValue": {
            "description": "A CSS value as authored \u2014 string, or a bare number for unitless values.",
            "type": [
                "string",
                "number"
            ]
        },
        "roleDecl": {
            "description": "Declaration of one color role.",
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "content": {
                    "description": "Emit + require + contrast-check a <role>-content pairing. Default true.",
                    "type": "boolean"
                },
                "soft": {
                    "description": "Emit a <role>-soft tint (explicit value or softMix derivation). Default true.",
                    "type": "boolean"
                },
                "description": {
                    "description": "Intent of the role \u2014 surfaced in the DS manifest for tooling/AI.",
                    "type": "string"
                }
            }
        },
        "scale": {
            "description": "One scale category's keys: kebab-case key \u2192 CSS value. Recommended keys have base.css fallbacks; any other key is equally valid.",
            "type": "object",
            "propertyNames": {
                "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$"
            },
            "additionalProperties": {
                "$ref": "#/$defs/tokenValue"
            }
        },
        "typeScale": {
            "description": "Modular type scale expanded into the --text-* ramp: { base, ratio }, with explicit typography.sizes winning per key.",
            "type": "object",
            "additionalProperties": false,
            "required": [
                "base",
                "ratio"
            ],
            "properties": {
                "base": {
                    "description": "The value of the origin step \u2014 a number with a CSS length unit (e.g. \"1rem\").",
                    "type": "string",
                    "minLength": 1
                },
                "ratio": {
                    "description": "Ratio between adjacent steps \u2014 1.125 minor third, 1.618 golden. Must exceed 1 (the compiler rejects a flat or inverted ramp).",
                    "type": "number",
                    "exclusiveMinimum": 1
                },
                "steps": {
                    "description": "Step names, small to large. Defaults to the recommended --text-* ramp.",
                    "type": "array",
                    "items": {
                        "$ref": "#/$defs/tokenKey"
                    },
                    "minItems": 1
                },
                "origin": {
                    "description": "Which step carries `base`. Defaults to md, else the middle step.",
                    "type": "string",
                    "minLength": 1
                },
                "precision": {
                    "description": "Decimal places before trailing zeros are trimmed. Default 4.",
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 20
                }
            }
        },
        "typography": {
            "description": "The design system's typographic voice. `fonts` is FAMILIES \u2014 sizes are --text-*, listed in `sizes` or generated from `scale`.",
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "fonts": {
                    "$ref": "#/$defs/scale"
                },
                "weights": {
                    "$ref": "#/$defs/scale"
                },
                "leading": {
                    "$ref": "#/$defs/scale"
                },
                "tracking": {
                    "$ref": "#/$defs/scale"
                },
                "sizes": {
                    "$ref": "#/$defs/scale"
                },
                "scale": {
                    "$ref": "#/$defs/typeScale"
                }
            }
        },
        "typographyOverride": {
            "description": "Per-theme / dark-scheme typography overrides. No `scale` here \u2014 a scale is a DECLARATION (it mints --text-* keys), and declarations live only in the base `system` tier.",
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "fonts": {
                    "$ref": "#/$defs/scale"
                },
                "weights": {
                    "$ref": "#/$defs/scale"
                },
                "leading": {
                    "$ref": "#/$defs/scale"
                },
                "tracking": {
                    "$ref": "#/$defs/scale"
                },
                "sizes": {
                    "$ref": "#/$defs/scale"
                }
            }
        },
        "motion": {
            "description": "Durations and easings, split because they vary independently. Referencing var(--duration-*) is what makes a recipe respect prefers-reduced-motion.",
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "durations": {
                    "$ref": "#/$defs/scale"
                },
                "easings": {
                    "$ref": "#/$defs/scale"
                }
            }
        },
        "systemTokens": {
            "description": "The closed category set (mirrors TOKEN_CATEGORIES): radius, size, typography, spacing, shadow, motion, border, disabledOpacity.",
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "radius": {
                    "$ref": "#/$defs/scale"
                },
                "size": {
                    "$ref": "#/$defs/scale"
                },
                "typography": {
                    "$ref": "#/$defs/typography"
                },
                "spacing": {
                    "$ref": "#/$defs/scale"
                },
                "shadow": {
                    "$ref": "#/$defs/scale"
                },
                "motion": {
                    "$ref": "#/$defs/motion"
                },
                "border": {
                    "$ref": "#/$defs/tokenValue"
                },
                "disabledOpacity": {
                    "$ref": "#/$defs/tokenValue"
                }
            }
        },
        "themeSystem": {
            "description": "systemTokens as an override tier: same shape, but typography may not declare a `scale`. (The TypeScript type additionally narrows keys to those the design system declared \u2014 a cross-field constraint JSON Schema cannot express; `sigx zero:validate` enforces it.)",
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "radius": {
                    "$ref": "#/$defs/scale"
                },
                "size": {
                    "$ref": "#/$defs/scale"
                },
                "typography": {
                    "$ref": "#/$defs/typographyOverride"
                },
                "spacing": {
                    "$ref": "#/$defs/scale"
                },
                "shadow": {
                    "$ref": "#/$defs/scale"
                },
                "motion": {
                    "$ref": "#/$defs/motion"
                },
                "border": {
                    "$ref": "#/$defs/tokenValue"
                },
                "disabledOpacity": {
                    "$ref": "#/$defs/tokenValue"
                }
            }
        },
        "theme": {
            "type": "object",
            "additionalProperties": false,
            "required": [
                "colorScheme",
                "colors"
            ],
            "properties": {
                "colorScheme": {
                    "description": "Which scheme this theme belongs to \u2014 drives light-dark() pairing, systemDark application and the emitted color-scheme declaration.",
                    "enum": [
                        "light",
                        "dark"
                    ]
                },
                "pair": {
                    "description": "The theme toggle() switches to.",
                    "type": "string",
                    "minLength": 1
                },
                "softMix": {
                    "description": "Soft-tint mix ratio (0\u20131) for -soft tokens. Default 0.16.",
                    "type": "number",
                    "minimum": 0,
                    "maximum": 1
                },
                "colors": {
                    "description": "Color token name \u2192 CSS color. Keys are declared role names, their -content / -soft derivatives, and the fixed base surfaces (base-100/200/300/base-content). Completeness against the role declaration is checked by `sigx zero:validate`, not here.",
                    "type": "object",
                    "propertyNames": {
                        "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$"
                    },
                    "additionalProperties": {
                        "type": "string",
                        "minLength": 1
                    }
                },
                "system": {
                    "description": "Per-theme overrides of the design-system-level `system` values \u2014 declared keys only.",
                    "$ref": "#/$defs/themeSystem"
                },
                "custom": {
                    "description": "Values for the design system's declared `custom` tokens.",
                    "type": "object",
                    "additionalProperties": {
                        "type": "string"
                    }
                },
                "extra": {
                    "description": "DS-specific extra tokens, emitted verbatim \u2014 the undeclared escape hatch (prefer `custom`; the validator warns here).",
                    "type": "object",
                    "additionalProperties": {
                        "type": "string"
                    }
                }
            }
        }
    }
}