[{"data":1,"prerenderedAt":44},["Reactive",2],{"blog-post-markdown-style-guide-common-mistakes":3},{"slug":4,"title":5,"description":6,"excerpt":7,"date":8,"category":9,"author":10,"keywords":11,"readingMinutes":12,"html":13,"headings":14,"tools":31,"related":33},"markdown-style-guide-common-mistakes","Markdown Style Guide: Common Mistakes and How to Catch Them","The 10 most common Markdown style mistakes — broken heading hierarchy, missing alt text, bare URLs, and more — with before/after examples and how to catch them before you publish.","Markdown is forgiving. Almost anything you type will render into something — which is exactly the problem. A skipped heading level, an image with no alt text, or a URL you forgot to wrap in link…","2026-07-17","workflows","Matt Rathbone","markdown style guide, markdown linter, markdown mistakes, markdown best practices, heading hierarchy, markdown accessibility, markdownlint, markdown validation",7,"\u003Cp>Markdown is forgiving. Almost anything you type will render into \u003Cem>something\u003C/em> — which is exactly the problem. A skipped heading level, an image with no alt text, or a URL you forgot to wrap in link syntax will all render &quot;fine&quot; in your editor&#39;s preview, then quietly cause real problems once the document ships: broken screen-reader navigation, a table of contents that doesn&#39;t nest correctly, a link that looks wrong on a different platform.\u003C/p>\n\u003Cp>None of these are syntax errors. Markdown doesn&#39;t have a compiler that stops you. That&#39;s exactly why linting matters — and why we built a \u003Ca href=\"/tools/markdown-linter\">Markdown Linter\u003C/a> that checks for these issues in the browser, no setup required.\u003C/p>\n\u003Cp>This post walks through the mistakes that actually show up in real documents, grouped by why they matter, with a before/after for each.\u003C/p>\n\u003Ch2 id=\"why-markdown-style-mistakes-matter\">Why Markdown Style Mistakes Matter\u003C/h2>\n\u003Cp>Unlike a programming language, Markdown failures are usually silent. Three categories of consequence show up over and over:\u003C/p>\n\u003Col>\n\u003Cli>\u003Cstrong>Accessibility\u003C/strong> — screen readers rely on a logical heading structure and on alt text to describe images. Get either wrong, and you&#39;ve made the document harder to use for anyone relying on assistive technology.\u003C/li>\n\u003Cli>\u003Cstrong>Structure and navigation\u003C/strong> — table-of-contents generators, doc-site sidebars, and &quot;on this page&quot; widgets all build their outline from your headings. A skipped level (H2 straight to H4) breaks that outline even though the page still looks normal.\u003C/li>\n\u003Cli>\u003Cstrong>Cross-renderer consistency\u003C/strong> — a bare URL, a code block missing its language tag, or trailing whitespace can render differently — or not at all — depending on whether the renderer is GitHub, a static site generator, or an internal wiki.\u003C/li>\n\u003C/ol>\n\u003Cp>None of these show up as a red squiggly line while you&#39;re typing. You only find out when the rendered page looks wrong, or an accessibility audit flags it, or someone tells you the sidebar navigation skipped a section.\u003C/p>\n\u003Ch2 id=\"the-10-rules-with-beforeafter-examples\">The 10 Rules, With Before/After Examples\u003C/h2>\n\u003Cp>Our \u003Ca href=\"/tools/markdown-linter\">Markdown Linter\u003C/a> checks for 10 specific issues, split into one hard error and nine warnings. Here&#39;s what each one actually catches.\u003C/p>\n\u003Ch3 id=\"1-heading-hierarchy-error\">1. Heading hierarchy (error)\u003C/h3>\n\u003Cp>The only rule in the tool that&#39;s treated as an \u003Cstrong>error\u003C/strong> rather than a warning, because it&#39;s the one most likely to break something structural, not just look untidy.\u003C/p>\n\u003Cp>Headings should step down one level at a time. You can jump back \u003Cem>up\u003C/em> freely (H3 back to H2 is fine — that&#39;s just starting a new section), but you should never skip a level going \u003Cem>down\u003C/em>.\u003C/p>\n\u003Cpre>\u003Ccode class=\"hljs language-markdown\">&lt;!-- Bad: skips H2 entirely --&gt;\n\u003Cspan class=\"hljs-section\"># Getting Started\u003C/span>\n\u003Cspan class=\"hljs-section\">### Installation\u003C/span>\n\u003C/code>\u003C/pre>\u003Cpre>\u003Ccode class=\"hljs language-markdown\">&lt;!-- Good --&gt;\n\u003Cspan class=\"hljs-section\"># Getting Started\u003C/span>\n\u003Cspan class=\"hljs-section\">## Installation\u003C/span>\n\u003C/code>\u003C/pre>\u003Cp>Why it matters beyond looks: screen readers let users jump between headings by level, and they announce the jump (&quot;entering level 3 heading&quot;) — a skipped level is confusing out of context. Search engines and TOC generators use the same hierarchy to build page outlines, so a skip can produce a lopsided or broken outline even though the text itself reads fine.\u003C/p>\n\u003Ch3 id=\"2-multiple-h1s\">2. Multiple H1s\u003C/h3>\n\u003Cp>A document should have exactly one H1 — the page title. A second H1 partway through usually means someone meant to use H2.\u003C/p>\n\u003Cpre>\u003Ccode class=\"hljs language-markdown\">&lt;!-- Bad --&gt;\n\u003Cspan class=\"hljs-section\"># My Guide\u003C/span>\nSome intro text.\n\u003Cspan class=\"hljs-section\"># Advanced Topics\u003C/span>\n\u003C/code>\u003C/pre>\u003Cpre>\u003Ccode class=\"hljs language-markdown\">&lt;!-- Good --&gt;\n\u003Cspan class=\"hljs-section\"># My Guide\u003C/span>\nSome intro text.\n\u003Cspan class=\"hljs-section\">## Advanced Topics\u003C/span>\n\u003C/code>\u003C/pre>\u003Ch3 id=\"3-duplicate-headings\">3. Duplicate headings\u003C/h3>\n\u003Cp>The same heading text appearing twice in one document — often from copy-pasting a section as a template for a new one and forgetting to rename it.\u003C/p>\n\u003Cpre>\u003Ccode class=\"hljs language-markdown\">&lt;!-- Bad --&gt;\n\u003Cspan class=\"hljs-section\">## Configuration\u003C/span>\n...\n\u003Cspan class=\"hljs-section\">## Configuration\u003C/span>\n\u003C/code>\u003C/pre>\u003Cp>This matters for anchor links specifically: most renderers generate a heading&#39;s URL fragment (\u003Ccode>#configuration\u003C/code>) from its text, so the second &quot;Configuration&quot; heading either silently gets a suffixed slug (\u003Ccode>#configuration-1\u003C/code>) that nobody linked to on purpose, or overwrites the first one&#39;s anchor depending on the renderer — either way, a link meant for the first section can land on the wrong one.\u003C/p>\n\u003Ch3 id=\"4-no-language-on-fenced-code-blocks\">4. No language on fenced code blocks\u003C/h3>\n\u003Cp>A \u003Ccode>```\u003C/code> fence with no language identifier after it loses syntax highlighting and, on some platforms, loses the copy-button / line-numbering features that are tied to language detection.\u003C/p>\n\u003Cpre>\u003Ccode class=\"hljs language-markdown\">&lt;!-- Bad --&gt;\n\u003Cspan class=\"hljs-code\">```\nfunction greet(name) {\n  return `Hello, ${name}`;\n}\n```\u003C/span>\n\u003C/code>\u003C/pre>\u003Cpre>\u003Ccode class=\"hljs language-markdown\">&lt;!-- Good --&gt;\n\u003Cspan class=\"hljs-code\">```javascript\nfunction greet(name) {\n  return `Hello, ${name}`;\n}\n```\u003C/span>\n\u003C/code>\u003C/pre>\u003Ch3 id=\"5-bare-urls\">5. Bare URLs\u003C/h3>\n\u003Cp>A plain URL sitting in text, not wrapped in \u003Ccode>[text](url)\u003C/code> link syntax.\u003C/p>\n\u003Cpre>\u003Ccode class=\"hljs language-markdown\">&lt;!-- Bad --&gt;\nSee https://example.com/docs/api-reference for details.\n\u003C/code>\u003C/pre>\u003Cpre>\u003Ccode class=\"hljs language-markdown\">&lt;!-- Good --&gt;\nSee the [\u003Cspan class=\"hljs-string\">API reference\u003C/span>](\u003Cspan class=\"hljs-link\">https://example.com/docs/api-reference\u003C/span>) for details.\n\u003C/code>\u003C/pre>\u003Cp>Some renderers auto-linkify bare URLs (GitHub does); others don&#39;t, and you end up with dead-looking plain text. Even where auto-linking works, a long raw URL is worse for readability and worse for screen readers, which read out the entire URL character by character instead of a meaningful label.\u003C/p>\n\u003Ch3 id=\"6-missing-alt-text\">6. Missing alt text\u003C/h3>\n\u003Cp>An image reference with empty alt text — \u003Ccode>![]()\u003C/code> instead of \u003Ccode>![description](...)\u003C/code>.\u003C/p>\n\u003Cpre>\u003Ccode class=\"hljs language-markdown\">&lt;!-- Bad --&gt;\n![](\u003Cspan class=\"hljs-link\">chart.png\u003C/span>)\n\u003C/code>\u003C/pre>\u003Cpre>\u003Ccode class=\"hljs language-markdown\">&lt;!-- Good --&gt;\n![\u003Cspan class=\"hljs-string\">Bar chart showing monthly signups from January to June\u003C/span>](\u003Cspan class=\"hljs-link\">chart.png\u003C/span>)\n\u003C/code>\u003C/pre>\u003Cp>This is the single highest-impact accessibility fix on this list. Screen reader users get nothing at all from an image with empty alt text — not even a filename fallback in most cases. It&#39;s also one of the easiest mistakes to make, since a missing alt attribute doesn&#39;t change how the image looks to a sighted reader.\u003C/p>\n\u003Ch3 id=\"7-hard-tabs-for-indentation\">7. Hard tabs for indentation\u003C/h3>\n\u003Cp>A line indented with a literal tab character instead of spaces.\u003C/p>\n\u003Cp>Hard tabs render inconsistently — different editors and renderers expand a tab to a different number of spaces (commonly 4 or 8), so indentation-sensitive content like nested lists can shift alignment depending on where it&#39;s viewed. Most Markdown style guides (including markdownlint&#39;s defaults) require spaces for exactly this reason.\u003C/p>\n\u003Ch3 id=\"8-trailing-whitespace\">8. Trailing whitespace\u003C/h3>\n\u003Cp>Whitespace at the end of a line. This is a genuinely dangerous one because Markdown gives \u003Cem>exactly two trailing spaces\u003C/em> special meaning: it forces a line break within a paragraph. One trailing space (or three, or an odd number) does nothing visible in most editors, so it silently changes rendering behavior in a way you won&#39;t notice until you inspect the raw output. (The linter is deliberately careful here — it only flags true trailing whitespace, not the intentional two-space line-break convention.)\u003C/p>\n\u003Ch3 id=\"9-multiple-consecutive-blank-lines\">9. Multiple consecutive blank lines\u003C/h3>\n\u003Cp>More than one blank line between blocks. Markdown collapses any run of blank lines down to a single paragraph break during rendering, so extra blank lines have zero visual effect — they&#39;re pure noise in the source that makes diffs longer and documents harder to scan in an editor.\u003C/p>\n\u003Ch3 id=\"10-line-length\">10. Line length\u003C/h3>\n\u003Cp>A prose line longer than 120 characters. Code blocks are excluded from this check, since code often has legitimate reasons to run long (and it&#39;s usually monospaced and horizontally scrollable in rendered output anyway).\u003C/p>\n\u003Cp>Long prose lines aren&#39;t a rendering problem — Markdown reflows paragraphs regardless of source line length — but they&#39;re a \u003Cem>diff\u003C/em> problem. In a docs-as-code workflow where changes are reviewed as Git diffs, a one-word edit to a 400-character line shows the entire line as changed, making the actual edit hard to spot in review.\u003C/p>\n\u003Ch2 id=\"in-browser-linting-vs-ci-pipeline-linting\">In-Browser Linting vs. CI Pipeline Linting\u003C/h2>\n\u003Cp>If you&#39;ve read our \u003Ca href=\"/blog/markdown-for-technical-writers-docs-as-code\">docs-as-code workflow guide\u003C/a>, you already know about \u003Ca href=\"https://github.com/DavidAnson/markdownlint\">markdownlint\u003C/a> and \u003Ca href=\"https://vale.sh\">Vale\u003C/a> — the standard tools for enforcing Markdown style in CI. Those are the right tools for a real docs pipeline: they run on every pull request, they&#39;re configurable per-project, and Vale in particular goes beyond structure into prose style (banned phrases, terminology consistency, passive voice).\u003C/p>\n\u003Cp>Our \u003Ca href=\"/tools/markdown-linter\">Markdown Linter\u003C/a> isn&#39;t a replacement for that setup — it&#39;s the fast check \u003Cem>before\u003C/em> you get anywhere near a pull request. Paste a document, get instant feedback, no \u003Ccode>npm install\u003C/code>, no config file, no CI run to wait on. It&#39;s useful in a few specific situations a CI pipeline doesn&#39;t cover well:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>Reviewing someone else&#39;s Markdown\u003C/strong> you don&#39;t have local tooling set up for — paste it and check in seconds\u003C/li>\n\u003Cli>\u003Cstrong>Before pasting into a CMS or wiki\u003C/strong> that doesn&#39;t run any linting at all, where structural issues silently affect SEO and accessibility with no safety net\u003C/li>\n\u003Cli>\u003Cstrong>Quick self-review while drafting\u003C/strong>, before you&#39;ve even opened a terminal\u003C/li>\n\u003Cli>\u003Cstrong>Teaching or onboarding\u003C/strong> — showing someone exactly what &quot;broken heading hierarchy&quot; looks like, with immediate before/after feedback, rather than pointing them at a CI log\u003C/li>\n\u003C/ul>\n\u003Cp>Think of it as the same relationship a linter extension in your editor has to your project&#39;s full CI test suite: faster feedback, narrower scope, and no substitute for the real pipeline once the stakes are high enough to need one.\u003C/p>\n\u003Ch2 id=\"try-it\">Try It\u003C/h2>\n\u003Cp>Paste your Markdown into the \u003Ca href=\"/tools/markdown-linter\">Markdown Linter\u003C/a> and you&#39;ll get line-numbered results, color-coded by severity, updating as you type. No signup, nothing leaves your browser.\u003C/p>\n\u003Ch2 id=\"related-reading\">Related Reading\u003C/h2>\n\u003Cul>\n\u003Cli>\u003Ca href=\"/tools/markdown-linter\">Markdown Linter tool\u003C/a> — check your Markdown for these 10 issues instantly\u003C/li>\n\u003Cli>\u003Ca href=\"/blog/markdown-for-technical-writers-docs-as-code\">Markdown for Technical Writers: The Docs-as-Code Workflow Guide\u003C/a> — markdownlint and Vale in a CI pipeline\u003C/li>\n\u003Cli>\u003Ca href=\"/blog/why-your-markdown-isnt-rendering\">Why Your Markdown Isn&#39;t Rendering\u003C/a> — troubleshooting guide for renderer-specific formatting failures\u003C/li>\n\u003Cli>\u003Ca href=\"/blog/markdown-platform-compatibility-guide\">Markdown Across Platforms\u003C/a> — how the same Markdown behaves differently on GitHub, Notion, Obsidian, and Confluence\u003C/li>\n\u003C/ul>\n",[15,19,22,25,28],{"id":16,"text":17,"level":18},"why-markdown-style-mistakes-matter","Why Markdown Style Mistakes Matter",2,{"id":20,"text":21,"level":18},"the-10-rules-with-beforeafter-examples","The 10 Rules, With Before/After Examples",{"id":23,"text":24,"level":18},"in-browser-linting-vs-ci-pipeline-linting","In-Browser Linting vs. CI Pipeline Linting",{"id":26,"text":27,"level":18},"try-it","Try It",{"id":29,"text":30,"level":18},"related-reading","Related Reading",[32],"markdown-linter",[34],{"slug":35,"title":36,"description":37,"excerpt":38,"date":39,"category":40,"author":41,"keywords":42,"readingMinutes":43},"markdown-linting-quality-assurance-complete-guide","Markdown Linting and Quality Assurance: Complete Guide for Automated Error Detection, Style Enforcement, and Documentation Standards","Master Markdown linting and quality assurance with comprehensive error detection tools, automated style enforcement, documentation standards, and continuous integration workflows for professional technical writing.","Markdown linting and quality assurance systems provide systematic error detection, style enforcement, and documentation standards that ensure consistent, professional, and maintainable technical…","2025-12-23","Tutorial","Matthew Rathbone","markdown linting, markdown quality assurance, markdownlint, markdown style guide, automated markdown validation, markdown CI/CD, documentation standards, markdown error detection",8,1792184946949]