From 5df3a548b30e56b7f7703533e880446ec0dec83f Mon Sep 17 00:00:00 2001 From: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> Date: Sun, 9 Mar 2025 01:02:15 +0530 Subject: [PATCH] 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 --- .../styles/components/vp-doc.css | 62 ++++++++++--------- src/client/theme-default/styles/utils.css | 8 ++- src/client/theme-default/styles/vars.css | 8 --- src/node/markdown/markdown.ts | 30 ++------- 4 files changed, 44 insertions(+), 64 deletions(-) diff --git a/src/client/theme-default/styles/components/vp-doc.css b/src/client/theme-default/styles/components/vp-doc.css index 89991184..714f601e 100644 --- a/src/client/theme-default/styles/components/vp-doc.css +++ b/src/client/theme-default/styles/components/vp-doc.css @@ -2,6 +2,10 @@ * Headings * -------------------------------------------------------------------------- */ +.vp-doc .header-wrapper { + position: relative; +} + .vp-doc h1, .vp-doc h2, .vp-doc h3, @@ -15,22 +19,45 @@ .vp-doc h1 { letter-spacing: -0.02em; +} + +.vp-doc h1, +.vp-doc h1 + .header-anchor { line-height: 40px; font-size: 28px; } +@media (min-width: 768px) { + .vp-doc h1, + .vp-doc h1 + .header-anchor { + font-size: 32px; + } +} + .vp-doc h2 { margin: 48px 0 16px; border-top: 1px solid var(--vp-c-divider); padding-top: 24px; letter-spacing: -0.02em; +} + +.vp-doc h2, +.vp-doc h2 + .header-anchor { line-height: 32px; font-size: 24px; } +.vp-doc h2 + .header-anchor { + top: 24px; +} + .vp-doc h3 { margin: 32px 0 0; letter-spacing: -0.01em; +} + +.vp-doc h3, +.vp-doc h3 + .header-anchor { line-height: 28px; font-size: 20px; } @@ -38,6 +65,10 @@ .vp-doc h4 { margin: 24px 0 0; letter-spacing: -0.01em; +} + +.vp-doc h4, +.vp-doc h4 + .header-anchor { line-height: 24px; font-size: 18px; } @@ -47,6 +78,7 @@ top: 0; left: 0; margin-left: -0.87em; + padding-right: 0.3em; font-weight: 500; user-select: none; opacity: 0; @@ -56,37 +88,11 @@ opacity 0.25s; } -.vp-doc .header-anchor:before { - content: var(--vp-header-anchor-symbol); -} - -.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 { +.vp-doc .header-wrapper:hover .header-anchor, +.vp-doc .header-wrapper .header-anchor:focus { 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 * -------------------------------------------------------------------------- */ diff --git a/src/client/theme-default/styles/utils.css b/src/client/theme-default/styles/utils.css index 65c7e55e..55b7190c 100644 --- a/src/client/theme-default/styles/utils.css +++ b/src/client/theme-default/styles/utils.css @@ -1,9 +1,11 @@ .visually-hidden { position: absolute; + overflow: hidden; + clip: rect(0, 0, 0, 0); width: 1px; height: 1px; + margin: -1px; + padding: 0; + border-width: 0; white-space: nowrap; - clip: rect(0 0 0 0); - clip-path: inset(50%); - overflow: hidden; } diff --git a/src/client/theme-default/styles/vars.css b/src/client/theme-default/styles/vars.css index 2a974f25..ac694c6b 100644 --- a/src/client/theme-default/styles/vars.css +++ b/src/client/theme-default/styles/vars.css @@ -316,14 +316,6 @@ --vp-layout-max-width: 1440px; } -/** - * Component: Header Anchor - * -------------------------------------------------------------------------- */ - -:root { - --vp-header-anchor-symbol: '#'; -} - /** * Component: Code * -------------------------------------------------------------------------- */ diff --git a/src/node/markdown/markdown.ts b/src/node/markdown/markdown.ts index fd6bf492..b15adedf 100644 --- a/src/node/markdown/markdown.ts +++ b/src/node/markdown/markdown.ts @@ -274,31 +274,11 @@ export async function createMarkdownRenderer( .map((t) => t.content) .join('') }, - permalink: (slug, _, state, idx) => { - const title = - state.tokens[idx + 1]?.children - ?.filter((token) => ['text', 'code_inline'].includes(token.type)) - .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: '​', - meta: { isPermalinkSymbol: true } - }), - new state.Token('link_close', 'a', -1) - ] - - state.tokens[idx + 1].children?.push(...linkTokens) - }, + permalink: anchorPlugin.permalink.linkAfterHeader({ + assistiveText: (title) => `Permalink to “${title}”`, + visuallyHiddenClass: 'visually-hidden', + wrapper: ['
', '
'] + }), ...options.anchor } as anchorPlugin.AnchorOptions).use(frontmatterPlugin, { ...options.frontmatter