MD090 - Horizontal rules should not precede headings¶
Aliases: no-hr-before-heading
Disabled by default. This rule is opt-in: enable it explicitly with
extend-enable. There is no markdownlint equivalent; this is a rumdl-specific
structure rule.
What this rule does¶
Flags a horizontal rule (---, ***, ___, or a spaced form such as - - -)
whose only separation from the heading below it is blank lines. The heading
already marks the section boundary, so the rule draws the same line twice.
Only blank lines may sit between the rule and the heading. A comment, a
reference definition, or any other line in between means the rule is not
directly above the heading, and nothing is reported. Both the rule and the
heading must be at the top level of the document: a horizontal rule inside a
blockquote or a list item belongs to that container and is left alone.
Containers whose body is ordinary Markdown, such as a fenced div or a MyST
directive, are treated like top-level content, so a rule directly above an
ATX heading inside one is still reported. Only ATX headings are reported
there: a container's opening marker followed by a dash run
(note above ---) is parsed as a setext heading though it opens a
container rather than a section, and since removing a rule deletes a line,
this rule leaves every setext heading inside a container alone rather than
guessing which ones are real.
Containers whose body is indented rather than fenced, such as a MkDocs
admonition or a content tab, are not reported at all: an indented dash run
is not read as a horizontal rule anywhere in rumdl. A MyST directive written
with backticks (```{note}) rather than colons is silent for the same
reason: its body starts out as a code fence, so nothing inside it is read as a
horizontal rule. Write the directive with colons to have it checked.
A --- written directly under a line of text is not a horizontal rule at all.
It is the underline of a setext heading, and this rule never touches it.
Why this matters¶
Generated Markdown, LLM output in particular, separates almost every section with a horizontal rule right before the next heading. Rendered, that is a line followed by a larger, bolder line; in source it is noise between sections that the headings already delimit. Removing the redundant rules keeps the heading structure as the single expression of the document's outline.
Why it is opt-in¶
Slide formats (Marp, Slidev, reveal.js, Pandoc slide decks) use a horizontal rule as the slide separator, and the next slide almost always opens with a heading. In those documents every rule this check would remove is a slide boundary, so the rule stays off unless a project turns it on.
Configuration¶
This rule has no configuration options.
Examples¶
Incorrect¶
Correct¶
A horizontal rule between two paragraphs is fine; the rule only speaks when a heading follows:
Automatic fixes¶
The rule is removed together with the blank lines between it and the heading. The blank line above the rule is kept, so the heading stays separated from the paragraph before it. When no blank line sits above the rule, the fix leaves one behind, so the text above never touches the heading. A run of rules directly above one heading is removed in one pass.