The h1 tag in HTML: headings, levels and how to size them

An h1 is the main title of a page, and h2 to h6 are the sections under it. The number says where a heading sits in the outline. How big it looks is a CSS decision.

The <h1> tag marks the main title of a page. HTML has six heading levels, <h1> to <h6>, and together they form the page's outline: h1 for the page, h2 for its sections, h3 for parts of a section.

The number describes rank, not size. Size belongs to CSS.

<h1>Home coffee guide</h1>
<h2>Beans</h2>
<h3>Light or dark roast</h3>
<h2>Grinding</h2>

Try it below. The right panel lists the page's headings as an outline. Turn on the two checkboxes to see the two most common mistakes.

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Heading outline viewer</title>
<style>
  body { margin: 0; padding: 12px; font-family: system-ui, sans-serif; background: #f4f5f7; color: #1d2330; }
  .wrap { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
  @media (max-width: 560px) { .wrap { grid-template-columns: 1fr; } }
  .box { background: #fff; border-radius: 10px; padding: 12px 14px; box-shadow: 0 2px 8px rgba(0, 0, 0, .08); }
  .label { font-size: 12px; font-weight: 700; color: #6b7280; text-transform: uppercase; letter-spacing: .4px; }

  /* the sample page: small sizes so it fits the box */
  #page h1 { font-size: 20px; margin: 6px 0 4px; }
  #page h2 { font-size: 16px; margin: 10px 0 2px; }
  #page h3, #page h4 { font-size: 14px; margin: 8px 0 2px; }
  #page p { font-size: 13px; margin: 0; color: #4b5563; }

  /* the outline panel */
  #outline { list-style: none; margin: 8px 0 0; padding: 0; font-size: 14px; }
  #outline li { padding: 3px 0; border-left: 2px solid #e5e7eb; padding-left: 8px; margin: 2px 0; }
  #outline code { font-size: 12px; color: #2563eb; margin-right: 6px; }
  #outline li.bad { border-left-color: #ea580c; background: #fff7ed; }
  #warn { margin-top: 10px; font-size: 13px; line-height: 1.45; }
  #warn p { margin: 4px 0; padding: 6px 8px; border-radius: 6px; }
  .err { background: #fff7ed; color: #9a3412; }
  .ok { background: #ecfdf3; color: #0f5132; }
  .toggles { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 10px; font-size: 14px; }
</style>
</head>
<body>
<div class="wrap">
  <div class="box">
    <div class="label">Sample page</div>
    <div id="page">
      <h1>Home coffee guide</h1>
      <p id="sub">Everything you need for a good cup.</p>
      <h2>Beans</h2>
      <h3 id="roast">Light or dark roast</h3>
      <p>Light keeps more acidity.</p>
      <h2>Grinding</h2>
      <p>Grind right before brewing.</p>
      <h2>Brewing</h2>
      <h3>Pour-over</h3>
      <h3>French press</h3>
    </div>
    <div class="toggles">
      <label><input type="checkbox" id="skip"> Skip a level</label>
      <label><input type="checkbox" id="twoH1"> Make the subtitle an h1</label>
    </div>
  </div>
  <div class="box">
    <div class="label">Heading outline</div>
    <ul id="outline"></ul>
    <div id="warn"></div>
  </div>
</div>

<script>
  const page = document.getElementById('page');

  // Swap an element for the same content under a new tag name
  function retag(el, tag) {
    const n = document.createElement(tag);
    n.id = el.id;
    n.textContent = el.textContent;
    el.replaceWith(n);
  }

  function buildOutline() {
    const heads = page.querySelectorAll('h1,h2,h3,h4,h5,h6');
    const out = document.getElementById('outline');
    const warn = [];
    let prev = 0, h1s = 0;
    out.innerHTML = '';

    heads.forEach((h) => {
      const level = Number(h.tagName[1]);   // "H3" -> 3
      const li = document.createElement('li');
      li.style.marginLeft = (level - 1) * 16 + 'px';
      li.innerHTML = '<code>' + h.tagName.toLowerCase() + '</code>';
      li.append(h.textContent);
      if (level === 1) h1s++;
      if (prev && level > prev + 1) {         // jumped down more than one level
        li.className = 'bad';
        warn.push('h' + prev + ' is followed by h' + level + ': h' + (prev + 1) + ' is skipped.');
      }
      if (level === 1 && h1s > 1) {
        li.className = 'bad';
        warn.push('A second h1: the page now has two main titles.');
      }
      out.append(li);
      prev = level;
    });

    document.getElementById('warn').innerHTML = warn.length
      ? warn.map((w) => '<p class="err">' + w + '</p>').join('')
      : '<p class="ok">One h1, and no level is skipped.</p>';
  }

  document.getElementById('skip').addEventListener('change', (e) => {
    retag(document.getElementById('roast'), e.target.checked ? 'h4' : 'h3');
    buildOutline();
  });
  document.getElementById('twoH1').addEventListener('change', (e) => {
    retag(document.getElementById('sub'), e.target.checked ? 'h1' : 'p');
    buildOutline();
  });

  buildOutline();
</script>
</body>
</html>
A sample page and its heading outline, built with querySelectorAll('h1,h2,h3,h4,h5,h6'). The checkboxes skip a level and turn the subtitle into a second h1.

What headings are for

A sighted reader skims a page by its big bold lines. The heading tags carry the same structure in the markup, where software can read it.

The same page as a reader sees it and as its heading tags describe it.
The same page as a reader sees it and as its heading tags describe it.
  • Screen readers can list every heading on the page and jump from one to the next. Many screen reader users move around a page this way, so the headings work as a table of contents.
  • Search engines read headings, together with the rest of the text, to understand what the page covers and how it is split into parts.
  • Browser tools and extensions that build a contents list or reader view read the same tags.

This is part of semantic HTML: choosing the element that says what a thing is. A bold <div> can look like a heading, but nothing reading the markup will treat it as one.

One h1 per page

The h1 names the whole page. On an article it is the article's title; on a product page, the product name. One h1 per page is the common convention, and it keeps the answer to "what is this page?" in one place.

HTML itself does not forbid a second h1, and nothing breaks if there is one.

The problem is meaning. Two h1 elements tell a screen reader user there are two main titles, and neither the reader nor a search engine can tell which one the page is about.

The h1 is also not the <title> element. The title sits in the <head> and appears in the browser tab and in search results.

Meta description and title covers that side. Keep the two close in wording so the page matches the result someone clicked.

Do not skip levels

Under the h1, go down one level at a time. A section is an h2, a part of that section is an h3. Going back up is fine: after an h3 the next section starts with an h2 again.

Left: a subtitle as a second h1 and an h4 picked for its size. Right: the same content with levels picked by meaning.
Left: a subtitle as a second h1 and an h4 picked for its size. Right: the same content with levels picked by meaning.

Jumping from h2 straight to h4 leaves a gap in the outline. Someone moving through the headings hears a level-four heading and can reasonably wonder whether a level-three section was missed.

These skips almost always come from picking a heading for its size. h4 looked right under that h2, so h4 it was. The fix is to use h3 and make it look however you want in CSS.

Default sizes and margins

Without any CSS, the browser gives every heading bold text, a font size and a top and bottom margin. The HTML standard lists these defaults, and they are all in em, so they scale with the surrounding text.

The browser's default heading styles. h4 is the same size as body text, and h5 and h6 are smaller than it.
The browser's default heading styles. h4 is the same size as body text, and h5 and h6 are smaller than it.

Two things surprise people. First, h5 and h6 are smaller than normal text by default. Second, the margins are large and grow for the lower levels, which is why a heading dropped into a card often leaves an odd gap above it.

Style the size with CSS

Pick the level for meaning, then set the look in a stylesheet. A small reset plus your own sizes is enough:

h1, h2, h3 {
  margin: 0 0 0.4em;   /* replace the default top and bottom margin */
  line-height: 1.15;
}
h1 { font-size: clamp(1.5rem, 1rem + 3vw, 2.75rem); }
h2 { font-size: clamp(1.2rem, 1rem + 1vw, 1.5rem); }

If headings in one place need to look quieter, target them in CSS instead of lowering the level:

.card h2 { font-size: 1.1rem; }

Compare the browser default with a reset and a fluid size:

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>h1 default vs clamp()</title>
<style>
  body { margin: 0; padding: 14px; font-family: system-ui, sans-serif; background: #f4f5f7; color: #1d2330; }
  .wrap { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
  @media (max-width: 560px) { .wrap { grid-template-columns: 1fr; } }
  .box { background: #fff; border-radius: 10px; padding: 0 14px 12px; box-shadow: 0 2px 8px rgba(0, 0, 0, .08); overflow-wrap: anywhere; }
  .label { font-size: 12px; font-weight: 700; color: #6b7280; text-transform: uppercase; letter-spacing: .4px; padding-top: 12px; }
  .box p { margin: 0; font-size: 14px; color: #4b5563; }
  .readout { margin-top: 10px; font: 12px/1.5 ui-monospace, Consolas, monospace; color: #2563eb; }
  #width { margin-top: 12px; font-size: 13px; color: #4b5563; }

  /* Right box: reset the margin, then a fluid size */
  .fluid h1 {
    margin: 0 0 .3em;
    font-size: clamp(1.5rem, 1rem + 3vw, 2.75rem);  /* 24px floor, 44px ceiling */
    line-height: 1.15;
    letter-spacing: -0.01em;
  }
  /* The left box has no h1 rule at all: those are the browser's defaults */
</style>
</head>
<body>
<div class="wrap">
  <div class="box">
    <div class="label">Browser default</div>
    <h1>Home coffee guide</h1>
    <p>font-size: 2em, margin: 0.67em 0</p>
    <div class="readout" data-for="0"></div>
  </div>
  <div class="box fluid">
    <div class="label">Reset + clamp()</div>
    <h1>Home coffee guide</h1>
    <p>clamp(1.5rem, 1rem + 3vw, 2.75rem)</p>
    <div class="readout" data-for="1"></div>
  </div>
</div>
<div id="width"></div>

<script>
  const heads = document.querySelectorAll('h1');

  // Show what the browser actually computed for each h1
  function show() {
    document.querySelectorAll('.readout').forEach((r) => {
      const cs = getComputedStyle(heads[r.dataset.for]);
      r.textContent = 'font-size ' + parseFloat(cs.fontSize).toFixed(1) + 'px, margin-top ' +
        parseFloat(cs.marginTop).toFixed(1) + 'px';
    });
    document.getElementById('width').textContent = 'Window width: ' + innerWidth +
      'px. Resize the window, or open this on a phone, and watch the right heading.';
  }

  addEventListener('resize', show);
  show();
</script>
</body>
</html>
Left: an h1 with no CSS. Right: margin reset and font-size set with clamp(). The readout shows the size the browser computed. Resize the window to watch it change.

A fluid h1 with clamp()

A 44px heading that looks right on a laptop can take four lines on a phone. clamp() lets the size follow the screen width between a floor and a ceiling:

h1 { font-size: clamp(1.5rem, 1rem + 3vw, 2.75rem); }
Part Value What it does
Minimum 1.5rem Never smaller than 24px (at the default 16px text size)
Preferred 1rem + 3vw Grows with the width of the window
Maximum 2.75rem Stops at 44px on wide screens

vw is one percent of the viewport width. Adding a rem part keeps the heading from getting tiny on small screens and lets it follow the reader's text size setting.

For a page to use the phone's real width at all, it needs the viewport meta tag. More type settings, such as line height and font choice, are in how to change the font in HTML.

A finished article header

A real page puts these together: one h1, a subtitle in a paragraph, and section h2 elements with id values so each one can be linked.

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Article header with h1 and section links</title>
<style>
  * { box-sizing: border-box; }
  body { margin: 0; font-family: system-ui, sans-serif; color: #1d2330; line-height: 1.6; background: #fff; }
  article { max-width: 640px; margin: 0 auto; padding: 0 18px 40px; }

  /* Header: padding stops the h1 margin from leaking outside the coloured box */
  header { padding: 22px 18px 18px; margin: 0 -18px 8px; background: #f1f5ff; }
  h1 {
    margin: 0;
    font-size: clamp(1.6rem, 1rem + 3vw, 2.6rem);
    line-height: 1.15;
  }
  .subtitle { margin: 6px 0 12px; font-size: 1.05rem; color: #4b5563; }  /* a p, not a heading */
  nav ul { display: flex; flex-wrap: wrap; gap: 6px 14px; margin: 0; padding: 0; list-style: none; font-size: 14px; }
  nav a { color: #2563eb; }

  h2 {
    font-size: clamp(1.2rem, 1rem + 1vw, 1.5rem);
    margin: 1.4em 0 .3em;
    scroll-margin-top: 12px;       /* a little room when a link jumps here */
  }
  .self { margin-left: 6px; color: #9ca3af; text-decoration: none; font-weight: 400; }
  .self:hover, .self:focus { color: #2563eb; }
  p { margin: 0 0 .8em; }
</style>
</head>
<body>
<article>
  <header>
    <h1>Home coffee guide</h1>
    <p class="subtitle">Beans, grinding and brewing, without the jargon.</p>
    <nav aria-label="On this page">
      <ul>
        <li><a href="#beans">Beans</a></li>
        <li><a href="#grinding">Grinding</a></li>
        <li><a href="#brewing">Brewing</a></li>
      </ul>
    </nav>
  </header>

  <h2 id="beans">Beans <a class="self" href="#beans" aria-label="Link to this section">#</a></h2>
  <p>Buy whole beans roasted in the last few weeks. Light roasts keep more acidity and fruit; dark roasts taste of the roast itself.</p>
  <p>Keep them in a closed container away from light and heat.</p>

  <h2 id="grinding">Grinding <a class="self" href="#grinding" aria-label="Link to this section">#</a></h2>
  <p>Grind right before brewing. Coarse for a French press, medium for pour-over, fine for espresso.</p>
  <p>A burr grinder gives more even pieces than a blade grinder.</p>

  <h2 id="brewing">Brewing <a class="self" href="#brewing" aria-label="Link to this section">#</a></h2>
  <p>Start with about 60 grams of coffee per litre of water and adjust to taste.</p>
  <p>Water just off the boil works for most methods.</p>
  <p>Taste, change one thing, and taste again.</p>
</article>
</body>
</html>
An h1 with a fluid size, a subtitle as a p, and section h2s with ids. The links at the top and the # next to each heading jump to that section.
<header>
  <h1>Home coffee guide</h1>
  <p class="subtitle">Beans, grinding and brewing, without the jargon.</p>
</header>
<h2 id="beans">Beans <a class="self" href="#beans" aria-label="Link to this section">#</a></h2>

The subtitle is a <p>: it describes the title, it is not a section. The # link lets someone copy an address that opens right at that section.

Linking to an anchor on the same page and building a table of contents go further with both.

When it does not work

What you see Cause Fix
Outline jumps from h2 to h4 A heading was picked for its size Use the next level down and set font-size in CSS
Two main titles in the outline, or a subtitle listed as a section The tagline under the h1 is an h1 or h2 Make the tagline a <p>
The h1 wraps onto three or four lines on a phone A fixed font-size sized for a wide screen Use clamp() with a rem floor
A gap appears above a coloured header box The heading's top margin collapses through its parent and pushes the box down Set margin-top: 0 on the heading, or give the parent padding
A heading is on the page but screen readers skip it It is hidden with display: none, which removes it for everyone Show it, or hide it only visually with a visually-hidden class

The margin case needs a word. Vertical margins of blocks that touch can merge into one.

If an h1 is the first thing inside a header with a background and no padding or border on top, its margin ends up outside the header. Padding on the header, as in the finished example, stops that.

For a heading that should exist for screen readers but not be seen, the usual pattern keeps it in the page and shrinks it out of sight:

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}

Heading problems are easiest to see in the page itself: whether the title fits on a phone, whether the section links land where they should. A screenshot shows one width, and an .html attachment may open as plain code on a phone.

To send the working version, paste the page into a NOS document and choose Create share link. HTML to link walks through it.

The page renders as written and its scripts run, so people can resize it, tap the section links and check the outline themselves. If you change the headings later, the same link shows the new version.

Questions people ask

Can a page have more than one h1?

HTML allows it and the page still works. One h1 per page is a convention, and a useful one: the h1 says what the whole page is, so a second one makes it unclear which title is the main one. Use h2 for everything below the main title.

Is the h1 the same as the title tag?

No. The title element sits in the head and appears in the browser tab and as the headline of a search result. The h1 is visible text on the page. They often say much the same thing, but they are separate elements and can be worded differently.

How do I make an h1 smaller in CSS?

Set its font-size, for example h1 { font-size: 1.75rem; }. Keep it an h1. Changing it to h3 because h3 looks smaller changes what the heading means, not just how it looks.

What is the default font size of an h1?

In the browser's default styles an h1 is 2em, which is 32px when the surrounding text is 16px. It is bold and has a margin of 0.67em above and below.

Does the h1 tag matter for search engines?

Search engines read headings, along with the rest of the page, to understand what the page is about and how it is divided. A clear h1 that matches the topic helps with that. There is no published number for how much weight a heading carries.

Keep reading