What's happening
The code-tabs/code-tabs-wrapper/code-tab-content shortcodes
(layouts/shortcodes/code-tab*.html) and the equivalent
tabs/tabs-wrapper/tab-content shortcodes render tab links as
Markdown fragment links, for example:
{{% code-tabs %}}
[Linux](#linux)
[macOS](#macos)
[Homebrew](#homebrew)
{{% /code-tabs %}}
assets/js/tabbed-content.js switches tabs entirely by DOM position
($(tab).index(this) matched against the same index in .code-tab-content)
and calls e.preventDefault() on click, so the #linux-style href is never
actually followed. No shortcode in this set renders a matching id
attribute anywhere in the page, so these fragments don't correspond to any
real anchor in the rendered HTML.
Why it matters
.ci/link-checker (lychee-based, run in pr-link-check.yml) validates
fragments and correctly reports these as broken: Fragment not found: #linux. Confirmed this isn't a link-checker bug by pulling PR #7701's
actual CI run artifact: the URL reaches lychee as a literal #linux, not
a file://...#linux path, and no id anywhere in the rendered page matches
it. (Also found in the process: production.lycherc.toml's [reporting] include_fragments = false doesn't actually suppress fragment checking —
the real CI command always carries --include-fragments regardless of
that setting, so it isn't wired to anything in the current link-checker
binary. Worth its own look, but unrelated to this shortcode issue.)
Because these shortcodes are widely used, this is a landmine, not an
isolated case:
code-tabs-wrapper: used in 170 content files
tabs-wrapper: used in 313 content files
Any PR that touches a page using either shortcode risks an unrelated
"Fragment not found" CI failure on a tab label the PR never wrote to.
#7701 hit this on [Linux](#linux) / [macOS](#macos) /
[Homebrew](#homebrew) purely from moving an existing page into shared
content — the fragments were already broken, just never checked before.
Proposed direction
Render a real id on each tab's content section, derived from the tab
label's slug, so #linux resolves to an actual element — for example on
<section class="code-tab-content"> in code-tab-content.html /
tab-content.html. This shouldn't change tabbedContent()'s
index-based click switching, since ids are additive; it also improves
direct/shared links to a specific tab, which currently only work via the
?t= query param.
Needs care since both shortcodes are shared across hundreds of pages:
- Confirm the slug derivation can't collide with an existing heading id
on the same page.
- Add or extend Cypress e2e coverage for tab switching and fragment
navigation before landing (see .agents/skills/cypress-e2e-testing).
- Verify rendered HTML for a representative sample of pages, not just
Hugo's exit code.
Verification once fixed
link-checker check reports no "Fragment not found" errors for
code-tabs/tabs anchors across the affected pages.
- Existing tab-switching behavior (index-based click handling,
?t=
query param persistence) is unchanged.
Relevant URLs
What's happening
The
code-tabs/code-tabs-wrapper/code-tab-contentshortcodes(
layouts/shortcodes/code-tab*.html) and the equivalenttabs/tabs-wrapper/tab-contentshortcodes render tab links asMarkdown fragment links, for example:
{{% code-tabs %}} [Linux](#linux) [macOS](#macos) [Homebrew](#homebrew) {{% /code-tabs %}}assets/js/tabbed-content.jsswitches tabs entirely by DOM position(
$(tab).index(this)matched against the same index in.code-tab-content)and calls
e.preventDefault()on click, so the#linux-style href is neveractually followed. No shortcode in this set renders a matching
idattribute anywhere in the page, so these fragments don't correspond to any
real anchor in the rendered HTML.
Why it matters
.ci/link-checker(lychee-based, run inpr-link-check.yml) validatesfragments and correctly reports these as broken:
Fragment not found: #linux. Confirmed this isn't a link-checker bug by pulling PR #7701'sactual CI run artifact: the URL reaches lychee as a literal
#linux, nota
file://...#linuxpath, and no id anywhere in the rendered page matchesit. (Also found in the process:
production.lycherc.toml's[reporting] include_fragments = falsedoesn't actually suppress fragment checking —the real CI command always carries
--include-fragmentsregardless ofthat setting, so it isn't wired to anything in the current link-checker
binary. Worth its own look, but unrelated to this shortcode issue.)
Because these shortcodes are widely used, this is a landmine, not an
isolated case:
code-tabs-wrapper: used in 170 content filestabs-wrapper: used in 313 content filesAny PR that touches a page using either shortcode risks an unrelated
"Fragment not found" CI failure on a tab label the PR never wrote to.
#7701 hit this on
[Linux](#linux)/[macOS](#macos)/[Homebrew](#homebrew)purely from moving an existing page into sharedcontent — the fragments were already broken, just never checked before.
Proposed direction
Render a real
idon each tab's content section, derived from the tablabel's slug, so
#linuxresolves to an actual element — for example on<section class="code-tab-content">incode-tab-content.html/tab-content.html. This shouldn't changetabbedContent()'sindex-based click switching, since ids are additive; it also improves
direct/shared links to a specific tab, which currently only work via the
?t=query param.Needs care since both shortcodes are shared across hundreds of pages:
on the same page.
navigation before landing (see
.agents/skills/cypress-e2e-testing).Hugo's exit code.
Verification once fixed
link-checker checkreports no "Fragment not found" errors forcode-tabs/tabsanchors across the affected pages.?t=query param persistence) is unchanged.
Relevant URLs
layouts/shortcodes/code-tabs.html,code-tabs-wrapper.html,code-tab-content.htmllayouts/shortcodes/tabs.html,tabs-wrapper.html,tab-content.htmlassets/js/tabbed-content.js.ci/link-checker/production.lycherc.toml,default.lycherc.toml