|$ curl https://forge-ai.dev/api/markdown?path=docs/html/headings
$cat docs/paragraphs-&-headings.md
updated Today·33 min read·published

Paragraphs & Headings

HTMLTextAccessibilityBeginner🎯Free Tools
Introduction

Headings and paragraphs turn a wall of text into a document. Assistive technologies offer “jump by heading” navigation; search engines use headings as strong topical signals; sighted users scan them as a visual outline. Misusing <h1><h6> for font size, or using bold instead of headings, breaks that outline.

This guide covers the heading outline, one-h1 guidance, skip links, heading versus bold, interaction with sectioning content, deep usage of p, br, hr, pre, and blockquote, line-breaking rules, and how to audit heading hierarchy for accessibility.

h1–h6 outline

There are six ranks. Rank conveys hierarchy, not style. An h3 is a subsection of the preceding higher-rank heading in the flat outline used by browsers and AT today.

outline.html
HTML
1<h1>Product documentation</h1>
2 <h2>Getting started</h2>
3 <h3>Install</h3>
4 <h3>Configure</h3>
5 <h2>API reference</h2>
6 <h3>Authentication</h3>
7 <h4>API keys</h4>
8 <h4>OAuth</h4>
9 <h3>Errors</h3>
10 <h2>Changelog</h2>
RankTypical roleAvoid
h1Page title / primary topicMultiple competing page titles
h2Major sectionsSkipping from h1 to h4 for style
h3–h4SubsectionsUsing for sidebar chrome size
h5–h6Deep nests (rare)Over-nesting; reconsider IA

best practice

Do not skip ranks for visual size. Style with CSS. If a subsection looks too large, change the stylesheet — not the heading level.
preview
One h1 guidance

HTML allows multiple h1 elements. Accessibility guidance and SEO practice still favor one primary h1 that names the page topic. Site logos in headers should not all be h1 on every page — use a paragraph, link, or styled text in the chrome, and reserve h1 for the page title in main.

one-h1.html
HTML
1<!-- Recommended pattern -->
2<header>
3 <p class="logo"><a href="/">ForgeLearn</a></p>
4 <nav>...</nav>
5</header>
6<main>
7 <h1>HTML Headings</h1>
8 ...
9</main>
10
11<!-- Problematic: every card is an h1 -->
12<section>
13 <h1>Card A</h1>
14 <h1>Card B</h1>
15</section>
📝

note

If a design system previously used h1 for the logo, demote it and keep a single h1 inside main. Visual size can stay identical via CSS.

info

In nested routes or apps with dialogs, still prefer one visible h1 in the primary document. Dialog titles are often h2 or labelled via aria-labelledby.
Heading vs bold

<strong> and <b> emphasize or stylistically offset phrasing content. They do not create outline entries. If something is a section title, it must be a heading element — not bold text in a div.

MarkupOutline?Use for
h1–h6YesSection titles
strongNoImportant phrasing
bNoStylistic offset without emphasis
div.font-boldNoNot a heading substitute
p + CSS sizeNoNot a heading substitute
vs-bold.html
HTML
1<!-- Wrong -->
2<div class="title">Billing settings</div>
3<p><b>Payment methods</b></p>
4
5<!-- Right -->
6<h2>Billing settings</h2>
7<h3>Payment methods</h3>

danger

CSS-only “headings” fail screen-reader heading navigation and often fail automated audits (axe, Lighthouse).
Sectioning content interaction

Sectioning content (section, article, nav, aside) groups thematic content. Each major section should generally start with a heading. The old HTML5 outline algorithm that computed ranks from nesting is not implemented for AT — author explicit ranks.

sectioning.html
HTML
1<main>
2 <h1>Blog</h1>
3 <article>
4 <h2>Post title</h2>
5 <section>
6 <h3>Introduction</h3>
7 <p>...</p>
8 </section>
9 <section>
10 <h3>Details</h3>
11 <p>...</p>
12 </section>
13 </article>
14 <aside>
15 <h2>About the author</h2>
16 </aside>
17</main>

warning

An empty <section> without a heading is usually a smell — prefer div for pure layout wrappers.
The p element

<p> represents a paragraph. It cannot contain flow content like div, lists, or tables — the parser will close the paragraph early. Keep paragraphs for prose; use lists and divs for structure.

p.html
HTML
1<p>A short paragraph of prose.</p>
2<p>
3 Paragraphs may contain
4 <a href="/x">links</a>,
5 <strong>importance</strong>,
6 and <code>code</code>.
7</p>
8
9<!-- Broken intent -->
10<p>Intro
11 <ul><li>A</li></ul>
12</p>
13<!-- Becomes: <p>Intro</p><ul>...</ul> -->

info

Do not use <p> as a generic spacing element. Use CSS margin on real content.
br and hr

<br> means a line break within text — addresses, poems, or line-oriented content — not vertical spacing between sections. <hr> is a thematic break between paragraph-level topics, not a decorative line (though it can be styled as one).

br-hr.html
HTML
1<p>
2 Ada Lovelace<br />
3 123 Algorithm Way<br />
4 London
5</p>
6
7<section>
8 <p>End of chapter discussion.</p>
9 <hr />
10 <p>Next topic begins here.</p>
11</section>
12
13<!-- Bad: spacing with br -->
14<p>Title</p>
15<br /><br /><br />
16<p>Body</p>

best practice

Prefer CSS gap/margin for spacing. Reserve br for true line breaks inside a textual unit.
pre and preserved formatting

<pre> preserves whitespace and typically uses a monospace font via UA styles. Combine with <code> for code blocks. Avoid indenting pre contents in source if those spaces should not appear.

pre.html
HTML
1<pre><code>function add(a, b) {{
2 return a + b;
3}}</code></pre>
4
5<!-- ASCII diagrams -->
6<pre>
7+-----+ +-----+
8| API | --> | DB |
9+-----+ +-----+
10</pre>
📝

note

Inside pre, HTML entities still apply. Escape < in code samples.
blockquote deep usage

<blockquote> represents a quotation from another source. Use cite attribute for a URL and a <footer> or <cite> element for human-readable attribution. Do not use blockquote merely to indent text.

blockquote.html
HTML
1<blockquote cite="https://html.spec.whatwg.org/">
2 <p>Elements, attributes, and attribute values in HTML are defined
3 (by this specification) to have certain meanings (semantics).</p>
4 <footer>— <cite>HTML Living Standard</cite></footer>
5</blockquote>
6
7<!-- Inline short quotes use q -->
8<p>She said <q cite="https://example.com">ship it</q>.</p>
preview
Line breaking rules

Browsers break lines according to Unicode rules, CSS white-space, word-break, overflow-wrap, and language. Authors influence breaks with soft hyphens, word joiners, and <wbr>.

ToolEffect
<wbr>Optional break opportunity
&shy; (soft hyphen)Break with hyphen when wrapped
white-space: nowrapPrevent wrapping
overflow-wrap: anywhereAllow aggressive breaks
<br>Forced break
breaks.html
HTML
1<p>https://example.com/very/<wbr />long/<wbr />path</p>
2<p>Supercali&shy;fragilistic</p>

info

For URLs and long identifiers in prose, wbr or overflow-wrap prevents horizontal overflow without littering br.
Accessibility heading hierarchy audits

Audit headings regularly — especially after CMS or design-system changes.

untitled.text
TEXT
1Manual:
21. Turn off CSS — does the outline still make sense?
32. Use a screen reader rotor / heading list
43. Tab to skip link, then navigate by headings
5
6Automated:
7- axe / Lighthouse: heading-order, empty headings
8- HTML validator: empty headings, misplaced elements
9- Custom script: document.querySelectorAll('h1,h2,h3,h4,h5,h6')
10
11Red flags:
12- No h1
13- Skipped levels (h2 then h4) without reason
14- Empty headings
15- Headings outside main for page title
16- Duplicate identical h2 spam for cards (consider h3 under a section h2)
audit-headings.js
JavaScript
1[...document.querySelectorAll('h1,h2,h3,h4,h5,h6')]
2 .map((h) => `${h.tagName} ${h.textContent.trim().slice(0, 60)}`);

best practice

Include a heading outline screenshot or dump in accessibility QA checklists for key templates.
Best practices
untitled.text
TEXT
1[ ] One primary h1 naming the page
2[ ] No rank skips for styling
3[ ] Sections/articles start with headings
4[ ] Skip link to main
5[ ] strong/b not used as fake headings
6[ ] br not used for spacing
7[ ] hr for thematic breaks only
8[ ] blockquote for real quotations
9[ ] pre/code for code samples with escaping
10[ ] Outline audited with AT or tooling
Multi-level patterns and card grids

Card grids are a common source of bad outlines. If the page topic is “Documentation”, that is the h1. A section titled “Guides” is an h2. Each card title under Guides is usually an h3 (or a link inside a heading). Making every card an h2 flattens the outline into noise.

cards-outline.html
HTML
1<main>
2 <h1>Documentation</h1>
3 <section aria-labelledby="guides-heading">
4 <h2 id="guides-heading">Guides</h2>
5 <ul class="card-grid">
6 <li>
7 <h3><a href="/docs/html/forms">Forms</a></h3>
8 <p>Build accessible forms.</p>
9 </li>
10 <li>
11 <h3><a href="/docs/html/tables">Tables</a></h3>
12 <p>Tabular data patterns.</p>
13 </li>
14 </ul>
15 </section>
16 <section aria-labelledby="ref-heading">
17 <h2 id="ref-heading">Reference</h2>
18 ...
19 </section>
20</main>

info

Prefer linking inside the heading (<h3><a>…</a></h3>) so the heading list and the link share the same name.
Empty, hidden, and decorative headings

Empty headings fail audits and confuse rotors. Headings that are visually hidden but present for screen readers can be useful for landmark labeling — but prefer aria-labelledby / aria-label on the section when a visible heading is not desired. Do not hide an h1 and show a fake visual title in a div.

empty-headings.html
HTML
1<!-- Bad -->
2<h2></h2>
3<h2 style="display:none">Invisible duplicate</h2>
4
5<!-- Better: visible heading -->
6<h2>Filters</h2>
7
8<!-- Or label the region without a heading node -->
9<section aria-label="Filters">...</section>

warning

display: none headings are removed from the accessibility tree in most browsers — they do not help SR users and still confuse authors maintaining the DOM.
Internationalized text blocks

When a paragraph or heading switches language, set lang on that element. Bidirectional quotations may need dir. Line breaking and hyphenation dictionaries follow language tags.

i18n-text.html
HTML
1<h2>Guest essay</h2>
2<p>The author writes:</p>
3<blockquote lang="de" cite="https://example.com/de">
4 <p>Semantik ist keine optionalität.</p>
5</blockquote>
6<p>Back to English analysis…</p>
Live outline demo

A small document showing skip-friendly structure, thematic break, and a quotation.

preview
CMS and markdown gotchas

Markdown # headings become h1 by default in many pipelines — disastrous when the page template already provides an h1 from the title field. Configure the renderer to start at h2, or demote programmatically. WYSIWYG editors often inject <br> for Enter and bold for “titles”; train authors and sanitize output.

demote.js
JavaScript
1// Demote markdown headings one level when page already has h1
2function demoteHeadings(html) {
3 return html.replace(/<(\/?)h([1-5])\b/gi, (_, slash, n) =>
4 `<${slash}h${Number(n) + 1}`
5 );
6}
🔥

pro tip

In MDX/docs sites, map the route title to h1 in the layout and start MDX content at h2.
$Blueprint — Engineering Documentation·Section ID: HTML-HEADINGS·Revision: 2.0

Community

Get help on Slack, Discord or VIP

Stuck on a guide? Join the community and ask.