---
title: "Markdown horizontal line: rules, traps and fixes"
slug: markdown-horizontal-rules
excerpt: "What forms a Markdown horizontal line, why a dash line can turn text into a heading, and how lists and front matter change the result."
quickAnswer: "A Markdown horizontal line is the divider the CommonMark spec calls a thematic break. This page covers the three characters that make one, the spacing and indent the spec accepts, and the lines that look close but fail the rule. It then walks through the heading trap and its fixes, how a break line and a list item compete, and how the dashes around front matter relate. The examples were checked with pandoc."
keyTakeaways:
  - "The first section lists the characters, spacing and indent that make a horizontal line."
  - "A separate section covers the heading trap, with an example and three fixes."
  - "The lists section compares break lines and bullet items, with tested examples."
  - "Front matter dashes get one short note and a link to the front matter post."
  - "A numbered check at the end helps you confirm each break line in a file."
publishedDate: 2026-10-20
dateModified: 2026-10-20
author: Bobby Huang
cluster: B
role: post
status: published
faq:
  - q: What turned my text into a heading after I added a line?
    a: "The setext section in the body shows the case and the three ways to fix it."
  - q: Which character is safest for a horizontal line?
    a: "The section on the heading trap compares the three characters, and the closing check suggests a default."
  - q: Can a horizontal line sit inside a list?
    a: "The lists section has a tested example and the spec rule behind it."
related:
  - markdown-syntax-guide
  - markdown-headings-anchor-links
  - what-is-front-matter
---

You wanted a divider between two sections. You typed three dashes under your last sentence, and the sentence turned into a heading instead.

In Markdown, a dash line can mean two different things, and the line above it decides which one you get. We'll go through how a horizontal line is built, the heading trap, how lists change the picture, and a quick routine for checking a file. The [basics guide](markdown-syntax-guide.md) has the other common elements.

## What makes a horizontal line

In CommonMark terms it's a thematic break. It renders as `<hr />` in HTML. A line forms one when it holds three or more of the same character, and that character must be a dash, star or underscore. Spaces or tabs may sit between the characters and at the end. An indent of three spaces or fewer is allowed before the first one.

All of these are horizontal lines:

```markdown
---
***
___
- - -
   ***
_____________
```

Pandoc printed `<hr />` for each of them in a local test. The last one shows that you can use more than three characters.

Some lines look close but fail the rule:

```markdown
--
---a---
*-*
```

Two dashes aren't enough, so `--` stays as text. Any other character on the line, like the `a` in `---a---`, breaks the rule too. Mixed characters fail as well; pandoc rendered `*-*` as an emphasized dash. Four spaces of indent is one too many: when a blank line precedes it, a line holding only `    ***` turns into code instead.

Pandoc's own manual gives a shorter version of the same rule and adds a tip: it strongly recommends blank lines around each horizontal line. That tip turns out to matter most for the dash, as the next section shows.

## The heading trap

Watch this case:

```markdown
Jane Doe
---
```

You might expect a paragraph with a line under it. Pandoc printed `<h2>Jane Doe</h2>` and nothing else. No `<hr />` at all.

The reason is a second CommonMark rule. A line of `=` or `-` characters placed right under a paragraph's text is a setext heading underline. Dashes give a level 2 heading. When a dash line could be read either way, the spec says the heading reading wins.

It gets worse with longer paragraphs. A setext heading can take in every line of the paragraph above it. A test file with `Jane Doe signed.` on one line, `Doe Holdings paid.` on the next and `---` under both came out as one `<h2>` holding both sentences.

### Three ways out

1. **Insert an empty line above the dashes.** The blank line ends the paragraph, so the dashes have no text left to underline. With `Jane Doe`, a blank line, then `---`, pandoc printed `<p>Jane Doe</p>` followed by `<hr />`.
2. **Switch to stars or underscores.** Only `=` and `-` can underline a setext heading. A `***` or `___` line right under the text gave a paragraph and an `<hr />` with no blank line at all.
3. **Space out the dashes.** A setext underline can't hold spaces between its characters. `- - -` placed right under `Jane Doe` gave a paragraph and an `<hr />` in a local test.

The first fix is the one pandoc's manual points toward, and it keeps the source easy to scan. The second is handy when you can't control the blank lines, such as text an agent appends to an existing file.

### Lines that aren't affected

The trap is limited to paragraph text. A `---` line right after a list or a block quote doesn't turn anything into a heading. Per the spec, a setext underline can't be a lazy continuation line of a block quote or list item. In local tests, `> Jane Doe` followed by `---` gave a block quote and then an `<hr />`, and a two-item list followed by `---` gave the list and then an `<hr />`.

For a heading you mean to have, the `##` form may be easier to spot in the source. The [heading and anchor post](markdown-headings-anchor-links.md) covers both heading styles and how links to them work.

## Horizontal lines and lists

Dashes and stars also start bullet items, so a line like `- - -` could be read two ways. It could be an empty bullet holding more bullets, or a horizontal line. The spec settles it: when a line could be a bullet or a thematic break, the thematic break wins.

```markdown
* Jane Doe
* * *
* Doe Holdings
```

Pandoc split that into two lists with an `<hr />` between them. You may have meant three bullets, but you get a list, a line and a second list.

To put a horizontal line inside a list item, use a different character from the bullet. The spec shows this with dash bullets and a star line:

```markdown
- Jane Doe
- * * *
```

Pandoc printed a list whose second item holds an `<hr />`. The [lists and block quotes post](markdown-block-quotes-nested-lists.md) explains how indent decides what belongs to which item.

## The dashes around front matter

Many tools read a leading YAML block, fenced by two `---` lines, as front matter, but plain CommonMark has no such idea: the opening `---` is a horizontal line, and the closing one may turn the line above it into a heading. The [front matter explainer](what-is-front-matter.md) covers the rest.

## Checking a file

When a horizontal line doesn't show up, or a heading appears out of nowhere, render the file and look at the HTML. Pandoc can read the file as CommonMark and print HTML:

```
pandoc -f commonmark -t html notes.md
```

Then work through these steps:

1. **Look at each `<h2>` you didn't write with `##`.** Check the source line under it. A dash line there is the heading trap.
2. **Count the `<hr />` tags.** Compare the count with the break lines you meant to have. A missing one may point to the trap or a rule-breaking character.
3. **Look for `<pre><code>` around a break.** That points to four or more spaces of indent.
4. **Check list edges.** If one list came out as two, a `* * *` or `- - -` line may sit between the items.
5. **Pick one style and keep it.** A `***` line plus blank lines on both sides avoids both the heading trap and any confusion with front matter dashes. For files your agents write, put that choice in their instructions in one sentence.

Code examples like the ones in this post need their own care. Inside a fenced block, break lines stay as plain text, so a `---` there never becomes a rule. The [code blocks post](markdown-code-blocks.md) explains how fences open and close.

## Where Sashmark fits

Work on Sashmark is still under way: it's planned as a calm desktop reader for Markdown, with editing, aimed at files agents revise a lot. It is in development and has not been released. The app itself has no AI features. Its first version is meant to render headings, lists, block quotes and code blocks live, with edits made in place on that page and untouched lines kept byte-for-byte on save. Front matter fields are meant to show up in a two-column table. For the status of each item, look at the [roadmap](/roadmap); the [planned features](/features) and [help pages](/help) give the detail.

Track progress at [sashmark.com](https://www.sashmark.com/).
