feat: add copy button to code blocks - #127
Merged
Merged
Conversation
…-unicons Replaces the npm package with a self-contained components/Icons/UilIcons.tsx that re-exports only the icons in use, shaving ~5 KB off the JS bundle and ~14 KB off first-load CSS. Updates all consumers (Nav, Footer, Modal, BuyMeCrypto, BlogButton) and removes BlogListClient/Skeleton components that were replaced by the Postit redesign. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… pre/table Adds a one-click copy button to every <pre> block rendered in blog posts. Clicking copies the snippet to the clipboard and briefly shows a checkmark (2 s) before reverting. Also sets tabindex="0" on <pre> and <table> elements for keyboard accessibility. Implementation: - rehypeCopyButton(): new exported rehype plugin in lib/posts.ts — visits hast nodes at build time, injecting the button and tabindex into the HTML string - PostContent.tsx: server component that renders the HTML and mounts CopyButtonHandler for the interactive layer - CopyButtonHandler.tsx: thin 'use client' component — attaches a delegated click listener to [data-post-content], manages per-button setTimeout dedup, and cleans up on unmount - blog.module.css: copy button styles including hover/copied states and dark-theme colour overrides Tests: 28 new tests (16 PostContent/CopyButtonHandler, 12 rehypeCopyButton) covering the full remark → rehype → highlight → stringify pipeline, clipboard success/failure paths, timer reset, delegation, and unmount cleanup. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Move overflow-x scrolling from <pre> onto the inner <code>. The <pre> was both the positioning anchor and the scroll container, so the absolutely positioned button was anchored to the content box and scrolled away with the code. <pre> is now a non-scrolling anchor; <code> handles the scroll. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sable The previous commit moved horizontal scroll from <pre> to the inner <code>, but left tabindex="0" on the now-non-scrolling <pre>. axe-core flagged the scrollable <code> as a keyboard trap (WCAG 2.1.1 scrollable-region-focusable), failing the e2e a11y suite. Put tabindex on the <code> (the scroll container) instead; the <pre> tabindex would just be a dead tab stop. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.
Summary
<pre>block in blog posts — clicking copies the snippet to clipboard and shows a checkmark for 2 s before resettingtabindex="0"on<pre>and<table>elements from Markdown for keyboard accessibility@iconscout/react-uniconsnpm package with a localcomponents/Icons/UilIcons.tsxbundle (removes ~5 KB JS / ~14 KB CSS from first load)Implementation
lib/posts.ts—rehypeCopyButtonplugin (exported)Visits hast nodes at build time, injecting a
<button class="copy-btn">with two icon spans (copy-icon/check-icon) into each<pre>, andtabindex="0"onto<pre>and<table>nodes.components/Blog/PostContent.tsx→ server componentRenders the HTML string with
data-post-contentattribute and mounts<CopyButtonHandler />.components/Blog/CopyButtonHandler.tsx→'use client'Thin client shell: attaches a delegated
clicklistener to[data-post-content], manages per-buttonsetTimeoutdedup (rapid re-click restarts the timer), cleans up on unmount.styles/pageStyles/blog.module.cssCopy button CSS — hover/focus states, copied feedback colour, dark-theme override.
Tests
28 new tests across 2 files (91 total, 0 fail):
tests/unit/lib/rehypeCopyButton.test.ts— 12 tests driving the real plugin through the full remark → rehype → highlight → stringify pipelinetests/unit/components/PostContent.test.tsx— 16 tests covering clipboard success/failure, timer reset (fake timers), delegated click, code text extraction, unmount cleanupTest plan
<pre>— it receives focus (tabindex="0")#2da44e)🤖 Generated with Claude Code