fix(a11y)!: use github-style header anchor generation

BREAKING CHANGES:
- `--vp-header-anchor-symbol` is removed
- The default permalink function of markdown-it-anchor is changed to `linkAfterHeader`. This will need updates in your custom themes.

This brings the original proposed behavior of #2039, #2040 which wasn't introduced to avoid breaking changes.

fixes #2980
closes #2982
fix/2980
Divyansh Singh 7 months ago
parent 8aad617446
commit 5df3a548b3

@ -2,6 +2,10 @@
* Headings * Headings
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */
.vp-doc .header-wrapper {
position: relative;
}
.vp-doc h1, .vp-doc h1,
.vp-doc h2, .vp-doc h2,
.vp-doc h3, .vp-doc h3,
@ -15,22 +19,45 @@
.vp-doc h1 { .vp-doc h1 {
letter-spacing: -0.02em; letter-spacing: -0.02em;
}
.vp-doc h1,
.vp-doc h1 + .header-anchor {
line-height: 40px; line-height: 40px;
font-size: 28px; font-size: 28px;
} }
@media (min-width: 768px) {
.vp-doc h1,
.vp-doc h1 + .header-anchor {
font-size: 32px;
}
}
.vp-doc h2 { .vp-doc h2 {
margin: 48px 0 16px; margin: 48px 0 16px;
border-top: 1px solid var(--vp-c-divider); border-top: 1px solid var(--vp-c-divider);
padding-top: 24px; padding-top: 24px;
letter-spacing: -0.02em; letter-spacing: -0.02em;
}
.vp-doc h2,
.vp-doc h2 + .header-anchor {
line-height: 32px; line-height: 32px;
font-size: 24px; font-size: 24px;
} }
.vp-doc h2 + .header-anchor {
top: 24px;
}
.vp-doc h3 { .vp-doc h3 {
margin: 32px 0 0; margin: 32px 0 0;
letter-spacing: -0.01em; letter-spacing: -0.01em;
}
.vp-doc h3,
.vp-doc h3 + .header-anchor {
line-height: 28px; line-height: 28px;
font-size: 20px; font-size: 20px;
} }
@ -38,6 +65,10 @@
.vp-doc h4 { .vp-doc h4 {
margin: 24px 0 0; margin: 24px 0 0;
letter-spacing: -0.01em; letter-spacing: -0.01em;
}
.vp-doc h4,
.vp-doc h4 + .header-anchor {
line-height: 24px; line-height: 24px;
font-size: 18px; font-size: 18px;
} }
@ -47,6 +78,7 @@
top: 0; top: 0;
left: 0; left: 0;
margin-left: -0.87em; margin-left: -0.87em;
padding-right: 0.3em;
font-weight: 500; font-weight: 500;
user-select: none; user-select: none;
opacity: 0; opacity: 0;
@ -56,37 +88,11 @@
opacity 0.25s; opacity 0.25s;
} }
.vp-doc .header-anchor:before { .vp-doc .header-wrapper:hover .header-anchor,
content: var(--vp-header-anchor-symbol); .vp-doc .header-wrapper .header-anchor:focus {
}
.vp-doc h1:hover .header-anchor,
.vp-doc h1 .header-anchor:focus,
.vp-doc h2:hover .header-anchor,
.vp-doc h2 .header-anchor:focus,
.vp-doc h3:hover .header-anchor,
.vp-doc h3 .header-anchor:focus,
.vp-doc h4:hover .header-anchor,
.vp-doc h4 .header-anchor:focus,
.vp-doc h5:hover .header-anchor,
.vp-doc h5 .header-anchor:focus,
.vp-doc h6:hover .header-anchor,
.vp-doc h6 .header-anchor:focus {
opacity: 1; opacity: 1;
} }
@media (min-width: 768px) {
.vp-doc h1 {
letter-spacing: -0.02em;
line-height: 40px;
font-size: 32px;
}
}
.vp-doc h2 .header-anchor {
top: 24px;
}
/** /**
* Paragraph and inline elements * Paragraph and inline elements
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */

@ -1,9 +1,11 @@
.visually-hidden { .visually-hidden {
position: absolute; position: absolute;
overflow: hidden;
clip: rect(0, 0, 0, 0);
width: 1px; width: 1px;
height: 1px; height: 1px;
margin: -1px;
padding: 0;
border-width: 0;
white-space: nowrap; white-space: nowrap;
clip: rect(0 0 0 0);
clip-path: inset(50%);
overflow: hidden;
} }

@ -316,14 +316,6 @@
--vp-layout-max-width: 1440px; --vp-layout-max-width: 1440px;
} }
/**
* Component: Header Anchor
* -------------------------------------------------------------------------- */
:root {
--vp-header-anchor-symbol: '#';
}
/** /**
* Component: Code * Component: Code
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */

@ -274,31 +274,11 @@ export async function createMarkdownRenderer(
.map((t) => t.content) .map((t) => t.content)
.join('') .join('')
}, },
permalink: (slug, _, state, idx) => { permalink: anchorPlugin.permalink.linkAfterHeader({
const title = assistiveText: (title) => `Permalink to “${title}`,
state.tokens[idx + 1]?.children visuallyHiddenClass: 'visually-hidden',
?.filter((token) => ['text', 'code_inline'].includes(token.type)) wrapper: ['<div class="header-wrapper">', '</div>']
.reduce((acc, t) => acc + t.content, '')
.trim() || ''
const linkTokens = [
Object.assign(new state.Token('text', '', 0), { content: ' ' }),
Object.assign(new state.Token('link_open', 'a', 1), {
attrs: [
['class', 'header-anchor'],
['href', `#${slug}`],
['aria-label', `Permalink to “${title}`]
]
}),
Object.assign(new state.Token('html_inline', '', 0), {
content: '&#8203;',
meta: { isPermalinkSymbol: true }
}), }),
new state.Token('link_close', 'a', -1)
]
state.tokens[idx + 1].children?.push(...linkTokens)
},
...options.anchor ...options.anchor
} as anchorPlugin.AnchorOptions).use(frontmatterPlugin, { } as anchorPlugin.AnchorOptions).use(frontmatterPlugin, {
...options.frontmatter ...options.frontmatter

Loading…
Cancel
Save