Lint MyST Markdown with rumdl¶
Use rumdl's myst flavor for MyST Markdown in Jupyter Book, Sphinx with
MyST-Parser, and the MyST document engine. It distinguishes content directives
from code directives and recognizes roles, directive options, MyST comments, targets, and block breaks
without turning off normal Markdown checks.
Config name: myst
Aliases: mystmd
Supported Patterns¶
Colon Directives¶
MyST uses ::: (or more colons) as structural containers for directives:
Nesting is supported by increasing the colon count:
The opener is 3+ colons immediately followed by {name} (e.g., :::{note},
::::{warning}). The closer is the same number of colons (or more) without a
{name} suffix.
Treated as Markdown content: The body of colon directives is linted as normal Markdown prose. Rules that check prose (MD013, MD034, link validation) still fire inside colon directives.
Backtick Directives¶
MyST also uses backtick code fences with a {name} info string:
For content-bearing directives (note, warning, tip, hint, important,
caution, danger, admonition, seealso, topic, sidebar, margin, exercise,
solution, dropdown, tab-item, grid, card, figure), the body is linted as
Markdown — in_code_block is cleared.
For code-bearing directives (code-cell, code-block, raw, eval-rst, literalinclude), the body remains treated as a code block and is not linted.
Directive Options¶
Directives can have options specified as :key: value lines immediately after
the opener:
```{figure} image.png
:alt: An image description
:width: 80%
Caption text with **Markdown** formatting.
```
Option lines are part of the directive and are not linted as prose.
Roles (Inline Directives)¶
MyST roles provide inline semantic markup:
The pattern is {rolename} followed by backtick-delimited content. These are
not flagged as malformed inline code.
Comments¶
MyST uses % for single-line comments:
Comment lines are excluded from line-length checks, from text rules such as MD044, and from paragraph reflow: a comment never joins the paragraph around it.
Targets¶
A line of the form (label)= defines a cross-reference target for the block
below it, and {ref} roles point at the label:
A target line is a label, not prose. rumdl does not check the label as text
(MD044 would otherwise rewrite it and break the links that use it), and a file
that starts with a target satisfies MD041 when a heading follows. A target
directly above a heading needs no blank line between them, and MD022 asks for
the blank line above the target instead. A line that only contains (label)=
inside a paragraph interrupts it, as in MyST. Text (label)= text is ordinary
text and is still checked. Targets are recognized only in the myst flavor.
Block breaks¶
A line starting with +++ is a block break, optionally followed by JSON
metadata (+++ {"tags": ["x"]}). The line is excluded from text checks, and the
paragraphs around it are never reflowed together. Only the contiguous +++
form is recognized.
Targets, block breaks and comments inside directive bodies, indented four or more columns, or inside blockquotes are not recognized.
Rule Behavior Changes¶
| Rule | Standard Behavior | MyST Behavior |
|---|---|---|
| MD013 | Check line length everywhere | Skip % comment, (label)= target and +++ break lines |
| MD022 | Blank lines around headings | Blank line goes above (label)= targets directly above a heading |
| MD041 | First line should be a heading | Skip leading comments, targets and +++ breaks |
| MD044 | Check proper names everywhere | Skip comment, target and block break lines |
| MD031 | Blanks around backtick/tilde fences | Also enforce blanks around colon directives (:::{name}) |
| MD038 | Flag spaces in inline code | Skip role syntax ({role}content``) |
| MD040 | Require language on code fences | Accept {name} as valid directive info string |
| MD046 | Detect code block style | Skip colon directives from style detection |
| MD048 | Detect fence style | Skip colon/backtick directives from fence style detection |
Content vs Code Directives¶
rumdl distinguishes between directives whose body contains Markdown and those whose body contains code:
| Category | Directives | Body linted? |
|---|---|---|
| Content | note, warning, tip, hint, important, caution, danger, admonition, seealso, topic, sidebar, margin, exercise, solution, dropdown, tab-item, grid, card, figure | Yes |
| Code | code-cell, code-block, raw, eval-rst, literalinclude | No |
Unrecognized directive names default to code-block behavior (body not linted).
Configuration¶
Or using the alias:
Or per-file:
CLI Usage¶
When to Use¶
Use the MyST flavor when:
- You write Markdown for Jupyter Book or Sphinx with MyST-Parser.
- Your files contain
:::{directive}or```{directive}blocks. - You use role syntax like
{ref}labelor `{math}`expression. - You are getting false positives from MD040 (missing language) on directive fences.
Do not use this flavor for Pandoc projects — Pandoc uses ::: with
different semantics (fenced divs without {name}). Use
pandoc for Pandoc projects.
See Also¶
- Flavors Overview — compare all flavors
- Pandoc Flavor — Pandoc fenced divs (transparent
:::blocks) - MyST Markdown documentation