---
title: Documentation
source: 'https://academia.sh/en/courses/design-systems/documentation'
course: 'Design Systems'
language: en
updated: '2026-08-19T05:19:54+00:00'
license: 'CC BY-SA 4.0'
---

# Documentation

The required sections of component documentation, the information a counter-example carries, computing documentation coverage at the section and component level, and comparing the prop table against the interface.

Once names match, it becomes possible to talk about a component, but that is not
enough to use it. Which state calls for `dropdown` instead of `search-field`, when a
`modal` gives way to a separate page instead, and the distinction between `badge` and
`notification-banner` cannot be read from names.

Documentation carries these decisions. Component documentation is not a usage guide; it
is **the readable form of the component's contract**: what it does, what it does not
do, what input it takes, when it should not be chosen. This lesson defines the doc's
required sections and shows how to measure coverage.

## The Doc's Required Sections

A component doc's sections correspond to the questions the person using it asks in
order:

- **Purpose.** What job does the component do? One sentence. A component whose purpose
  takes two sentences to explain is usually two components.
- **When to use.** In which context it is the right choice. This section does not
  describe the component; it gives a **selection criterion**.
- **When not to use.** The counter-example. It says which component to use instead.
- **Prop table.** The names of the props, the values they accept, their defaults, and
  whether they are required.
- **Example.** Usage that can be copied and run. Source text, not a picture.
- **Accessibility note.** Keyboard behavior, focus order, and the line between what the
  component provides on its own and what the person using it must provide.
- **Related components.** Links to neighboring components; the person using it may have
  started from the wrong place.

Six of these seven sections are familiar. Across most systems, the two most often
missing are the counter-example and related components; both do the same job:
steering the person **away from** this component.

## The Information a Counter-Example Carries

The example shows the correct use of a component. The counter-example shows something
else: the component's **boundary**. Neither substitutes for the other.

A concrete example from the catalog interface: `badge` shows a record's status —
"on the shelf," "on loan," "reserved." `notification-banner` reports an event to the
user — "your loan was completed." Both are small, colored, short-text boxes describing
a state. Only the counter-example puts the distinction into writing: a badge is a
property of an **object** and stays with it; a notification banner is the result of an
**action** and appears after it. This cannot be read from the prop table, since the two
components' props are nearly identical.

The counter-example's second job is closing the escape hatch. As shown in the
Component-Based Interface Development course, a component hands its caller an escape
hatch, and if the doc does not say where that hatch ends, every team draws its own
boundary. "Do not put a button inside a badge — use `button` for a clickable state" is
a rule, and it can be checked.

## Computing Coverage

Tracking documentation as "done" or "not done" is not useful; docs are written
partially and go stale partially. The program below takes the catalog's nineteen
documented components, computes coverage at the section and component level, sorts
gaps by usage impact, and finally compares the prop table against the real interface.

```js
// docs.mjs — component-level and section-level documentation coverage

// A component doc's required sections.
const SECTION = ["purpose", "when-to-use", "when-not-to-use", "prop-table", "example", "accessibility-note", "related"];

// For each component, the sections it has and its usage count (same catalog as lesson 01).
const DOCS = [
  { name: "button",              usage: 412, has: ["purpose", "when-to-use", "when-not-to-use", "prop-table", "example", "accessibility-note", "related"] },
  { name: "link",                usage: 268, has: ["purpose", "when-to-use", "when-not-to-use", "prop-table", "example", "accessibility-note", "related"] },
  { name: "badge",               usage: 188, has: ["purpose", "when-to-use", "when-not-to-use", "prop-table", "example", "accessibility-note"] },
  { name: "card",                usage: 157, has: ["purpose", "when-to-use", "when-not-to-use", "prop-table", "example", "accessibility-note", "related"] },
  { name: "text-field",          usage: 143, has: ["purpose", "when-to-use", "when-not-to-use", "prop-table", "example", "accessibility-note", "related"] },
  { name: "checkbox",            usage:  96, has: ["purpose", "when-to-use", "prop-table", "example", "accessibility-note", "related"] },
  { name: "search-field",        usage:  71, has: ["purpose", "when-to-use", "when-not-to-use", "prop-table", "example", "accessibility-note", "related"] },
  { name: "notification-banner", usage:  63, has: ["purpose", "when-to-use", "prop-table", "accessibility-note"] },
  { name: "dropdown",            usage:  57, has: ["prop-table", "example"] },
  { name: "empty-state",         usage:  52, has: ["purpose", "when-to-use", "when-not-to-use", "prop-table", "example", "accessibility-note", "related"] },
  { name: "modal",               usage:  45, has: ["purpose", "when-to-use", "prop-table", "accessibility-note"] },
  { name: "pagination",          usage:  41, has: ["purpose", "when-to-use", "when-not-to-use", "prop-table", "example", "accessibility-note", "related"] },
  { name: "skeleton",            usage:  38, has: ["purpose", "when-to-use", "prop-table", "example"] },
  { name: "radio-group",         usage:  34, has: ["purpose", "when-to-use", "when-not-to-use", "prop-table", "example", "related"] },
  { name: "breadcrumb",          usage:  29, has: ["purpose", "when-to-use", "when-not-to-use", "prop-table", "example", "accessibility-note", "related"] },
  { name: "tabs",                usage:  24, has: ["purpose", "when-to-use", "prop-table", "example"] },
  { name: "accordion",           usage:  17, has: ["purpose", "prop-table", "example"] },
  { name: "tooltip",             usage:  12, has: ["prop-table"] },
  { name: "table",               usage:   9, has: ["prop-table", "example"] },
];

// Section-level coverage: which section is missing most often?
console.log("section              has doc      component share   usage share");
const totalUsage = DOCS.reduce((t, b) => t + b.usage, 0);
for (const sec of SECTION) {
  const has = DOCS.filter((b) => b.has.includes(sec));
  const k = has.reduce((t, b) => t + b.usage, 0);
  console.log(
    `${sec.padEnd(20)} ${String(has.length).padStart(6)}/${DOCS.length} ` +
    `${((has.length / DOCS.length) * 100).toFixed(1).padStart(15)}% ` +
    `${((k / totalUsage) * 100).toFixed(1).padStart(11)}%`
  );
}

// Component-level coverage and gap impact.
const rows = DOCS.map((b) => {
  const missing = SECTION.filter((sec) => !b.has.includes(sec));
  return { ...b, missing, coverage: (SECTION.length - missing.length) / SECTION.length, impact: missing.length * b.usage };
}).sort((a, b) => b.impact - a.impact);

console.log("\ncomponent             coverage   usage   gap impact   missing sections");
for (const r of rows) {
  if (r.missing.length === 0) continue;
  console.log(
    `${r.name.padEnd(21)} ${(r.coverage * 100).toFixed(0).padStart(7)}% ${String(r.usage).padStart(7)} ` +
    `${String(r.impact).padStart(12)}   ${r.missing.join(", ")}`
  );
}

const unweightedCoverage = rows.reduce((t, r) => t + r.coverage, 0) / rows.length;
const weightedCoverage = rows.reduce((t, r) => t + r.coverage * r.usage, 0) / totalUsage;
console.log(`\nunweighted coverage (component average) : ${(unweightedCoverage * 100).toFixed(1)}%`);
console.log(`usage-weighted coverage                 : ${(weightedCoverage * 100).toFixed(1)}%`);

// Comparing the prop table against the real interface.
const INTERFACE = {
  button:   ["type", "size", "state", "icon", "width"],
  dropdown: ["size", "state", "searchable", "multiple"],
  badge:    ["tone", "size"],
  modal:    ["size", "dismissible", "title"],
};
const DOCUMENTED = {
  button:   ["type", "size", "state", "icon"],
  dropdown: ["size", "state"],
  badge:    ["tone", "size", "icon"],
  modal:    ["size", "title"],
};

console.log("\nprop table drift");
console.log("component   undocumented               documented but not in interface");
for (const name of Object.keys(INTERFACE)) {
  const missing = INTERFACE[name].filter((o) => !DOCUMENTED[name].includes(o));
  const extra = DOCUMENTED[name].filter((o) => !INTERFACE[name].includes(o));
  console.log(`${name.padEnd(11)} ${JSON.stringify(missing).padEnd(26)} ${JSON.stringify(extra)}`);
}
const totalProps = Object.values(INTERFACE).reduce((t, a) => t + a.length, 0);
const documentedCount = Object.keys(INTERFACE).reduce((t, name) => t + INTERFACE[name].filter((o) => DOCUMENTED[name].includes(o)).length, 0);
console.log(`prop table coverage: ${documentedCount}/${totalProps} (${((documentedCount / totalProps) * 100).toFixed(1)}%)`);
```

```
section              has doc      component share   usage share
purpose                  16/19            84.2%        95.6%
when-to-use              15/19            78.9%        94.6%
when-not-to-use          10/19            52.6%        79.4%
prop-table               19/19           100.0%       100.0%
example                  16/19            84.2%        93.2%
accessibility-note       12/19            63.2%        89.1%
related                  10/19            52.6%        74.2%

component             coverage   usage   gap impact   missing sections
dropdown                   29%      57          285   purpose, when-to-use, when-not-to-use, accessibility-note, related
notification-banner        57%      63          189   when-not-to-use, example, related
badge                      86%     188          188   related
modal                      57%      45          135   when-not-to-use, example, related
skeleton                   57%      38          114   when-not-to-use, accessibility-note, related
checkbox                   86%      96           96   when-not-to-use
tabs                       57%      24           72   when-not-to-use, accessibility-note, related
tooltip                    14%      12           72   purpose, when-to-use, when-not-to-use, example, accessibility-note, related
accordion                  43%      17           68   when-to-use, when-not-to-use, accessibility-note, related
table                      29%       9           45   purpose, when-to-use, when-not-to-use, accessibility-note, related
radio-group                86%      34           34   accessibility-note

unweighted coverage (component average) : 73.7%
usage-weighted coverage                 : 89.4%

prop table drift
component   undocumented               documented but not in interface
button      ["width"]                  []
dropdown    ["searchable","multiple"]  []
badge       []                         ["icon"]
modal       ["dismissible"]            []
prop table coverage: 10/14 (71.4%)
```

## What Section-Level Coverage Says

The first table shows which section is missing system-wide. The prop table is one
hundred percent full, since it can be generated from the interface and usually is. The
counter-example and related-components sections, by contrast, sit at 52.6%: only one
component in two has them.

This distribution is not random: the section that can be auto-generated is full; **the
section that requires writing down a decision is empty**. A documentation gap is often
not a matter of time but of an undecided call — writing a counter-example requires the
component's boundary to already be decided, and it has not been.

The gap between the two columns is also meaningful: the counter-example exists for
52.6% of components but covers 79.4% of usage, so what is missing tends to be the
less-used components. The related-components section shows a narrower gap — 52.6%
against 74.2% — showing the gap also reaches heavily used components, and the second
table confirms it.

## Ranking the Gaps

The second table shows that coverage percentage and work priority differ. `badge` sits
at 86% coverage — one of the best-looking components in the list — but its gap impact
is 188, third-highest, because it is used in 188 places and its one gap touches
everyone. `table`, at 29% coverage, looks far worse, but its impact is only 45; it is
used in nine places.

The gap-impact product — missing section count times usage count — combines both into
a single ranking. `dropdown`, at the top of the ranking, was also seen in the previous
lesson: an undocumented candidate used in 57 places. When two separate audits point at
the same component, that component is next in line.

The 15.7-point gap between unweighted and weighted coverage is also worth reading: the
two answer different questions — unweighted asks "how much of the catalog is
documented," weighted asks "how much of the usage lands on a documented component."
Reporting only the weighted number hides low-usage components staying undocumented;
reporting only the unweighted number misses components like `badge` that have one gap
but wide reach. Both are reported together.

## The Prop Table Going Stale

The last block shows documentation's most insidious form of decay: the doc exists, but
it is wrong. `button`'s `width` prop and `dropdown`'s two props were added to the
interface but never written into the table — **missing** information: the person using
the component does not know the prop exists, writes their own workaround, and produces
drift.

The `badge` row carries a different defect: the `icon` prop is still in the doc but no
longer exists in the interface — **wrong** information, more expensive than missing
information. The person using it trusts the doc, passes the prop, nothing happens, and
they look for the bug in their own code.

This block also shows the direction of the fix: as long as the prop table is written by
hand it will inevitably go stale; generated from the interface definition, drift
becomes structurally impossible. The sections that must be written by hand — purpose,
selection criterion, counter-example — are already the ones that cannot be generated.
Documentation tooling follows this distinction: generate what can be generated, force
what cannot to be written.

## Summary

- Component documentation has seven sections, and each one answers a specific question
  the person using it asks.
- The counter-example carries boundary information the prop table cannot show; it is
  the only section that puts the distinction between two similar-looking components
  into writing.
- Sections that can be auto-generated stay full; sections that require writing down a
  decision stay empty — a documentation gap is often a decision gap.
- Coverage is reported both unweighted and usage-weighted together; one hides the
  neglect of low-usage components, the other hides a single gap in a widely used one.
- The product of missing-section count and usage produces a work ranking different from
  the coverage percentage.
- A prop documented but absent from the interface is more expensive than a prop present
  in the interface but undocumented; when the prop table is generated from the
  interface definition, this drift becomes structurally impossible.

## Next Step

Comparing the doc's prop table against the interface left a question open: adding a
`width` prop to `button` and removing the `icon` prop from `badge` do not carry the
same weight. The first breaks no usage; the second breaks every place that passes that
prop. The next lesson ties this distinction to the version number: it writes a script
that compares two versions' component interfaces, classifies the changes, and computes
the next version number, then generates the migration guide from it.
