Skip to main content

· 2 min read

Custom markdownlint Rules for Docs Conventions

A docs site I worked on had a set of conventions that mattered. Every page needed a slug and a title in the frontmatter. Code tabs needed a blank line above and below them. Certain old URL shapes were no longer allowed. Custom admonition components had to be formatted in a specific way. TODO markers in published content were a soft error worth catching. None of these were things off-the-shelf markdownlint cared about. They mattered to us, and to nobody else.

The instinct was to write a contributor guide and hope. That works for a week. A linter is the same idea expressed in a form that keeps working without anyone having to read it.

Rules That Encode Project Decisions

I added a small set of project-specific rules to the markdownlint config. Each one captured a decision the team had already made, written in a form a CI step could enforce.

The frontmatter rule failed any file missing a slug or title. The link rule blocked patterns we no longer wanted appearing, like legacy framework placeholders or old domain prefixes. The admonition rule required our custom syntax to be opened and closed in the shape the renderer expected. The codetabs rule required blank lines around the wrapper so the parser would resolve them correctly. The TODO rule flagged comments writers had left for themselves and forgotten to remove.

Each rule was tiny. Together they made the things we cared about machine-checkable, which meant they stopped being things people had to remember.

The Allowlist Conversation

The trickiest rule was the one that disallowed certain image paths but allowed aliased path imports. Real projects always have exceptions. The lesson was to not pretend they do not exist: the rule reads an explicit allowlist, and adding a new entry is a small PR with the path and a one-line reason. Reviewers see the exception and accept it intentionally rather than the linter being silently lax.

What I Learned

A linter is not the place for clever rules. It is the place for the boring ones, the conventions that came out of decisions you have already had three times. Encoding them as rules is how you stop having that conversation a fourth time. The savings compound. Every new contributor inherits the conventions for free, and reviewers spend their attention on the things only humans can catch.