@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.
- package/package.json +1 -1
- package/skills/wix-app/SKILL.md +33 -31
- package/skills/wix-app/references/DASHBOARD_PAGE.md +6 -10
- package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +17 -8
- package/skills/wix-app/references/WIX_PATTERNS_DOCS.md +53 -131
- package/skills/wix-app/references/dashboard-page/UX_SUCCESS_MODEL.md +1 -2
- package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +88 -73
- package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +46 -22
- package/skills/wix-app/references/editor-react-component/COMPONENT-CONTRACT.md +20 -6
- package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +9 -0
- package/skills/wix-app/references/editor-react-component/FUNCTION-HANDLERS.md +71 -31
- package/skills/wix-app/references/editor-react-component/PARTS.md +5 -2
- package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +4 -5
- package/skills/wix-app/scripts/pkg-root.cjs +73 -0
- package/skills/wix-app/scripts/scan-a11y-code.cjs +310 -40
- package/skills/wix-app/scripts/scan-a11y-eslint.cjs +118 -24
- package/skills/wix-app/scripts/scan-a11y-render.cjs +551 -0
- package/skills/wix-app/scripts/scan-a11y-review.cjs +373 -0
- package/skills/wix-base44-connector/SKILL.md +67 -43
- package/skills/wix-base44-connector/scripts/utils.cjs +39 -3
- package/skills/wix-docs/SKILL.md +47 -20
- package/skills/wix-headless/SKILL.md +1 -0
- package/skills/wix-headless/entry/bootstrap.mjs +106 -50
- package/skills/wix-headless/entry/skill.md +5 -3
- package/skills/wix-headless/references/CAPABILITIES.md +7 -2
- package/skills/wix-headless/references/SDK_HANDOFF.md +2 -1
- package/skills/wix-headless/references/inline-recipes/experience-store.md +48 -0
- package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +350 -114
- package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +1 -1
- package/skills/wix-headless/references/managed/AUTHENTICATION.md +25 -5
- package/skills/wix-headless/references/managed/CREATE.md +5 -0
- package/skills/wix-headless-fast/SKILL.md +25 -7
- package/skills/wix-headless-fast/entry/skill.md +23 -14
- package/skills/wix-headless-fast/install/fast-path.mjs +48 -5
- package/skills/wix-headless-fast/install/pin-agents-md.mjs +57 -0
- package/skills/wix-headless-fast/references/shared/CUSTOM_OPERATIONS.md +3 -3
- package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +204 -35
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +146 -36
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-store.ts +8 -2
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +95 -20
- package/skills/wix-headless-fast/references/storefront/seed/SEED.md +21 -2
- package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +104 -18
- package/skills/wix-manage/SKILL.md +16 -10
- package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +4 -1
- package/skills/wix-manage/references/bookings/create-appointment-service.md +3 -1
- package/skills/wix-manage/references/bookings/create-class-service.md +3 -1
- package/skills/wix-manage/references/bookings/create-course-service.md +4 -2
- package/skills/wix-manage/references/bookings/end-to-end-booking-flow.md +68 -3
- package/skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-flow-seasonal-promotion.md +1 -1
- package/skills/wix-manage/references/google-ads/billing-and-payment.md +3 -3
- package/skills/wix-manage/references/google-ads/create-performance-max-campaign.md +8 -8
- package/skills/wix-manage/references/google-ads/get-campaign-suggestions.md +4 -4
- package/skills/wix-manage/references/google-ads/google-ads-dashboard-navigation.md +1 -1
- package/skills/wix-manage/references/google-ads/install-and-create-account.md +9 -9
- package/skills/wix-manage/references/google-ads/manage-campaign-success-guide.md +108 -14
- package/skills/wix-manage/references/google-ads/query-campaign-analytics.md +7 -7
- package/skills/wix-manage/references/seo/manage-content-plan.md +182 -136
- package/skills/wix-manage/references/seo/manage-google-search-console.md +272 -0
- package/skills/wix-manage/references/sites/upload-static-site.md +283 -0
- package/skills/wix-manage/references/stores/change-store-currency.md +1 -1
- package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed-with-install.md +3 -1
- package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed.md +3 -1
- package/skills/wix-vibe-headless/platforms/base44-ecom-light.md +1 -1
- package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +69 -27
- package/skills/wix-vibe-headless/references/storefront/app/components/CartDrawer.jsx +32 -14
- package/skills/wix-vibe-headless/references/storefront/app/context/CartContext.jsx +14 -3
- package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductCard.js +41 -35
- package/skills/wix-vibe-headless/references/storefront/app/hooks/useProductDetail.js +77 -30
- package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-cart.js +31 -2
- package/skills/wix-vibe-headless/references/storefront/app/rest/wix-store-catalog.js +31 -6
- package/skills/wix-app/references/dashboard-page/COLLECTION_TOOLKIT.md +0 -80
- package/skills/wix-app/references/dashboard-page/ENTITY_PAGE_TOOLKIT.md +0 -140
- package/skills/wix-app/references/dashboard-page/PATTERNS_BUNDLE_READING.md +0 -112
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductCard.tsx +0 -100
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductDetailView.tsx +0 -130
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductGrid.tsx +0 -48
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/ShopView.tsx +0 -66
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/VariantPicker.tsx +0 -103
- package/skills/wix-headless-fast/references/storefront/app-astro/pages/products/[slug].astro +0 -56
- 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
|
|
4
|
-
|
|
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
|
|
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
|
-
###
|
|
18
|
+
### Own Accessibility per Part
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
`
|
|
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
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
|
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 |
|
|
100
|
+
| Request | Pass to the command |
|
|
86
101
|
| --- | --- |
|
|
87
|
-
| Specific file | That file
|
|
88
|
-
| A component name or "this component" |
|
|
89
|
-
| Full audit |
|
|
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
|
-
|
|
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
|
|
112
|
+
## Automated Review
|
|
98
113
|
|
|
99
114
|
`<SKILL_ROOT>` is the absolute directory containing the active `SKILL.md`. Run
|
|
100
|
-
|
|
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-
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
-
|
|
169
|
-
|
|
170
|
-
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
-
-
|
|
215
|
-
|
|
216
|
-
- confirmed issues are fixed when safe;
|
|
217
|
-
- the
|
|
218
|
-
-
|
|
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** —
|
|
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`** —
|
|
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`
|
|
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 [
|
|
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
|
-
|
|
102
|
-
// (visitor may have manually paused — don't restart against their will)
|
|
112
|
+
const skipInitialAutoPlaySync = React.useRef(true);
|
|
103
113
|
React.useEffect(() => {
|
|
104
|
-
if (
|
|
105
|
-
|
|
114
|
+
if (skipInitialAutoPlaySync.current) {
|
|
115
|
+
skipInitialAutoPlaySync.current = false;
|
|
116
|
+
return;
|
|
106
117
|
}
|
|
107
|
-
|
|
118
|
+
setIsPlayOn(autoPlay ?? true);
|
|
119
|
+
}, [autoPlay]);
|
|
108
120
|
|
|
109
|
-
const handlePause = () =>
|
|
110
|
-
const handleResume = () =>
|
|
121
|
+
const handlePause = () => setIsPlayOn(false);
|
|
122
|
+
const handleResume = () => setIsPlayOn(true);
|
|
111
123
|
```
|
|
112
124
|
|
|
113
|
-
|
|
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
|
|
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={
|
|
204
|
+
onClick={isPlayOn ? handlePause : handleResume}
|
|
181
205
|
aria-label={
|
|
182
|
-
|
|
206
|
+
isPlayOn ? ARIA_LABELS.pauseButton : ARIA_LABELS.playButton
|
|
183
207
|
}
|
|
184
208
|
>
|
|
185
|
-
{
|
|
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
|
-
-
|
|
46
|
-
`
|
|
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
|
-
-
|
|
50
|
-
|
|
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`.
|
|
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
|
-
|
|
4
|
-
|
|
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
|
-
|
|
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,
|
|
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={
|
|
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
|
-
|
|
46
|
-
`onMouseLeave
|
|
82
|
+
Zero config requires SDK names, not `onDoubleClick`, `onMouseEnter`, or
|
|
83
|
+
`onMouseLeave`, in public props.
|
|
47
84
|
|
|
48
85
|
## Custom Callbacks
|
|
49
86
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
|
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
|
-
- [ ]
|
|
72
|
-
- [ ]
|
|
73
|
-
- [ ]
|
|
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.
|
|
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
|
-
-
|
|
53
|
-
|
|
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
|
|
88
|
+
- [ ] The accessibility review command passes, or its remaining findings are triaged.
|