Summary
Headings in rendered pages already receive id attributes via goldmark's parser.WithAutoHeadingID(), so fragment URLs like /guide/configuration/#build-options already work. What's missing is the UX affordance: a clickable anchor link (the # icon) that appears when hovering over a heading, letting readers copy or share a direct link to any section.
Desired behaviour
- On hover, a
# (or link icon) appears inline with h2 and h3 headings in the page body
- Clicking it navigates to
#<heading-id> and can be copied from the address bar
- Should also apply to
h4 if present
- Implementation should be pure CSS — no JS required
Implementation sketch
CSS approach (no JS, no template changes needed)
.prose h2,
.prose h3,
.prose h4 {
position: relative;
}
.prose h2 > a.heading-anchor,
.prose h3 > a.heading-anchor,
.prose h4 > a.heading-anchor {
opacity: 0;
margin-left: 0.4em;
font-weight: 400;
color: var(--color-muted);
text-decoration: none;
transition: opacity 0.15s;
}
.prose h2:hover > a.heading-anchor,
.prose h3:hover > a.heading-anchor,
.prose h4:hover > a.heading-anchor {
opacity: 1;
}
Go: custom goldmark renderer
The anchor <a> element needs to be injected into the rendered HTML. The cleanest approach is a custom goldmark NodeRenderer that wraps the default heading renderer and appends the anchor element. Something like:
// In internal/parser/anchors.go
// HeadingAnchorRenderer wraps default heading rendering and appends
// <a class="heading-anchor" href="#<id>">#</a> to h2–h4 elements.
Alternatively, a post-render string replacement pass on the HTML output would work but is less elegant.
Acceptance criteria
Summary
Headings in rendered pages already receive
idattributes via goldmark'sparser.WithAutoHeadingID(), so fragment URLs like/guide/configuration/#build-optionsalready work. What's missing is the UX affordance: a clickable anchor link (the#icon) that appears when hovering over a heading, letting readers copy or share a direct link to any section.Desired behaviour
#(or link icon) appears inline withh2andh3headings in the page body#<heading-id>and can be copied from the address barh4if presentImplementation sketch
CSS approach (no JS, no template changes needed)
Go: custom goldmark renderer
The anchor
<a>element needs to be injected into the rendered HTML. The cleanest approach is a custom goldmarkNodeRendererthat wraps the default heading renderer and appends the anchor element. Something like:Alternatively, a post-render string replacement pass on the HTML output would work but is less elegant.
Acceptance criteria
h2,h3,h4headings in.proseget<a class="heading-anchor" href="#<id>">#</a>injectedidalready generated byWithAutoHeadingID()h1is excluded (the page title heading is not a fragment target)