Teaching MDX Pages (Docs That Teach)
| Field | Value |
|---|---|
| Type | Skill Resource |
| Source | ~/.copilot/skills/technical-writing/references/docs-that-teach/teaching-mdx-guide.md |
| Description | Not specified |
Source Content
Teaching MDX Pages (Docs That Teach)
The Docs That Teach standard — how to author a world-class, print-friendly, accessible MDX documentation page that teaches a concept in five minutes using real @dmwd-io/design-system components. Formerly its own skill, now this skill’s teaching-page mode: covered by the same scripts/lint.py pipeline and Diátaxis mode selection as everything else in technical-writing.
Route here for: “write a doc page”, “create an MDX page”, “document this concept”, “write a guide”, “explainer”, “docs for the design system”, “make this readable”, “turn this into a teaching doc”, any task whose output is a .mdx doc in the design system or a consuming app.
Query the Storybook MCP before using any component — never invent one.
The goal is not a wiki page. The goal is a page a developer would choose over a Google search — technically accurate, enjoyable, skimmable, useful on paper, and accessible by default. Documentation that teaches, not just documents.
The one rule everything serves
Every section, table, diagram, code block, and component must help the reader understand faster. If an element does not earn its place by speeding comprehension, cut it. Brevity is not the goal; learning density is.
The reader should leave with: a correct mental model, the reason it matters, practical examples, the common mistakes, and one memorable takeaway. Write for both the skimmer (needs the answer now) and the deep reader (wants the reasoning).
Write at an eighth-grade reading level
Aim for roughly an eighth-grade reading level. That is the sweet spot that makes a doc worth reading — the moment a reader feels they can’t follow it, they leave for a search engine. The audience is still developers, so this is about plain sentences, not a dumbed-down vocabulary.
Keep the real term; cut the needless complexity around it. Say git, not “the place where we store code”. Say CI, pull request, container, env var — your reader knows these, and spelling them out as plain English is slower, not clearer. Plain language means short sentences and common connective words, not removing the words devs already use.
| Keep — devs know these | Simplify — needless complexity |
|---|---|
git, CI, PR, container, API, env var, cache | ”utilize” → use · “in order to” → to · “leverage” → use |
| The precise technical noun for the thing | ”the place where we store code” → git |
| One definition on first use, only if the reader might not know it | ”at this point in time” → now · “in the event that” → if |
The rules that get you there:
- One idea per sentence. Aim under 20 words; split anything over 25. Long sentences are what actually make docs hard — not technical words.
- Active voice, present tense. “CI runs the checks”, not “the checks are run by CI”.
- Common word for the glue. Prefer “use”, “help”, “so”, “about”, “before” over “utilize”, “facilitate”, “consequently”, “regarding”, “prior to”.
- One new concept per paragraph, five sentences or fewer (the
technical-writingskill already caps this). - Read it aloud. If you run out of breath, the sentence is too long.
Check it (advisory). Run the readability helper for a grade estimate and a list of the longest sentences to split:
python3 ~/.copilot/skills/technical-writing/scripts/readability.py path/to/page.mdxIt targets grade ≈ 8 but never fails the build — necessary technical terms inflate every grade formula, so treat the number as a guide and act on the long-sentence list, which you can fix without dropping a single term.
Earn a highlight in every paragraph
Write so every paragraph leaves at least one line the reader wants to highlight — ideally every sentence. Not a line you wrapped in quotation marks. A line so clean and true that a reader underlines it, screenshots it, or builds a whole LinkedIn post around it. That is the bar on every page, in Markdown, MDX, and plain technical writing alike.
A line earns the highlight three ways:
- It stands alone. Rip it out of the paragraph and it still lands.
- It compresses a truth. It says in one breath what most writers take a paragraph to circle.
- It is earned, not hype. Concrete and a little contrarian beats loud adjectives every time.
The plainest sentence is usually the most quotable, so this pairs with the eighth-grade rule — sharpen the point, do not inflate the words. The technique:
- Land each paragraph on its sharpest sentence — open or close with it, never bury it in the middle.
- Use contrast: “X is not Y. It is Z.” The shape itself is memorable.
- Cut the throat-clearing in front of the good line; the wind-up is rarely the part worth keeping.
- Vary rhythm — a short, blunt line after two long ones hits hardest.
| Flat | Highlightable |
|---|---|
| Reusable workflows help you avoid duplicating CI config. | Copy-paste is a loan — you borrow speed today and repay it in every repo you forgot to update. |
| Good docs are easy to read. | The moment a reader feels they can’t follow you, they leave for a search box. |
| Automating more of the pipeline is usually good. | The best workflow isn’t the one with the most automation. It’s the one that makes the right path obvious. |
The highlight test — run it on every paragraph: which sentence would a reader underline? If the honest answer is “none”, rewrite one until one earns it. One highlight per paragraph, not a firework in every clause — when every line strains to be profound, none of them land.
Reading-time budget
| Target | Maximum | What it means |
|---|---|---|
| 5 minutes | 8 minutes | ~1,000–1,600 words of prose, plus visuals. If it runs longer, split it into two linked pages — one concept per page. |
A page that takes 12 minutes is two pages. Link them; do not merge them.
Frontmatter — always, with the default fields
Every teaching page opens with YAML frontmatter. Use it whenever the format allows (.md and .mdx both do). Six fields are required by default; the rest are recommended when they apply.
| Field | Type | Why |
|---|---|---|
title | string | Specific and searchable — drives the index and the page H1 intent. |
created | ISO date YYYY-MM-DD | When the page was first written. |
last_updated | ISO date YYYY-MM-DD | When the content last changed — never before created. |
tags | non-empty list | Findability and grouping — [ci, platform]. |
tldr | one-line string | The skimmer’s “why read this” — one sentence, ≤ 240 chars. The TLDR test: a reader who reads only this must walk away with both the load-bearing claim and the operating consequence. One without the other means rewrite it. |
contributors | non-empty list | Who wrote and maintains it — [Jane Doe]. |
Recommended when they apply, matching the design system’s docs-index.json: status (draft / accepted / superseded), appliesWhen, last_verified.
---title: Understanding Reusable Workflowscreated: 2026-06-24last_updated: 2026-06-24status: acceptedtags: [ci, platform]tldr: Share one CI definition across repos instead of copy-pasting pipeline YAML.contributors: [David Holmes]---Lint it. A page is not done until its frontmatter passes the checker:
python3 ~/.copilot/skills/technical-writing/scripts/lint_docs_that_teach_frontmatter.py path/to/page.mdx# or a whole tree:python3 ~/.copilot/skills/technical-writing/scripts/lint_docs_that_teach_frontmatter.py docs/# need a starting block?python3 ~/.copilot/skills/technical-writing/scripts/lint_docs_that_teach_frontmatter.py --scaffoldExit 0 means every required field is present and well-formed; exit 1 lists what is missing or malformed. It accepts contribution / authors as aliases for contributors but nudges you to the canonical name.
Storybook titles come from folders
Storybook derives a docs page’s title and sidebar slot from its file path. Put the page in the right folder and let the folder structure own navigation. Do not add authored title or layout overrides as boilerplate.
Page structure
Use this spine unless the topic clearly needs otherwise. Each heading is a promise the section must keep.
| Section | Job | Length |
|---|---|---|
| Title | Specific and searchable — answers a real question, never “Overview” | 1 line |
| Opening summary | What is this, why it matters, when you’d use it | 2–4 sentences |
| Mental model | One metaphor, rule of thumb, or before/after that makes the concept click | 1–3 sentences |
| Visual overview | A Mermaid diagram when flow, architecture, ownership, lifecycle, or a decision is involved | 1 diagram + 1 takeaway sentence |
| Main explanation | Layered: what it is → why it exists → how it works → when to use → when not → what goes wrong | 4–7 short sections |
| Key takeaway | One memorable lesson or decision rule — never “In conclusion” | 1–3 sentences |
Full skeleton with real components: page-template.mdx.
Format decision rules
Pick the format by the reader’s job, not by habit. The most common failure is prose where a table or headings belong.
| Reader needs to… | Use | Never |
|---|---|---|
| Scan unordered points (benefits, risks, rules, mistakes) | Bulleted list | A numbered list (implies false sequence) |
| Follow an ordered procedure (install, migrate, troubleshoot) | Numbered list | Bullets (loses the order) |
| Compare named options across shared dimensions | ComparisonTable or a Markdown table | Bold names mashed into one paragraph |
| Compare two or three named approaches in depth | A #### heading per option | A table with multi-sentence cells |
| Understand flow, architecture, ownership, lifecycle, or a decision | Mermaid diagram via InteractiveMermaid | A wall of prose describing the flow |
| See a trend, proportion, or before/after in numbers | A chart component (LineChart, BarChart, …) | A table the reader has to mentally plot |
| Pause on a warning, tip, security, or decision note | Callout with the right tone | Bold text pretending to be a warning |
| Read code that demonstrates one idea | CodeBlock (static) | A giant full-file dump |
| Run a command and see expected output | CodeBlock language="bash" (static) or PodTerminal (live) | A screenshot of a terminal |
| See a UI state words cannot convey | Media (figure + caption) | A decorative stock image |
Two rules that override instinct, inherited from technical-writing:
- Named-thing comparisons always get headings or a table — never bold names collapsed into a paragraph.
- Every table cell is filled — use
-for any blank.
Every diagram, chart, image, and table must teach. Put a short italic “what to notice” line beneath each one — telling the reader exactly what to pay attention to turns a reference table into a teaching moment. If you cannot write that one sentence, delete the element.
A comparison or failure-pattern table also earns a discriminating column — one that converts each row from vocabulary into a testable condition (“what proves X happened”, “how you’d detect this”). That column is the difference between giving the reader vocabulary and giving them operating discipline.
MDX components — use the real design system, never invent
The design system already ships a component for nearly everything a teaching doc needs. Query the Storybook MCP for the real props before using any component (list-all-documentation → get-documentation). Never invent a prop, tone, or variant — it is a hard rule of this codebase.
The mapping from “what you want” to the real @dmwd-io/design-system export, with verified prop shapes and import lines, lives in component-map.md. The short version:
| You want | Real component | Note |
|---|---|---|
| Callout / note / warning / security box | Callout | tone="info|success|warning|danger", optional title, role |
| Code sample | CodeBlock | code, language, optional filename |
| Terminal command + output | CodeBlock language="bash" (static) or PodTerminal (live) | static prints fine; live needs a print fallback |
| Decision / comparison matrix | ComparisonTable | columns + rows, one highlighted column |
| Before / after | DiffDisplay | change logs, version diffs |
| Diagram | InteractiveMermaid | chart, fileName, height — follow technical-writing’s diagram rules (../mermaid.md) |
| Figure / screenshot | Media | renders <figure>/<figcaption>; alt required, add caption |
| Quote / callout of a voice | QuoteHighlight | testimonials, expert notes |
| Steps / checklist | StepList / Checklist | ordered vs check-off |
| Charts | LineChart, BarChart, AreaChart, … | title, labeled axes, units, a “what to notice” line |
| On-page nav | DocsAutoToc, TableOfContentsRail, ArticleJumpNav | auto-generated from headings |
| Optional / platform-specific content | Tabs | never hide essential info here — print loses it |
| Secondary detail | Accordion | never hide required steps, warnings, or decisions |
There is no Quiz / CheckYourUnderstanding component in the catalog. For a check-your-understanding beat, use a Callout role="note" with the question, and an Accordion for the answer — and in print, show both.
Print-friendly is non-negotiable
The page must teach on paper. Before shipping, confirm:
- No essential information lives only inside
TabsorAccordion— print collapses both. - Every interactive component has a
printFallbackprop or an adjacent static equivalent. - Diagrams read in grayscale (Mermaid does); meaning never depends on color alone.
- Tables fit the page width; code blocks wrap or stay readable; figures are not tiny.
- Links have meaningful text (the URL is invisible on paper).
When an interactive demo is the only place a concept lives, add a one-sentence static summary right beside it.
Tell a story, don’t dump notes
Good documentation feels like someone smart walking beside the reader. The spine: start with the problem → show why the old way hurts → introduce the better mental model → demonstrate it → show the result → leave a decision rule. The reader should feel “this is worth my time” by the end of the opening summary.
The opening builds on the hook — it never recovers from it. After a strong first line, the next sentences must add motion — push into the world where the claim is visible — not explain, soften, or hedge. A strong scene can even precede the thesis. The failure mode is a great first line followed by two paragraphs of defensive reasoning.
Use stories to make examples stick
A rule tells the reader what to do. A story shows them what happens when they don’t — and that is the one they remember. Self-help books live on this move: here’s the thing, here’s how it played out, here’s why we do it this way. Use the same three beats whenever a point matters enough to stick.
The pattern is claim → vignette → lesson:
- Claim. State the principle plainly in one line.
- Vignette. Drop a short, relatable story — a mistake someone made, a lucky save, what actually happened in practice. Two to four sentences with a named protagonist, the specific friction they hit, and a damning specific consequence line to close on. “A team” is weaker than “Jack’s team”; “things broke” is weaker than “the on-call pager fired at 2am and the bill doubled”. Specificity is what persuades.
- Lesson. Name the takeaway out loud. Never make the reader guess what the story was for.
Reuse the character. Once you name a protagonist, the reader tracks them — so call them back. Tie the later prescription to the same person you introduced in the diagnosis (“this is the rule that would have saved Jack’s team”). The callback weaves the doc together so it reads as one woven argument, not a stack of separate sections. Mark any composite as composite — “names changed, the pattern is real.”
The rules that keep it honest and sharp:
- Mistakes teach best. Readers absorb borrowed pain faster than they absorb a rule — someone else’s 2am outage is cheap tuition.
- Concrete beats abstract. “A team set retries to infinite and DDoSed their own database by lunch” lands; “misconfiguration can cause issues” slides off.
- Vignette, not chapter. A few sentences for one point. The doc is not a story; it uses stories.
- Reserve them for what must stick. Not every paragraph needs a tale — use one where a dry example would slide right off.
- Never fabricate specifics. Real stories must be true. Hypotheticals are fine but must read as hypothetical — “say a team ships…”, “imagine…” — never invented metrics or fake company names dressed up as fact.
| Dry rule | Claim → vignette → lesson |
|---|---|
| Misconfigured retries can cause cascading failures. | Retries need a budget. One team set theirs to “infinite” to be safe; a slow dependency hiccuped, every client retried forever, and they DDoSed their own database by lunch. Give every retry a ceiling — “safe” without a limit is just a slower outage. |
| Centralize CI to avoid drift. | Shared CI saves you from silent drift. One team copied their pipeline into twelve repos, patched a security fix in three, and forgot the rest — the gap surfaced in an audit a year later. Define it once so a fix reaches everyone, not just the repos you remember. |
How I work
- Name the reader and the one thing they should be able to do or decide afterward. If unclear, ask.
- Pick the Diátaxis mode with the
technical-writingskill — teaching docs are usually explanation or tutorial. One mode per page. - Write the frontmatter first —
title,created,last_updated,tags,tldr,contributors(use--scaffoldto start). Then outline the spine from the page-structure table; each heading is a one-line promise. - Query the Storybook MCP for every component you plan to use; copy real props from
get-documentation. - Draft, choosing each format with the decision-rules table. Follow
technical-writing’s diagram rules (../mermaid.md) before any diagram. - Add print fallbacks and the “what to notice” line under every visual.
- Cut to the 5-minute budget. Split if over 8 minutes.
- Run the linters —
scripts/lint_docs_that_teach_frontmatter.py,scripts/lint_docs_that_teach.py(Tabs/Accordion print-survival, “what to notice” coverage, component allowlist), and thetechnical-writingMarkdown linter (../../scripts/markdown_lint.py), all to exit 0 — then the review rubric (review-rubric.md).
Self-rubric (the publish gate)
Full rubric: review-rubric.md. The fast version:
- The main idea is obvious within 30 seconds; a skimmer can follow headings, tables, and diagrams alone.
- Plain language at ~eighth-grade level — short sentences, common connective words, technical terms kept but not over-explained (
readability.pyis advisory). - Ordered steps are numbered; unordered points are bulleted; comparisons are tables or
####headings. - Every diagram, chart, image, and table teaches and has a “what to notice” sentence; comparison/failure tables carry a discriminating (testable-condition) column.
- Real examples with expected output and at least one common mistake.
- The points that must stick are carried by a short story (claim → vignette → lesson) with a named protagonist and a damning consequence line — reused on callback where it ties diagnosis to prescription; real stories are true, hypotheticals read as hypothetical.
- Every component’s props came from the Storybook MCP — none invented.
- Print-safe: nothing essential hidden in tabs/accordions; interactive elements have fallbacks; grayscale-readable.
- Accessible: one H1, no skipped levels, alt text, table headers, never color alone, descriptive links, acronyms defined on first use.
- Every paragraph has at least one highlightable line — a reader would underline it (the highlight test).
- Ends with a memorable Key Takeaway or Decision Rule — not “In conclusion”.
- Frontmatter carries
title,created,last_updated,tags,tldr,contributors;lint_frontmatter.pyexits 0. - Reads in ≤ 8 minutes; the
technical-writingMarkdown linter (../../scripts/markdown_lint.py) exits 0.
References
page-template.mdx— the full gold-standard MDX skeleton with real components and frontmatter.component-map.md— every teaching need mapped to the real@dmwd-io/design-systemcomponent, with verified props and import lines.review-rubric.md— the six-dimension publish gate (clarity, structure, visual learning, practical use, accessibility, print, enjoyability).scripts/lint_docs_that_teach_frontmatter.py— checks the default frontmatter fields are present and well-formed;--scaffoldprints a starter block.scripts/lint_docs_that_teach.py— flags essential content hidden entirely inside Tabs/Accordion (print collapses both), visuals with no “what to notice” line, and JSX components not in an optional--allowlist.scripts/readability.py— advisory eighth-grade-level estimate plus the longest sentences to split; never fails the build.- The
technical-writingskill — Diátaxis mode selection and house style, plus the mechanical Markdown/MDX rules and linter (../../scripts/markdown_lint.py) and the diagram-type selection and pre/post lint it now owns (../mermaid.md). - Stripe Docs · Cloudflare Docs · Diátaxis — the bar.