# Copy key

A key that copies a value and says so. Its lamp sits dark in the cap; when the text is on the clipboard the lamp lights, winks once, and the legend says Copied before it goes back to Copy. If the browser refuses the clipboard, the text is selected for the keyboard and the lamp lights amber.

Kit part · Free · https://tactileui.dev/parts/copy-key

- Element: `<button class="tui-key tui-copy-key" data-copy-value>`
- Needs: `kit.css` and `kit.mjs` ([how to install the kit](https://tactileui.dev/docs/installation#the-kit))
- Categories: Buttons
- Finishes: Aluminium, Graphite, Oak, Walnut, Porcelain, Black, Daylight, Night, Paper, Phosphor, E-paper, AMOLED
- Volumes: Tactile, Quiet

## Variants

### At rest

```html
<button class="tui-key tui-copy-key" type="button" aria-label="Copy" tabindex="-1"><span class="tui-lamp" aria-hidden="true"></span><span class="tui-copy-key__legend">Copy</span></button>
```

### Copied · lamp lit, green

```html
<button class="tui-key tui-copy-key" type="button" data-state="copied" aria-label="Copy" tabindex="-1"><span class="tui-lamp" data-lamp="green" aria-hidden="true"></span><span class="tui-copy-key__legend">Copied</span></button>
```

### Refused · lamp amber, text selected

```html
<button class="tui-key tui-copy-key" type="button" data-state="selected" aria-label="Copy" tabindex="-1"><span class="tui-lamp" data-lamp="amber" aria-hidden="true"></span><span class="tui-copy-key__legend">Selected</span></button>
```

### Small · --sm

```html
<button class="tui-key tui-copy-key tui-key--sm" type="button" aria-label="Copy" tabindex="-1"><span class="tui-lamp" aria-hidden="true"></span><span class="tui-copy-key__legend">Copy</span></button>
```

### Beside its text

```html
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap"><code id="ck-v5" style="display:inline-block;padding:10px 12px;border-radius:var(--tui-r-m);font:500 var(--tui-t-small)/1.2 var(--tui-sans);color:var(--tui-ink);background:var(--tui-well-fill) var(--tui-well);box-shadow:var(--tui-inset);overflow-wrap:anywhere;user-select:all">git clone tactile</code><button class="tui-key tui-copy-key" type="button" aria-label="Copy" tabindex="-1"><span class="tui-lamp" aria-hidden="true"></span><span class="tui-copy-key__legend">Copy</span></button></div>
```

### Disabled · nothing to copy

```html
<button class="tui-key tui-copy-key" type="button" aria-label="Copy" disabled tabindex="-1"><span class="tui-lamp" aria-hidden="true"></span><span class="tui-copy-key__legend">Copy</span></button>
```

## Every state

- Rest
- Hover
- Pressing
- Focus
- Copied
- Selected

Hover, pressed and focus are forced with `.is-hover`, `.is-down` and `.is-focus`; checked and disabled are real.

## API

| Name | Kind | Values | What it does |
| - | - | - | - |
|  | element | with data-copy-value or data-copy-from | The key. Empty, it is given its lamp and its legend by the script. |
| data-copy-value | attribute | the text itself | The value to copy. |
| data-copy-from | attribute | a selector, such as #cmd | An element that holds the value: an input or textarea by its value, anything else by its text. It is also what is selected if the clipboard refuses. |
| aria-label | attribute | on the key | Says what is copied: “Copy install command”. Start it with the visible word. |
| .tui-copy-key__legend | class | inside the key | The word on the key: Copy, then Copied or Selected for a moment. |
| .tui-copy-key__status | class | after the key | A polite status line, added by the script, that says aloud what happened. |
| tui-copy | event | detail { ok, text } | Bubbles from the key once the clipboard has answered; ok is false if the text was only selected. |
| copyFromKey(key) | function | a key | Does what a press does and resolves to whether the clipboard took the text. |
| copyKeyView(state, legend) · copyText(attrs, read) | function | pure | The legend, lamp and spoken words for each stage; the text a key points at. |

## Markup

```html
<!-- A value in the key -->
<button class="tui-key tui-copy-key" type="button" data-copy-value="npm install @northwind/cli" aria-label="Copy install command"></button>

<!-- Or the text of an element on the page, which is also what gets selected if the clipboard refuses -->
<code id="cmd">pnpm add @northwind/cli</code>
<button class="tui-key tui-copy-key" type="button" data-copy-from="#cmd" aria-label="Copy the command"></button>

<!-- Dressed by the script; this is what it leaves -->
<button class="tui-key tui-copy-key" type="button" data-copy-value="tok_8c1e" aria-label="Copy token">
  <span class="tui-lamp" aria-hidden="true"></span><span class="tui-copy-key__legend">Copy</span>
</button>
<span class="tui-copy-key__status" role="status"></span>
```

```js
import { copyFromKey } from '/kit/kit.mjs';

document.addEventListener('tui-copy', (e) => {
  if (!e.detail.ok) hint('Press Ctrl+C or Cmd+C to take the selected text');
});

await copyFromKey(document.querySelector('#token-key'));   // true if the clipboard took it
```

The demo on the page:

```html
<div class="tui-panel" style="width:min(100%,460px);display:grid;gap:16px;text-align:left"><div class="tui-panel__head" style="margin-bottom:0"><span class="tui-label">Install · Northwind CLI</span><span class="tui-status"><span class="tui-lamp" data-on></span>Published</span></div>
  <div style="display:grid;gap:8px">
    <span class="tui-label">Command</span>
    <div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap"><code id="ck-cmd" style="display:inline-block;padding:10px 12px;border-radius:var(--tui-r-m);font:500 var(--tui-t-small)/1.2 var(--tui-sans);color:var(--tui-ink);background:var(--tui-well-fill) var(--tui-well);box-shadow:var(--tui-inset);overflow-wrap:anywhere;user-select:all">npm install @northwind/cli</code><button class="tui-key tui-copy-key" type="button" data-copy-from="#ck-cmd" aria-label="Copy the install command"></button></div>
  </div>
  <div style="display:grid;gap:8px">
    <span class="tui-label">Access token</span>
    <div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap"><code id="ck-tok" style="display:inline-block;padding:10px 12px;border-radius:var(--tui-r-m);font:500 var(--tui-t-small)/1.2 var(--tui-sans);color:var(--tui-ink);background:var(--tui-well-fill) var(--tui-well);box-shadow:var(--tui-inset);overflow-wrap:anywhere;user-select:all">tok_8c1e2a7f40d9</code><button class="tui-key tui-copy-key" type="button" data-copy-value="tok_8c1e2a7f40d9" aria-label="Copy access token"></button></div>
  </div>
  <label class="tui-choice"><input class="tui-switch" type="checkbox" data-ck-refuse> The clipboard says no</label>
  <output class="tui-muted" style="font-size:var(--tui-t-small)" data-ck-out aria-live="polite">Nothing copied yet</output></div>
```

## Usage notes

Copy the install command or the token: the lamp lights and winks, the legend says Copied, and a moment later the key is as it was. Switch on “The clipboard says no” and press again: the text is selected and the lamp is amber, so you can copy it yourself.

## Accessibility

- It is a native `<button>` named “Copy install command”, with the visible word first in the name so voice control can say it.
- The result is said aloud by a polite status line: “Copied”, or “The clipboard said no. The text is selected; copy it with your keyboard.” The lamp and the legend say the same to the eye, so colour is never the only sign.
- When the clipboard is refused the text is selected where it is, or set in a small labelled field beside the key, and the focus is on it, so the next keystroke can copy it.
- The legend goes back after 1.6 seconds; the focus stays on the key throughout.
- The wink is a 360ms change of the lamp, which reduced motion removes; the lamp stays lit for the whole confirmation. On a phone the cap is 44px tall.

## How it’s built

It is the kit’s key with the kit’s lamp set in the cap, dark at rest. Nothing is drawn for the copied stage that the lamp does not already know how to draw: a lit lamp, green for done and amber for a question. One listener on the page answers every key that carries `data-copy-value`. It asks the clipboard first, then tries the old way from a hidden field, and only when both refuse does it select the text for the person to take. The key’s width is held between Copy and Copied so a toolbar does not shift.

## Rules

Copy a command, a token or an address with one press, hear that it worked, and keep working when the clipboard says no.

The full rules are in `copy-key.rules.md` in the brain, which ships with the Kit.

## Related parts

- [Key](https://tactileui.dev/parts/key): Every button, and every link that acts like one: a cap on a skirt that drops below the plate when pressed.
- [Toggle key](https://tactileui.dev/parts/toggle-key): One key that latches below the plate and stays there. It is the kit’s own key with one state to keep, down or up, so it is the way to say mute, pin, bold or armed without the weight of a switch. A lamp in the cap may light while it is down.
- [Input group](https://tactileui.dev/parts/input-group): One recess that holds a field and everything that belongs to it. Words that are part of the value but are not typed, such as https:// or a currency, are printed on the floor of the well behind an engraved line; a glyph sits at the start; small keys sit at the end to clear the text, copy it, or show a password.
- [Toast](https://tactileui.dev/parts/toast): A confirmation after an action: a small plate with a lamp and one factual sentence, read out politely, gone in 3.6 seconds.
