Skip to content

Copyable

Terminal window
$ npx shadcn@latest add https://lacodda.github.io/dowel/r/copyable.json
See it liveAn id, a hash, a path - copied with one click.→

The rule comes from nitid: if a value is worth showing, it is worth being able to copy. Selecting a monospaced id by hand is a small daily tax.

It is a <button>, so the keyboard reaches it and a screen reader says what it does.

It is named by its label and then by the value it shows — “Copy a1b2c3”. The label alone would be a name without the words drawn on the button, and someone telling voice control what they see says those words (WCAG 2.5.3).

The confirmation is announced, not only drawn. A tick that appears silently tells a sighted user it worked and tells nobody else.

A refusal is reported. The clipboard needs a secure context and sometimes a permission; a button that looks like it worked and did not is worse than one that admits it failed.

<Copyable
value={commit.sha}
label={t('copy')}
copiedLabel={t('copied')}
onCopy={(ok) => ok || toast(t('copy.failed'))}
>
{commit.short}
</Copyable>

Both labels are required, on purpose. They are what a screen reader announces, and a default would be an English word this component invented — one the product’s translations never reach. Requiring them means a product that forgets fails to compile rather than shipping English to a reader who does not read it.

Props

Read from the types in ui/copyable.tsx, the file a product copies - so the description of each prop is also what an editor shows over it there.

PropTypeDefault
valuestringWhat lands on the clipboard. Defaults to the visible text.
childrenrequiredstringThe visible text.
labelrequiredstringWhat the button is called, for a screen reader. Required, and deliberately without a default: a string the component invents is a string the product cannot translate, and it would ship in English to every reader who does not read English.
copiedLabelrequiredstringWhat is announced after a successful copy. Required for the same reason.
onCopy(ok: boolean) => voidTold what happened, for a product that wants its own toast.

And every prop of React's HTMLAttributes<HTMLButtonElement> except onCopy, passed on.

Accessibility

The examples on the stand, through axe 4.14.0 in a browser, in both themes, against WCAG 2.2 A and AA and axe's best practices: 18 rules applied and passed, and none failed. The accessibility report has every item.

History

Each release that changed ui/copyable.tsx - the file a product copies - newest first.

  1. v0.38.0
    • fix name the button by its label and the value it shows 04826ea
  2. v0.28.0
    • feat a floor for pointer targets, and a way to reach it 2d397f5
  3. v0.7.0
    • feat budget what a primitive costs and forbid words of its own 8163f87
  4. v0.5.0
    • feat add the nine base primitives 81a2fd9