Skip to content

Commit 845056b

Browse files
committed
improve permalink
1 parent 7767d85 commit 845056b

File tree

7 files changed

+73
-41
lines changed

7 files changed

+73
-41
lines changed

astro.config.mjs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import sitemap from '@astrojs/sitemap'
22
import { defineConfig } from 'astro/config'
3+
import { h } from 'hastscript'
34
import rehypeAutolinkHeadings from 'rehype-autolink-headings'
45
import rehypeSlug from 'rehype-slug'
56

@@ -17,7 +18,17 @@ export default defineConfig({
1718
dark: 'github-dark',
1819
},
1920
},
20-
rehypePlugins: [rehypeSlug, [rehypeAutolinkHeadings, { behavior: 'wrap' }]],
21+
rehypePlugins: [
22+
rehypeSlug,
23+
[
24+
rehypeAutolinkHeadings,
25+
{
26+
behavior: 'append',
27+
properties: { class: 'heading-link', ariaLabel: 'Link to this section' },
28+
content: h('span.heading-link-icon', '#'),
29+
},
30+
],
31+
],
2132
},
2233
i18n: {
2334
defaultLocale: 'ko',

package-lock.json

Lines changed: 6 additions & 30 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@
2121
},
2222
"homepage": "https://www.winterjung.dev",
2323
"dependencies": {
24-
"astro": "^5.1.1",
24+
"@astrojs/rss": "^4.0.10",
2525
"@astrojs/sitemap": "^3.2.1",
26-
"@astrojs/rss": "^4.0.10"
26+
"astro": "^5.1.1",
27+
"hastscript": "^9.0.1"
2728
},
2829
"devDependencies": {
2930
"@astrojs/check": "^0.9.4",

src/layouts/PostLayout.astro

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,23 @@ const displayDate = lastmod || date
3636
</main>
3737
<!-- Plausible Analytics -->
3838
<script defer data-domain="winterjung.dev" src="https://plausible.io/js/script.js"></script>
39+
<!-- Heading link copy to clipboard -->
40+
<script>
41+
document.querySelectorAll('.heading-link').forEach(link => {
42+
link.addEventListener('click', async (e) => {
43+
e.preventDefault()
44+
const url = decodeURI(new URL(link.getAttribute('href') || '', window.location.href).href)
45+
await navigator.clipboard.writeText(url)
46+
47+
const icon = link.querySelector('.heading-link-icon')
48+
if (icon) {
49+
const original = icon.textContent
50+
icon.textContent = '✓'
51+
setTimeout(() => { icon.textContent = original }, 1000)
52+
}
53+
})
54+
})
55+
</script>
3956
</body>
4057
</html>
4158

src/pages/[lang]/[slug].astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
import { getCollection } from 'astro:content'
3+
import { defaultLang, languages } from '../../i18n/ui'
34
import PostLayout from '../../layouts/PostLayout.astro'
4-
import { languages, defaultLang } from '../../i18n/ui'
55
66
export async function getStaticPaths() {
77
const nonDefaultLangs = Object.keys(languages).filter((lang) => lang !== defaultLang) as Array<

src/pages/[lang]/index.astro

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
---
22
import { getCollection } from 'astro:content'
3-
import BaseLayout from '../../layouts/BaseLayout.astro'
4-
import { languages, defaultLang, ui } from '../../i18n/ui'
3+
import { defaultLang, languages, ui } from '../../i18n/ui'
54
import { useTranslations } from '../../i18n/utils'
5+
import BaseLayout from '../../layouts/BaseLayout.astro'
66
77
export function getStaticPaths() {
88
return Object.keys(languages)

src/styles/global.css

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -335,12 +335,39 @@ nav {
335335
font-size: 0.875rem;
336336
}
337337

338-
/* H2 Permalink styling */
339-
h2 a {
340-
color: inherit;
338+
/* Heading permalink styling */
339+
.heading-link {
340+
color: var(--color-text-secondary);
341341
text-decoration: none;
342+
margin-left: 0.3em;
343+
opacity: 0;
344+
transition: opacity 0.15s;
342345
}
343346

344-
h2 a:hover {
345-
text-decoration: underline;
347+
.heading-link:hover {
348+
color: var(--color-link);
349+
text-decoration: none;
350+
}
351+
352+
h2:hover .heading-link,
353+
h3:hover .heading-link,
354+
h4:hover .heading-link,
355+
h5:hover .heading-link,
356+
h6:hover .heading-link {
357+
opacity: 1;
358+
}
359+
360+
.heading-link-icon {
361+
font-size: 0.8em;
362+
}
363+
364+
/* Mobile: always show heading link */
365+
@media (max-width: 768px) {
366+
.heading-link {
367+
opacity: 0.5;
368+
}
369+
370+
.heading-link:active {
371+
opacity: 1;
372+
}
346373
}

0 commit comments

Comments
 (0)