← Blog index
Published

How to add copy-paste only text in HTML (plus a React component)

When finalizing an article for this blog, I often copy-paste text into tools like Grammarly and ChatGPT for feedback. But these tools often struggle with the structure because the copy-pasted text is missing context-- like that a certain line is actually a caption for an image, or a sentence is separated from the main content in a visually distinct block.

To work around this, I found myself tweaking the copied text repeatedly. This got tedious fast, so I came up with a solution using copy-paste only text. Not only does this save me time, but it also improves quoting from this site.

The gist of copy-paste only text is applying the following piece of CSS to an element:

font-size: 0;
line-height: 0;
opacity: 0;
overflow: hidden;
width: 1px;
height: 1px;
padding: 0;
pointer-events: none;
clip: rect(0, 0, 0, 0);
border-width: 0;

This makes the text invisible on the page but ensures it's included when a user selects and copies a section.

This works great for inline content, but for layout-affecting content like a <br />, you need to combine it with the following CSS. This will pull the element out of the layout so that it doesn't cause issues like unintentional whitespace.

position: absolute;
top: 0;
left: 0;
margin: -1px;

However, these four lines can interfere with text selection. For example, triple-clicking a paragraph usually selects the entire paragraph, but with copy-paste only text, it now stops at the invisible text, rather than selecting the entire paragraph. Try it out by triple-clicking the text in this paragraph (if you have a mouse).

For finishing touches, you can add a @media print query to hide the element with display: none. This ensures copy-paste only text doesn't show up in printed versions of the page.

Finally, add an aria-hidden attribute to be kind to screen readers. This keeps the invisible text from being unnecessarily announced, improving the experience for those users.

React

For this website, I wrote a CopyPasteOnly component that wraps the text you want to be copy-paste only. This is a React component that uses the CSS snippet above. The version at the time of writing this article is below, but you can find the latest version on GitHub.

import { css } from '@emotion/react'
import styled from '@emotion/styled'
import { ReactNode } from 'react'
const Container = styled('span', {
shouldForwardProp: (prop) => prop !== 'inline',
})<{ inline?: boolean }>(({ inline = false }) => [
css`
font-size: 0;
line-height: 0;
opacity: 0;
overflow: hidden;
width: 1px;
height: 1px;
padding: 0;
pointer-events: none;
clip: rect(0, 0, 0, 0);
border-width: 0;
@media print {
display: none;
}
`,
!inline &&
css`
position: absolute;
top: 0;
left: 0;
margin: -1px;
`,
])
interface Props {
children: ReactNode
inline?: boolean
}
/**
* Adds invisible text that only appears when copy-pasting.
* Useful for providing context like captions or asides.
*/
const CopyPasteOnly = ({ children, inline }: Props) => (
<Container aria-hidden inline={inline}>
{children}
</Container>
)
export default CopyPasteOnly

Here's how you might use this component in practice:

<figure>
<CopyPasteOnly>Picture:&nbsp;</CopyPasteOnly>
<img src="example.jpg" alt="An image that serves as an example and is not actually an image" />
<figcaption>
<CopyPasteOnly>Caption:&nbsp;</CopyPasteOnly>
A description of the example image.
</figcaption>
</figure>

I use CopyPasteOnly extensively across this site—for asides, code snippets, and image captions. See it in action by copying the text of this article and pasting it somewhere else.

Hope that helps.

More like this