@wix/agent-skills 1.19.0 → 1.19.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/package.json +1 -1
  2. package/skills/wix-app/SKILL.md +33 -31
  3. package/skills/wix-app/references/DASHBOARD_PAGE.md +6 -10
  4. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +17 -8
  5. package/skills/wix-app/references/WIX_PATTERNS_DOCS.md +53 -131
  6. package/skills/wix-app/references/dashboard-page/UX_SUCCESS_MODEL.md +1 -2
  7. package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +88 -73
  8. package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +46 -22
  9. package/skills/wix-app/references/editor-react-component/COMPONENT-CONTRACT.md +20 -6
  10. package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +9 -0
  11. package/skills/wix-app/references/editor-react-component/FUNCTION-HANDLERS.md +71 -31
  12. package/skills/wix-app/references/editor-react-component/PARTS.md +5 -2
  13. package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +4 -5
  14. package/skills/wix-app/scripts/pkg-root.cjs +73 -0
  15. package/skills/wix-app/scripts/scan-a11y-code.cjs +310 -40
  16. package/skills/wix-app/scripts/scan-a11y-eslint.cjs +118 -24
  17. package/skills/wix-app/scripts/scan-a11y-render.cjs +551 -0
  18. package/skills/wix-app/scripts/scan-a11y-review.cjs +373 -0
  19. package/skills/wix-base44-connector/SKILL.md +67 -43
  20. package/skills/wix-base44-connector/scripts/utils.cjs +39 -3
  21. package/skills/wix-docs/SKILL.md +47 -20
  22. package/skills/wix-headless/SKILL.md +1 -0
  23. package/skills/wix-headless/entry/bootstrap.mjs +106 -50
  24. package/skills/wix-headless/entry/skill.md +5 -3
  25. package/skills/wix-headless/references/CAPABILITIES.md +7 -2
  26. package/skills/wix-headless/references/SDK_HANDOFF.md +2 -1
  27. package/skills/wix-headless/references/inline-recipes/experience-store.md +48 -0
  28. package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +350 -114
  29. package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +1 -1
  30. package/skills/wix-headless/references/managed/AUTHENTICATION.md +25 -5
  31. package/skills/wix-headless/references/managed/CREATE.md +5 -0
  32. package/skills/wix-headless-fast/SKILL.md +25 -7
  33. package/skills/wix-headless-fast/entry/skill.md +23 -14
  34. package/skills/wix-headless-fast/install/fast-path.mjs +48 -5
  35. package/skills/wix-headless-fast/install/pin-agents-md.mjs +57 -0
  36. package/skills/wix-headless-fast/references/shared/CUSTOM_OPERATIONS.md +3 -3
  37. package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +204 -35
  38. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +146 -36
  39. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-store.ts +8 -2
  40. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +95 -20
  41. package/skills/wix-headless-fast/references/storefront/seed/SEED.md +21 -2
  42. package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +104 -18
  43. package/skills/wix-manage/SKILL.md +16 -10
  44. package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +4 -1
  45. package/skills/wix-manage/references/bookings/create-appointment-service.md +3 -1
  46. package/skills/wix-manage/references/bookings/create-class-service.md +3 -1
  47. package/skills/wix-manage/references/bookings/create-course-service.md +4 -2
  48. package/skills/wix-manage/references/bookings/end-to-end-booking-flow.md +68 -3
  49. package/skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-flow-seasonal-promotion.md +1 -1
  50. package/skills/wix-manage/references/google-ads/billing-and-payment.md +3 -3
  51. package/skills/wix-manage/references/google-ads/create-performance-max-campaign.md +8 -8
  52. package/skills/wix-manage/references/google-ads/get-campaign-suggestions.md +4 -4
  53. package/skills/wix-manage/references/google-ads/google-ads-dashboard-navigation.md +1 -1
  54. package/skills/wix-manage/references/google-ads/install-and-create-account.md +9 -9
  55. package/skills/wix-manage/references/google-ads/manage-campaign-success-guide.md +108 -14
  56. package/skills/wix-manage/references/google-ads/query-campaign-analytics.md +7 -7
  57. package/skills/wix-manage/references/seo/manage-content-plan.md +182 -136
  58. package/skills/wix-manage/references/seo/manage-google-search-console.md +272 -0
  59. package/skills/wix-manage/references/sites/upload-static-site.md +283 -0
  60. package/skills/wix-manage/references/stores/change-store-currency.md +1 -1
  61. package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed-with-install.md +3 -1
  62. package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed.md +3 -1
  63. package/skills/wix-vibe-headless/platforms/base44-ecom-light.md +1 -1
  64. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +69 -27
  65. package/skills/wix-vibe-headless/references/storefront/app/components/CartDrawer.jsx +32 -14
  66. package/skills/wix-vibe-headless/references/storefront/app/context/CartContext.jsx +14 -3
  67. package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductCard.js +41 -35
  68. package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductDetail.js +77 -30
  69. package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-cart.js +31 -2
  70. package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-catalog.js +31 -6
  71. package/skills/wix-app/references/dashboard-page/COLLECTION_TOOLKIT.md +0 -80
  72. package/skills/wix-app/references/dashboard-page/ENTITY_PAGE_TOOLKIT.md +0 -140
  73. package/skills/wix-app/references/dashboard-page/PATTERNS_BUNDLE_READING.md +0 -112
  74. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductCard.tsx +0 -100
  75. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductDetailView.tsx +0 -130
  76. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductGrid.tsx +0 -48
  77. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ShopView.tsx +0 -66
  78. package/skills/wix-headless-fast/references/storefront/app/components/storefront/VariantPicker.tsx +0 -103
  79. package/skills/wix-headless-fast/references/storefront/app-astro/pages/products/[slug].astro +0 -56
  80. package/skills/wix-headless-fast/references/storefront/app-astro/pages/shop.astro +0 -23
@@ -1,13 +1,13 @@
1
1
  # Accessibility Implementation and Review
2
2
 
3
- Use this reference while authoring Editor React Component props and JSX and
4
- after every JSX edit.
3
+ Use this reference while authoring Editor React Component props and JSX, then
4
+ run the review command once the JSX is complete.
5
5
 
6
6
  ## Contents
7
7
 
8
8
  - [Implementation Contract](#implementation-contract)
9
9
  - [Review Scope](#review-scope)
10
- - [Automated Scanners](#automated-scanners)
10
+ - [Automated Review](#automated-review)
11
11
  - [Finding Triage](#finding-triage)
12
12
  - [Manual Review](#manual-review)
13
13
  - [Pre-Fix Checks for Non-Interactive Controls](#pre-fix-checks-for-non-interactive-controls)
@@ -15,29 +15,42 @@ after every JSX edit.
15
15
 
16
16
  ## Implementation Contract
17
17
 
18
- ### Route ARIA Through `a11y`
18
+ ### Own Accessibility per Part
19
19
 
20
- Do not add individual public props such as `ariaLabel`, `ariaDescribedBy`, or
21
- `role`. Use the platform `A11y` type and convert it at the semantic target.
20
+ Use the platform `A11y` type instead of individual public props such as
21
+ `ariaLabel`, `ariaDescribedBy`, or `role`. For each part:
22
+
23
+ - Prefer a native element.
24
+ - Keep roles, heading levels, keyboard and focus behavior, relationships, live
25
+ regions, and widget state in component code.
26
+ - Read `a11y.ariaLabel` only when a control has no visible name.
27
+
28
+ Every `a11y` field that reaches the DOM becomes an editor control. Read only the
29
+ field the part needs and write it as its HTML attribute,
30
+ `aria-label={a11y?.ariaLabel}`. Never spread the whole object.
22
31
 
23
32
  ```tsx
24
- import type { A11y, Direction } from '@wix/editor-react-types';
25
- import { convertA11yKeysToHtmlFormat } from '@wix/react-component-utils';
26
-
27
- export type TabsProps = {
28
- id: string;
29
- className?: string;
30
- direction?: Direction;
31
- a11y?: A11y;
33
+ import type { A11y } from '@wix/editor-react-types';
34
+
35
+ type ToggleProps = {
36
+ elementProps?: { toggle?: { className?: string; a11y?: A11y } };
32
37
  };
33
38
 
34
- <nav {...(a11y && convertA11yKeysToHtmlFormat(a11y))}>{tabs}</nav>;
39
+ function Toggle({ elementProps }: ToggleProps) {
40
+ const { a11y: toggleA11y, ...toggleProps } = elementProps?.toggle ?? {};
41
+
42
+ return (
43
+ <button {...toggleProps} aria-label={toggleA11y?.ariaLabel ?? ARIA_LABELS.toggle}>
44
+ <ChevronIcon aria-hidden="true" />
45
+ </button>
46
+ );
47
+ }
35
48
  ```
36
49
 
37
- Apply root accessibility to the elected root. If requirements assign
38
- accessibility to a named inner part, route it through that part's
39
- `elementProps.<name>.a11y` contract and convert it on the actual semantic
40
- element.
50
+ Keep the root's typed `a11y?: A11y` prop even when it reads no field. Destructure
51
+ `a11y` out of an `elementProps` entry before spreading the entry; a spread entry
52
+ records the nested object as a whole. Image alt text comes from the `Image`
53
+ type's `alt` field, not from `a11y`.
41
54
 
42
55
  ### Provide Accessible Names
43
56
 
@@ -53,6 +66,7 @@ Never hardcode an `aria-label` string directly in JSX.
53
66
  ```tsx
54
67
  // constants.ts
55
68
  export const ARIA_LABELS = {
69
+ toggle: 'Toggle details',
56
70
  playButton: 'Play animation',
57
71
  pauseButton: 'Pause animation',
58
72
  } as const;
@@ -80,36 +94,47 @@ including an icon plus visible text, usually do not need another ARIA label.
80
94
 
81
95
  ## Review Scope
82
96
 
83
- Run this review after creating or editing JSX.
97
+ Run this review once the JSX is complete and again after each fix pass, at
98
+ most two passes. Do not rerun after a clean result unless JSX changed.
84
99
 
85
- | Request | Scan and inspect |
100
+ | Request | Pass to the command |
86
101
  | --- | --- |
87
- | Specific file | That file and rendered wrappers or shared components it imports |
88
- | A component name or "this component" | All hand-edited files in its component folder plus relevant shared imports |
89
- | Full audit | Every component under `src/extensions/site/components/` plus relevant shared imports |
102
+ | Specific file | That file; its component folder is rendered |
103
+ | A component name or "this component" | The component folder |
104
+ | Full audit | `src/extensions/site/components/` |
90
105
 
91
- Exclude `*.generated.ts`; it is regenerated from JSX and CSS.
106
+ `*.generated.ts` is regenerated from JSX and CSS and is never scanned.
92
107
  Imported shared components are inspection context, not automatic edit scope.
93
108
  Report a confirmed shared-component issue instead of changing a broadly reused
94
109
  primitive unless the requested fix requires that shared change and its impact is
95
110
  understood.
96
111
 
97
- ## Automated Scanners
112
+ ## Automated Review
98
113
 
99
114
  `<SKILL_ROOT>` is the absolute directory containing the active `SKILL.md`. Run
100
- both commands from the consumer Wix package so dependencies resolve from that
101
- project. Pass `.tsx` or `.jsx` paths relative to the current directory.
115
+ from the consumer Wix package so dependencies resolve from that project.
102
116
 
103
117
  ```bash
104
- node <SKILL_ROOT>/scripts/scan-a11y-eslint.cjs <file1> [file2] ...
105
- node <SKILL_ROOT>/scripts/scan-a11y-code.cjs <file1> [file2] ...
118
+ node <SKILL_ROOT>/scripts/scan-a11y-review.cjs <component-dir | files...>
106
119
  ```
107
120
 
108
- Do not treat scanner startup failure as a clean result. The ESLint scanner
109
- reports JSX accessibility rules. The semantic scanner follows imports up to
110
- four levels and emits resolution evidence and confidence. Neither scanner
111
- covers every Editor React Component contract, so complete the manual review
112
- even when both return zero findings.
121
+ One command, one report. It runs the jsx-a11y ESLint rules, a semantic scanner
122
+ that follows imports and checks the per-part `a11y` contract, and a render
123
+ audit: the component is rendered with `defaultProps` in Node (an SSR check),
124
+ loaded into jsdom with its CSS Modules, and audited with axe-core.
125
+ `component.preview.tsx` must render without falling back to the placeholder.
126
+
127
+ The JSON report has `summary.line`, then `findings` grouped by rule with a
128
+ count, locations or DOM target, the scanner message, and the axe help link,
129
+ then `notChecked`. Exit `0` means every scanner ran and found nothing, `1`
130
+ means findings, `2` means the review is inconclusive; never treat `2` as clean.
131
+ Exit `2` with `render FAILED (missing-deps)` means `jsdom` or `axe-core` is not
132
+ installed: install them (SKILL.md step 2) and rerun. Exit `2` with
133
+ `render FAILED (loader)` means the audit could not load a module the component
134
+ imports; that is a scanner limit, not a component defect. Change the import
135
+ only if `tsc` also rejects it; otherwise stop and report the loader limit in the
136
+ final summary as a check that could not run. Color contrast, target size, and
137
+ keyboard behavior need a browser and remain manual.
113
138
 
114
139
  ## Finding Triage
115
140
 
@@ -154,42 +179,31 @@ evidence supports.
154
179
 
155
180
  ## Manual Review
156
181
 
157
- Verify all of the following after triaging scanner output.
158
-
159
- ### Semantic Targeting
160
-
161
- - `a11y` is typed and converted on the correct semantic element.
162
- - Wrappers and polymorphic components preserve their documented semantics.
163
- - Configurable heading or tag choices reach the rendered element.
164
- - Extension overrides preserve generated accessibility fields.
165
-
166
- ### Names and Visual Content
167
-
168
- - Icon-only controls have configurable or stable system-owned names.
169
- - Visually hidden text remains in the accessibility tree when needed.
170
- - Meaningful images and icons are named; decorative repetitions are hidden.
171
- - Accessibility strings are not hardcoded directly in JSX.
172
-
173
- ### Hidden Content and Focus
174
-
175
- - Hidden or collapsed state agrees across visuals, focusability, and the
176
- accessibility tree.
177
- - Invisible content cannot receive focus unless the interaction requires it.
178
- - Disabled and inert states behave consistently for keyboard and assistive
179
- technology users.
180
-
181
- ### Interaction and Structure
182
-
183
- - Interactive-looking elements use native semantics or complete role, focus,
184
- and keyboard behavior.
185
- - `onClick` on a non-interactive tag is checked, including conditional spreads.
186
- - Nested interactive children and existing focus management are inspected
187
- before semantics are added to a wrapper.
188
- - Lists, headings, landmarks, tabs, menus, dialogs, and breadcrumbs keep their
189
- intended structure.
182
+ In the default rendered state the command checks names, alt text, ARIA
183
+ validity, nesting, list and heading structure, hidden-but-focusable content,
184
+ the `a11y` contract, and SSR safety. Verify what it cannot see:
185
+
186
+ - Every meaningful non-default state (expanded, selected, playing, error,
187
+ empty, hover/focus) keeps correct names, focusability, hidden state, and
188
+ structure; the command audits only the default render.
189
+ - Wrappers and polymorphic components preserve their documented semantics;
190
+ extension overrides preserve generated accessibility fields.
191
+ - Accessible names describe the action, and visually hidden text that carries
192
+ meaning stays in the accessibility tree.
193
+ - State hidden through `--display` or transforms agrees with focusability and
194
+ the accessibility tree; disabled and inert states behave consistently.
195
+ - Custom widgets (tabs, menus, dialogs, sliders) implement their full APG
196
+ keyboard pattern (arrow keys, Home/End, Escape, roving `tabIndex`) and
197
+ structural relationships, or use a plain native element instead of
198
+ borrowing the role.
199
+ - Interactive controls have a hit area of at least 24×24 CSS px and visible
200
+ focus.
190
201
  - The root implements the direction contract, and every `ReactNode` slot
191
202
  isolates nested content with `dir="ltr"`.
192
203
 
204
+ - An auto-rotating set of readable parallel items uses `aria-live="off"` while
205
+ it is rotating and `aria-live="polite"` while it is stopped.
206
+
193
207
  ## Pre-Fix Checks for Non-Interactive Controls
194
208
 
195
209
  Before adding `role="button"`, `tabIndex`, and keyboard handlers to a non-native
@@ -211,12 +225,13 @@ Prefer a native element when it preserves product behavior.
211
225
 
212
226
  The accessibility review is complete only when:
213
227
 
214
- - both scanners start successfully;
215
- - every finding is triaged;
216
- - confirmed issues are fixed when safe;
217
- - the manual review is complete; and
218
- - both scanners are rerun on every changed `.tsx` or `.jsx` file.
228
+ - the last run exited `0` or `1`; exit `2` is acceptable only for a reported
229
+ `loader` limit;
230
+ - every finding is triaged and confirmed issues are fixed when safe;
231
+ - the command was rerun after the last fix pass; and
232
+ - the manual review is complete.
219
233
 
234
+ After two fix passes, stop and report the remaining findings with their triage.
220
235
  Preserve visual and runtime behavior. Fix the semantic owner: root, named inner
221
236
  part, shared primitive, or call site. Then return to the main workflow for the
222
237
  Wix build, manifest generation, TypeScript check, and relevant project tests.
@@ -9,10 +9,12 @@ Every such component must ship an on-stage **play/pause button**.
9
9
 
10
10
  - [Apply When](#apply-when)
11
11
  - [Required Contract](#required-contract)
12
+ - [Announce Manually Selected Parallel Content](#announce-manually-selected-parallel-content)
12
13
  - [Define Props](#1-define-props)
13
14
  - [Manage Playback State](#2-manage-playback-state)
14
15
  - [Add the Play/Pause Button](#3-add-the-playpause-button)
15
16
  - [Suppress Autoplay in `component.preview.tsx`](#4-suppress-autoplay-in-componentpreviewtsx)
17
+ - [Edit the generated `component.preview.tsx`](#edit-the-generated-componentpreviewtsx)
16
18
  - [Checklist](#checklist)
17
19
 
18
20
  ## Apply When
@@ -41,12 +43,26 @@ implementation detail.
41
43
  existing playback prop names and add only the missing safety contract.
42
44
  Carousels, sliders, slideshows, and galleries **must** define `autoPlay`
43
45
  (default `true`).
44
- 2. **Playback state** — `isPlaying` + `handlePause` / `handleResume`
46
+ 2. **Playback state** — play/pause toggle state, derived `isPlaying`, and
47
+ `handlePause` / `handleResume`
45
48
  3. **Play/pause button** — overlay `<button>` with inline SVG icon and CSS
46
49
  positioning; it must be a named part with `elementProps` wiring, a hover
47
50
  design state, and a standalone `:focus-visible` keyboard indicator
48
51
  4. **Modify `component.preview.tsx`** — suppress autoplay in editor design mode
49
- 5. **Respect `prefers-reduced-motion`** — suppress autoplay when the OS requests reduced motion
52
+ 5. **Respect `prefers-reduced-motion`** — start paused when the OS requests reduced motion
53
+
54
+ ### Announce Manually Selected Parallel Content
55
+
56
+ For carousel-like components that swap readable parallel items, make the item
57
+ wrapper a live region:
58
+
59
+ - Use `aria-live={isPlaying ? 'off' : 'polite'}` and `aria-atomic="false"`.
60
+ - `isPlaying = isPlayOn && !isHovered && !isStoppedByFocus`. Reduced motion
61
+ starts paused; editor mode, `autoPlay={false}`, and pause set it false.
62
+ Keyboard focus remains stopped until the rotation control restarts; hover
63
+ pauses temporarily.
64
+ - Previous/next changes items. Use root `onMouseEnter`/`onMouseLeave`;
65
+ leaving resumes unless focus has stopped rotation.
50
66
 
51
67
  ## 1. Define Props
52
68
 
@@ -83,36 +99,44 @@ support repeat behavior; do not add it only to match the example.
83
99
 
84
100
  ## 2. Manage Playback State
85
101
 
86
- Track play/pause state with `useState`. Initialize it from `autoPlay` and the OS reduced-motion preference — when the visitor has requested reduced motion, the animation starts paused:
102
+ Track play/pause button state with `useState`. Initialize it from `autoPlay`
103
+ and reduced motion; reduced motion starts paused, but a visitor can start it.
87
104
 
88
105
  ```tsx
89
106
  import { useReducedMotion } from '@wix/react-component-utils';
90
107
 
91
- // inside the component:
92
108
  const reducedMotion = useReducedMotion();
93
109
 
94
- const [isPlaying, setIsPlaying] = React.useState((autoPlay ?? true) && !reducedMotion);
95
-
96
- // when autoPlay prop changes, re-sync (but still respect reduced motion)
97
- React.useEffect(() => {
98
- setIsPlaying((autoPlay ?? true) && !reducedMotion);
99
- }, [autoPlay]);
110
+ const [isPlayOn, setIsPlayOn] = React.useState(() => (autoPlay ?? true) && !reducedMotion);
100
111
 
101
- // when reduced motion is enabled, suppress playback; never re-enable on its own
102
- // (visitor may have manually paused — don't restart against their will)
112
+ const skipInitialAutoPlaySync = React.useRef(true);
103
113
  React.useEffect(() => {
104
- if (reducedMotion) {
105
- setIsPlaying(false);
114
+ if (skipInitialAutoPlaySync.current) {
115
+ skipInitialAutoPlaySync.current = false;
116
+ return;
106
117
  }
107
- }, [reducedMotion]);
118
+ setIsPlayOn(autoPlay ?? true);
119
+ }, [autoPlay]);
108
120
 
109
- const handlePause = () => setIsPlaying(false);
110
- const handleResume = () => setIsPlaying(true);
121
+ const handlePause = () => setIsPlayOn(false);
122
+ const handleResume = () => setIsPlayOn(true);
111
123
  ```
112
124
 
113
- **`reducedMotion`** is `true` when the hook reports that reduced motion is enabled. When active, the animation starts paused — the visitor can still press the play button to start it manually. It is a runtime browser signal, not a manifest/data prop; never expose it as a component prop or in the manifest.
125
+ `reducedMotion` is a runtime signal, not a manifest/data prop.
126
+
127
+ Derive runtime playback from `isPlayOn` plus only needed conditions. Without
128
+ another condition, use `const isPlaying = isPlayOn`. A carousel, slideshow,
129
+ slider, or gallery also uses hover and focus-stop state:
130
+
131
+ ```tsx
132
+ const isPlaying = isPlayOn && !isHovered && !isStoppedByFocus;
133
+ ```
114
134
 
115
- Pass `isPlaying` to the animation renderer and toggle between `handlePause` / `handleResume` on button click.
135
+ Pass `isPlaying` to the renderer. The button uses `isPlayOn` and toggles
136
+ between `handlePause` / `handleResume` on click. In a carousel-like component,
137
+ focus entering anything except that button sets `isPlayOn` to `false` and
138
+ `isStoppedByFocus` to `true`; `handleResume` sets `isPlayOn` to `true` and
139
+ clears `isStoppedByFocus`.
116
140
 
117
141
  ## 3. Add the Play/Pause Button
118
142
 
@@ -177,12 +201,12 @@ Wire the named part completely:
177
201
  styles.playButton,
178
202
  elementProps?.playButton?.className,
179
203
  )}
180
- onClick={isPlaying ? handlePause : handleResume}
204
+ onClick={isPlayOn ? handlePause : handleResume}
181
205
  aria-label={
182
- isPlaying ? ARIA_LABELS.pauseButton : ARIA_LABELS.playButton
206
+ isPlayOn ? ARIA_LABELS.pauseButton : ARIA_LABELS.playButton
183
207
  }
184
208
  >
185
- {isPlaying ? <PauseIcon /> : <PlayIcon />}
209
+ {isPlayOn ? <PauseIcon /> : <PlayIcon />}
186
210
  </button>
187
211
  ```
188
212
 
@@ -33,6 +33,10 @@ export type PlanCardProps = {
33
33
  heading?: string;
34
34
  plans?: Array<Plan>;
35
35
  onClick?: (event: React.MouseEvent) => void;
36
+ onMouseIn?: (event: React.MouseEvent) => void;
37
+ onMouseOut?: (event: React.MouseEvent) => void;
38
+ onFocus?: (event: React.FocusEvent) => void;
39
+ onBlur?: (event: React.FocusEvent) => void;
36
40
 
37
41
  elementProps?: {
38
42
  cta?: { className?: string; href?: string };
@@ -42,13 +46,13 @@ export type PlanCardProps = {
42
46
 
43
47
  Rules:
44
48
 
45
- - Route all ARIA attributes through `a11y`; do not add individual `ariaLabel`,
46
- `role`, or similar props.
49
+ - Follow `ACCESSIBILITY.md`. Expose only `ariaLabel`, for a control without a
50
+ visible name, through the typed `a11y` contract. Keep semantics and state in
51
+ code. Do not add one-off ARIA props or spread the whole object.
47
52
  - Expose only content and behavior that the site owner controls. Keep derived
48
53
  values internal.
49
- - Add only callbacks required by the component specification. Use supported SDK
50
- event names and types for public callbacks; keep implementation-only handlers
51
- internal.
54
+ - Default to common optional SDK callbacks by capability; add specialized ones
55
+ when requested. Keep implementation handlers internal.
52
56
  - Use `Array<T>`, not `T[]`, for exported arrays.
53
57
 
54
58
  ## Numeric Range Constraints
@@ -175,7 +179,16 @@ export const defaultProps = {
175
179
  ### Wix Data Types
176
180
 
177
181
  Use `Image`, `Link`, `Video`, `Audio`, `VectorArt`, `RichText` from
178
- `@wix/editor-react-types`. See `node_modules/@wix/react-component-schema/dist/editor-react-types.d.ts` for the full list.
182
+ `@wix/editor-react-types`. Model authored media with the corresponding Wix
183
+ media type, whether it is a top-level prop or a field in an array item. Name
184
+ the field for the media itself (`image`, `video`, `audio`, and so on), not for
185
+ one representation of it.
186
+
187
+ Do not represent media as a URL/source string or split its metadata across
188
+ primitive props. Preserve the media object through the public contract so the
189
+ component can consume all of its supported data. See
190
+ `node_modules/@wix/react-component-schema/dist/editor-react-types.d.ts` for the
191
+ full list.
179
192
 
180
193
  ## Defaults and Resources
181
194
 
@@ -241,5 +254,6 @@ Do not extract tiny fragments merely to satisfy a line-count threshold.
241
254
  - [ ] Named inner parts have `elementProps` wiring; leaf components avoid exported `children`.
242
255
  - [ ] One-body-visible arrays use the active-item contract and render all bodies.
243
256
  - [ ] Array elements are objects with semantic named fields. No separate `id` field added to item types; React keys use item fields (stable unique → slug → index), not a typed `id`.
257
+ - [ ] Authored media uses the corresponding Wix media type, not URL/source strings or flattened metadata.
244
258
  - [ ] Defaults live only in the props file (no JSX fallbacks).
245
259
  - [ ] Resources are Wix-hosted, prop-supplied, or locally bundled.
@@ -195,6 +195,12 @@ and do not expose a visual prop solely for that purpose.
195
195
  }
196
196
  ```
197
197
 
198
+ The same override risk applies to paintable properties. A layout-only inner
199
+ wrapper must stay visually transparent; if it isn't, promote it to its own
200
+ named part instead of hardcoding an opaque style on an unexposed node, since
201
+ that style would otherwise mask the parent part's own
202
+ `background`/`border`/`box-shadow`.
203
+
198
204
  ## Interaction and Motion
199
205
 
200
206
  - Set `pointer-events: auto` on the root and each interactive element.
@@ -254,3 +260,6 @@ contract; the contrast relationship is.
254
260
  - [ ] Visual containers have deliberate hierarchy rather than bare scaffolding.
255
261
  - [ ] Every explicit foreground has a known contrasting background; transparent
256
262
  components inherit foregrounds from their host.
263
+ - [ ] No unexposed descendant inside a named part paints its own opaque
264
+ `background`/`border`/`box-shadow`; a wrapper needing its own surface is
265
+ promoted to a named part instead.
@@ -1,57 +1,92 @@
1
1
  # Function Handlers
2
2
 
3
- Use this reference only when the component specification exposes callbacks to
4
- site owners. Keep implementation-only handlers out of the public props type.
3
+ When creating a component or changing interactions, expose common optional
4
+ callbacks by capability, without requiring an explicit request.
5
+
6
+ ## Default Support
7
+
8
+ Apply defaults to the public interaction surface:
9
+
10
+ | Component capability | Default optional props |
11
+ | --- | --- |
12
+ | Pointer/click interaction, including native controls | `onClick`, `onMouseIn`, `onMouseOut` |
13
+ | Focusable control | `onFocus`, `onBlur` |
14
+ | Editable value, text, or selection | `onChange` |
15
+
16
+ Combine matching rows; leave decorative elements alone. Explicit API restrictions
17
+ override defaults. Specialized callbacks (`onDblClick`, `onPlay`, `onEnded`,
18
+ `onActivate`, etc.) require a request.
19
+
20
+ Classify composite controls by their interactive descendants. For example, a
21
+ radio-based rating input matches all three rows and exposes all six callbacks.
5
22
 
6
23
  ## Standard SDK Handlers
7
24
 
8
25
  Use the exact SDK prop name and wire it to the matching React DOM event.
9
26
 
10
- | SDK prop | React DOM prop | Type |
11
- | --- | --- | --- |
12
- | `onClick` | `onClick` | `(event: React.MouseEvent) => void` |
13
- | `onDblClick` | `onDoubleClick` | `(event: React.MouseEvent) => void` |
14
- | `onChange` | `onChange` | `(event: React.ChangeEvent<HTMLElement>) => void` |
15
- | `onFocus` | `onFocus` | `(event: React.FocusEvent) => void` |
16
- | `onBlur` | `onBlur` | `(event: React.FocusEvent) => void` |
17
- | `onMouseIn` | `onMouseEnter` | `(event: React.MouseEvent) => void` |
18
- | `onMouseOut` | `onMouseLeave` | `(event: React.MouseEvent) => void` |
19
-
20
- Declare only the handlers the component actually exposes.
27
+ | SDK prop | React DOM prop | Type | Zero config `domEvent` |
28
+ | --- | --- | --- | --- |
29
+ | `onClick` | `onClick` | `(event: React.MouseEvent) => void` | `POINTER` |
30
+ | `onDblClick` | `onDoubleClick` | `(event: React.MouseEvent) => void` | `POINTER` |
31
+ | `onChange` | `onChange` | `(event: React.ChangeEvent<HTMLElement>) => void` | `CHANGE` |
32
+ | `onFocus` | `onFocus` | `(event: React.FocusEvent) => void` | `FOCUS` |
33
+ | `onBlur` | `onBlur` | `(event: React.FocusEvent) => void` | `FOCUS` |
34
+ | `onMouseIn` | `onMouseEnter` | `(event: React.MouseEvent) => void` | `POINTER` |
35
+ | `onMouseOut` | `onMouseLeave` | `(event: React.MouseEvent) => void` | `POINTER` |
36
+
37
+ React `onChange` fires on live text edits and selection changes. Pass that
38
+ `ChangeEvent` directly. Do not synthesize a commit callback from blur or cast a
39
+ `FocusEvent` to `ChangeEvent`.
40
+
41
+ Callbacks only notify; they never enable controlled mode. Internal handlers run
42
+ component logic, then call the optional callback once, preserving event payloads
43
+ and disabled/read-only guards. Behavior must work without callbacks or consumer
44
+ prop updates. Forward directly when no internal logic is needed. Add controlled
45
+ state only when requested; preserve existing controlled APIs when editing.
21
46
 
22
47
  ```tsx
48
+ import * as React from 'react';
49
+
23
50
  export type ActionProps = {
24
51
  onClick?: (event: React.MouseEvent) => void;
25
- onDblClick?: (event: React.MouseEvent) => void;
26
52
  onMouseIn?: (event: React.MouseEvent) => void;
27
53
  onMouseOut?: (event: React.MouseEvent) => void;
54
+ onFocus?: (event: React.FocusEvent) => void;
55
+ onBlur?: (event: React.FocusEvent) => void;
28
56
  };
29
57
 
30
58
  export const Action: React.FC<ActionProps> = (props) => {
31
- const { onClick, onDblClick, onMouseIn, onMouseOut } = props;
59
+ const { onClick, onMouseIn, onMouseOut, onFocus, onBlur } = props;
60
+ const [count, setCount] = React.useState(0);
61
+
62
+ const handleClick = (event: React.MouseEvent) => {
63
+ setCount((value) => value + 1);
64
+ onClick?.(event);
65
+ };
32
66
 
33
67
  return (
34
68
  <button
35
69
  type="button"
36
- onClick={onClick}
37
- onDoubleClick={onDblClick}
70
+ onClick={handleClick}
38
71
  onMouseEnter={onMouseIn}
39
72
  onMouseLeave={onMouseOut}
40
- />
73
+ onFocus={onFocus}
74
+ onBlur={onBlur}
75
+ >
76
+ Clicked {count} times
77
+ </button>
41
78
  );
42
79
  };
43
80
  ```
44
81
 
45
- Do not expose DOM names such as `onDoubleClick`, `onMouseEnter`, or
46
- `onMouseLeave` in the component API; zero config recognizes the SDK names.
82
+ Zero config requires SDK names, not `onDoubleClick`, `onMouseEnter`, or
83
+ `onMouseLeave`, in public props.
47
84
 
48
85
  ## Custom Callbacks
49
86
 
50
- A component-level notification that is not one of the standard SDK DOM events
51
- uses `() => void` unless the component contract explicitly defines a serializable
52
- payload. Do not expose a React synthetic event for custom callbacks.
53
-
54
- Use this pattern:
87
+ Custom notifications use `() => void`; zero config emits no event parameter for
88
+ unrecognized callback names. Custom callbacks must not declare React event
89
+ parameters. Use a standard SDK handler above when the event is public:
55
90
 
56
91
  ```tsx
57
92
  export type VideoPlayerProps = {
@@ -62,14 +97,19 @@ export type VideoPlayerProps = {
62
97
 
63
98
  export const VideoPlayer: React.FC<VideoPlayerProps> = (props) => {
64
99
  const { onPlay, onPause, onEnded } = props;
65
- return <video onPlay={onPlay} onPause={onPause} onEnded={onEnded} />;
100
+ return (
101
+ <video
102
+ controls
103
+ onPlay={() => onPlay?.()}
104
+ onPause={() => onPause?.()}
105
+ onEnded={() => onEnded?.()}
106
+ />
107
+ );
66
108
  };
67
109
  ```
68
110
 
69
111
  ## Checklist
70
112
 
71
- - [ ] The specification explicitly requires each public callback.
72
- - [ ] Standard handler names match the SDK contract exactly.
73
- - [ ] Mismatched SDK/DOM names are mapped at the rendered element.
74
- - [ ] Custom callbacks do not leak React event objects.
75
- - [ ] Internal handlers remain implementation details.
113
+ - [ ] Defaults match capabilities; specialized callbacks are requested.
114
+ - [ ] SDK names, DOM mappings, and payloads match the contract.
115
+ - [ ] Internal behavior works independently and notifies callbacks once.
@@ -24,14 +24,17 @@ would plausibly control independently in the editor. It receives:
24
24
 
25
25
  - one prefixed global class: `'<component-name>-<part-name>'`
26
26
  - one CSS Module class
27
- - one matching `elementProps` entry, spread onto the element
27
+ - one matching `elementProps` entry, spread onto the element. Declare `a11y` on
28
+ the entry only when the part reads `ariaLabel`, and destructure it out before
29
+ spreading (see `ACCESSIBILITY.md`)
28
30
 
29
31
  Apply this filter to every candidate:
30
32
 
31
33
  - A state or variant (`selected`, `active`, `open`, `disabled`) is not a part.
32
34
  Implement it as a prefixed design-state modifier on the affected part.
33
35
  - A hidden and shown version of the same element is one part, not two.
34
- - A grouping or layout-only wrapper is not a part.
36
+ - A grouping or layout-only wrapper is not a part. A wrapper that isn't
37
+ visually transparent is no longer layout-only — promote it to a named part.
35
38
  - A static child whose styling and data are fully owned by its parent is not a
36
39
  part. For example, an image whose source and appearance both belong to its
37
40
  carousel slide can remain module-class-only.
@@ -12,8 +12,7 @@ contracts changed by the request:
12
12
  2. Separate user-authored content and stable behavior from derived values and
13
13
  visual styling.
14
14
  3. Identify interaction and selectable states affected by the request.
15
- 4. Keep implementation behavior internal unless the component specification
16
- requests a public callback.
15
+ 4. Include common callbacks by capability; keep implementation handlers internal.
17
16
  5. Identify runtime concerns such as browser APIs, autoplay, editor-only
18
17
  behavior, or live site context before implementation.
19
18
 
@@ -49,8 +48,8 @@ export const ProfileCard: React.FC<ProfileCardProps> = (props) => {
49
48
  - When local state intentionally mirrors a prop, synchronize it when that prop
50
49
  changes. Do not use effect-managed state for a value that can be derived
51
50
  during render.
52
- - Keep implementation-only handlers internal. Expose a callback only when the
53
- specification makes it part of the component API.
51
+ - Internal handlers run component behavior, then notify optional callbacks.
52
+ Callback presence never enables controlled mode.
54
53
 
55
54
  ### Structure and Parts
56
55
 
@@ -86,4 +85,4 @@ export const ProfileCard: React.FC<ProfileCardProps> = (props) => {
86
85
  - [ ] Direction and accessibility contracts are wired to the correct elements.
87
86
  - [ ] Public handlers use supported SDK names; internal handlers stay internal.
88
87
  - [ ] CSS changed by the request keeps editable selectors flat and scoped.
89
- - [ ] The accessibility review runs after JSX edits.
88
+ - [ ] The accessibility review command passes, or its remaining findings are triaged.