Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions .claude/skills/convert-interact-pattern/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
---
name: convert-interact-pattern
description: Convert a standalone HTML/CSS/JS @wix/interact animation demo into a structured prose pattern guideline — selector roles, required styles, adaptation notes, a selector contract, suggested controls, and an interact template. Handles both generic and Wix compositions. Returns the guideline inline in chat or writes it to a markdown file. Use when turning an interact demo into a reusable, adaptable animation pattern spec.
---

# Convert Interact Pattern

Turn a standalone `@wix/interact` animation demo (HTML + CSS + JS) into a **plain-prose guideline** that
describes the animation as a reusable, adaptable pattern: which DOM roles matter, which styles are
structural, how to adapt it to a real composition, and the interact config to reproduce it.

This skill is self-contained. It does not depend on any particular repository or build step.

## Output

When you finish, deliver the guideline in whichever way fits the request:

- **Inline in chat** — print the full markdown as your answer (default when the user just wants the result).
- **As a file** — write it to `<pattern-id>.guideline.md` in the current working directory, or to a path the
user specifies.

If the user hasn't said which, ask briefly or default to inline. Do not assume any repo layout or run any
project build/typecheck commands.

## Output Contract

Read the bundled exemplar **`reference/example.guideline.md`** (in this skill's folder) and match its
structure exactly. The guideline MUST have, in this order:

1. `# <Name>` — the H1 display name.
2. A one-line tagline.
3. `## Summary` with these bullets:
- `` - **ID:** `kebab-case-id` `` — a stable id, backtick-wrapped.
- `- **Target shape:** <one sentence>` — short structural fit guidance (what kind of section it suits).
- `- **Description:** <one or two sentences>`
4. `## Demo HTML` — a fenced `html` block showing structure only.
5. `## Selector Contract` — numbered hard selector rules and invalid adaptations.
6. `## Role Guidance` — a `| Role | Guidance |` table of short role-level mapping signals.
7. `## Adaptation Notes` — a numbered list of formulas, edge cases, and force-fit behavior.
8. `## Required Elements` — a `| Key | Role | Demo Selector | Purpose |` table. Choose one of two patterns based on how items are structured in the demo:
- **Fixed items** (`card1`, `card2`, …) — use when the item count is known at authoring time and each element gets its own `data-interact-key`. See `reference/example-fixed.guideline.md`.
- **List container** (`listContainer`) — use when items are dynamic or grabbed at runtime from a parent. A single `listContainer` key points to the parent; a `customEffect` iterates over its children. See `reference/example-list.guideline.md`.
9. `## Required Styles` — one `### \`role\` — \`selector\`` block per style, each with a fenced `css`
block (real kebab-case CSS) and a `Reason:` line.
10. `## Suggested Controls` — see below; always present, never empty.
11. `## Interact Template` — the config as fenced `ts` blocks (helper functions + the interaction),
using the required-element keys, not raw demo selectors.

Keep the `# ` H1, the `**ID:**` line, and the `**Target shape:**` line in exactly that format — they form
the machine-readable summary a catalog can parse out of the file.

## Workflow

1. **Determine target platform.** Ask the user whether the target composition is a Wix site, or infer
from context if it's already clear. This controls whether **Wix DOM Mapping** applies throughout the
rest of the steps.
2. Read the demo HTML, CSS, and JS, and read the bundled `reference/example.guideline.md` for format.
3. Identify the animation mechanism, not the decoration: scroll source, sticky or moving stages, repeated items, transforms, ranges, and any dynamic formulas.
4. Convert demo selectors into reusable roles. Prefer roles like `scrollSource`, `stickyStage`, `stickyFrame`, `collection`, `stackList`, `horizontalTrack`, and `repeatedCard`. If Wix, also note the corresponding Wix selector for each role (see **Wix DOM Mapping** below).
5. Write `## Selector Contract` before `## Role Guidance` for non-negotiable mapping rules and known invalid adaptations. If Wix, include Wix-specific selector constraints inline.
6. Keep `## Required Elements` to the minimum viable pattern. For repeated items, choose the right pattern:
- **Fixed items**: use when count is known. Require three items minimum; keys MUST end in a digit (`card1`, `card2`, …) so they compact into `card{n}`; instruct adaptation to extend `card4..cardN`; add a repeated-card-keys note after the table.
- **List container**: use when items are dynamic or the count is not fixed. A single `listContainer` key points to the parent; document how the `customEffect` selects children.
If Wix, add Wix selector guidance in the Purpose column.
7. Put structural CSS in `## Required Styles`; keep visual styling out unless it is required for animation correctness.
8. Convert imperative demo JS into a static `## Interact Template` with helper functions. Use illustrative values and tell the agent which values must be recomputed.
9. Always author `## Suggested Controls` (see rules below).
10. Keep the guidance lean. Prefer 3-5 high-signal bullets per section and do not restate the same constraint across Selector Contract, Role Guidance, and Adaptation Notes.
11. Output the finished guideline (inline or as a file, per **Output** above).

## Wix DOM Mapping

*Apply this section only when the target is a Wix composition.*

Translate generic roles to Wix selectors using these rules:

- **`scrollSource`** — the outermost section comp: `#comp-<id>` (the element whose scroll drives the `viewProgress` trigger).
- **`stickyStage`** — internal-container-root: `#comp-<id>` with `data-testid="internal-container-root"`. This is the sticky outer wrapper, not its content child. Use the rendered `#comp-...` id, not `DESKTOP--...`.
- **`collection`** — internal-container-content: `#<stickyStageCompId> [data-testid="internal-container-content"]`. This must always be a child of `stickyStage` and must resolve to a different element.
- **Repeated items** — use rendered `#comp-<id>` ids. Never use `DESKTOP--...` ids; those are editor-internal and absent from the live DOM.

Before generating the guideline, confirm in the DOM that `stickyStage` and `collection` resolve to different elements. If they collapse to the same selector, reject the pattern (see Extraction Rules).

## Suggested Controls

Every guideline must name **1-3 suggested controls** — the pattern's core user-facing knobs
(spread, spacing, intensity, scroll distance, perspective, scale, tilt, offset, duration), chosen
for quality over quantity. Describe each knob *conceptually* — what it changes and a sensible range.
How a knob becomes a valid binding is the generator's job, not this guideline's; do not specify
binding targets, transforms, or the interact binding model here. Author a one-line intro, then
one `### <control-id>` block per control with these bullets:

- `**Label:** <Title Case>`
- `**Group:** <Layout | Motion | ...>`
- `**Type:** range | select | color | toggle | text`
- `**Default:** <value>`
- `**Description:** <what the knob changes>`
- `**Constraints:** min / max / step / unit` (as applicable)
- `**Suggested variable:** <--kebab-name>` (optional) — a CSS custom property the pattern's styles or
keyframes could read, offered as a hint for the generator. This is a naming suggestion, not a binding.

Pick knobs that are stable and safe to expose; avoid incidental demo numbers that would break the
pattern when changed.

## Extraction Rules

- Preserve role ownership. Scroll distance belongs to the scroll source; sticky pinning belongs to a shared frame/stage; collection sizing belongs to the collection/track; item transforms belong to repeated item roots.
- Treat literal demo numbers as examples. Translate them into formulas or adaptation notes when they depend on viewport, item count, card size, gap, or layout.
- Keep DOM roles distinct when the pattern depends on them. If a pattern needs `stickyStage` and `collection`, their selectors must not collapse to the same element.
- Use specific selectors for repeated items. Avoid broad selectors like `.image` unless the section truly has no better mapping.
- Target repeated item roots, not raw `img` descendants, unless the animation specifically operates on image content inside a stable card root.
- For repeated items, prefer formula-driven guidance over serializing one near-identical style entry per card when the placement can be derived from item index and count.
- Use `overflow: clip` for viewProgress clipping roles. Avoid `overflow: hidden` on viewProgress ancestors.
- Include forced-fit guidance only when CSS can safely create the missing local structure without moving DOM nodes.
- State invalid adaptations explicitly, but once is enough. The agent needs crisp negative examples, not repeated warnings.
- **Reject and report** when a pattern's structural invariant cannot be satisfied in the target composition (e.g., `stickyStage` and `collection` must remain distinct selectors). Do not produce a guideline for a broken mapping — tell the user which role collapsed and why.

## Review Checklist

- The guideline has all eleven sections and the parseable summary block (H1, `**ID:**`, `**Target shape:**`).
- Every Interact Template effect key appears in `## Required Elements`.
- Every structural role used in `## Required Styles` appears in `## Role Guidance`.
- Repeated keys end in a digit and a repeated-card-keys note is present; item-count changes are explained.
- Dynamic distances and ranges say how to recompute them.
- `## Suggested Controls` has 1-3 controls, each with a label, range, and what it changes (no binding spec).
- The guideline warns against the most likely broken mapping.
- *(Wix only)* Wix selector patterns (`#comp-...`, `data-testid`) are present in the Selector Contract and Required Elements Purpose column.
- The result is delivered inline or written to the requested file.
4 changes: 4 additions & 0 deletions .claude/skills/convert-interact-pattern/agents/openai.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
interface:
display_name: "Convert Interact Pattern"
short_description: "Convert HTML demos to reusable animation pattern guidelines."
default_prompt: "Convert this full HTML @wix/interact animation demo into a prose pattern guideline with selector roles, adaptation rules, required styles, suggested controls, and an interact template. Ask whether the target is a Wix composition to apply Wix DOM mapping. Return it inline or as a markdown file."
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
# Card Spread

Stacked cards fan out horizontally on scroll.

## Summary

- **ID:** `card-spread`
- **Target shape:** Best for 3–7 similarly sized sibling items inside a single sticky stage, including mixed-content wrappers where the cards can share one overlapped grid row.
- **Description:** Five cards stacked at the center of the viewport fan out left/right and shrink slightly as the section scrolls past.

## Demo HTML

```html
<section class="scroll-section">
<div class="cards-container-wrapper">
<div id="cards-collection">
<h2 class="static-title">Title</h2>
<div id="card-1" class="card">1</div>
<div id="card-2" class="card">2</div>
<div id="card-3" class="card">3</div>
<div id="card-4" class="card">4</div>
<div id="card-5" class="card">5</div>
</div>
</div>
</section>
```

## Selector Contract

1. Role ownership is strict: `scrollSection` owns the timeline, `stickyStage` owns sticky/clipping, `collection` owns the centered inner stage, and `repeatedCard` owns the overlapped card-stage layout plus spread transform.
2. `stickyStage` and `collection` must be different selectors. In Wix, `stickyStage` is the internal-container-root `#comp-...` with `data-testid="internal-container-root"` and `collection` is its `[data-testid="internal-container-content"]` child.
3. If `collection` also contains non-repeated siblings such as titles or copy, keep `collection` as a grid and overlap only the repeated cards in a shared card stage row. Do not convert the whole mixed wrapper to flex.
4. Keep card-spread layout styles on the repeated card roots, not on raw `img` descendants or broad selectors when concrete card component ids exist.
5. Repeated cards share one overlapped stage inside the collection, not sticky items. Use rendered `#comp-...` ids, not `DESKTOP--...` ids.

## Role Guidance

| Role | Guidance |
| --- | --- |
| `scrollSource` | The tall section that drives the `viewProgress` trigger. |
| `stickyStage` | A sticky viewport-height wrapper that keeps the cards pinned during scroll. |
| `collection` | The grid layout owner that can keep static siblings in flow while repeated cards share one overlapped card stage. |
| `repeatedCard` | Repeated sibling items that share one overlapped grid cell and then spread horizontally. |

## Adaptation Notes

1. Preserve the section root outer layout; the sticky stage and centered collection are inner roles, not section-root roles.
2. Use viewport units only for the outer timeline container and sticky stage. Size cards relative to the collection stage so their proportions stay close to the source composition.
3. If `collection` contains a title or other static siblings, leave them in their own normal grid row and place only the repeated cards into a shared lower grid row so the non-animated content stays untouched.
4. Animate spread with `translateX(...) scale(...)` on the card roots instead of resizing card height unless the real section truly depends on viewport-sized cards.
5. When item count changes, recompute width, spacing, and outer translation distances instead of copying the demo offsets literally.
6. If the outer cards land mostly outside the visible stage, reduce card width or spread distance before returning the result.

## Required Elements

| Key | Role | Demo Selector | Purpose |
| --- | --- | --- | --- |
| `scrollSection` | `scrollSource` | `.scroll-section` | The `viewProgress` source for the entire pattern. |
| `stickyStage` | `stickyStage` | `.cards-container-wrapper` | Sticky pin only: `position: sticky`, `100vh`, `overflow: clip`. Wix: `#comp-...` with `data-testid="internal-container-root"` — not the collection. |
| `collection` | `collection` | `#cards-collection` | Centered mixed-content stage for the spread. Wix: `#<stickyStageCompId> [data-testid="internal-container-content"]` — must differ from `stickyStage`. |
| `card1` | `repeatedCard` | `.scroll-section #card-1` | Minimum repeated spread card; extend outward for `card4..cardN`. |
| `card2` | `repeatedCard` | `.scroll-section #card-2` | Repeated spread card. |
| `card3` | `repeatedCard` | `.scroll-section #card-3` | Repeated spread card. |
| `card4` | `repeatedCard` | `.scroll-section #card-4` | Repeated spread card. |
| `card5` | `repeatedCard` | `.scroll-section #card-5` | Repeated spread card. |

> Repeated card keys must keep their trailing index (`card1`, `card2`, …) so they compact into the `card{n}` group; extend the row as `card4..cardN` for more items.

## Required Styles

### `scrollSource` — `.scroll-section`

```css
.scroll-section {
height: 400vh;
}
```

Reason: creates enough scroll distance for the full `viewProgress` spread to play out.

### `stickyStage` — `.cards-container-wrapper`

```css
.cards-container-wrapper {
position: sticky;
top: 0;
height: 100vh;
overflow: clip;
}
```

Reason: pins the stage to the viewport and clips the spreading cards while the source section scrolls.

### `collection` — `#cards-collection`

```css
#cards-collection {
position: relative;
display: grid;
grid-template-columns: 1fr;
grid-template-rows: auto 1fr;
width: 100%;
height: 100vh;
margin: 0 auto;
justify-items: center;
}
```

Reason: creates a mixed-content grid stage so static siblings stay in flow while repeated cards overlap in a shared card row. The collection owns the composition space; child card percentages resolve against this stage.

### `repeatedCard` — `#cards-collection > .card`

```css
#cards-collection > .card {
grid-column: 1;
grid-row: 2;
place-self: start center;
width: 20vw;
height: 55%;
transform-origin: center center;
will-change: transform;
}
```

Reason: overlaps repeated cards in one shared grid cell with top alignment and centered placement before the animation distributes them, preserving their proportion relative to the collection stage.

### `repeatedCard` — `.card`

```css
.card {
margin: 0;
}
```

Reason: prevents repeated cards from drifting apart because of default spacing.

## Suggested Controls

Always expose at least the spread distance and ending scale; add more only when the adapted experience introduces new stable knobs.

### `spread`

- **Label:** `Spread`
- **Group:** `Layout`
- **Type:** `range`
- **Default:** `40`
- **Description:** Controls how far the outer cards fan out from the center stage at the end of the scroll range.
- **Constraints:** `min: 20`, `max: 50`, `step: 2`, `unit: vw`
- **Suggested variable:** `--card-spread-unit`

### `end-scale`

- **Label:** `Card Scale`
- **Group:** `Layout`
- **Type:** `range`
- **Default:** `0.85`
- **Description:** Controls the ending scale of the cards at maximum spread.
- **Constraints:** `min: 0.7`, `max: 1`, `step: 0.01`, `unit: x`
- **Suggested variable:** `--card-end-scale`

## Interact Template

```ts
const RANGE = {
rangeStart: { name: 'cover', offset: { unit: 'percentage', value: 20 } },
rangeEnd: { name: 'cover', offset: { unit: 'percentage', value: 80 } },
easing: 'cubic-bezier(0.42, 0, 0.58, 1)',
fill: 'both' as const,
};

// Demo offsets for five cards — recompute from real item count and spread.
const SPREAD_TRANSLATIONS = ['-40vw', '-20vw', '0', '20vw', '40vw'] as const;

// Combined per-card effect: translateX + scale shrink in a single keyframe pair.
const cardSpreadEffect = (key: string, endTranslate: string) => ({
key,
keyframeEffect: {
name: `${key}-spread`,
keyframes: [
{ transform: 'translateX(0) scale(1)' },
{ transform: `${endTranslate} scale(0.85)` },
],
},
...RANGE,
});

const interaction = {
key: 'scrollSection',
trigger: 'viewProgress',
effects: SPREAD_TRANSLATIONS.map((translate, index) =>
cardSpreadEffect(`card${index + 1}`, `translateX(${translate})`),
),
};
```
Loading