What is DSDS?DSDS (Design System Doc Spec) is a YAML format for documenting design systems. It puts every piece of docs — components, tokens, themes, and anything else — in a machine-readable shape: a graph of entries, each carrying typed sections.
Write your documents in YAML — every example on this site, the default npm run check sweep, and the tooling all assume it. The schema itself is written in JSON Schema (that's the language the rules are written in, a separate thing from the format your documents need to be in). Since YAML is a superset of JSON, a JSON document would technically still parse — but nothing in this repo discovers or exercises that path, so don't rely on it.
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 — the schema catches errors before they reach consumers
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@ @@
@@ @@
@@ @@@@@@@@@@@ @@@@@@@@@@ @@
@@ @@ @@ @@ @@ @@
@@ @@ @@ @@ @@
@@ @@ @@ @@@@@@@@@@ @@
@@ @@ @@ @@ @@
@@ @@ @@ @@ @@ @@
@@ @@@@@@@@@@@ @@@@@@@@@@ @@
@@ @@
@@ @@@@@@@@@@@ @@@@@@@@@@ @@
@@ @@ @@ @@ @@ @@
@@ @@ @@ @@ @@
@@ @@ @@ @@@@@@@@@@ @@
@@ @@ @@ @@ @@
@@ @@ @@ @@ @@ @@
@@ @@@@@@@@@@@ @@@@@@@@@@ @@
@@ @@
@@ @@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Document structureA DSDS document is a base: schemaVersion, a name, and a list of entries.
The bare minimumThis much is valid YAML, but not a valid document yet — entries is required:
schemaVersion: "0.20.0"
name: Acme Design System
Adding a system entrySystem-wide facts (version, organization, url, license, platforms) live on this list's own kind: system entry, not on the base document directly:
schemaVersion: "0.20.0"
name: Pizza Party Design System
entries:
- id: pizza-party-design-system
kind: system
name: Pizza Party Design System
description: The design system behind Pizza Party Super-time.
metadata:
organization: Pizza Party Super-time LLC
version: 0.1.0
status: {status: alpha}
platforms: [web-component]
Adding more entriesAny entry — a component, a token, a theme, or the generic entry kind — can sit alongside the system entry in the same entries array. Here, a system entry plus a component entry:
schemaVersion: "0.20.0"
name: Pizza Party Design System
entries:
- id: pizza-party-design-system
kind: system
name: Pizza Party Design System
description: The design system behind Pizza Party Super-time.
metadata:
organization: Pizza Party Super-time LLC
version: 0.1.0
status: {status: alpha}
platforms: [web-component]
- id: button
kind: component
name: Button
description: Triggers an action.
Splitting across files with refsFor a larger system, keep each entry in its own file and point at it with refs (rel: file) instead of inlining everything — pointing at a sibling document that owns another entry:
schemaVersion: "0.20.0"
name: Pizza Party Design System
entries:
- id: pizza-party-design-system
kind: system
name: Pizza Party Design System
description: The design system behind Pizza Party Super-time.
metadata:
organization: Pizza Party Super-time LLC
version: 0.1.0
status: {status: alpha}
platforms: [web-component]
refs:
- href: ./components/button.dsds.yaml
rel: file
role: Button component, documented in its own sibling fileUse a multi-file split for large systems where a different team owns each component. Use one file with everything inlined for smaller systems. scripts/compose.js can concatenate many hand-authored fragment files into one document before validation — see the repo's own examples/base/.
Entry kindsEvery entry has a kind field. There are 5 well-known values, plus an open option for anything else.
| Kind | Description |
|---|
| system | The design system as a whole — version, organization, url, license, platforms, plus system-wide documentation. |
| component | A reusable UI element — buttons, inputs, modals. Carries its own sourceFiles, imports, traits (variants and states), and combos, on top of the fields every entry shares. |
| token | A single design token. Carries tokenType and a source pointer to the real DTCG value — never the value itself. |
| theme | A named set of token overrides — dark mode, high-contrast, a brand variant. Points at its own DTCG source file. |
| entry | The generic, open kind for anything else — a foundation, a pattern, a guide. Has no fields beyond what every entry shares. |
| (custom) | A custom kind like acme.icon-library, for a document that wants its own recognizable name instead of the generic entry. |
Fields every entry sharesEvery entry kind shares one open base: id, kind, name, description (required), plus purpose, metadata, related, extends, refs, sections, $extensions (optional).
related, extends, and refs are three separate pointer lists, not three names for the same thing — each is scoped to a different kind of connection: related for entries that are similar in usage (rel: alternative-to, rel: pairs-with), extends for inheritance (rel: extends), and refs for anything else, including outside resources (rel: source, rel: storybook). This entry-level refs is the same field name as the base document's refs used above to split into files (rel: file) — same mechanism, different level, different job.
See How the schema is organized for how the schema itself is put together.
StatusStatus lives in metadata.status, always as an object — there's no bare-string shorthand. A component-wide status:
metadata:
status: {status: stable}Scope a status to one platform when a component ships on more than one:
metadata:
status:
platform: react
status: deprecated
deprecationNotice: Use icon-button instead — this variant never got contrast-tested.
The section systemStructured docs live in the sections array on each entry. Each section has a kind field naming its type — guidelines, definitions, steps, or the generic section. Any entry kind can use any section kind; nothing restricts which section kinds go with which entry kind.
Every section also carries a for field (human, agent, or all) naming its audience — see Humans and agents on the Overview page.
Guidelines: rules paired with why they existEach guideline item pairs a statement with a level (an RFC 2119 requirement level) and, optionally, alternatives, evidence, or a checkedBy/checks verification pair. A component with a guidelines section:
id: button
kind: component
name: Button
description: Triggers an action.
purpose: Gives users a single, consistent way to trigger an action across the product.
metadata:
status: {status: stable}
tags: [actions, form]
sections:
- kind: guidelines
for: all
items:
- statement: Limit each surface to one primary button.
level: shouldThe level field's values are lowercase kebab-case, like every DSDS vocabulary: must, should, should-not, must-not, may. Tools display them as badges: MUST, SHOULD, SHOULD NOT, MUST NOT. Agents treat must/must-not items as hard limits.
A guidelines section also carries framing: when-to-use for a fit judgment (is this entry the right choice at all), or how-to-use (the default) for an implementation rule once it's chosen.
Definitions: a glossary, anatomy, or prop listA definitions section pairs a term with its definition — use it for anatomy parts, naming conventions, or a component's own prop/event list when there's no real source file to extract from. context (a field every section kind can carry, not just definitions — see sections/section) names which of those jobs it's doing (anatomy, terms, keyboard, events, or a namespaced custom value) — optional, but it's what lets a tool find "the anatomy table" without matching on the human-facing title. Here's an anatomy definitions section:
sections:
- kind: definitions
for: all
title: Anatomy
context: anatomy
items:
- term: Container
definition: The interactive root element. Receives background, border, radius, and padding.
- term: Label
definition: The visible text of the button.
Steps: a procedure or checklistA steps section is an ordered procedure or an unordered checklist (ordered: false). Each item can point back at the guideline it verifies via checks (rel: depends-on). A self-check checklist:
sections:
- kind: steps
for: agent
ordered: false
title: Self-check before shipping
items:
- title: Icon-only buttons have an aria-label
checks:
- to: button#aria-label-required
rel: depends-on
Freeform: narrative proseEvery section kind — including the generic section — can also carry freeform: headed, nestable prose alongside its own structured items:
sections:
- kind: section
for: all
title: Overview
freeform:
- title: About
body: Button is the primary interactive primitive in this design system.
A component's own top-level fieldsUnlike sections, a few facts about a component live directly on the entry, not inside a section — they're facts about the component as a build artifact, not documentation content:
- sourceFiles — one entry per platform, pointing at the real source file a tool can extract the component's API from. Replaces hand-typed prop tables.
- imports — one entry per platform, with the install package and the exact import statement.
- traits — every way the component can vary: a kind: enum dimension (like size: sm | md | lg) or a kind: boolean toggle (like hover or loading). A trait's own optional setBy (consumer or component) says whether it's a value the consumer sets, or a condition the component sets on its own — kind alone doesn't tell you that, since a boolean trait can be either (disabled is usually a prop the consumer passes in; hover never is).
- combos — pairing rules between traits, tokens, or entries (e.g. "loading and disabled must not both be set").
Here's sourceFiles, traits, and combos together:
sourceFiles:
- platform: react
file: ./src/Button.tsx
traits:
- id: tone
kind: enum
name: Tone
setBy: consumer
values:
- id: default
description: Neutral. General-purpose actions.
- id: critical
description: Destructive or irreversible actions only.
- id: loading
kind: boolean
name: Loading
description: An async operation triggered by the button is in progress.
setBy: consumer
- id: hover
kind: boolean
name: Hover
description: Background darkens slightly when the pointer is over the button.
setBy: component
combos:
- subject: loading
level: should-not
items: [disabled]
note: Loading already makes the button non-interactive.
Going beyond the basics$extensions$extensions is a place for vendor or tool-specific data, at the document, entry, or section level. Keys MUST use a namespace (e.g. com.figma) so a tool integration never collides with a future field the spec adds. Here, linking a component to its Figma source:
id: button
kind: component
name: Button
description: Triggers an action.
$extensions:
com.figma:
context: Links this entry to its source Figma component for design-file lookups.
nodeId: "12:4045"
Custom kindsWhen the generic entry kind isn't specific enough, use a custom kind instead — it's checked against the same open entry.schema.yaml base the generic kind is:
id: material-icons
kind: acme.icon-library
name: Material Icons
description: The icon set Acme's products ship with.See Extending the schema for a third option too — profiles, for making an existing kind's optional fields required on your own project — and for more on when to reach for each of the three.
Minimal examplesThese are close to the smallest valid entry for each kind. Copy one, fill in your content, and add sections as your docs grow.
ComponentA minimal standalone component entry:
id: button
kind: component
name: Button
description: Triggers an action.
TokenA token needs id, kind, name, description, and (usually) tokenType. Use source to point back at the DTCG file that holds the real value:
id: color.action.primary
kind: token
name: Action Primary
description: The fill color for high-emphasis interactive surfaces.
tokenType: colorA described token adds metadata.group (the recommended way to group related tokens — there's no separate token-group kind) and a guideline:
id: color.action.primary
kind: token
name: Action Primary
description: The fill color for high-emphasis interactive surfaces.
tokenType: color
source: ./tokens.dtcg.json
metadata:
status: {status: stable}
group: color.action
sections:
- kind: guidelines
for: all
items:
- statement: Pair with `color.surface.default` or `color.surface.raised` only — contrast is unverified on any other background.
level: must
A pattern, using the generic entry kindA pattern, documented as a generic entry:
id: empty-state
kind: entry
name: Empty State
description: A pattern shown in place of a list or grid that has no content yet.
purpose: Tells the user why an area is empty and what to do next, instead of leaving a blank surface.
sections:
- kind: guidelines
for: all
framing: when-to-use
items:
- statement: Use an empty state the first time a user visits a list or grid that has no content yet.
level: should
Shared contentContent that isn't itself a design-system artifact — an accessibility rule that applies to more than one entry, stated once and pointed at from everywhere it applies — lives in the base document's shared array, addressed via entryId#itemId and rel: same-as — a shared rule, referenced instead of restated:
schemaVersion: "0.20.0"
name: Pizza Party Design System
entries:
- id: button
kind: component
name: Button
description: Triggers an action.
sections:
- kind: guidelines
for: all
items:
# No `statement` here — this item IS the "touch-target" rule
# declared once on shared-a11y below, not a second copy of it.
- level: must
refs:
- to: shared-a11y#touch-target
rel: same-as
shared:
- id: shared-a11y
name: Shared Accessibility Rules
description: Cross-cutting accessibility rules, stated once and referenced from every entry they apply to.
sections:
- kind: guidelines
for: all
items:
- id: touch-target
statement: Minimum touch target 44x44px.
level: must
Validate your documentUsing the bundled schemaAdd $schema to get editor autocompletion and inline validation:
$schema: https://designsystemdocspec.org/v0.20.0/dsds.bundled.yaml
id: my-component
kind: component
name: My Component
description: What this component is and does.
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 and test corpus
npm run check
# Validate your own file
node scripts/validate.js my-system.dsds.yaml
Next stepsYou've seen the basics. Here's where to go deeper.
| Resource | Description |
|---|
| Full Spec | Complete schema reference for every field and constraint |
| Schema files | The raw .schema.yaml files — use for editor autocompletion |
| Example files | Complete, valid example documents for every entry and section kind |
| GitHub Discussions | Ask questions, share ideas, propose changes |
- Copy the minimal component example above
- Replace it with your own design system's first component
- Add sections as your docs grow
- Validate with npm run check to catch schema errors early