The schema says what a document can contain. This page says what a tool works out when it reads one. For example: which item a # address points to, where a reused rule gets its text, and what replaces something deprecated.

This page sets requirements. MUST, MUST NOT, SHOULD, SHOULD NOT and MAY mean what RFC 2119 says, and only when written in capitals. The requirements apply to a conforming consumer: anything that reads a DSDS document and does something with it. Conformance covers the other kinds of tool.

Each MUST ends with what checks it, in parentheses, as the specification's §1.2 describes. That's a validator rule, a test vector, or consumer when no check on a document can see it. npm run check fails if a MUST names nothing. scripts/resolve/resolve.js is the reference implementation. The test vectors hold the answers it gives, and every implementation has to give the same ones.

1. Terms
  • A project is all the documents a consumer reads together (§3).
  • An address is the part of a ref's to after the #. In shared-a11y#focus-visible, the address is focus-visible.
  • An item is one member of a section's items, or one freeform entry.
  • A chain is a path a consumer follows from one thing to the next, through uses, extends, same-as or replaced-by (§9).
2. Addresses 2.1 Entries

A consumer MUST treat entries and shared as one set of ids, so no two entries in a project can share an id (DSDS-04).

2.2 Items

A to written as entryId#address points at one thing inside an entry. A consumer MUST work out each thing's address with these rules (vector: addresses):

  1. An item with an id uses that id.
  2. An item without an id uses an id made from its own text: a guideline's statement, a definition's term, or a step's or freeform entry's title. To make it, lowercase the text, turn each run of characters other than a–z and 0–9 into one dash, and remove any dash at either end. "Name it!" becomes name-it. This works for freeform entries at any depth.
  3. An item with neither, because it only reuses another item with same-as (§4), uses the address of the item it reuses. That's the part of its same-as target after the #.
  4. Anything else inside an item that has its own id, such as an example, uses that id.
  5. A component's trait uses its id. One value of an enum trait uses traitId.valueId, as in button#variant.primary. Combos use the same form. It can't be misread, because a trait id can't contain a dot.
2.3 When two things share an address

Two things on one entry can end up with the same address. A consumer MUST pick the one whose address has the strongest source, in this order (vector: addresses; DSDS-05):

  1. an id written in the document (rules 1, 4 and 5);
  2. an id made from the text (rule 2);
  3. an address taken from a reused item (rule 3).

If two things tie for the strongest source, the address is ambiguous. A consumer MUST NOT choose between them, because it's an error (DSDS-05). The fix is to give one of the two its own id.

So when an item and another item reusing it sit on the same entry, their shared address points at the item that states the rule.

3. Projects

A consumer looks up refs across a whole project.

  • A project MUST include every document a consumer can reach by following rel: file refs from the documents it was given, then from those, and so on (DSDS-08; vector: cross-file).
  • A consumer MUST NOT follow a rel: file ref out of the folder that holds the document, or its subfolders (consumer). A document is data from whoever wrote it, and could point anywhere. See Security.
  • rel: file is only for DSDS documents. A code file or a DTCG token file is rel: source, and a consumer doesn't read it as part of the project (DSDS-25).
  • When the project's system entry sets metadata.sourceRoot, a consumer MUST read each relative sourceFiles path starting from there (DSDS-11). Without it, the path starts from the component's own document. A relative sourceRoot starts from the document that holds the system entry. If sourceRoot points outside that document's folder, the consumer still decides whether to open it.
  • A consumer given several documents at once SHOULD treat them as one project. That's how a standalone entry file can point at the files next to it, since it has no field to list them.
  • A ref to something not in the project is an error when the project is known to be complete: a base document with no rel: file refs, read on its own. Otherwise the target may be in a file the consumer didn't read, so it's a warning (DSDS-05, DSDS-08).
4. Reused content

An item with a same-as ref reuses the content of the item it points at.

  • A consumer MUST show a reusing item with its effective content: each content field the item states itself, plus every other content field from the item it reuses (vector: reuse). The content fields are:
    • for a guideline: level, statement and checkedBy;
    • for a definition: term, definition and usage;
    • for a step: title and description.
  • If the reused item itself reuses another, a consumer MUST follow the chain to the item that states the content (vector: reuse). It SHOULD say which item that is.
  • A reusing item MUST point at an item of the same section kind (DSDS-25). A guideline can't reuse a definition.
  • When both items state a level, the two MUST match (DSDS-10). An item that means to state a different level is a refines, not a same-as.
  • When the target of a same-as ref can't be found, a consumer SHOULD show the reusing item's level and the link, rather than nothing (see sections/guidelines).

refines works differently. A for: agent item uses refines to point at the human rule it states more precisely for an agent. A consumer reading for an agent SHOULD apply both rules. The refinement doesn't replace the rule it refines.

5. Relationships and their reverse

A relationship is written once, on the thing it starts from. A conforming consumer works out the reverse direction itself, and MUST NOT require it to be written on the other end too (vector: inverse).

A pointer in refs says what kind it is with rel. A pointer in a field named for one kind of pointer has no rel, and a consumer MUST read it as the kind that field stands for (vector: inverse). When an object has a field for a kind of pointer, that kind MUST go in the field, not in refs (DSDS-25). Eight of the fourteen kinds have their own field, and refs holds the other six. source also has fields that each hold one: a token's or theme's source, and the file in each of a component's sourceFiles.

Every rel, where it's written, and what a consumer does with it:

relWhere it's writtenWhat it meansRead backwardsWhat a consumer does
usesuses, on an entry or a section itemThis is built from the target, needs it, or follows it. A dialog uses its button. A button uses a color token, and the pattern it follows.used byFollows uses backwards to answer "what is affected if this changes?" Follows it as a chain (§9).
same-asrefs, on an itemThis item reuses another item's content instead of repeating it.reused byShows the reusing item with its effective content (§4).
replaced-byreplacedBy, in the deprecated status of an entry, trait or trait valueThe target replaces this.replacesFollows it to the successor (§7).
filerefsAnother DSDS document in the same project. A code or token file is source instead.—Reads the target as part of the project (§3).
sourcerefs; a token's or theme's source; a component's sourceFiles[].fileWhere this is built: a repository, a source file, a DTCG token file or a package.—MAY fetch it. Reads a relative sourceFiles path from the system's sourceRoot (§3).
designrefsThe design file this comes from.—MAY link to it.
demorefs, or an example's refA live demo, such as a Storybook story.—MAY link to it or embed it.
relates-torefsWorth reading alongside this, when no other kind fits. On a guideline item with no statement and an href, it's where the rule is tracked.the same both waysMAY show it as "see also". For an item with no statement, SHOULD show the level and the link (§4).
extendsan entry's extendsThis inherits from the target.extended byNever merges the target in (§6). Follows it as a chain (§9).
alternative-toalternatives, on an entry or a guideline itemUse the target instead, in the situation the entry or rule describes.the same both waysOffers the target when this one doesn't fit.
citesa guideline's evidenceThe outside standard the rule is based on.cited bySHOULD show it, so a reader can tell a requirement from a house preference.
checked-bya guideline's checksWhat checks the rule: a test, lint rule or agent eval (an href, with role saying which), or a checklist step (a to).checksRuns it or links to it. checkedBy: automated needs one with an href (DSDS-03).
contracta component's specsA machine-readable API contract that was already generated.—Reads the component's props, slots and events from it, with whatever parser its format needs.
refinesa guideline's refines, on a for: agent itemThis states a human rule more precisely, for an agent.refined byWhen reading for an agent, applies both rules (§4).

A "—" means the pointer leads outside the project, so nothing in the project reads it backwards. Custom namespaced rel values are covered in §8.

When a consumer shows an entry's relationships, it SHOULD include the ones written on other entries that point at it. A pointer inside an item belongs to that item. So a step that uses button is a relationship from the step, not from its whole entry.

6. extends

extends says one entry inherits from another: a dark theme from a light one, or an icon button from a button.

A consumer MUST NOT copy the extended entry's fields or sections into the entry that extends it (consumer). The extending entry is complete as written. A consumer that reads it on its own reads all of it. A consumer MAY show the extended entry's content beside it, as long as it's clearly labelled as coming from that entry.

Why not merge them? An entry's meaning would then depend on another document the consumer may not have. And every consumer would need the same rules for merging lists, fields and overrides, or they'd disagree. What a theme changes lives in its DTCG source, which has its own rules for that.

7. Successors

A successor is what replaces something deprecated. replaced-by points from the deprecated thing to its successor.

  • The successor is named in replacedBy. It sits in the status that marks the entry, trait or trait value as deprecated, next to its deprecationNotice, where a reader learns the thing is going away. The schema only accepts replacedBy in a deprecated status. In one platform's status, it names that platform's successor.
  • To find what to use instead, a consumer MUST follow replaced-by (vector: successors). If the successor is deprecated too, with its own replaced-by, a consumer MUST follow that as well and use where it ends (vector: successors).
  • When there's more than one successor, a consumer MUST NOT pick one on its own (vector: successors). The deprecationNotice says when to choose which. A consumer that can't apply it MUST leave the choice to whoever it's working for (consumer). For example, an agent replacing variant="secondary-green" across a codebase asks, or leaves a note, instead of guessing.
8. Unknown values
  • When a consumer writes a document back out, it MUST keep any $extensions content it doesn't understand (consumer).
  • A consumer MAY ignore a custom namespaced rel (such as acme.themes) or a custom kind it doesn't understand. It MUST NOT report the document as invalid because of one (consumer).
  • A field the schema doesn't declare, on a generic entry or section, is most likely a typo or a leftover. A validator reports it as a warning (DSDS-24).
9. Chains and loops

A consumer follows four kinds of pointer from one thing to the next. Each one answers a question:

  • uses: what is affected if this changes?
  • extends: what does this inherit from?
  • same-as: where is this content stated?
  • replaced-by: what replaces this?

A chain MUST NOT lead back to where it started, because then its question has no answer (DSDS-06; vector: cycles). A loop is an error, and a consumer that finds one MUST stop following it instead of going round forever (vector: cycles).

uses and extends link entries. same-as and replaced-by link items, traits, trait values and entries. Each kind is checked on its own: a uses ref and an extends ref pointing at each other don't make a loop.

10. Problems

What scripts/resolve/resolve.js reports, and the rule that catches each one in a document:

ProblemWhat it meansRule
duplicate-entryTwo entries in the project have the same id.DSDS-04
unresolved-entryA ref points at an entry the project doesn't have.DSDS-08, DSDS-05
unresolved-itemA ref points at an address the entry doesn't have.DSDS-05
ambiguous-addressAn address points at two things, and neither one wins (§2.3).DSDS-05
level-mismatchA reusing item states a different level from the item it reuses.DSDS-10
same-as-kindA same-as ref points at a different kind of item.DSDS-25
replaced-by-misplacedA replaced-by pointer is on something that can't be deprecated.the schema, DSDS-25
replaced-by-not-deprecatedA replaced-by pointer is on something that isn't deprecated.the schema
cycleA chain leads back to where it started.DSDS-06
11. Test vectors

Each folder in examples/vectors/ holds one or more documents and an expected.json: what a conforming consumer should work out from them. They cover addresses (§2), a project split across files (§3), reused content (§4), reverse relationships (§5), successors (§7) and loops (§9).

They're published with the conformance suite at /v0.22.0/conformance-suite/manifest.json, next to the broken examples that validators are tested against. The manifest's consumerContract explains how to compare results. In short, everything has to match except each problem's message, which is text for people to read.

In this repo, node scripts/resolve/resolve.js prints what any project resolves to, and npm run check checks every vector.