diff --git a/.specs/copy-button.md b/.specs/copy-button.md new file mode 100644 index 00000000..8b80b008 --- /dev/null +++ b/.specs/copy-button.md @@ -0,0 +1,114 @@ +--- +name: copy-button +category: actions +structure: monolithic +status: approved +spec_version: 1 +checksum: f3173c1d9c26f7a94e7c053f1a5fca826724244384859ff48fe263badbf6b0f0 +created: 2026-06-02 +last_updated: 2026-06-02 +--- +# Copy Button — Component Spec + +## Purpose + +Icon-only control that copies a string to the clipboard and briefly confirms success. Composes `IconButton` for visuals and interaction tokens. + +## Props + +| Prop | Type | Default | Required | JSDoc | +|---|---|---|---|---| +| `value` | `string` | `—` | yes | Text copied to the clipboard on activation. | +| `ariaLabel` | `string` | `'Copy'` | no | Accessible name while idle. | +| `copiedLabel` | `string` | `'Copied'` | no | Accessible name while the copied state is shown. | +| `kind` | `'primary' \| 'secondary' \| 'outlined' \| 'transparent' \| 'danger'` | `'transparent'` | no | Visual variant forwarded to `IconButton`. | +| `size` | `'small' \| 'medium' \| 'large'` | `'small'` | no | Size token forwarded to `IconButton`. | +| `disabled` | `boolean` | `false` | no | Disables interaction and applies disabled tokens. | + +## Events + +| Event | Payload | Notes | +|---|---|---| +| `copy` | `string` | Emitted after a successful clipboard write with the copied value. | + +## Slots + +| _none_ | — | — | + +## States + +- Visual states: `default`, `copied`, `disabled` +- `data-state` mirrors `default` or `copied` +- `data-disabled` mirrors the `disabled` prop + +## Motion & Animations + +| Trigger | Animation / Transition | Token | Reduced-motion fallback | +|---|---|---|---| +| state change | `transition-colors duration-150 ease-out` (via `IconButton`) | inline | `motion-reduce:transition-none` | + +## Tokens + +| Region | Token (DESIGN.md) | +|---|---| +| typography | .text-button-lg | +| surface | `var(--bg-surface)` | +| text | `var(--text-default)` | +| spacing | `var(--spacing-3)` | +| shape | `var(--shape-elements)` | +| ring | `var(--ring-color)` | + +## Theme gaps + +| Figma variable | Temporary primitive | Follow-up | +|---|---|---| +| _none_ | — | — | + +## Accessibility (WCAG 2.1 AA) + +- Visible focus: inherited from `IconButton` (`focus-visible:ring-2 focus-visible:ring-[var(--ring-color)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--bg-canvas)]`) +- Keyboard map: `Tab` focuses; `Enter`/`Space` activates copy. +- ARIA: `aria-label` toggles between `ariaLabel` and `copiedLabel` when copied. +- Contrast ≥4.5:1 (text) / ≥3:1 (large + icons), including disabled state. +- `motion-reduce:transition-none motion-reduce:transform-none` on animated states. +- Touch target ≥40×40 px where the control is interactive. + +## Usage + +```vue + + + +``` + +## Stories (Storybook) + +- Default +- Disabled + +## Constraints — DO NOT + + + +- Do not add props beyond the Props table above. If you need a prop that is not listed, emit `BLOCKED: missing prop ` and stop — do not invent. +- Do not add events beyond the Events table above. Same rule for slots and sub-components. +- Do not invent imports. Every `@aziontech/webkit/*` path must exist in `packages/webkit/package.json#exports`. Every relative import must resolve to a real file. Every npm package must be installed. +- Do not use HEX/RGB/HSL colors, Tailwind palette names (e.g. `bg-blue-500`), raw typography classes (e.g. `text-sm`), `any`, `@ts-ignore`, or `class` inside `defineProps`. +- Do not install or import positioning/animation libraries (`@floating-ui/*`, `popper.js`, `tippy.js`, `gsap`, `framer-motion`, `motion`, `@vueuse/motion`, `@formkit/auto-animate`, drag-drop runtimes, scroll virtualization libs). Use CSS + Vue primitives (``, ``). See `.claude/rules/dependencies.md`. +- Do not improvise animations. Every `animate-*` / `transition-*` class must come from `packages/theme/src/tokens/semantic/animations.js`; every motion-bearing class pairs with `motion-reduce:*` on the same class string; no component-local `@keyframes`. +- Do not create class presets in JavaScript (`const kindClasses = {...}`, `const sharedClasses = [...]`, `const sizeClasses = {...}`, `const rootClasses = computed(...)`). Variants live on `data-*` attributes consumed by Tailwind `data-[attr=value]:`. All utilities live inline on the root element's `class` attribute. No `