Skip to content

code-tabs and tabs shortcodes render tab-label fragments with no matching id #7703

Description

@jstirnaman

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:linkslink management (validation, checking, fixing)area:site-uiDocumentation site UI: templates, styles, JS/TS

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions