All skills
wix avatar

/wix-app

@dc6f1fa official
by Wix.comwix/skills33 stars
33

Build and review Wix CLI app extensions — dashboard pages, modals, plugins, menu plugins, custom element widgets, Editor React components, site plugins, embedded scripts, backend APIs, backend events, service plugins, data collections, and App Market readiness. Use when building ANY feature or extension for a Wix CLI app or preparing a Wix app for App Market review. Triggers on: add, build, create, implement, help me, dashboard, widget, plugin, backend, API, event, collection, embedded script, service plugin, Editor React component, checkout, shipping, tax, discount, SPI, CMS, schema, tracking, popup, admin panel, menu item, modal, validate, test, verify, register extension, App Market, app review, submission readiness.

Use this Skill: https://skilld.dev/gh/wix/skills/wix-app

This session only. Nothing lands on disk.

referenceseditor-react-componentDESIGN-STATES.md

≈2.5k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Design States

Use this reference when a named part changes appearance through interaction or a selectable value. A design state styles an element differently for an eligible interaction (hover, focus, disabled, invalid) or per a selectable value in its data (selected, active, open, …). Author states in the component's .tsx and .module.css.

Contents

1. Choose Supported States

Element Author these states
Interactive — button/a/input/select/textarea/summary, an interactive role, or an interactive handler (onClick/onMouseEnter/onFocus/…) hover
Input field — select/textarea; input except hidden/button/submit/reset/image; or an input-widget role (checkbox/radio/switch/slider/spinbutton/textbox/searchbox/combobox/listbox) focus
Other interactive element whose editable focus appearance the user explicitly requests focus
Disableable — button/input/select/textarea/fieldset or a disableable role disabled (+ invalid for input/select/textarea)
Has a selectable/variant value in its data — selected/active/current/open/expanded/checked/featured that custom state
Whole component opens/collapses (expandable panel, disclosure, dropdown, drawer), even when a click toggles it root isOpen prop state — see §6
None of the above no states — resting style only

An explicit role overrides the tag's implicit semantics: <input role="button"> is a non-input control, while <div role="checkbox"> is an input field.

A focus design state is an editor styling control, not the keyboard focus indicator itself. Do not add a global --focus modifier to a non-input control unless the user explicitly requests editable focus styling. A control that restyles its own outline, background, or border must still carry a standalone :focus-visible rule as its keyboard affordance.

A custom state may also be driven by a root-level boolean prop (e.g. isFeatured) instead of by markup or item data — see §5. For an active-item component, drive each item's --active class by comparing its index with the active-index prop rather than storing a per-item boolean.

2. Name the State Class

Flat: the element's own global class + --<state>. Because inner-part global classes are prefixed with the component name, the state class is prefixed too — e.g. pricing-card-cta--hover, pricing-card-plan-row--selected. Never bare (cta--hover would collide with other components on the page) and never nested (card__row--selected).

3. Author CSS

Put the resting value in the bare class; put only the state override in the state selector. When both set the same property, write the state selector before the bare class. Manifest generation can otherwise record the state value as the part's resting defaultValue, even though runtime CSS renders correctly. The state selector still wins at runtime through its specificity.

  • Native design state — pair the pseudo-class with the :global modifier. Every exposed native state needs both selectors in the same rule; do not split the pair across states. A standalone :focus-visible accessibility rule on a non-input control is not an editor design state and has no global modifier.
  • Custom — the :global modifier alone.

The bare selector is the short module class (.cta); the :global(...) state class is the prefixed global one.

Check text contrast against every background it appears on, including resting, hover, and selected states. Normal text needs at least 4.5:1; large text needs 3:1. Pale gray small text on white or a tinted selected row commonly fails. The local static accessibility scan cannot measure rendered color contrast.

.cta:global(.pricing-card-cta--hover),
.cta:hover {
  background: #4f46e5;
}
.cta {
  background: #6366f1;
  font-family: 'Inter', sans-serif;
  font-size: 14px;
  color: #ffffff;
} /* resting */
.cta:global(.pricing-card-cta--disabled),
.cta:disabled {
  opacity: 0.5;
}
.cta:focus-visible {
  outline: 2px solid currentColor;
} /* keyboard indicator only; not an editor design state */
.plan-row:global(.pricing-card-plan-row--selected) {
  border-color: #6366f1;
}

4. Wire React

  • Native — render the correct interactive element (<button>, an interactive role, or a handler). No custom state class is needed, but the element must still satisfy the accessibility contract for its semantics.
  • Custom — toggle the global state class from the element's data. Every state class toggled in TSX needs a matching :global() rule in §3; without one the manifest generator silently drops the state.
  • Inner elements — every named inner element gets an elementProps entry; spread it so editor-driven states reach it. On a raw HTML element also merge elementProps?.<key>.className inline; on a skill-built sub-component the spread alone suffices (it merges className itself). The elementProps key stays the short part name (cta) even though the element's global class is prefixed (pricing-card-cta) — don't rename the key to match the class.

Wire native and custom states as follows:

<button
  type="button"
  {...elementProps?.cta}
  className={classNames('pricing-card-cta', styles.cta, elementProps?.cta?.className)}
>
  {label}
</button>

<li
  {...elementProps?.planRow}
  className={classNames(
    'pricing-card-plan-row',
    styles.planRow,
    row.selected && 'pricing-card-plan-row--selected',
    elementProps?.planRow?.className,
  )}
>
  {row.label}
</li>

5. Use Prop-Triggered States on the Root Only

A prop-triggered state is a custom state switched by a single boolean prop, rather than by interactive markup (native) or a per-item data flag (class-triggered). Use it only when one component-level boolean should flip the whole component's appearance — e.g. isLoading, isFeatured.

Mark the prop with the ElementState<boolean> type from @wix/react-component-utils. It is an identity alias — the prop still behaves as a plain boolean at runtime and stays a normal boolean in the component's data — but the manifest generator detects the marker and emits a custom state on the root element.

import type { ElementState } from '@wix/react-component-utils';

export type PricingCardProps = {
  // …other props…
  isFeatured?: ElementState<boolean>;
};

Rules:

  • Boolean only, root only. A prop trigger always attaches to the component root — never to an inner element. Inner-element states must be native or class-triggered (§1–§4).
  • The state name is the prop name in kebab-case (isFeatured → is-featured; no is/has stripping). The manifest records props: { isFeatured: true } as the trigger.
  • To give the state styling, pair the root's module class with a prefixed :global(.<component-name>--<state>) rule. With a matching class the manifest entry carries that className and the editor lists it with the design-panel states; with none it is props-only and appears in the on-stage state picker, which sets the prop. An open/collapsed toggle must stay props-only (§6).
.pricingCard:global(.pricing-card--is-featured) {
  border-color: #f5a623;
}

Prefer a native state (interactive markup) or a class trigger (per-item data such as row.selected) whenever one fits — those are the common cases and work at any depth. Reach for a prop trigger only for a root-level boolean switch or an open/collapsed toggle (§6).

6. Expose Open/Collapsed Toggles

For a component that opens or collapses, expose its state to the editor even when a click toggles it. useState alone gives the manifest no state picker. For one-of-many item bodies, use the active-item contract instead.

  • Add isOpen?: ElementState<boolean> and set isOpen: false in defaultProps.
  • Seed and resync internal state from the prop; clicks update internal state.
  • Always render the collapsible content; hide it with CSS plus aria-hidden and inert when closed. Never mount it with open && ….
  • Style the open state with inner-part classes (<component-name>-content--open) and matching :global() rules, not only inline styles or refs measured during render.
  • Keep the root state props-only for the on-stage picker (§5).
const [open, setOpen] = React.useState(Boolean(isOpen));
React.useEffect(() => setOpen(Boolean(isOpen)), [isOpen]);

<div
  {...elementProps?.content}
  aria-hidden={!open}
  {...(!open && { inert: '' })}
  className={classNames(
    'info-panel-content',
    styles.content,
    open && 'info-panel-content--open',
    elementProps?.content?.className,
  )}
>
  <p>{text}</p>
</div>
.content { display: none; }
.content:global(.info-panel-content--open) { display: block; }

Checklist

  • Each named part declares only states its semantics or data supports.
  • Eligible native design-state selectors pair the pseudo-class with the prefixed global modifier.
  • Non-input controls retain a standalone :focus-visible keyboard indicator.
  • Custom state classes are flat, global, and prefixed by component and part.
  • Named inner parts spread and merge their elementProps entry.
  • Every state class toggled in TSX has a matching :global() CSS rule.
  • ElementState<boolean> is used only for a component-level root state.
  • An open/collapsed component exposes a props-only isOpen root state, syncs internal state from it, and always renders its content.

Source: SKILL.md on GitHub

1 warningtoday4 checks · Risk SAFE
  • Gen Agent Trust Hubtoday

    This skill is a specialized development toolkit for building extensions on the Wix platform. It provides comprehensive instructions for creating dashboard pages, backend APIs, and site plugins using the Wix CLI and SDKs. No malicious patterns were detected; the skill's behaviors, such as dependency management, command execution for builds, and local script execution for code reviews, are entirely consistent with its purpose as a developer productivity tool for the Wix ecosystem.

  • Sockettoday

    1 alert: gptAnomaly

  • Snyktoday

    Risk: LOW · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at dc6f1fa. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub yesterday.

Activeupdated 2 days ago
compatibility
requires `@wix/cli` >= 1.1.192.

README badge

README badge for wix/skills/wix-app

Builds dashboard pages, modals, plugins, custom widgets, Editor React components, backend APIs, events, service plugins, and data collections for Wix CLI apps. Provides decision logic, API patterns, and validation workflows; scaffolding is owned by the Wix CLI via `wix generate --params`.

Generated from the current SKILL.md.

What extension types does this skill cover?
All Wix CLI app extension types: dashboard pages, modals, plugins, menu plugins, custom element widgets, Editor React components, site plugins, embedded scripts, backend APIs, backend events, service plugins, and data collections.
Does this skill scaffold the extension files for me?
The Wix CLI owns scaffolding via `wix generate --params` for all extension types except Backend API. This skill provides decision logic, API guidance, and business-logic patterns to fill in the generated stubs. Backend API files must be created manually.
What Wix CLI version is required?
The skill requires @wix/cli >= 1.1.192.
Do I need to create a Data Collection extension for app-specific data?
Yes, if you're saving or persisting app-specific data, managing domain entities in a dashboard, or running a service plugin that reads app-configured data. The skill infers this automatically—you don't need to explicitly request it.
Does this skill cover Wix Stores API usage?
Yes. When using any Wix Stores API (products, inventory, orders), the skill requires dual V1/V3 catalog support and references the Stores Versioning guide for module selection and field mapping.

Generated from the current SKILL.md. These answers refresh after source changes.