What is DSDS?
DSDS (Design System Documentation Spec) is a JSON-based format for documenting design systems. It gives every piece of documentation — components, tokens, themes, styles, patterns — a predictable, machine-readable structure.
Key idea: DSDS documents the how and why of your design system — not the token values themselves. It complements the W3C Design Tokens Format which handles the what.
What you get
- Structured — every section has a defined shape, no guessing
- Machine-readable — tools can parse, generate, validate, and transform it
- Portable — not locked to any docs tool or platform
- Extensible — add vendor metadata without breaking compatibility
- Validatable — JSON Schema catches errors before they reach consumers
Document structure
A DSDS file requires dsdsVersion and one of two shapes:
Single-entity files
Use entity when each component, token, or pattern lives in its own file. The kind field on the entity identifies its type.
{
"$schema": "https://designsystemdocspec.org/v0.2.1/dsds.bundled.schema.json",
"dsdsVersion": "0.2.1",
"entity": {
"kind": "component",
"identifier": "button",
"name": "Button",
"metadata": [
{ "kind": "description", "value": "An interactive element that triggers an action." },
{ "kind": "status", "status": "stable" }
],
"documentBlocks": []
}
}
Multi-entity files
Use documentation when you want to organize multiple entities in one file, or as a manifest that references individual entity files.
{
"dsdsVersion": "0.2.1",
"documentation": [
{
"name": "My Design System",
"components": [
{ "kind": "component", "identifier": "button", "...": "" }
],
"tokenGroups": [
{ "kind": "token-group", "identifier": "color-palette", "...": "" }
],
"themes": [
{ "kind": "theme", "identifier": "dark", "...": "" }
]
}
]
}
- dsdsVersion — always "0.2" for this version
- documentation — array of named groups, each with typed entity arrays
- components, tokenGroups, themes, styles, patterns — separate arrays for each entity type. All are optional.
Linking files with $ref
Entity arrays accept $ref objects that point to other DSDS files. This lets you keep entities in individual files while assembling them in a manifest:
{
"dsdsVersion": "0.2.1",
"documentation": [
{
"name": "Acme Design System",
"components": [
{ "$ref": "./button.dsds.json#/entity" },
{ "$ref": "./link.dsds.json#/entity" }
]
}
]
}
The #/entity fragment is a JSON Pointer targeting the entity value in the referenced file. Tooling resolves $ref objects before validation using a library like json-schema-ref-parser.
Tip: Use single-entity files + a $ref manifest for large systems where each component is owned by a different team. Use a single multi-entity file for smaller systems or when you want everything in one place.
Entity types
Every entity has a kind field that tells tools what type of thing it is. There are six kinds.
| Kind | Code | Description |
|---|
| component | "component" | A reusable UI element — buttons, inputs, modals. Supports anatomy, API, variants, states, and design specs. |
| token | "token" | A single design token — color, spacing, typography. Carries type, category, and usage rules. Link to W3C DTCG definitions via source. |
| token-group | "token-group" | A nested group of related tokens. Recursive — groups can contain groups. |
| theme | "theme" | A named set of token overrides — dark mode, high-contrast, compact density, brand variants. Lists which token names change; the DTCG file holds the actual values. |
| style | "style" | A design base — color, typography, spacing, elevation. Carries principles, scales, and motion definitions. |
| pattern | "pattern" | A broad action pattern — nav, error messaging, empty states. Describes how components compose to solve a user need. |
Common properties
Every entity shares these fields:
{/* Hand-rolled: cross-entity summary that intentionally diverges from any single schema. */}
| Property | Required | Description |
|---|
| kind | Yes | Entity type |
| identifier | Yes* | Machine-readable identifier (^[a-z][a-z0-9-]*$, except tokens) |
| name | Yes* | Human-readable label |
| description | Yes* | What this entity is and does (CommonMark supported) |
| status | No | "stable", "experimental", "draft", "deprecated" — or an object with per-platform status |
| since | No | Version when this entity first shipped |
| tags | No | Array of freeform category tags |
| documentBlocks | No | Array of typed document block objects |
| agents | No | Agent-optimized context for AI/LLM consumption |
* Required for component, style, and pattern. Token requires identifier + tokenType.
The optional agents object provides context written for AI/LLM consumers — intent for quick relevance checks, keywords for retrieval, constraints with enforcement levels and optional evidence and inline examples, disambiguation against similar entities (with optional entityType), and antiPatterns with corrective alternatives. The verified and verifiedAgainst fields signal data freshness.
Status: string or object
For the simple case, status is a string:
{ "status": "stable" }
When you need per-platform tracking:
{
"status": {
"overall": "stable",
"platforms": {
"react": { "status": "stable", "since": "1.0.0" },
"ios": { "status": "experimental", "since": "3.0.0" }
}
}
}
The document block system
Structured docs live in the documentBlocks array on each entity. Each document block entry is typed by its kind field.
| Kind | Applies To | Description |
|---|
| import | component | Platform-specific import statements for the component |
| anatomy | component, pattern | Named parts that make up the entity, with optional design-token mapping per part |
| api | component | Properties, events, slots, and CSS custom properties |
| events | component, pattern | Events emitted by the component — trigger conditions, return shapes, DOM behavior |
| variants | component, pattern | Named variant axes (e.g. size, color) with discrete values |
| states | component, pattern | Interactive states (hover, disabled, …) with per-state token overrides |
| purpose | all | When to use / when not to use, with options for negative cases |
| guideline | all | Actionable guidance paired with rationale and enforcement level |
| accessibility | component, pattern | WCAG level, keyboard interactions, ARIA attributes, screen-reader behavior |
| content | component, pattern | Writing and labelling guidelines for UI text |
| interaction | component, pattern | Step-by-step action flows with optional wireframes |
| design-specifications | component, pattern | Concrete measurements, spacing, and sizing values for implementors |
| scale | style | Named stops along a design scale (e.g. spacing-100…spacing-900) |
| motion | style | Named animation/transition presets with duration, easing, and purpose |
| principle | style | Guiding design principles with rationale and examples |
Every document block type also accepts an optional agents property for AI/LLM-optimized context scoped to that section. See Schema Architecture for details.
Purpose: when to use it
The purpose guideline lists scenarios. Each use case is "positive" (do use) or "negative" (don't use, with an option):
{
"kind": "purpose",
"useCases": [
{
"description": "When the user needs to trigger an action such as submitting a form.",
"kind": "positive"
},
{
"description": "When the action navigates to a different page.",
"kind": "negative",
"alternative": {
"identifier": "link",
"rationale": "Links carry native navigation semantics."
}
}
]
}
Guidelines: how to use it
Each guideline pairs a guidance statement with a rationale and an enforcement level:
{
"kind": "guideline",
"items": [
{
"guidance": "Limit each surface to one primary button.",
"rationale": "Multiple primary buttons dilute visual hierarchy.",
"kind": "required",
"category": "visual-design"
}
]
}
Enforcement levels: required (MUST), encouraged (SHOULD), informational (MAY, default), discouraged (SHOULD NOT), prohibited (MUST NOT).
Minimal examples
These are the smallest valid entities — the bare minimum. Copy one, fill in your content, and add document block entries as you go.
Component
A component needs kind, identifier, name, and description. Everything else is optional.
{
"kind": "component",
"identifier": "button",
"name": "Button",
"metadata": [
{
"kind": "description",
"value": "A clickable element that triggers an action."
},
{
"kind": "status",
"status": "stable"
},
{
"kind": "category",
"value": "action"
}
],
"documentBlocks": [
{
"kind": "purpose",
"useCases": [
{
"description": "When the user needs to trigger an action such as submitting a form or confirming a dialog.",
"kind": "positive"
},
{
"description": "When the action is navigation to another page.",
"kind": "negative",
"alternative": {
"identifier": "link",
"rationale": "Links communicate navigation; buttons communicate actions."
}
}
],
"agents": {
"intent": "Clarify when to use this component.",
"keywords": [
"when to use",
"use case"
]
}
}
],
"agents": {
"intent": "Trigger a user-initiated action within the current view without causing page navigation.",
"constraints": [
{
"rule": "Do not use for navigating to a different page or URL.",
"level": "must-not"
},
{
"rule": "Limit each surface to one primary-emphasis button.",
"level": "must"
},
{
"rule": "Always provide an accessible label via visible text or aria-label.",
"level": "must"
}
],
"disambiguation": [
{
"entity": "link",
"distinction": "Use button for in-page actions; use link for navigation to a URL."
},
{
"entity": "icon-button",
"distinction": "Use button when a visible text label is present; use icon-button for icon-only affordances."
}
],
"antiPatterns": [
{
"description": "Using a button to navigate to another page.",
"instead": "Use a link element with href."
},
{
"description": "Placing multiple primary buttons on the same surface.",
"instead": "Use one primary and one or more secondary or tertiary buttons."
}
],
"keywords": [
"action",
"submit",
"click",
"CTA",
"trigger",
"call-to-action",
"interactive"
]
}
}
Token
A token needs kind, identifier, and tokenType. Use the optional source property to link back to the W3C DTCG file that holds the authoritative value.
{
"kind": "token",
"identifier": "color-text-primary",
"tokenType": "color",
"source": {
"file": "tokens/color.tokens.json",
"path": "color.text.primary"
}
}
Token group
Groups arrange tokens in a tree. The children array can contain tokens or nested groups.
{
"kind": "token-group",
"identifier": "color-text",
"metadata": [
{
"kind": "description",
"value": "Semantic text color tokens."
},
{
"kind": "status",
"status": "stable"
},
{
"kind": "category",
"value": "semantic"
}
],
"tokenType": "color",
"source": {
"file": "tokens/color.tokens.json",
"path": "color.text"
},
"children": [
{
"kind": "token",
"identifier": "color-text-primary",
"tokenType": "color",
"source": {
"file": "tokens/color.tokens.json",
"path": "color.text.primary"
}
},
{
"kind": "token",
"identifier": "color-text-secondary",
"tokenType": "color",
"source": {
"file": "tokens/color.tokens.json",
"path": "color.text.secondary"
}
},
{
"kind": "token",
"identifier": "color-text-disabled",
"tokenType": "color",
"source": {
"file": "tokens/color.tokens.json",
"path": "color.text.disabled"
}
}
],
"agents": {
"intent": "Define and organize a set of related design tokens, providing shared defaults for type and category.",
"constraints": [
{
"rule": "Do not reference base palette tokens directly in component code.",
"level": "must-not",
"context": "Always reference semantic tokens that point to palette values."
}
],
"keywords": [
"token",
"group",
"palette",
"scale",
"family",
"collection"
]
}
}
Theme
A theme provides overrides — an array of token names that differ from the default. The DTCG file holds the actual override values.
{
"kind": "theme",
"identifier": "dark",
"name": "Dark Mode",
"metadata": [
{
"kind": "description",
"value": "Dark color mode for reduced-light environments."
},
{
"kind": "status",
"status": "stable"
},
{
"kind": "category",
"value": "color-mode"
}
],
"overrides": [
{
"token": "color-background-default",
"description": "Inverted default surface for dark mode."
},
{
"token": "color-text-primary",
"description": "High-contrast text on dark surfaces."
},
{
"token": "color-border-default",
"description": "Subtle border visible on dark backgrounds."
}
],
"agents": {
"intent": "Apply a dark color palette for low-light environments and user preference, maintaining WCAG AA contrast throughout.",
"constraints": [
{
"rule": "Always apply at the application root, not on individual components.",
"level": "must"
},
{
"rule": "Do not mix dark-mode and light-mode semantic tokens on the same surface.",
"level": "must-not"
}
],
"disambiguation": [
{
"entity": "high-contrast",
"distinction": "Use dark for low-light comfort; use high-contrast for users with low vision requiring maximum contrast."
}
],
"antiPatterns": [
{
"description": "Applying dark theme only to part of a page while the rest remains light.",
"instead": "Use inline theme switching for isolated components; apply the theme at the root for global coverage."
}
],
"keywords": [
"dark-mode",
"color-mode",
"night",
"low-light",
"theme",
"override"
]
}
}
Style
A style documents a design base. Guidelines like principles, scale, and motion are specific to styles.
{
"kind": "style",
"identifier": "spacing",
"name": "Spacing",
"metadata": [
{
"kind": "description",
"value": "The spatial system governing layout rhythm and component density."
},
{
"kind": "status",
"status": "stable"
}
],
"documentBlocks": [
{
"kind": "principles",
"items": [
{
"title": "Use the scale",
"description": "Always pick a value from the spacing scale. Avoid arbitrary pixel values."
}
],
"agents": {
"intent": "State the foundational beliefs governing this style.",
"keywords": [
"principles",
"guidelines"
]
}
}
],
"agents": {
"intent": "Document spacing scale tokens and enforce their consistent application across layout, components, and typography.",
"constraints": [
{
"rule": "Do not substitute spacing tokens with typography or icon-size tokens.",
"level": "must-not"
},
{
"rule": "Always use the next scale step up or down rather than custom values.",
"level": "should"
}
],
"disambiguation": [
{
"entity": "typography style",
"distinction": "Spacing governs whitespace and layout gaps; typography governs type size, weight, and line height."
}
],
"keywords": [
"spacing",
"scale",
"gap",
"padding",
"margin",
"whitespace",
"layout",
"density"
]
}
}
Pattern
A pattern describes how components compose to solve a user need. It supports interactions for flow steps.
{
"kind": "pattern",
"identifier": "form-validation",
"name": "Form Validation",
"metadata": [
{
"kind": "description",
"value": "Validates user input on form submission and communicates errors inline."
},
{
"kind": "status",
"status": "stable"
},
{
"kind": "links",
"items": [
{
"kind": "component",
"identifier": "form-field",
"role": "Displays inline error messages",
"required": true
},
{
"kind": "component",
"identifier": "button",
"role": "Triggers form submission",
"required": true
},
{
"kind": "component",
"identifier": "toast",
"role": "Confirms successful submission"
}
]
}
],
"agents": {
"intent": "Guide multi-component composition for surfacing, communicating, and resolving form validation errors.",
"constraints": [
{
"rule": "Always include both an inline field error and a summary when multiple fields fail.",
"level": "must"
},
{
"rule": "Do not use toast notifications as the sole error communication for form validation.",
"level": "must-not"
}
],
"disambiguation": [
{
"entity": "empty-state",
"distinction": "Use error-messaging for validation and system errors; use empty-state for the absence of content."
},
{
"entity": "alert",
"distinction": "Use error-messaging for form validation flows; use alert for non-form system or status messages."
}
],
"antiPatterns": [
{
"description": "Showing errors only on submit without inline field feedback.",
"instead": "Show inline errors on field blur and in a summary on submit."
}
],
"keywords": [
"error",
"validation",
"form",
"feedback",
"inline-error",
"error-summary",
"alert"
]
}
}
Full document {#minimal}
Here's a complete, minimal DSDS file with one of each entity type:
{
"$schema": "../schema/dsds.bundled.schema.json",
"dsdsVersion": "0.2.1",
"metadata": {
"systemName": "Minimal Example"
},
"documentation": [
{
"name": "Minimal Design System",
"components": [
{
"kind": "component",
"identifier": "button",
"name": "Button",
"metadata": [
{
"kind": "description",
"value": "A clickable button for triggering actions."
},
{
"kind": "status",
"status": "stable"
}
],
"agents": {
"intent": "Trigger a user-initiated action within the current view without causing page navigation.",
"constraints": [
{
"rule": "Do not use for navigating to a different page or URL.",
"level": "must-not"
},
{
"rule": "Limit each surface to one primary-emphasis button.",
"level": "must"
},
{
"rule": "Always provide an accessible label via visible text or aria-label.",
"level": "must"
}
],
"disambiguation": [
{
"entity": "link",
"distinction": "Use button for in-page actions; use link for navigation to a URL."
},
{
"entity": "icon-button",
"distinction": "Use button when a visible text label is present; use icon-button for icon-only affordances."
}
],
"antiPatterns": [
{
"description": "Using a button to navigate to another page.",
"instead": "Use a link element with href."
},
{
"description": "Placing multiple primary buttons on the same surface.",
"instead": "Use one primary and one or more secondary or tertiary buttons."
}
],
"keywords": [
"action",
"submit",
"click",
"CTA",
"trigger",
"call-to-action",
"interactive"
]
}
}
],
"tokenGroups": [
{
"kind": "token-group",
"identifier": "tokens",
"children": [
{
"kind": "token",
"identifier": "color-text-primary",
"tokenType": "color",
"source": {
"file": "tokens/color.tokens.json",
"path": "color.text.primary"
}
},
{
"kind": "token",
"identifier": "color-text-secondary",
"tokenType": "color",
"source": {
"file": "tokens/color.tokens.json",
"path": "color.text.secondary"
}
}
],
"agents": {
"intent": "Define and organize a set of related design tokens, providing shared defaults for type and category.",
"constraints": [
{
"rule": "Do not reference base palette tokens directly in component code.",
"level": "must-not",
"context": "Always reference semantic tokens that point to palette values."
}
],
"keywords": [
"token",
"group",
"palette",
"scale",
"family",
"collection"
]
}
},
{
"kind": "token-group",
"identifier": "color-text",
"source": {
"file": "tokens/color.tokens.json",
"path": "color.text"
},
"children": [
{
"kind": "token",
"identifier": "color-text-primary",
"tokenType": "color",
"source": {
"file": "tokens/color.tokens.json",
"path": "color.text.primary"
}
},
{
"kind": "token",
"identifier": "color-text-secondary",
"tokenType": "color",
"source": {
"file": "tokens/color.tokens.json",
"path": "color.text.secondary"
}
}
],
"agents": {
"intent": "Define and organize a set of related design tokens, providing shared defaults for type and category.",
"constraints": [
{
"rule": "Do not reference base palette tokens directly in component code.",
"level": "must-not",
"context": "Always reference semantic tokens that point to palette values."
}
],
"keywords": [
"token",
"group",
"palette",
"scale",
"family",
"collection"
]
}
}
],
"themes": [
{
"kind": "theme",
"identifier": "dark",
"name": "Dark Mode",
"metadata": [
{
"kind": "description",
"value": "Dark color mode for low-light environments."
},
{
"kind": "status",
"status": "stable"
}
],
"overrides": [
{
"token": "color-text-primary",
"description": "High-contrast text on dark surfaces."
},
{
"token": "color-text-secondary",
"description": "Muted text for secondary content in dark mode."
}
],
"agents": {
"intent": "Apply a dark color palette for low-light environments and user preference, maintaining WCAG AA contrast throughout.",
"constraints": [
{
"rule": "Always apply at the application root, not on individual components.",
"level": "must"
},
{
"rule": "Do not mix dark-mode and light-mode semantic tokens on the same surface.",
"level": "must-not"
}
],
"disambiguation": [
{
"entity": "high-contrast",
"distinction": "Use dark for low-light comfort; use high-contrast for users with low vision requiring maximum contrast."
}
],
"antiPatterns": [
{
"description": "Applying dark theme only to part of a page while the rest remains light.",
"instead": "Use inline theme switching for isolated components; apply the theme at the root for global coverage."
}
],
"keywords": [
"dark-mode",
"color-mode",
"night",
"low-light",
"theme",
"override"
]
}
}
],
"styles": [
{
"kind": "style",
"identifier": "color",
"name": "Color",
"metadata": [
{
"kind": "description",
"value": "The color system governing palette usage, semantic mapping, and accessibility."
},
{
"kind": "status",
"status": "stable"
}
],
"agents": {
"intent": "Document spacing scale tokens and enforce their consistent application across layout, components, and typography.",
"constraints": [
{
"rule": "Do not substitute spacing tokens with typography or icon-size tokens.",
"level": "must-not"
},
{
"rule": "Always use the next scale step up or down rather than custom values.",
"level": "should"
}
],
"disambiguation": [
{
"entity": "typography style",
"distinction": "Spacing governs whitespace and layout gaps; typography governs type size, weight, and line height."
}
],
"keywords": [
"spacing",
"scale",
"gap",
"padding",
"margin",
"whitespace",
"layout",
"density"
]
}
}
],
"patterns": [
{
"kind": "pattern",
"identifier": "form-validation",
"name": "Form Validation",
"metadata": [
{
"kind": "description",
"value": "Validates user input and communicates errors on form submission."
},
{
"kind": "status",
"status": "stable"
}
],
"agents": {
"intent": "Guide multi-component composition for surfacing, communicating, and resolving form validation errors.",
"constraints": [
{
"rule": "Always include both an inline field error and a summary when multiple fields fail.",
"level": "must"
},
{
"rule": "Do not use toast notifications as the sole error communication for form validation.",
"level": "must-not"
}
],
"disambiguation": [
{
"entity": "empty-state",
"distinction": "Use error-messaging for validation and system errors; use empty-state for the absence of content."
},
{
"entity": "alert",
"distinction": "Use error-messaging for form validation flows; use alert for non-form system or status messages."
}
],
"antiPatterns": [
{
"description": "Showing errors only on submit without inline field feedback.",
"instead": "Show inline errors on field blur and in a summary on submit."
}
],
"keywords": [
"error",
"validation",
"form",
"feedback",
"inline-error",
"error-summary",
"alert"
]
}
}
]
}
]
}
Validate your document
DSDS ships with a JSON Schema for validating your documents.
Using the bundled schema
Add $schema to get editor autocompletion and inline validation:
{
"$schema": "https://designsystemdocspec.org/v0.2.1/dsds.bundled.schema.json",
"dsdsVersion": "0.2.1",
"entity": {
"kind": "component",
"identifier": "my-component",
"name": "My Component",
"metadata": []
}
}
Using the CLI
# Clone the repo
git clone https://github.com/somerandomdude/design-system-documentation-schema.git
cd design-system-documentation-schema
npm install
# Validate the built-in examples
npm run validate
# Validate your own file
npx ajv validate -s spec/schema/dsds.bundled.schema.json -d my-system.dsds.json
Next steps
You've seen the basics. Here's where to go deeper.
| Resource | Description |
|---|
| Full Spec | Complete schema reference for every field and constraint |
| Interactive Samples | Side-by-side JSON ↔ rendered documentation for real-world entities |
| JSON Schema | The raw .schema.json files — use for editor autocompletion |
| Example Files | Complete, valid example documents for every entity and guideline type |
| GitHub Discussions | Ask questions, share ideas, propose changes |
Getting started recipe:
- Copy the minimal document above
- Replace the example entities with your own design system's components and tokens
- Add documentBlocks entries to each entity as your docs grow
- Validate with npm run validate to catch schema errors early
Design System Documentation Spec (DSDS) 0.2.1 — Draft Specification
GitHub