The previous answer is good, but I wish to elaborate on a possible solution for a more stringent definition of uncopyable.
If we define 'uncopyable' to mean it can't be copied through:
- Text selection by mouse
- Opening the developer console and copying from HTML
- Copying it from a script file that has it embedded
- Copying it straight from a network request (e.g. the network request analyzer)
- Copying it through a fake screen reader
For example: you present the user with several secure token which you want to protect against being logged, living in a clipboard longer than intended, being read by an extension, etc.
Then the requirements are not met by a pure CSS approach, because you ultimately need to write data to a place which is accessible through the code or queried using javascript on the page.
If we exclude 'copying' to mean e.g. parsing the visuals of the page with something like OCR, querying pixels on the page or in elements, or actively manipulating the page (writing to it), then something like this works:
- A javascript function which does not keep state (globalThis can be read by extensions with certain permissions, etc)
- An ephemeral or function-call-life public-private keypair for the client generated client-side within the function, which you send the public key of to the server to get back a private key-wrapped secret, which you decrypt
- A HTML5 Canvas the function itself creates, uses a fillText call to write the pixels to the canvas, then adds the canvas to the DOM.
The canvas will then simply look like text in the DOM, but not be text-copyable or interceptable without breaking the encryption.
In the scenario where you are also worried about malicious interference that may be able to write to the document - you can write an extension that can take the private-key-wrapped payload and decrypt + display it within the extension, or pass pure pixels back and have a script on the page which can commit it to the DOM.
It's worth noting, of course, that any pixels written to the DOM can be read by other things on the page, which is why displaying it in an extension is the 'most secure' solution from our research - but doesn't do it inline like the question requested.