---
name: component-md-authoring
description: >-
  Author the two judgment sections of a component's `component.md`, Screen-reader spec and Behavioral spec, and keep it PR-fresh. Use when a
  component's states, composed children, aria behavior, or error/empty/
  loading handling changes and it already has (or should grow) a
  `component.md`, or when asked to "add component.md for X" / "write the
  screen-reader spec for X" / "why is check:component-md-fresh warning". NOT
  the yaml prop/slot/event/token contract itself (primitive-authoring owns
  that, this skill only owns the two authored yaml fields,
  `screenReader`/`behavioral`, plus the optional `intent` field); NOT gen-ui
  corpus/retrieval wiring (a2ui-maintenance); NOT a component's CSS token
  audit (a capability referenced from adia-ui-factory's theme-audit skill,
  not yet a shipped skill here, ticket 10036).
disable-model-invocation: false
user-invocable: true
---

# component-md-authoring

`component.md` (gh#2615) is a generated shell, per-component, sitting next
to its `.yaml` SoT (`packages/web-components/components/<name>/component.md`,
or `packages/web-modules/<cluster>/<name>/component.md` for a composite/
shell). Every section except two is mechanically transcluded from the yaml
by `scripts/build/gen-component-md.mjs`, Intent, API (props/events/slots),
Structural (Light DOM anatomy + states + composes), Tokens, Rules,
Anti-patterns, Related. This skill's whole charter is the two sections that
aren't: **Screen-reader spec** and **Behavioral spec**.

## The load-bearing decision: where the authoring happens

You do not hand-edit `component.md`. You edit the yaml's `screenReader:`
and `behavioral:` fields (and, ideally, `intent:`), `component.md` is
regenerated from them. This is deliberate, not incidental:

- **No second source of truth.** plan-2615's evidence pass on gh#2615 found
  most of component.md's "intent layer" already lives in the yaml
  (`a2ui.rules`, `anti_patterns`, `related`, examples). The two genuine
  gaps, screen-reader and behavioral judgment, get the SAME treatment:
  authored once, in yaml, transcluded everywhere else (component.md today;
  gen-ui corpus derivation once a2ui-maintenance wires it in).
- **`component.md` is Class R, derived on main, not authored in the PR
  (gh#3172, ADR-0069).** Because the authored content lives in a yaml
  field, `component.md` is 100% mechanically regenerable, a PR commits
  only the `screenReader`/`behavioral` yaml edit; `check:component-md-fresh`
  runs advisory-only inside `check:pr-ready` (WARN, never fails the run)
  and the `push: main` `derived-resync` job regenerates `component.md`
  itself once the PR merges. A hand-edit directly in `component.md` will
  still be silently clobbered by the next `npm run docs:component-md` or
  by `derived-resync` on main, that's the guard rail, not a bug, even
  though nothing blocks the PR on it.

## Authoring a component's two sections

1. Confirm the component doesn't already have adequate coverage, read its
   existing `states:`, `a2ui.rules`, and `.class.js` source. Per
   primitive-authoring's own first principle, **source wins**: verify every
   claim you're about to write (focus order, aria attribute names, event
   names) against the actual `.class.js`/`.js` file, not just the yaml
   prose.
2. **Screen-reader spec**, focus order across composed children (order
   `showModal()`/connect moves focus, what wraps at the tab boundary),
   live-region announcement sequence (what fires `role="alert"` or an
   `aria-live` region, and when), and any keyboard map beyond the trait
   default (`pressable`/`focusable` already cover Enter/Space/click, only
   document what's ADDITIONAL, e.g. arrow-key grid nav, Escape-dismiss).
   Do not restate a static `aria-*` attribute the yaml's `props`/`states`
   already document plainly, that's derived content, not new judgment.
3. **Behavioral spec**, dismiss/error/empty/loading states and
   transitions NOT already modeled by `states:`. Distinguish "fetching" vs
   "confirmed empty" where both exist (see `table.yaml`'s `screenReader`/
   `behavioral` for a worked example: three distinct states, not one).
   Name what is explicitly NOT handled (no built-in error state, no
   built-in loading state) as clearly as what is, an absence is often the
   more actionable fact for a consumer.
4. Both fields require `minLength: 20` (schema-enforced), a placeholder
   one-liner will fail `check:components-valid`. Write real prose, grounded
   in source, not a restatement of the component's `description`.
5. Regenerate and verify:

   ```bash
   node scripts/build/components.mjs --validate   # schema-valid yaml
   npm run docs:component-md                      # regenerate component.md
   npm run check:component-md-fresh                # advisory gate (check:pr-ready); derived-resync owns main
   ```

6. If this is the component's FIRST component.md (yaml previously had
   neither field), run `npm run build:components` too, the corpus/catalog
   rebuild picks up the new yaml content, and `npm run eval:diff --
   --engine zettel` should show no regression (preserve-not-regress floor,
   owned by a2ui-maintenance).

## Scope (gh#2615 pilot)

`scripts/build/gen-component-md.mjs`'s `SCAN_ROOTS` covers
`packages/web-components/components/` and `packages/web-modules/chat/`
today, the pilot 5 (`button`, `modal`, `table`, `field`, `chat-shell`).
Extending to every web-modules cluster, or sweeping the remaining ~145
primitives, is deliberately out of scope for this pass (file a follow-up
task rather than silently expanding `SCAN_ROOTS` for one-off need, a
cluster added there without a plan for authoring every component inside it
just produces components with a `.yaml` but no eligible `component.md`,
which the generator already handles gracefully by skipping them, but which
defeats the point of a rollout plan).

## Cross-references

- `primitive-authoring`'s authoring-cycle: add "new/changed states or aria
  behavior → component-md-authoring's authored sections may need a pass"
  to your own SoT-change checklist when editing a yaml that already has a
  `component.md` sibling.
- `scripts/schemas/component.yaml.schema.json`, `intent`/`screenReader`/
  `behavioral` field definitions (all optional; a component with a `.yaml`
  but neither authored field simply has no `component.md` yet).
- `scripts/verify/check-component-md-fresh.mjs`, the freshness gate:
  byte-freshness (component.md matches a fresh render). Advisory-only in
  `check:pr-ready` (gh#3172, ADR-0069), a PR commits the yaml edit alone
  and `derived-resync` regenerates `component.md` on `push: main`; the
  same-PR coverage check this gate used to run was removed outright
  (LLD-0020 §1c), not demoted.
