Markdown Style Guide
crefft > Documentation > Markdown
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
Extends Google Markdown Style Guide.
Quick Reference
| Task | Tool | Command |
|---|---|---|
| Lint | markdownlint1 | npx --package=markdownlint-cli@0.46.0 markdownlint '**/*.md' |
| Format | Prettier2 | npx prettier@4.0.0-alpha.8 --write '**/*.md' |
| Diagrams | Mermaid3 | via markdown code blocks |
| Link check | markdown-link-check4 | Link-check command |
Linting: markdownlint
Projects MUST use markdownlint1 to enforce consistent Markdown style.
npm install --save-dev markdownlint-cli@0.46.0
npx --package=markdownlint-cli@0.46.0 markdownlint '**/*.md'
Why markdownlint
markdownlint1 provides automated enforcement of Markdown conventions, catching common issues like inconsistent heading styles, improper list formatting, and missing blank lines. It ensures consistent style across teams without manual review effort.
Configuration (.markdownlint.json)
Projects MUST use the following base configuration:
{
"default": true,
"MD013": false,
"MD033": false,
"MD041": false
}
Disabled rules:
MD013: Line length (Prettier2 handles this)MD033: Inline HTML (sometimes needed)MD041: First line must be heading (not always applicable)
Formatting: Prettier
Projects MUST use Prettier2 for Markdown formatting with the following configuration:
{
"proseWrap": "always",
"printWidth": 80,
"tabWidth": 2
}
Why Prettier
Prettier2 eliminates debates about formatting details by automatically enforcing a consistent style. It handles line wrapping, spacing, and alignment automatically, allowing authors to focus on content rather than formatting.
Why tabWidth 2
tabWidth controls the indentation Prettier2 emits for nested list items,
and markdownlint1 rule MD007/ul-indent defaults to an expected indent of
two spaces. Setting tabWidth to 4 makes the two mandatory tools contradict
each other: Prettier writes four-space nested items and markdownlint then
reports MD007/ul-indent [Expected: 2; Actual: 4] on the file Prettier just
formatted, so no file can satisfy both. tabWidth: 2 keeps MD007 at its
default and leaves the base .markdownlint.json above unchanged.
Projects that MUST use four-space indentation for portability with legacy
Markdown parsers MUST relax the affected rule in .markdownlint.json rather
than leaving the conflict in place:
{
"MD007": { "indent": 4 }
}
Only MD007 needs relaxing. Prettier 3.9.6 applies tabWidth to continuation
and nesting indentation only, and still emits a single space after every list
marker, so the MD030/list-marker-space override shown in the upstream
compatibility notes5 is unnecessary — setting it to 3 makes
markdownlint1 reject Prettier’s own output.
The trade-off is that MD007: { "indent": 4 } diverges from the
markdownlint1 default, so every consumer of the documentation must copy it;
two-space indentation needs no configuration at all.
Key Conventions
Line Length
Prose MUST wrap at 80 characters. URLs, code blocks, and tables MAY exceed this limit when necessary.
Headings
# Document Title (only one H1)
## Section
### Subsection
Headings MUST:
- Use ATX-style (
#) not Setext-style (underlines) - Have one blank line before and after
- Omit trailing punctuation
Code Blocks
Code blocks MUST specify the language:
```python
def hello():
print("Hello, World!")
```
```bash
echo "Hello"
```
Inline code SHOULD be used for: variable_name, function(), ClassName
Links
[Link text](https://example.com)
[Reference-style link][ref]
[ref]: https://example.com
Tables
| Header 1 | Header 2 | Header 3 |
|----------|----------|----------|
| Cell 1 | Cell 2 | Cell 3 |
| Cell 4 | Cell 5 | Cell 6 |
Diagrams: Mermaid
Projects SHOULD use Mermaid3 for diagrams in documentation. It renders in GitHub, GitLab, and most documentation systems.
Why Mermaid
Mermaid3 diagrams are version-controllable text files, making them easy to review, diff, and maintain. They render automatically in most modern platforms without requiring external image hosting or build steps. Changes to diagrams appear in pull request diffs alongside code changes.
When to Use Mermaid
- Flowcharts and process diagrams
- Sequence diagrams
- Entity-relationship diagrams
- State diagrams
- Architecture diagrams
- Git graphs
Flowchart
```mermaid
flowchart TD
A[Start] --> B{Is it working?}
B -->|Yes| C[Great!]
B -->|No| D[Debug]
D --> B
```
Sequence Diagram
```mermaid
sequenceDiagram
participant Client
participant API
participant Database
Client->>API: POST /users
API->>Database: INSERT user
Database-->>API: user_id
API-->>Client: 201 Created
```
Entity Relationship
```mermaid
erDiagram
USER ||--o{ ORDER : places
ORDER ||--|{ ORDER_ITEM : contains
PRODUCT ||--o{ ORDER_ITEM : "ordered in"
```
Validating Mermaid
# Install mermaid-cli
npm install -g @mermaid-js/mermaid-cli@12.0.0
# Validate syntax
mmdc -i diagram.mmd -o diagram.png
# Or use online editor
# https://mermaid.live
See the Mermaid Live Editor8 for interactive diagram creation and validation.
ASCII Diagrams
For simple diagrams that MUST work everywhere (including plain text viewers), projects MAY use ASCII art:
┌─────────────┐ ┌─────────────┐
│ Client │────>│ API │
└─────────────┘ └──────┬──────┘
│
v
┌─────────────┐
│ Database │
└─────────────┘
ASCII Box Drawing Characters
Box drawing:
┌ ┐ └ ┘ ─ │ ├ ┤ ┬ ┴ ┼
Arrows:
-> <- ^ v > < A V
Double lines:
╔ ╗ ╚ ╝ ═ ║ ╠ ╣ ╦ ╩ ╬
Checking ASCII Alignment
Ensure your editor uses a monospace font. In CI, you can validate alignment using a simple script that checks character positions, but this is rarely necessary if you use proper editor support.
Link Checking
Projects SHOULD validate links using markdown-link-check4:
npm install --save-dev markdown-link-check@3.15.0
npx markdown-link-check@3.15.0 --config .markdown-link-check.json README.md
Why Link Checking
Broken links degrade documentation quality and user trust. Automated link checking catches broken references before they reach users, especially important for documentation that references external resources that may change or move.
Configuration (.markdown-link-check.json)
{
"ignorePatterns": [
{ "pattern": "^https://localhost" }
],
"timeout": "20s",
"retryOn429": true
}
Every invocation MUST pass this file explicitly:
--config .markdown-link-check.json on the command line and
config-file: .markdown-link-check.json for the GitHub Action below.
Why the Configuration Must Be Passed Explicitly
markdown-link-check4 loads a configuration file only when one is named on the
command line; it does not discover .markdown-link-check.json by convention.
The GitHub Action does default a configuration path, but the default is
mlc_config.json, not the filename used here. Omitting the flag therefore
silently discards the ignorePatterns, timeout, and retryOn429 settings,
and a local https://localhost link fails the run with a connection error
instead of being ignored.
Pre-commit Configuration
Projects SHOULD configure pre-commit hooks to run markdownlint1 and Prettier2:
repos:
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.46.0
hooks:
- id: markdownlint
args: [--disable, MD013, MD033, MD041, --]
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v4.0.0-alpha.8
hooks:
- id: prettier
types: [markdown]
See pre-commit9 documentation for more information on configuring hooks.
CI Pipeline
Projects SHOULD run Markdown linting and link checking in CI. The following
is a complete workflow, not a fragment: save it as
.github/workflows/docs.yml and it runs as written.
name: Docs
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: DavidAnson/markdownlint-cli2-action@eb5ca3ab411449c66620fe7f1b3c9e10547144b0 # v18.0.0
with:
globs: "**/*.md"
- uses: >- # v1
gaurav-nelson/github-action-markdown-link-check@5c5dfc0ac2e225883c0e5f03a85311ec2830d368
with:
config-file: .markdown-link-check.json
Why Each Key Is Required
onis what makes the file a workflow. Without it GitHub Actions has no trigger to attach the run to, so the job never executes.globsoverrides the markdownlint-cli2-action default of*.{md,markdown}, which matches only the repository root. Without it every nested directory —docs/,guides/, and the rest — is silently unlinted.config-fileis required because the action’s default ismlc_config.json; see Link Checking above.permissions: contents: readdrops the default write scopes from theGITHUB_TOKEN. Linting needs only to read the checkout, so a compromised action or dependency cannot push commits or open releases.
See GitHub Actions10 documentation for more CI/CD configuration options.
See Also
- Google Markdown Style Guide - Base style guide
- EditorConfig template - Editor settings for consistent indentation and line endings
- CI/CD Guide - Continuous integration setup
- Documentation Guides - Index of documentation guides
- CommonMark11 - Markdown specification
- GitHub Flavored Markdown12 - Extended Markdown syntax
References
Footnotes
-
markdownlint - A Node.js style checker and lint tool for Markdown/CommonMark files ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8
-
Prettier - An opinionated code formatter with support for Markdown ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7
-
Mermaid - JavaScript-based diagramming and charting tool that renders Markdown-inspired text definitions ↩ ↩2 ↩3 ↩4
-
markdown-link-check - Checks all hyperlinks in Markdown files for validity ↩ ↩2 ↩3
-
Using markdownlint with Prettier - Upstream compatibility notes for the two tools ↩
-
Excalidraw - Virtual whiteboard for sketching hand-drawn like diagrams ↩
-
draw.io - Free online diagram software for making flowcharts, process diagrams, and more ↩
-
Mermaid Live Editor - Interactive editor for creating and validating Mermaid diagrams ↩
-
pre-commit - A framework for managing and maintaining multi-language pre-commit hooks ↩
-
GitHub Actions - Automation platform for CI/CD workflows ↩
-
CommonMark - A strongly defined, highly compatible specification of Markdown ↩
-
GitHub Flavored Markdown - GitHub’s extended Markdown syntax specification ↩