# `<llumi-copy-button>`

Copy-to-clipboard icon button with animated success/error feedback.

## What it is

A minimal icon button that copies text to the clipboard and shows live state transitions:

| State | Visual |
|-------|--------|
| idle | copy icon |
| copying | spinning indicator (button disabled) |
| copied | green ✓ icon, resets after 1 s |
| error | red × icon, resets after 1 s |

---

## Usage

> **Requires the base theme.** This is a light-DOM element styled by the design-system stylesheet — load `@llumi/design-system/base.css` (or `base.css`) on the page, or the button renders unstyled.

```html
<!-- 1. Register the element (auto side-effect) -->
<script type="module">
  import "@llumi/design-system/copy-button";
</script>

<!-- 2. Drop the tag wherever you need it -->
<llumi-copy-button value="Text to copy"></llumi-copy-button>
```

Or with named import:

```js
import { LlumiCopyButton } from "@llumi/design-system/copy-button";
```

---

## Copy source precedence

The text that gets written to the clipboard is resolved from the first source that is available, in this order:

1. **`getValue` (JS function property)** — highest precedence. Assign a function:
   ```js
   document.querySelector("llumi-copy-button").getValue = () => editor.getValue();
   ```
2. **`get-value` (attribute)** — a dotted path to a function on `globalThis`, resolved at click time via plain property traversal (no `eval`, no `new Function`). The component warns in the console if the path does not resolve to a function:
   ```html
   <llumi-copy-button get-value="myApp.getSnippet"></llumi-copy-button>
   ```
   ```js
   window.myApp = { getSnippet: () => editor.getValue() };
   ```
3. **`value` (string attribute)** — a plain, static string:
   ```html
   <llumi-copy-button value="npm install @llumi/design-system"></llumi-copy-button>
   ```
4. **`""` (empty string)** — fallback when none of the above is set.

---

## Attributes and properties

| Name | Type | Default | Notes |
|------|------|---------|-------|
| `value` | `string` attr + prop | `""` | Static text to copy. Lowest-precedence source. |
| `title` | `string` attr + prop | `"Copy to clipboard"` | Native tooltip; mirrored to `aria-label`. |
| `getValue` | `() => string` prop only | `undefined` | JS function property. No HTML attribute equivalent. Highest-precedence source. |
| `get-value` | `string` attr | — | Dotted path on `globalThis` resolving to a function. Resolved at click time. |
| `disabled` | — | — | Managed internally while copying; do not set manually. |

---

## Events

Both events bubble and are composed.

### `llumi-copy`

Fires on a successful clipboard write.

```ts
element.addEventListener("llumi-copy", (e: CustomEvent<{ text: string }>) => {
  console.log("Copied:", e.detail.text);
});
```

### `llumi-copy-error`

Fires when `navigator.clipboard.writeText` rejects (e.g. permission denied in a non-HTTPS context or missing user gesture).

```ts
element.addEventListener("llumi-copy-error", (e: CustomEvent<{ error: unknown }>) => {
  console.error("Copy failed:", e.detail.error);
});
```

---

## Styling

### Styling the button (light DOM)

The component renders in **light DOM**, so the inner `<button>` is a normal,
fully styleable element — no shadow piercing required:

```css
llumi-copy-button button {
  width: 3rem;
  height: 3rem;
  border-radius: 9999px;
}
```

### CSS custom properties

Each token has a sensible `oklch` fallback, so the component is usable without a design-system theme.

| Token | Role | Fallback |
|-------|------|---------|
| `--llumi-color-success` | Border + icon colour in the copied state | `oklch(62.7% 0.194 149.214)` (green) |
| `--llumi-color-danger` | Border + icon colour in the error state | `oklch(57.7% 0.245 27.325)` (red) |
| `--llumi-color-surface` | Button background | `oklch(100% 0 0)` (white) |
| `--llumi-color-border` | Button border | `oklch(92% 0.004 286.32)` (light grey) |
| `--llumi-color-fg` | Icon colour (idle) | `oklch(21% 0.006 285.885)` (near-black) |
| `--llumi-color-ring` | Focus-visible outline | `oklch(55.8% 0.288 302.321)` (purple) |
| `--llumi-radius` | Button border-radius | `0.5rem` |
