fix(content): strip link affordance from self-referencing links#16935
Open
ethangui wants to merge 1 commit into
Open
fix(content): strip link affordance from self-referencing links#16935ethangui wants to merge 1 commit into
ethangui wants to merge 1 commit into
Conversation
37809f8 to
d4f01d8
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 37809f8840
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
d4f01d8 to
561cff9
Compare
Inside the Reader app's content container (the .reader-content-container scope), any link whose href resolves to the current page now renders as plain text instead of an underlined click target. Mechanism: - Gatsby Link (via @gatsbyjs/reach-router) auto-sets aria-current="page" on anchors whose href matches the current pathname. - A CSS rule in global.css strips the link affordance from those anchors. - A companion useEffect in components/Wrapper sets tabindex="-1" and aria-disabled="true" so keyboard users don't land on invisible focus stops or activate the no-op link via Enter. In-page anchor links (/foo#section on /foo) and child-page links (/foo/bar on /foo) don't receive aria-current, so they're preserved. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
561cff9 to
fc8f7db
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The user experience
A reader on
/docs/migrate/managed-migrationsopens the page. The first thing they see is a warning callout. The last sentence in step 3 reads: "This is automated if you are running a managed migration." That phrase is underlined and styled like every other link. They click it. Nothing happens — the link points to the page they're already on.The same thing happens in
/blog/posthog-vs-heapat the FAQ accordion ("Heap" →/blog/posthog-vs-heap), in/handbook/brand/art-requestsmid-paragraph ("request one from the design team" →/handbook/brand/art-requests), and in dozens of other places.The problem
Self-referencing links —
<a href>values that resolve to the current page — appear in ~13 authored places across the site plus another ~110 emitted by one component, from four distinct root causes:ProductComparisonTabledetects the current page fromwindow.location.pathname. SSR has nowindow, so during static render the "compare" link for the current product ships in the HTML and gets removed only after client hydration. Visible to non-JS consumers (Slack/Discord unfurlers, Bing crawler, archive.org, the pre-hydration flash on slow connections).All four look identical to the reader: a clickable-looking link that does nothing.
Reference: how Wikipedia handles this
MediaWiki has had a convention for ~20 years (Wikipedia:Self link): when wikitext like
[[Heap]]appears on the Heap article, the platform automatically renders it as bold body text, not a link. The reader sees that the phrase is meaningful (bold) but un-clickable (no underline, no pointer cursor) — which signals "you are here" without requiring the reader to recognize a custom convention.Wikipedia's Manual of Style additionally discourages authors from writing self-links on purpose, but the auto-strip exists as a safety net for accidental ones.
The fix
Two changes that together remove both the visual and the keyboard affordance of self-referencing links inside
.reader-content-container(the wrapper around all MDX content in the Reader app).1. CSS rule in
src/styles/global.cssStrips the visual link affordance from anchors that have
aria-current="page". The bold weight stays because it's already inherited from surrounding markup (<strong>wrappers in markdown bullets, container-level[&_a]:font-semiboldrules in blockquotes).Why this works without JavaScript or template changes: Gatsby's
Linkwraps@gatsbyjs/reach-router'sLink, which automatically setsaria-current="page"on links whosehrefmatches the current pathname. The attribute is already in the SSR HTML — we just style against it.2. Keyboard a11y
useEffectinsrc/components/WrapperCSS alone can hide a link visually but can't neutralize the underlying
<a href>. Without this companion, keyboard users would tab to the now-invisible link and could activate it via Enter — a WCAG 2.4.7 (focus visible) regression for the population this PR aims to help.The effect runs after every Wrapper render and sets
tabindex="-1"andaria-disabled="true"on each.reader-content-container a[aria-current="page"]. Idempotent set-attribute calls; cheap.Effect on each class
[Heap]should likely point to/blog/best-heap-alternatives). Once the URL is corrected, the link no longer matchesaria-current="page"and this rule stops applying — the link works normally again.<a>tags still ship in SSR HTML, but they render as plain text. Post-hydration the component removes them entirely. Pre/post-hydration are now visually identical.False positives correctly preserved
/docs/surveys/installation/fluttercontains[PostHogObserver](/docs/surveys/installation/flutter#step-two-install-posthogobserver). The#anchormakes the href != pathname soaria-current="page"is not set; the link stays clickable./docs/advanced/proxycontains[self-hosted proxy reference](/docs/advanced/proxy/proxy-reference). Different path; noaria-current="page"; preserved.What this doesn't touch
.reader-content-container. The site's existing active-link styling in those components is unchanged.Review feedback addressed
useEffect.@gatsbyjs/reach-router, name the scope precisely, flag thepointer-events: noneside effects on context menu / glossary / hover prefetch, soften the anchor/child-page guarantee to a tripwire for future router changes, and cross-reference the Wrapper effect.Verification
Ran
pnpm build:minimaland inspected the rendered HTML for:aria-current="page", renders as plain text in browser/blog/posthog-vs-heapwith JS disabled → Class D "compare" links present in HTML but render as plain text/docs/surveys/installation/flutter→ in-page anchor link preserved/docs/advanced/proxy→ child-page link preservedAlso rebuilt from a cleaned cache without the safelist entry to confirm the CSS rule survives Tailwind's PurgeCSS pass on its own.
Checklist