This guide describes how to maintain and create package-level CONFIG.md files in a way that stays accurate as the monorepo evolves.
Each package config document should:
- stand alone for external consumers of that package,
- document every supported package-owned field,
- link shared nested structures to
packages/core/CONFIG.mdinstead of duplicating them, - describe actual runtime behavior, not just TypeScript shape,
- stay current when config behavior changes.
Config documentation must be reviewed whenever a change affects consumer-facing configuration behavior.
Typical triggers:
- a new authorable field is added,
- a supported enum value changes,
- a default changes,
- runtime logic changes how a field is interpreted,
- migrations change how legacy configs are read,
- editor exports start including a new field that consumers are likely to encounter.
Most maintenance updates should be CONFIG.md updates only.
Existing packages already have a source-of-truth minimum example at packages/<package>/examples/minimal-example.json, but you usually will not need to touch it. See Minimum Example Maintenance at the end of this document for the cases where it should change.
Document fields where they are owned.
| Field type | Where to document it |
|---|---|
| Package-owned top-level fields | The package’s CONFIG.md |
| Package-owned nested objects | The package’s CONFIG.md |
Shared @cdc/core nested types |
packages/core/CONFIG.md |
| Internal-only runtime/editor fields | Usually do not document field-by-field; mention them briefly in a package-level Fields You Can Ignore section when consumers may encounter them |
| Unsupported editor export artifacts | Usually do not document field-by-field; mention them briefly in a package-level Fields You Can Ignore section when consumers may encounter them |
Update the shared core config reference whenever a package doc needs to describe:
- shared theme tokens,
- shared version strings,
- shared data normalization metadata,
- shared dataset-loading metadata,
- shared filter structures,
- shared table/download structures,
- shared axis/series/legend/annotation/region structures,
- shared markup variable structures,
- shared trend indicator structures,
- shared data-driven color structures,
- any other nested type reused across multiple packages.
Each package CONFIG.md should follow this order:
- Title and short description
- One-sentence pointer to any shared
@cdc/coreconfig reference - Organization section that explains the doc order
- Pointer to the package README example
- Identity and data source
- Core calculation or data-mapping sections
- Copy/content sections
- Layout/visual sections
- Feature-specific enhancement sections
- Fields You Can Ignore
That order is preferred over alphabetical order because it helps consumers build an accurate mental model faster.
For field tables, use these columns unless there is a strong reason not to:
| Column | Purpose |
|---|---|
Field |
Exact config path |
Type |
Runtime-facing type, not just internal type aliases |
Required |
Whether the field must be authored explicitly |
Default |
Actual default when omitted |
Description |
What the field does |
Allowed values / Notes |
Enums, constraints, caveats, and behavior notes |
For behavior summaries, use a two-column table:
| Column | Purpose |
|---|---|
Behavior |
Runtime concern |
Details |
How the package actually behaves |
When documenting or updating a package, inspect these sources in this order:
- The package config type files.
- The package defaults or initial state.
- The main runtime component that loads and renders the config.
- The editor panel, if one exists.
- Example configs and tests.
- Shared
@cdc/corehelpers used by the package.
This sequence matters because type files alone are usually incomplete. Runtime code often reveals:
- supported fields missing from the package type,
- fields that are legacy but still accepted,
- editor-only fields that should not be documented as part of the public contract,
- defaults or migrations applied at load time,
- enum values that only appear in UI constants.
Before writing or revising docs, verify all of the following:
- which fields are truly required for a working consumer config,
- which fields are defaulted automatically,
- which fields are package-owned versus shared,
- which nested types come from
@cdc/core, - which enum values are actually supported in runtime/editor code,
- which fields are deprecated but still accepted,
- which fields are internal-only and should be marked as such,
- whether example configs include editor export artifacts that should not be copied into consumer configs.
When describing fields:
- prefer “what this does” over “what this is called in code”,
- call out when behavior depends on another field,
- explain conditional relevance, such as “only meaningful for
tp5”, - document legacy quirks honestly instead of hiding them,
- use exact enum values from runtime constants,
- keep descriptions short, but include the operational consequence of the field.
For shared config references, prefer a canonical repository URL to the shared document rather than a sibling relative path between package folders.
Why:
- npm package names are not resolvable as Markdown documentation links on their own,
- sibling relative links may not survive cleanly when a package is consumed outside the monorepo context,
- a canonical GitHub URL makes the target unambiguous for both repo readers and package consumers.
Use this checklist when a package gets config docs for the first time:
- Identify the package-owned config type and default state.
- Read the main runtime component to see how config is loaded, normalized, and consumed.
- Read the editor panel to capture user-facing sections and enum values.
- Scan examples and tests to catch real-world fields, legacy fields, and omissions from the type file.
- Identify every shared nested type and either link to existing
packages/core/CONFIG.mdsections or add the missing shared documentation there first. - Draft the package
CONFIG.mdusing the standard section order. - Create
examples/minimal-example.jsonas the source of truth for the minimum example. - Add a single copy-pasteable README example that mirrors that JSON.
- Mark that README example with
README_EXAMPLE_CONFIG_STARTandREADME_EXAMPLE_CONFIG_END. - Add a sync test so the README example stays aligned with
minimal-example.json. - Reuse the minimal example in a smoke story when practical.
- Add a short
Fields You Can Ignoresection for internal, legacy, or editor-export fields that consumers may encounter. - Do a final accuracy pass against runtime code before considering the doc complete.
Every documented package should have a source-of-truth example at packages/<package>/examples/minimal-example.json.
That example should:
- render a working package instance,
- include only the fields that are practically necessary,
- avoid optional advanced features,
- include
version, - remain stable over time.
Minimal examples should change rarely.
Do not change the example version string during routine config-doc maintenance unless the work explicitly includes a human-requested version update. Treat the example version as a separately managed value, even if package metadata or other repo files suggest a different version.
Do update the minimal example when:
- the true minimum working config changes,
- the current example no longer renders correctly,
- the current example is no longer a good canonical minimal integration example.
Do not update the minimal example just because:
- a new optional field was added,
- a new feature was documented,
- a package gained an advanced configuration mode that is not part of the minimum path.
When you do need to update the minimal example, keep the README example and its sync test aligned with it:
packages/<package>/README.mdshould contain a single copy-pasteable JavaScriptconst config = { ... }example- that example should be marked with
README_EXAMPLE_CONFIG_STARTandREADME_EXAMPLE_CONFIG_END - the package test should compare the README example to
examples/minimal-example.json - the minimal example can also be reused in a smoke story when practical