chore(content): keep home copy button working after client-side navigation - #1975
Merged
Merged
Conversation
…ation The install copy button bound a click listener per button from a hoisted script, which Astro runs once per full load. With ClientRouter, entering the home page from another page swaps the DOM and the new button has no listener, so clicking it does nothing. Delegate a single click listener on the document and resolve the button and status node at click time. Claude-Session: https://claude.ai/code/session_01GWZkDPigd3w6kov2DsnSrV
Contributor
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Rewrote the
Hero.astroinstall-copy script from per-button listeners bound at module init to a single delegatedclicklistener ondocument, resolving the button viaclosest('button[data-copy]')and the status node viaquerySelectorat click time; reset timers moved into a per-buttonWeakMap. - Verified the fix's premise against the installed Astro 7.2.8
ClientRouter(dist/transitions/router.jsrunScripts()+data-astro-exectracking) and Blume's own client-router documentation (blume/src/components/layout/head-scripts.ts): page scripts execute once per real page load, module execution is deduped by URL via the browser module map, so the delegated listener binds exactly once per document — no accumulation on repeated soft navigations, and it survives the swap becausedocumentpersists across swaps while the old per-button bindings were wiped with the body. Resolving both nodes at click time also sidesteps the stale-node trap the old module-init capture would have had after a swap. - Behavior is otherwise preserved line-for-line:
is-copied,aria-livestatus announctet (success + failure), and the 1.6s reset (comment, logic, and the double-click window clear are all carried over), and theinstanceof Elementguard matches the existing delegated-listener idiom incomponents/blume/NavTree.astro:397.
The chore(content) scope follows the repo's commit convention (docs-site-internal change, chore type). Noted that the Showcase tabs and blog filter scripts share the same soft-nav pattern — properly left for a follow-up to keep this PR scoped.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
More templates
@orpc/ai-sdk
@orpc/arktype
@orpc/bun
@orpc/client
@orpc/cloudflare
@orpc/contract
@orpc/experimental-effect
@orpc/evlog
@orpc/hibernation
@orpc/json-schema
@orpc/experimental-msw
@orpc/nest
@orpc/next
@orpc/node
@orpc/openapi
@orpc/opentelemetry
@orpc/pinia-colada
@orpc/pino
@orpc/publisher
@orpc/ratelimit
@orpc/server
@orpc/shared
@orpc/swr
@orpc/tanstack-query
@orpc/trpc
@orpc/valibot
@orpc/zod
commit: |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Contributor
dinwwwh
added a commit
that referenced
this pull request
Sep 2, 2026
… client-side navigation (#1976) The code example tabs on the home page stopped responding after navigating to another page and coming back through the header logo. The blog index tag filter had the same problem. Both scripts bound their listeners once per full page load, but Astro's ClientRouter replaces the page body on every soft navigation, so the fresh markup came back without any handlers. Each script now re-resolves its elements in an `init` that runs at load and again on `astro:after-swap`, matching how Blume's own components handle re-entry. ## Fixes - Home showcase: tab clicks, ArrowLeft/ArrowRight/Home/End, the overflow fade hints, and the wheel-to-horizontal scroll all work after re-entering the home page. - Blog index: tag filter buttons work after re-entering `/blog`. - A single ResizeObserver re-targets the current tab strip after each swap instead of leaving a new observer per visit. ## Testing - Dev server, all soft navigations (confirmed by a window global surviving every hop): home → docs → home, home → blog → home → blog, and a second round trip. - Tab click, ArrowRight, ArrowLeft, and End switch the selected tab and panel after re-entry; blog tags filter the cards after re-entry; the copy button from #1975 still fires. - No server errors, no new console errors.
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.

What happens
The install command copy button on the home page does nothing if you reach the home page through a link inside the site. Open any docs page, click the logo to come back, click the button. Nothing is copied and the icon never flips to the check. Reload the page and it works again.
Why
Hero.astrobinds a click listener to each[data-copy]button from a hoisted script. Astro runs that script once per real page load. The site usesClientRouter, so going from docs back to home does not reload. It fetches the new HTML and swaps the body in place. The button in that new body is a fresh element. The script that bound the old listener already ran and does not run again, so the new button has no listener.The change
The listener now sits on
documentinstead of on each button.documentsurvives the swap. When a click lands, the handler walks up from the target withclosest('button[data-copy]')and looks up[data-copy-status]at that moment, so it always sees the current elements. The per-button reset timer moved into aWeakMapkeyed by button. That keeps the double-click guard and lets swapped-out buttons be garbage collected. The copied state, the aria-live announcement, the failure text and the 1.6s reset are unchanged.How I checked
On orpc.dev I hooked
navigator.clipboard.writeText, soft navigated from/docs/getting-startedback to/and clicked. No write, nois-copied. With this branch inblume devthe same steps writenpx skills add middleapi/orpcand the status reads "Copied", both on first load and after the soft navigation.The Showcase tabs and the blog filter bind listeners the same way and probably have the same problem. I left them out to keep this small.