Why Don't Markdown IDs Work When They Start with a Number?

Subtitle: Uncovering the Secret of Automatic vs. Manual ID Generation in Parsers

When writing documents in Markdown, you often assign custom IDs to headers to create direct links to specific sections. Usually, it works perfectly, just like this:

### My Awesome Title {#my-awesome-title}

...

[Go to the awesome title](#my-awesome-title)

But what about a case like this? You want to designate “Chapter 1” with an ID of 1-section.

### Chapter 1. Getting Started {#1-section}

Have you ever had the frustrating experience where, for some reason, this ID doesn’t work or the link breaks? This isn’t a bug. It’s closer to an intentional design choice in most Markdown parsers.

This article will clearly explain why starting a Markdown ID with a number causes problems and what the best ways are to solve it.

The Root of the Problem: A Clash with CSS

The fundamental reason for this issue isn’t with Markdown itself, but with the relationship between HTML and CSS, the final languages your Markdown is converted to.

  1. HTML Standards: In the older HTML4, an ID could not start with a number. While this rule was relaxed in HTML5, making id="1-section" technically valid, the real problem lies elsewhere.
  2. The Limits of CSS Selectors: The real issue is with CSS. To select an element by its ID, CSS uses the hash (#) symbol. For example, id="my-id" is selected with #my-id. But what happens if the ID is 1-section? A CSS parser won’t recognize #1-section as an ID. Instead, it tries to interpret the 1 following the # as a numerical value for another purpose, breaking the selector.

To avoid these potential downstream problems, most Markdown parsers intentionally block or disallow user-specified IDs that start with a number. Think of it as a built-in safeguard.

“But Why Does ### 1. Title Generate a Numeric ID Automatically?”

This is where many people get confused. If you write the following,

### 1. Getting Started

the parser automatically and correctly creates a header with a numeric ID, like <h3 id="1-getting-started">. Why the different behavior?

The key is that parsers treat automatic generation and manual specification differently.

Category Auto-Generated ID (### 1. Title) Manually-Specified ID ({#1-section})
Controller The Markdown parser generates it based on its own internal rules. The user forces a specific ID.
Rules Applied Flexible, since the parser controls the output. Stricter validation is applied to external input.
Purpose To automate consistent document structure. To enforce a specific, desired ID.
Result id="1-getting-started" is allowed id="1-section" is blocked

In short, the parser trusts the IDs it creates for itself will work within its own system, but when a user manually provides an ID, it enforces the safest possible rules (i.e., starting with a letter) to prevent issues with external technologies like CSS and JavaScript.

The Solutions: What Should You Do?

Now that we know the cause, let’s look at the solutions.

This is the simplest, most reliable, and most compatible method for all environments. Just add a meaningful prefix to the ID.

### Chapter 1. Getting Started {#ch-1}

### Section 1. Overview {#sec-1-summary}

You can use any letter or prefix you like, such as ch (chapter), sec (section), or h (header). This ensures you will never run into issues when referencing the ID in CSS or JavaScript.

Method 2: Use Raw HTML (The Definitive Solution)

You might have a special case where you absolutely must use an ID like id="1-section". While it’s not possible with Markdown syntax, the great thing about Markdown is that you can mix it with HTML.

Simply write the header you want to have a custom ID as a direct HTML tag, and you can completely bypass the parser’s restrictions.

<h3 id="1-section">Chapter 1. Getting Started</h3>

This method is the most certain way to get the exact ID you want, 100% of the time.

Conclusion: The Key Takeaways

  • When you manually specify a Markdown ID like {#1-my-id}, it fails because parsers include a safeguard to ensure compatibility with CSS.
  • An ID that is automatically generated by the parser (from a heading like ### 1. Title) can start with a number because the parser controls and understands this process.
  • The best solution is to always use a letter prefix, like {#ch-1}, for your custom IDs.
  • If you absolutely must use an ID that starts with a number, write that specific element using raw HTML.

Now that you know the secret behind Markdown IDs, you can structure your documents freely without worrying about broken links ever again.