I’ve added a new feature on my blog following the advice and example from Salma Alam-Naylor: “How to build a copy code snippet button and why it matters”
It is impossible to highlight and copy code blocks when you are unable to use your hands.
[…] it is absolutely imperative that you include a way for people with limited use of their hands to copy that code in a single action
Last year I improved syntax highlighting on my blog but I’ve never appreciated the value of a “copy code” button until now. In hindsight the necessity is so obvious and it’s so easy to implement. I’m doing it and you should too!
Is it working?
If I didn’t mess it up you should see “Copy Code” buttons below all my snippets.
Salma is encoding the snippets using schema.org markup which is too clever for my scuffed static site generator (why didn’t I just use Eleventy?..) — for now I’m copying the text content of the pre
element.
<pre id="pre-hash123">
<code>[syntax highlighted code]</code>
</pre>
<button data-copy="pre-hash123" disabled>
Copy Code
</button>
For unique IDs I have a hash of the code which is effectively the same as random numbers. I was tempted to use an aria attribute like aria-controls
over data-copy
but it doesn’t seem suitable. The Rules of ARIA basically say don’t use ARIA so I’m not going to overthink it. Having the copy button immediately adjacent to the code should be enough context, right?
I added the disabled
attribute and hide the button with CSS by default:
[data-copy^='pre'] {
&:disabled {
display: none;
}
}
My logic here is that the button requires JavaScript so I hide it until activated. My buttons are positioned in a way that should prevent layout shift when they appear.
My JavaScript is almost identical to Salma’s JavaScript:
const copyButtons = document.querySelectorAll("[data-copy^='pre']");
copyButtons.forEach((button) => {
button.disabled = false;
const codeId = button.dataset.copy;
button.addEventListener("click", () => {
navigator.clipboard.writeText(
document.getElementById(codeId).textContent,
);
});
});
The only difference is that I’m coping the pre
text content rather than the schema meta content. I’d like to add schema later when I have more time (love a bit of metadata).
I always get confused between textContent
and innerText
. Both work but from testing on my blog innerText
removed empty new lines, so I opted for textContent
.
For lack of better UI I added some visual feedback:
navigator.clipboard.writeText(
document.getElementById(codeId).textContent,
).then(() => {
button.textContent = "Copied!";
});
Can this promise be rejected? My browser settings deny clipboard access but I guess that is only for reading.
I think I’ve done a good enough job for now! Thanks to Salma’s guide for explaining why this is necessary and how to do it. If you have any suggestions to improve usability @ me on the socials 👇

dbushell.com | Sciencx (2025-02-14T10:00:00+00:00) Copy Code Button. Retrieved from https://www.scien.cx/2025/02/14/copy-code-button/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.