Qiita-Style Heading Anchors and note.com Line Spacing in a Hugo Blog

Redesigning Heading Anchor Links Qiita-Style and Tuning Paragraph Spacing to Match note.com in Hugo

* This page contains promotional content

The anchor link with an anchor icon (fa-anchor) that was permanently displayed at the end of the headings (h2 and so on) on this blog had started to feel a bit uncool to me.
Referring to the heading design of Qiita (in Japanese), I decided to rethink how it is displayed, including the effect on SEO. While I was at it, I also adjusted the line height and margins of the body text to match the article style of note (in Japanese).
This is a record of the work, in which I left almost all of the code changes to generative AI instead of doing them by hand, and checked the local appearance with Playwright MCP as I went.


Adjusting the body line height and margins

Referring to the articles published on note.com, I reviewed .blog-post p / .blog-post li in static/css/custom.css.

Comparison with the reference articles

Using Playwright MCP, I measured getComputedStyle directly on both my own site and note.com, and compared the numbers.

Itemnote.com (PC)After the change (PC)note.com (390px)After the change (767px and below)
Font size18px18px (unchanged)16px16px
line-height36px (2.0x)36px32px (2.0x)32px
Paragraph margin36px top and bottommargin: 0 0 36px30px top and bottommargin: 0 0 30px

What I changed

  • Changed line-height from 1.8 to 2
  • Added margin: 0 0 36px to paragraphs
  • Added a media query that shrinks to font-size: 16px / line-height: 2 / margin: 0 0 30px at the 767px-and-below breakpoint

What I did not change

I left the left and right margins (padding) alone, because measurement showed note.com at 16px against my current 15px, which is almost the same.

Verification

With hugo server -D (localhost:1313) I checked both PC width and mobile width (390px) through Playwright MCP, and after the styles took effect I confirmed the appearance with screenshots. The temporary files used for verification (.playwright-mcp/ and the screenshot PNGs) were deleted once the work was done.


I inspected Qiita’s article pages directly in the browser (using getComputedStyle and forcePseudoState over CDP) and confirmed that the following implementation is the basis.

  • The icon is fa-link (a chain). It is placed outside the heading on the left with position: absolute; left: -33px;
  • Normally display: none, and shown with flex only while the heading is hovered

With that in mind, I made the following changes.

layouts/_default/single.html

- {{ .Content | replaceRE "(<h[1-9] id=\"([^\"]+)\".+)(</h[1-9]+>)" "${1}&nbsp;<a class=\"headline-hash\" href=\"#${2}\"><i class=\"fas fa-anchor\"></i></a> ${3}" | safeHTML}}
+ {{ .Content | replaceRE "(<h[1-9] id=\"([^\"]+)\".+)(</h[1-9]+>)" "${1}<a class=\"headline-hash\" href=\"#${2}\" aria-label=\"見出しへのリンク\"><i class=\"fas fa-link\" aria-hidden=\"true\"></i></a>${3}" | safeHTML}}
  • Changed the icon from fa-anchor to fa-link
  • Added aria-label to improve screen reader support
  • Removed &nbsp; because the positioning switches to absolute

static/css/custom.css

.blog-post h1,
.blog-post h2,
.blog-post h3,
.blog-post h4:not(.see-also),
.blog-post h5,
.blog-post h6 {
  position: relative;
}

.blog-post h1,
.blog-post h2 {
  margin-top: 2.4em;
  padding-bottom: 0.25em;
  border-bottom: 1px solid rgba(0, 0, 0, 0.12);
}

.headline-hash {
  position: absolute;
  top: 0;
  left: -1.3em;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.3em;
  height: 100%;
  color: rgba(0, 0, 0, 0.6);
  text-decoration: none;
  opacity: 0;
  transition: opacity 0.15s ease, color 0.15s ease;
}

.headline-hash .fa-link {
  font-size: 0.6em;
}

.headline-hash:hover {
  color: #0366d6;
}

.blog-post h1:hover .headline-hash,
.blog-post h2:hover .headline-hash,
.blog-post h3:hover .headline-hash,
.blog-post h4:hover .headline-hash,
.blog-post h5:hover .headline-hash,
.blog-post h6:hover .headline-hash,
.headline-hash:focus {
  opacity: 1;
}

/* Below 992px there is no offset column and therefore no room to place the icon to the left of the heading, so fall back to showing it at the end */
@media only screen and (max-width: 991px) {
  .headline-hash {
    position: static;
    display: inline-flex;
    width: auto;
    height: auto;
    margin-left: 0.4em;
    opacity: 0.4;
  }
}

I added an underline to h1 and h2 to make the section breaks clearer.
Qiita switches between display: none and flex, which makes the element impossible to reach with keyboard focus, so I did not follow that part and changed it to opacity control instead, making it visible on :focus as well.
In the Bootstrap grid, the left offset on the article body column (col-md-offset-1 / col-lg-offset-2) is only applied at 992px and above, so below 992px I dropped the absolute positioning and prepared a fallback in @media (max-width: 991px) that always shows the icon inline and faintly at the end of the heading.


Verification

  • Checked the rendering with a local hugo server -D
  • Forced the hover state with CDP’s forcePseudoState and confirmed with a screenshot that the chain icon appears to the left of the heading at desktop width (1280px)
  • Confirmed the same hover behavior with Playwright MCP as well
  • Confirmed the fallback where the icon is always shown faintly at the end at mobile width (390px)
  • Confirmed with ReadLints that there are no syntax errors in the CSS or the template

Summary

I replaced the heading anchor’s fa-anchor with fa-link and changed it to a Qiita-style design that appears only on hover, and along with that matched the body line height and paragraph margins to note.com’s style based on actual measurements.
Absolute positioning to the left of the heading only holds up in layouts of 992px and above, so on narrow screens I prepared a fallback that displays the icon at the end of the heading to avoid breaking the layout.

I had been thinking myself that the line spacing was a little tight, so maybe it has become a bit easier to read?

See also