Overview
Ditto spec files use YAML frontmatter in .ditto.md files. Everything lives between the --- delimiters; the markdown body below the closing --- is unused. There are two types: workspace specs and component specs.
Workspace spec
A repo has a single workspace.ditto.md somewhere under the CLI’s configured roots. It holds universal style guide rules that carry no tags — these apply to every surface in every component. It also carries an inventory of all tags available on the platform.
---
workspace: true
# Managed by Ditto — do not edit below
tags: [body, button, call-to-action, dialog-title, heading, nav]
rules:
- name: Write in active voice
description: Lead with verbs, avoid passive constructions
section: Voice & Tone
locales:
de-DE:
- name: Use informal address
description: Use "Du" instead of "Sie" for all user-facing copy
section: Formality
---
| Key | Managed by | Description |
|---|
workspace | Developer | Must be true. Marks this as the workspace spec. |
tags | CLI (pull) | All tags that exist on the platform. Used as a reference when tagging surfaces. |
rules | CLI (pull) | Universal style guide rules with no tag scope — apply to all surfaces everywhere. |
locales | CLI (pull) | Locale-scoped style guide rules, keyed by locale code. |
Component spec
Each component that renders user-facing text gets an index.ditto.md Ditto spec file in its directory.
---
component: DialogueModal
tags: [dialog, confirmation]
surfaces:
headline:
tags: [heading, dialog-title]
maxLength: 60
content:
tags: [body, dialog-body]
maxLength: 240
actionText:
tags: [call-to-action]
maxLength: 25
cancelText:
tags: [button]
maxLength: 25
# Managed by Ditto — do not edit below
rules:
- name: Confirmation dialogs should be direct
description: Keep confirmation copy terse and unambiguous
section: Voice & Tone
- surface: actionText
name: Calls to action should use active voice
description: Always lead with a verb
examples:
- from: "Your settings"
to: "Open settings"
section: Voice & Tone
- term: sign up
disallowed:
- signup
- sign-up
description: Always use as two words (verb form)
section: Terminology
locales:
de-DE:
- name: Use informal address
description: Use "Du" instead of "Sie" for all user-facing copy
section: Formality
---
component
The component name (string). Set when you run ditto-spec scaffold.
Component-level tags (array of strings) that describe what the component is in your design system — e.g. [dialog, confirmation] for a confirmation modal, [card, product] for a product card. Style guide rules matching any of these tags apply to all surfaces in the component, cascading content governance to every piece of text it renders. Edit these freely.
This is the primary mechanism for integrating Ditto specs into a design system. A component’s tags capture its role as a design system element, pulling in rules about how that type of component should read — tone, voice, constraints. The individual surfaces then carry their own tags for more specific rules (see below).
surfaces
Each key is a surface — a distinct piece of user-facing text the component renders.
| Property | Required | Description |
|---|
tags | Yes | Per-surface tags that describe the text’s role — e.g. [heading, dialog-title], [call-to-action]. Used for matching style guide rules to this specific surface. |
maxLength | No | Maximum character length. A hard layout constraint, not a stylistic preference. |
rules
Populated by ditto-spec pull. Style guide rules come in two shapes:
| Shape | Fields | Scope |
|---|
| Style rule | name, description, examples (optional {from, to} pairs), section | Without surface: all surfaces. With surface: "<key>": that surface only. |
| Terminology entry | term, disallowed, description, section | Without surface: all surfaces. With surface: "<key>": that surface only. |
locales
Populated by ditto-spec pull. Keyed by locale code (e.g. de-DE). Contains the same style guide rule shapes as rules, scoped to a specific locale. Locale-scoped rules apply in addition to base rules when writing copy for that locale.
Style guide rule hierarchy
| Scope | Where | Applies to |
|---|
| Workspace | workspace.ditto.md rules[] | Every surface in every component |
| Component-level | Component’s rules[], no surface field | Every surface in this component |
| Per-surface | Component’s rules[], with surface: "<key>" | That one surface |
| Locale-scoped | locales.<code>[] (workspace or component) | Same hierarchy as above, but only when writing copy for that locale |
Base style guide rules always apply. Locale-scoped rules apply only when writing copy for the matching locale — they never conflict because each locale is a separate scope.
Developer-owned vs CLI-managed keys
| Keys | Owner | Edit by hand? |
|---|
component, tags, surfaces | Developer | Yes — add, remove, and modify freely. |
rules, locales, workspace tags | CLI (pull) | No — overwritten on every pull. |
Never edit rules or locales by hand. Run ditto-spec pull to update them from the platform.
Surface naming conventions
| Scenario | Key to use | Example |
|---|
| String prop | The prop name | title, description |
| Nested prop | Dot notation | primaryAction.label |
| Children prop | $children | $children |
| Hardcoded/internal string | Descriptive role name | headline, bodyText, submitLabel |
Check the tags key in workspace.ditto.md for tags available on the platform. Prefer reusing an existing tag over creating a new one — only tags that exist on the platform will match style guide rules.
Tagging in a design system
Ditto specs support two levels of tagging that mirror how design systems organize components:
- Component-level tags describe the component itself — its role in the design system. A
DialogueModal tagged [dialog, confirmation] pulls in style guide rules about how confirmation dialogs should read. These rules apply to every surface in the component.
- Surface-level tags describe each individual piece of text — its function within the component. An
actionText surface tagged [call-to-action] pulls in rules specific to CTAs (e.g. “lead with a verb”). These rules apply only to that surface.
Both levels work together. For a DialogueModal:
tags: [dialog, confirmation] # rules about dialogs apply to ALL surfaces
surfaces:
headline:
tags: [heading, dialog-title] # + rules about headings apply here
actionText:
tags: [call-to-action] # + rules about CTAs apply here
cancelText:
tags: [button] # + rules about buttons apply here
The headline surface inherits style guide rules matched by dialog, confirmation (from the component), plus rules matched by heading, dialog-title (from the surface). If a rule matches both levels, it appears once at component level — broader scope wins.
This means you can create style guide rules on the Ditto platform scoped to design system concepts (dialog, card, form, navigation) and have them automatically cascade to every component tagged with that concept, while surface-level tags layer on more specific guidance.
File discovery
The CLI searches directories listed in the roots config for any *.ditto.md files. workspace.ditto.md is identified by its workspace: true key; all other .ditto.md files are treated as component Ditto specs.