code block copy button is always visible

hover-revealed means most people never find it, and touch has no hover at all.
70% white on the dark block, brightening on hover; the pre reserves right
padding so a long first line scrolls up to the button instead of under it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-07 01:35:50 +00:00
co-authored by Claude Opus 5
parent 6a682fae98
commit f283ebcba3
3 changed files with 12 additions and 6 deletions
+5 -2
View File
@@ -497,8 +497,11 @@ reasoned, not observed. Typecheck and the sidecar tests are clean.
The bubble's copy button copies the _whole reply_. When the reply is prose ending in one command you're
meant to run, that's the wrong unit, and you end up selecting the line by hand — the one chore a command
in a chat exists to save you. Fenced blocks now carry a button in their top-right corner. Hover-revealed
on a pointer device, always visible on touch, because there is no hover there to reveal it with.
in a chat exists to save you. Fenced blocks now carry a button in their top-right corner, **always
visible** — a control you have to discover by waving the pointer at it is one most people never find, and
on touch there is no hover to find it with at all. It sits at 70% white on the block's dark background
and brightens on hover; the block reserves right padding for it, so a long first line scrolls up to the
button rather than under it.
Inline `` `code` `` deliberately gets nothing: it's short enough to select, and a button per backticked
word would be noise.
+2
View File
@@ -138,6 +138,8 @@
.chat-md .chat-code pre {
margin: 0;
/* Room for the copy button, so a long first line scrolls up to it rather than under it. */
padding-right: 2.75em;
}
.chat-md pre {
@@ -33,7 +33,7 @@ export const CodeBlock = ({ children, ...props }: ComponentPropsWithoutRef<'pre'
};
return (
<div className="chat-code group/code relative">
<div className="chat-code relative">
<pre ref={ref} {...props}>
{children}
</pre>
@@ -42,9 +42,10 @@ export const CodeBlock = ({ children, ...props }: ComponentPropsWithoutRef<'pre'
onClick={handleCopy}
title="Copy code"
aria-label="Copy code"
// Hover-revealed on a pointer device, always visible on touch — there is no hover to reveal it
// with. Colours are hardcoded light-on-dark because the block behind it is #0d1117 in both themes.
className="absolute top-1.5 right-1.5 cursor-pointer rounded border border-white/15 bg-white/10 p-1 text-white/80 opacity-70 transition-opacity hover:!opacity-100 focus-visible:opacity-100 md:opacity-0 md:group-hover/code:opacity-70"
// Always visible, not hover-revealed: a control you have to discover by waving the pointer at it
// is a control most people never find, and on touch there is no hover to find it with at all.
// Colours are hardcoded light-on-dark because the block behind it is #0d1117 in both themes.
className="absolute top-1.5 right-1.5 cursor-pointer rounded border border-white/15 bg-white/10 p-1 text-white/70 transition-colors hover:border-white/30 hover:bg-white/20 hover:text-white"
>
{copied ? <Check className="h-3.5 w-3.5 text-green-400" /> : <Copy className="h-3.5 w-3.5" />}
</button>