| Field | Value |
|---|
| ID | FRD-039 |
| Owner | David Holmes |
| Status | Shipped |
| Priority | P1 — Developer Experience |
| Size | XL (Refactor) |
| Target Release | v2.0.0 |
| Last Updated | 2026-05-26 |
Document Summary
Ensure every Storybook code panel displays a self-contained, copy-pasteable code snippet that works when pasted into a consumer application. This requires eliminating inline Demo wrapper functions in render callbacks, adding explicit parameters.docs.source.code overrides to every story that uses a render function, ensuring all source strings include necessary imports, and enforcing these rules through an automated CI audit script (scripts/checks/audit-story-dx.mjs).
Introduction
Overview
The Storybook “Show code” panel is the primary way developers discover how to use design system components. Today, many stories display auto-generated source that includes internal decorator wrappers, missing imports, or render-callback boilerplate that does not work when copied into a real application. This erodes trust in the documentation and increases support burden.
Goals
- Every code panel in Storybook shows a snippet that compiles and renders correctly when pasted into a consumer app with the design system installed.
- No story displays an inline
Demo function, decorator internals, or Storybook-specific scaffolding in its code panel.
- Every story that uses a
render function has an explicit parameters.docs.source.code override.
- All source code strings are self-contained — they include the necessary import statements at the top.
scripts/checks/audit-story-dx.mjs enforces all of the above in CI with a non-zero exit code on violations.
Non-Goals
- Rewriting component implementations or APIs.
- Adding new stories beyond what is needed to fix source truthfulness.
- Changing the visual appearance of Storybook documentation pages.
- Migrating away from Storybook or changing the docs framework.
Scope
In Scope
| Item | Description |
|---|
| Render-callback audit | Identify every story using render without a parameters.docs.source.code override |
| Source code overrides | Add parameters.docs.source.code to every render-override story |
| Import completeness | Ensure every source code string includes all required import statements |
| Demo function removal | Eliminate inline Demo wrapper functions from render callbacks and source strings |
| Audit script hardening | Extend scripts/checks/audit-story-dx.mjs to detect all violation categories |
| CI integration | Ensure pnpm run dx:audit fails the build on any source truthfulness violation |
Out of Scope
| Item | Reason |
|---|
| Story visual redesign | Separate initiative; visual changes are not related to source correctness |
| Component API changes | Source truthfulness is about documentation accuracy, not API design |
| MDX documentation pages | MDX pages use inline code blocks that are already authored manually |
| Third-party addon updates | Addon versions are managed in a separate dependency update cycle |
Users and Pain Points
| User | Pain Point |
|---|
| Application developer | Copies code from Storybook, gets compile errors because imports are missing |
| Application developer | Copies code and sees Demo wrapper function that does not exist in their codebase |
| Application developer | Sees decorator boilerplate in the code panel and cannot distinguish it from the real usage |
| Design system maintainer | Receives repeated support questions about code panel accuracy |
| CI pipeline | No automated check catches source truthfulness regressions |
Definitions
| Term | Definition |
|---|
| Source truthfulness | The property that a code panel snippet compiles and renders correctly when pasted into a consumer application |
| Render callback | A story’s render function that overrides default Storybook rendering |
| Source code override | The parameters.docs.source.code string that explicitly controls what appears in the code panel |
| Self-contained snippet | A code block that includes all imports, type annotations, and JSX needed to render without modification |
| Demo function | An inline wrapper component (typically named Demo or Render) used inside a render callback for local state |
Current State
scripts/checks/audit-story-dx.mjs exists and runs in CI via pnpm run dx:audit, but its rule set does not cover all source truthfulness violations.
- Many stories using render callbacks rely on Storybook’s auto-generated source, which includes internal wrappers and omits imports.
- Some stories have
parameters.docs.source.code but the strings are incomplete (missing imports or referencing unexported helpers).
- There is no systematic inventory of which stories are compliant and which are not.
createComponentDocs sets source.type: "dynamic" and excludeDecorators: true, which helps but does not solve render-callback cases.
Proposed Solution
Phase 1: Audit and inventory
Run scripts/checks/audit-story-dx.mjs in its current form to produce a baseline violation report. Manually review a sample of stories to identify violation categories not yet covered by the script.
Phase 2: Extend the audit script
Add detection rules to scripts/checks/audit-story-dx.mjs for:
- Stories with a
render function but no parameters.docs.source.code.
- Source code strings that do not start with an
import statement.
- Source code strings containing
Demo or Render as a component name.
- Source code strings referencing identifiers not present in any import statement.
Phase 3: Fix violations
For each violating story file:
- If the story uses a render callback for local state, extract the state logic into the source code override as a self-contained component.
- Add the full import block to the top of the source code string.
- Remove any
Demo/Render wrapper references.
- Verify the snippet compiles by running
tsc --noEmit against a scratch file (or manual verification).
Phase 4: CI enforcement
Ensure pnpm run dx:audit exits with code 1 when any violation is found. Remove the --warn-only flag from CI pipelines if it is currently used.
Requirements
| ID | Requirement | Priority |
|---|
| REQ-01 | Every story with a render callback must have parameters.docs.source.code | Must |
| REQ-02 | Every source code string must begin with import statements | Must |
| REQ-03 | No source code string may reference Demo or Render as a component name | Must |
| REQ-04 | scripts/checks/audit-story-dx.mjs must detect all four violation categories | Must |
| REQ-05 | pnpm run dx:audit must exit non-zero on any violation | Must |
| REQ-06 | Source code strings must include type imports when TypeScript types are used | Should |
| REQ-07 | Source code strings should use the public package import path, not relative paths | Should |
Functional Requirements
| ID | Description | Acceptance |
|---|
| FR-01 | Audit script detects render callbacks missing source overrides | Script lists all files and story names that violate |
| FR-02 | Audit script detects source strings missing imports | Script lists the specific source string and missing import |
| FR-03 | Audit script detects Demo/Render wrapper references | Script flags the component name and file |
| FR-04 | Audit script produces a machine-readable JSON report | JSON file written to reports/dx-audit.json |
| FR-05 | Each fixed story’s code panel shows a working snippet | Manual verification against a sample of 10 stories |
Non-Functional Requirements
| ID | Description | Target |
|---|
| NFR-01 | Audit script execution time | Under 10 seconds for the full story corpus |
| NFR-02 | Zero regression tolerance | CI blocks merge on any new violation |
| NFR-03 | Developer experience | Audit output clearly identifies the file, story name, and violation type |
API/Interface Requirements
| Interface | Requirement |
|---|
parameters.docs.source.code | String value; must be valid JSX/TSX that compiles independently |
scripts/checks/audit-story-dx.mjs CLI | Accepts --warn-only flag; outputs violation report to stdout; exits 0 or 1 |
reports/dx-audit.json | Array of { file, story, violation, message } objects |
Accessibility Requirements
| ID | Requirement |
|---|
| A11Y-01 | No accessibility changes — this FRD targets code panel content, not rendered UI |
| A11Y-02 | Source code snippets in overrides must include aria-label and role props when the component requires them, so developers copy accessible patterns by default |
Content and Documentation Requirements
| ID | Requirement |
|---|
| DOC-01 | Update the “How to Write a Story” guide in src/docs/ to document the source truthfulness rules |
| DOC-02 | Add a section to CONTRIBUTING.md explaining how to write a parameters.docs.source.code override |
| DOC-03 | Document the audit script’s violation categories in a JSDoc header within the script file |
Dependencies
| Dependency | Type | Risk |
|---|
@storybook/addon-docs source rendering | External | Storybook version upgrades may change source extraction behavior |
scripts/checks/audit-story-dx.mjs | Internal | Script must be updated before story fixes begin |
createComponentDocs in src/lib/storybook-docs.ts | Internal | May need adjustment to accommodate source overrides alongside source.type: "dynamic" |
Risks and Tradeoffs
| Risk | Impact | Mitigation |
|---|
| Large number of stories to fix (XL scope) | Slow delivery | Prioritize components with the highest usage first; batch by component directory |
| Source strings drift from component API changes | Stale documentation | Audit script catches new violations in CI; add a reminder in the PR template |
| Overly strict audit rules produce false positives | Developer friction | Allow // dx:ignore inline comments for intentional exceptions |
| Manual source strings are harder to maintain than auto-generated | Maintenance burden | Limit overrides to render-callback stories only; rely on auto-generation for simple stories |
Open Questions
| # | Question | Status |
|---|
| 1 | Should source overrides use the @dmwd/design-system package path or @/components/ alias? | Open |
| 2 | Should the audit script also validate that source strings compile (via a lightweight TSX parse)? | Open |
| 3 | What is the maximum acceptable source string length before we link to an external example instead? | Open |
| 4 | Should we generate source overrides from a template to reduce manual authoring? | Open |
Acceptance Criteria
LLM Handoff Instructions
When an LLM agent picks up this FRD:
- Run
pnpm run dx:audit to get the current violation report.
- Read
scripts/checks/audit-story-dx.mjs to understand existing detection rules.
- Read
src/lib/storybook-docs.ts to understand how createComponentDocs configures source rendering.
- For each violating story file, read the story’s
render function and the component’s public API to write an accurate source override.
- Source override format: start with imports from the package path, then a single exported function component that demonstrates the story’s use case.
- After fixing a batch, run
pnpm run dx:audit and pnpm typecheck to verify.
- Do not modify component implementations — only story files and the audit script.
Decision Log
| Date | Decision | Rationale |
|---|
| 2026-05-26 | Use parameters.docs.source.code rather than CSF source transforms | Direct string overrides are simpler and more predictable than transform functions |
| 2026-05-26 | Extend existing audit script rather than creating a new one | scripts/checks/audit-story-dx.mjs already runs in CI; extending it avoids pipeline changes |
Document History
| Date | Version | Author | Changes |
|---|
| 2026-05-26 | 0.1 | David Holmes | Initial draft |