{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://designsystemdocspec.org/v0.15.2/document-blocks/variants.schema.json", "title": "Variants document block", "description": "Every way a component can be configured — a toggle (flag variant, like 'disabled') or a set of options (enum variant, like size: sm | md | lg). Each can explain why it exists.", "$defs": { "variantValue": { "type": "object", "description": "A single option within an enum variant dimension.", "required": [ "identifier", "description" ], "properties": { "identifier": { "type": "string", "description": "Machine-readable value identifier (ex: 'sm', 'md', 'lg', 'primary', 'secondary')." }, "name": { "type": "string", "description": "Human-readable name (ex: 'Small', 'Medium', 'Large', 'Primary', 'Secondary')." }, "description": { "$ref": "../common/rich-text.schema.json#/$defs/richText", "description": "What this value looks like, when to use it, and how it differs from other values in this dimension." }, "rationale": { "$ref": "../common/rich-text.schema.json#/$defs/richText", "description": "Why this value exists (ex: 'High emphasis directs users to the one key action'). For guidance comparing values, use `use-cases` instead." }, "examples": { "type": "array", "description": "Examples showing this variant value in isolation or in context.", "items": { "$ref": "../common/example.schema.json#/$defs/example" }, "minItems": 1 }, "tokens": { "$ref": "../common/token-overrides.schema.json#/$defs/tokenOverrides", "description": "Token overrides applied when this value is selected." } }, "additionalProperties": false }, "flagVariant": { "type": "object", "description": "A toggle — on or off. Use it for binary capabilities like 'disabled' or 'full-width'. No `values` array; it's simply active or not.", "required": [ "kind", "identifier", "description" ], "properties": { "kind": { "type": "string", "const": "flag", "description": "Identifies this variant as a boolean flag." }, "identifier": { "type": "string", "description": "Machine-readable identifier of the flag (ex: 'disabled', 'full-width', 'icon-only', 'loading')." }, "name": { "type": "string", "description": "Human-readable name of the flag (ex: 'Disabled', 'Full Width', 'Icon Only', 'Loading')." }, "description": { "$ref": "../common/rich-text.schema.json#/$defs/richText", "description": "What this flag controls and how it affects the component's appearance or behavior when active." }, "rationale": { "$ref": "../common/rich-text.schema.json#/$defs/richText", "description": "Why this flag exists (ex: 'Prevents interaction during submission to avoid duplicate requests')." }, "examples": { "type": "array", "description": "Examples showing the component with this flag active.", "items": { "$ref": "../common/example.schema.json#/$defs/example" }, "minItems": 1 }, "tokens": { "$ref": "../common/token-overrides.schema.json#/$defs/tokenOverrides", "description": "Token overrides applied when this flag is active." } }, "additionalProperties": false }, "enumVariant": { "type": "object", "description": "A set of options where you pick one, like 'size' (sm | md | lg) or 'emphasis' (primary | secondary | ghost).", "required": [ "kind", "identifier", "description", "values" ], "properties": { "kind": { "type": "string", "const": "enum", "description": "Identifies this variant as an enumerated set of values." }, "identifier": { "type": "string", "description": "Machine-readable name for this dimension (ex: 'size', 'emphasis') — the axis, not one of its values." }, "name": { "type": "string", "description": "Human-readable name of the variant dimension (ex: 'Size', 'Emphasis', 'Shape')." }, "description": { "$ref": "../common/rich-text.schema.json#/$defs/richText", "description": "What this dimension of variation controls and how its values affect the component's appearance or behavior." }, "values": { "type": "array", "description": "The possible values. A single value is fine — dimensions often start with just one while more are added over time. Tools SHOULD keep this order.", "items": { "$ref": "#/$defs/variantValue" }, "minItems": 1 } }, "additionalProperties": false }, "variants": { "type": "object", "description": "Every way a component or pattern can be configured — a toggle or a set of options. Multiple items combine independently. A variant is a choice the consumer makes up front (ex: 'size', 'full-width'); a condition entered at runtime (hover, disabled, loading) is a state — document that in `states` instead.", "required": [ "kind", "items" ], "properties": { "kind": { "type": "string", "const": "variants", "description": "Identifies this block as a variants spec." }, "items": { "type": "array", "description": "The variant dimensions, in order. Tools SHOULD keep this order.", "items": { "type": "object", "description": "One variant dimension. `kind: 'flag'` makes it a toggle; `kind: 'enum'` makes it a set of options. We use `if`/`then` instead of `oneOf` so an invalid entry gives one clear error instead of many.", "required": [ "kind" ], "properties": { "kind": { "enum": [ "flag", "enum" ] } }, "allOf": [ { "if": { "required": [ "kind" ], "properties": { "kind": { "const": "flag" } } }, "then": { "$ref": "#/$defs/flagVariant" } }, { "if": { "required": [ "kind" ], "properties": { "kind": { "const": "enum" } } }, "then": { "$ref": "#/$defs/enumVariant" } } ] }, "minItems": 1 }, "$extensions": { "$ref": "../common/extensions.schema.json#/$defs/extensions" } }, "additionalProperties": false } } } Identifies this block as a variants spec. The variant dimensions, in order. Tools SHOULD keep this order.
Min items: 1
All vendor-specific extensions . Keys MUST use a namespace of at least two dot-separated segments (reverse domain recommended), Example: 'com.figma', 'acme.tooling'; the pattern is case-tolerant. Tools that don't recognize an extension MUST keep it. Extension data SHOULD NOT duplicate core schema fields.
References: flagVariant, enumVariant, extensions { "kind": "variants", "items": [ { "kind": "enum", "identifier": "emphasis", "name": "Emphasis", "description": "Controls the visual weight of the button to establish a visual hierarchy among actions on a surface.", "values": [ { "identifier": "primary", "description": "High-emphasis — the main action on the surface." }, { "identifier": "secondary", "description": "Medium-emphasis — important but not primary." }, { "identifier": "ghost", "description": "Low-emphasis — tertiary actions and dense layouts." }, { "identifier": "danger", "description": "Signals a destructive or irreversible action." } ] }, { "kind": "enum", "identifier": "size", "name": "Size", "description": "Controls the physical dimensions and internal padding of the button.", "values": [ { "identifier": "sm", "name": "Small", "description": "Compact size for toolbars, table rows, and dense layouts. 32px height." }, { "identifier": "md", "name": "Medium", "description": "Default size for most contexts. 40px height." }, { "identifier": "lg", "name": "Large", "description": "Touch-optimized size for mobile-first surfaces. 48px height." } ] }, { "kind": "flag", "identifier": "full-width", "name": "Full Width", "description": "The button stretches to fill the full width of its parent container." }, { "kind": "flag", "identifier": "icon-only", "name": "Icon Only", "description": "The button renders a single icon with no visible label. An aria-label is required." } ] }
Identifies this variant as a boolean flag. Machine-readable identifier of the flag (ex: 'disabled', 'full-width', 'icon-only', 'loading'). What this flag controls and how it affects the component's appearance or behavior when active. Human-readable name of the flag (ex: 'Disabled', 'Full Width', 'Icon Only', 'Loading'). Why this flag exists (ex: 'Prevents interaction during submission to avoid duplicate requests'). Examples showing the component with this flag active.
Min items: 1
Token overrides applied when this flag is active.
References: richText, example, tokenOverrides [ { "kind": "flag", "identifier": "full-width", "name": "Full Width", "description": "When active, the button stretches to fill the full width of its parent container. Label text is centered.", "rationale": "- Use when: When the button is the sole action in a narrow container such as a mobile sheet, a card footer, or a single-column form.\n- Avoid when: When multiple buttons appear side by side in a button group or dialog footer. Use `default width` instead — Full-width buttons in a horizontal group force a stacked layout, which breaks the expected left-to-right reading order for action groups." }, { "kind": "flag", "identifier": "icon-only", "name": "Icon Only", "description": "When active, the button renders a single icon with no visible label. An aria-label is required." } ]
Identifies this variant as an enumerated set of values. Machine-readable name for this dimension (ex: 'size', 'emphasis') — the axis, not one of its values. What this dimension of variation controls and how its values affect the component's appearance or behavior. The possible values. A single value is fine — dimensions often start with just one while more are added over time. Tools SHOULD keep this order.
Min items: 1
Human-readable name of the variant dimension (ex: 'Size', 'Emphasis', 'Shape').
References: richText, variantValue [ { "kind": "enum", "identifier": "emphasis", "name": "Emphasis", "description": "Controls the visual weight of the button. Determines background fill, border treatment, and text color to establish a visual hierarchy among actions on a surface.", "values": [ { "identifier": "primary", "description": "High-emphasis — the main action on the surface." }, { "identifier": "secondary", "description": "Medium-emphasis — important but not primary." }, { "identifier": "ghost", "description": "Low-emphasis — tertiary actions and dense layouts." }, { "identifier": "danger", "description": "Signals a destructive or irreversible action." } ] }, { "kind": "enum", "identifier": "size", "name": "Size", "description": "Controls the physical dimensions and internal padding of the button. All sizes maintain a minimum 44×44 CSS pixel tap target.", "values": [ { "identifier": "small", "description": "Compact size for toolbars, table rows, and dense layouts." }, { "identifier": "medium", "description": "The default size. Suitable for most contexts including forms, dialogs, and page-level actions." }, { "identifier": "large", "description": "Touch-optimized size for mobile-first surfaces and marketing pages." } ] } ]
Machine-readable value identifier (ex: 'sm', 'md', 'lg', 'primary', 'secondary'). What this value looks like, when to use it, and how it differs from other values in this dimension. Human-readable name (ex: 'Small', 'Medium', 'Large', 'Primary', 'Secondary'). Why this value exists (ex: 'High emphasis directs users to the one key action'). For guidance comparing values, use use-cases instead. Examples showing this variant value in isolation or in context.
Min items: 1
Token overrides applied when this value is selected.
References: richText, example, tokenOverrides [ { "identifier": "primary", "name": "Primary", "description": "High-emphasis — the main action on the surface. Uses a solid, filled background. Limit to one primary button per surface.", "rationale": "- Use when: When the action is the most important on the surface — the one the user is most likely to take (ex: Save, Submit, Confirm).\n- Avoid when: When a surface already has a primary button. Adding a second dilutes visual hierarchy. Use `secondary` instead — Secondary emphasis maintains importance without competing with the existing primary action." }, { "identifier": "secondary", "name": "Secondary", "description": "Medium-emphasis — important but not the primary action. Uses a visible border and transparent background." }, { "identifier": "ghost", "name": "Ghost", "description": "Low-emphasis — for tertiary actions, toolbar actions, or dense layouts where visual weight must be minimized." }, { "identifier": "danger", "name": "Danger", "description": "Signals a destructive or irreversible action. Use for delete, remove, or disconnect actions.", "rationale": "- Use when: When the action is destructive or irreversible — deleting a record, revoking access, removing a team member.\n- Avoid when: When the action is not destructive, even if it feels important or urgent. Use `primary` instead — The danger color is a strong signal reserved for destruction. Using it for non-destructive actions dilutes its meaning." } ]