Md063 - Heading Capitalization
September 3, 2026 ยท View on GitHub
Aliases: heading-capitalization
What This Rule Does
Enforces consistent capitalization styles for markdown headings. This rule supports title case, sentence case, and all caps styles.
Note: This rule is disabled by default. You must explicitly enable it in your configuration.
Why This Matters
- Consistency: Ensures all headings follow the same capitalization style
- Professional appearance: Properly capitalized headings look more polished
- Readability: Consistent formatting helps readers navigate your document
- Style guide compliance: Many style guides require specific heading capitalization
Examples
Title Case (default)
Incorrect
# the quick brown fox
## getting started with javascript
### self-documenting code practices
Correct
# The Quick Brown Fox
## Getting Started with JavaScript
### Self-Documenting Code Practices
Sentence Case
Incorrect
# The Quick Brown Fox
## Getting Started With JavaScript
Correct
# The quick brown fox
## Getting started with JavaScript
All Caps
Incorrect
# introduction
## Getting Started
Correct
# INTRODUCTION
## GETTING STARTED
Configuration
| Option | Type | Default | Description |
|---|---|---|---|
style | string | "title-case" | Capitalization style: title-case, sentence-case, or all-caps |
lowercase-words | string[] | (see below) | Words to keep lowercase in title case |
ignore-words | string[] | [] | Words to preserve exactly (brand names) |
preserve-cased-words | boolean | true | Auto-preserve words with internal capitals |
sentence-case-restart-after | string[] | [] | Punctuation that starts a new sentence in sentence case |
min-level | integer | 1 | Minimum heading level to check (1-6) |
max-level | integer | 6 | Maximum heading level to check (1-6) |
Default Lowercase Words
a, an, and, as, at, but, by, for, in, nor, of, on, or, so, the, to, up, yet
Example Configurations
Enable With Title Case (default)
[MD063]
enabled = true
style = "title-case"
Sentence Case
[MD063]
style = "sentence-case"
Sentence Case That Restarts After a Colon
[MD063]
style = "sentence-case"
sentence-case-restart-after = [":"]
All Caps
[MD063]
style = "all-caps"
Preserve Brand Names
[MD063]
style = "title-case"
ignore-words = ["iPhone", "macOS", "GitHub", "JavaScript", "TypeScript"]
preserve-cased-words = true
Only Check H1 and H2 Headings
[MD063]
style = "title-case"
min-level = 1
max-level = 2
Custom Lowercase Words
[MD063]
style = "title-case"
lowercase-words = ["a", "an", "the", "and", "but", "or", "for", "nor", "on", "at", "to", "by", "from", "with", "without", "into", "onto", "upon", "vs", "via"]
Special Handling
Sentence Boundaries Inside a Heading
By default sentence case capitalizes only the heading's first word, so everything after a colon is lowercased:
# Requirement 1: Struct to Logger Slice Conversion
Becomes:
# Requirement 1: struct to logger slice conversion
Set sentence-case-restart-after to the punctuation that should start a new sentence:
[MD063]
style = "sentence-case"
sentence-case-restart-after = [":"]
Now the word after the colon is capitalized like the heading's first word:
# Requirement 1: Struct to logger slice conversion
Each entry is matched against the end of a word, so the boundary is only recognized where whitespace follows it. That is what keeps Well-Known Ports intact when - is configured, and leaves https://example.com/A/B alone when : is:
# Ports: Well-Known ports explained
A boundary with nothing after it (# Setup:) changes nothing, and words preserved by ignore-words, preserve-cased-words or an MD044 proper name keep winning over the restart, so iPhone after a colon stays iPhone rather than becoming IPhone.
Boundaries are recognized in the parts of a heading the rule capitalizes, which is plain text and link text:
# Topic [see:](./guide.md) More detail
Inline code, HTML and image alt text are preserved verbatim rather than capitalized, so a boundary inside one of them is not treated as a sentence break:
# Topic `see:` more detail
The default is empty, which is the behavior described at the top of this section.
First-Person Pronoun in Sentence Case
Sentence case keeps the English pronoun I uppercase wherever it appears, including the contractions I'd, I'll, I'm and I've.
Straight and typographic apostrophes are both recognized, as is surrounding punctuation or emphasis:
# How do I debug playbooks?
# What **I'd** change
Other uppercase single letters are handled normally, so # Compare A and B becomes # Compare a and b.
A mid-sentence lowercase i is also left lowercase because it may be a variable rather than a pronoun.
Inline Code
Inline code spans are preserved as-is:
# Using `const` in JavaScript
Becomes:
# Using `const` in JavaScript
The backticked content is never modified.
Inline HTML
HTML tags are never capitalized, whatever the element, so attribute names and values such as anchor IDs and image paths keep their bytes:
## goals and scope<a id="goals-and-scope"></a>
## the <del>old-name-here</del> is gone
Becomes:
## Goals and Scope<a id="goals-and-scope"></a>
## The <del>old-name-here</del> Is Gone
An element opened and closed on the same line keeps its content verbatim, as does an HTML comment. Text after an unpaired tag such as <br> or <img> is heading text and is capitalized like the rest. A self-closing tag such as <span/> and a void element such as <br> or <param> open no element, so a later closing tag of the same name belongs to the enclosing element. Tags are read in order as a browser reads them, so a tag written inside another tag's attribute value is part of that value. A tag written inside inline code is code, and one written with a backslash-escaped \< is text, so neither is markup.
Markup that renders nothing, such as an empty anchor element or a comment, does not move the heading's first or last word: ## where to<a id="where-to"></a> becomes ## Where To<a id="where-to"></a> because to is still the last word, and in sentence case the text after a leading anchor is the start of the sentence. An element with visible text, such as <kbd>ctrl</kbd>, is an element of its own and counts. So is an element that paints content of its own without holding text, such as <img> or <input>: ## go to <img src="icon.png"> keeps to lowercase, as ## go to  does.
Links
Link text is capitalized, but URLs are preserved:
# See the [getting started](./guide.md) guide
Becomes:
# See the [Getting Started](./guide.md) Guide
Custom Header IDs
Custom header IDs (Kramdown syntax) are preserved:
# getting started {#intro}
Becomes:
# Getting Started {#intro}
Mixed-Case Words
When preserve-cased-words is enabled (default), words with internal capitals are preserved:
# using GitHub actions
Becomes:
# Using GitHub Actions
Note: "GitHub" is preserved because it has an internal capital letter.
Hyphenated Words
Each part of a hyphenated word is capitalized in title case:
# self-documenting code
Becomes:
# Self-Documenting Code
Automatic Fixes
This rule can automatically fix capitalization issues. Run:
rumdl check --fix yourfile.md
Markdown with Gherkin
Under the mdg flavor, MD063 copies a Gherkin keyword and its colon through
verbatim and recases only the part after it, so
## Scenario Outline: add two numbers becomes
## Scenario Outline: Add two numbers rather than losing its keyword. A keyword
counts only when it is spelled exactly, and Scenario Outline is two words, so
recasing a heading wholesale would turn the structure into prose.
The split takes the heading's first colon, provided no backtick precedes it: a
dialect keyword is one or two plain words, so a colon behind a backtick sits
inside a code span and # See `x: y` Notes is recased as it would be under
any other flavor. A later colon belongs to the name and is recased with it.
Because the split happens before the heading is parsed into segments, the
keyword keeps its own spacing and never counts as the heading's first or last
word, and a trailing {#custom-id} is preserved.
See Markdown with Gherkin Flavor for the full flavor specification.
Related Rules
Rationale
Consistent heading capitalization improves document readability and professionalism. This rule uses the titlecase crate which implements John Gruber's title case algorithm, ensuring proper handling
of edge cases like articles, prepositions, and hyphenated words.
The rule is disabled by default because capitalization preferences vary widely between style guides (AP, Chicago, APA) and personal preferences. Enable it only if your project requires consistent heading capitalization.