From 68ec0789e9c1d7571f20c924f1ab31468cda4aa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mendon=C3=A7a?= Date: Wed, 3 Jun 2026 15:34:37 -0300 Subject: [PATCH 1/4] feat: add Copy Button and Tooltip components Introduce copy-button and tooltip with Storybook coverage, icon swap transitions on IconButton, and package exports for consumers. --- .specs/copy-button.md | 114 +++++++ .specs/tooltip.md | 131 ++++++++ .../actions/copy-button/CopyButton.stories.js | 108 +++++++ .../stories/webkit/overlay/Tooltip.stories.js | 190 ++++++++++++ packages/webkit/package.json | 2 + .../actions/copy-button/copy-button.vue | 96 ++++++ .../actions/icon-button/icon-button.vue | 67 +++- .../icon-button/presets/icon-transition.js | 24 ++ .../components/overlay/tooltip/package.json | 11 + .../tooltip/presets/popup-scale-transition.js | 31 ++ .../overlay/tooltip/tooltip.figma.ts | 18 ++ .../components/overlay/tooltip/tooltip.vue | 291 ++++++++++++++++++ 12 files changed, 1074 insertions(+), 9 deletions(-) create mode 100644 .specs/copy-button.md create mode 100644 .specs/tooltip.md create mode 100644 apps/storybook/src/stories/webkit/actions/copy-button/CopyButton.stories.js create mode 100644 apps/storybook/src/stories/webkit/overlay/Tooltip.stories.js create mode 100644 packages/webkit/src/components/actions/copy-button/copy-button.vue create mode 100644 packages/webkit/src/components/actions/icon-button/presets/icon-transition.js create mode 100644 packages/webkit/src/components/overlay/tooltip/package.json create mode 100644 packages/webkit/src/components/overlay/tooltip/presets/popup-scale-transition.js create mode 100644 packages/webkit/src/components/overlay/tooltip/tooltip.figma.ts create mode 100644 packages/webkit/src/components/overlay/tooltip/tooltip.vue 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 `