Skip to content

Commit 03b489f

Browse files
psjamespclaude
andcommitted
Make article headings linkable (#52)
Add a Goldmark heading render hook that renders each Markdown heading with its auto-generated id plus a hover-revealed anchor link, so any article section can be linked to and shared. IDs still come from .Anchor, so existing in-page `#section` links keep working, and author `{.class}` attributes (e.g. .wp-block-heading) are preserved. - themes/.../_markup/render-heading.html — the render hook - themes/.../static/css/heading-anchors.css — hover styling + scroll-margin to clear the sticky header; loaded async alongside the other theme CSS - single.html — the in-article smooth-scroll handler now also updates the URL hash, so clicking a section link makes it copyable from the address bar fa-link is already in the icon subset, so generated assets are unchanged. Closes #52 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent f94ff81 commit 03b489f

4 files changed

Lines changed: 57 additions & 4 deletions

File tree

themes/powershell-community/layouts/_default/baseof.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@
111111
{{- $asyncCSS := slice
112112
"https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/themes/prism-tomorrow.min.css"
113113
"/css/alerts.css"
114-
"/css/code-copy.css" -}}
114+
"/css/code-copy.css"
115+
"/css/heading-anchors.css" -}}
115116
{{- with .Site.Params.algolia }}{{ $asyncCSS = $asyncCSS | append "https://cdn.jsdelivr.net/npm/@algolia/algoliasearch-netlify-frontend@1/dist/algoliasearchNetlify.css" }}{{ end -}}
116117
{{ range $asyncCSS }}
117118
<link rel="stylesheet" href="{{ . }}" media="print" onload="this.media='all'">

themes/powershell-community/layouts/_default/single.html

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,13 +207,16 @@ <h4 class="text-lg font-semibold text-gray-900 mb-3 line-clamp-2">
207207
});
208208
}
209209

210-
// Smooth scrolling for anchor links within the article
210+
// Smooth scrolling for anchor links within the article. Also update the
211+
// URL hash so a linked section is shareable/copyable from the address bar.
211212
document.querySelectorAll('.prose a[href^="#"]').forEach(anchor => {
212213
anchor.addEventListener('click', function (e) {
213-
e.preventDefault();
214-
const target = document.querySelector(this.getAttribute('href'));
214+
const hash = this.getAttribute('href');
215+
const target = document.querySelector(hash);
215216
if (target) {
217+
e.preventDefault();
216218
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
219+
history.pushState(null, '', hash);
217220
}
218221
});
219222
});
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{{- /* Heading render hook: makes article headings directly linkable.
2+
Renders the heading with its auto-generated id (from .Anchor, i.e.
3+
Goldmark's autoHeadingID) plus a hover-revealed anchor link, so any
4+
section can be linked to and shared. Because the id still comes from
5+
.Anchor, pre-existing `#section` links keep resolving unchanged.
6+
The `{.class}` attribute authors add (e.g. .wp-block-heading) is
7+
preserved. Styling lives in static/css/heading-anchors.css. */ -}}
8+
{{- $id := .Anchor -}}
9+
{{- $class := "ps-heading" -}}
10+
{{- with .Attributes.class }}{{ $class = printf "%s %s" $class . }}{{ end -}}
11+
<h{{ .Level }} id="{{ $id }}" class="{{ $class }}">
12+
{{- .Text | safeHTML -}}
13+
<a class="ps-heading-anchor" href="#{{ $id }}" aria-label="Link to this section" title="Link to this section"><i class="fas fa-link" aria-hidden="true"></i></a>
14+
</h{{ .Level }}>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/* Linkable article headings — a hover-revealed anchor link next to each
2+
heading. Rendered by layouts/_markup/render-heading.html. */
3+
4+
.ps-heading {
5+
position: relative;
6+
/* Clear the sticky site header (bg-white shadow-lg sticky top-0) when
7+
jumping to an anchor so the heading isn't hidden underneath it. */
8+
scroll-margin-top: 5.5rem;
9+
}
10+
11+
.ps-heading-anchor {
12+
opacity: 0;
13+
margin-left: 0.4em;
14+
font-size: 0.7em;
15+
color: #9ca3af;
16+
text-decoration: none;
17+
vertical-align: middle;
18+
transition: opacity 0.15s ease, color 0.15s ease;
19+
}
20+
21+
.ps-heading:hover .ps-heading-anchor,
22+
.ps-heading-anchor:focus-visible {
23+
opacity: 1;
24+
}
25+
26+
.ps-heading-anchor:hover {
27+
color: #2563eb;
28+
}
29+
30+
/* Touch devices can't hover — keep the anchor discoverable but subtle. */
31+
@media (hover: none) {
32+
.ps-heading-anchor {
33+
opacity: 0.5;
34+
}
35+
}

0 commit comments

Comments
 (0)