diff --git a/.github/workflows/interactdocs.yml b/.github/workflows/interactdocs.yml index 113114e6..1fa9e00e 100644 --- a/.github/workflows/interactdocs.yml +++ b/.github/workflows/interactdocs.yml @@ -64,19 +64,21 @@ jobs: - name: Prepare deployment directory run: | # Create the site structure: - # / -> root index.html and assets - # /docs/ -> docs app + # / -> landing page, examples page + # /assets/ -> landing + examples assets (includes shared/, lib/ from build-landing.sh) + # /docs/ -> docs app # /playground/ -> playground app - # /rules/ -> rules markdown files + # /rules/ -> rules markdown files mkdir -p _site/docs mkdir -p _site/playground mkdir -p _site/rules mkdir -p _site/assets - # Copy root landing page and assets (includes assets/lib/ from build-landing.sh) - cp index.html _site/ - cp -r assets/* _site/assets/ + # Copy website app (landing page, examples page, assets including shared/) + cp apps/website/index.html _site/ + cp apps/website/examples.html _site/ + cp -r apps/website/assets/* _site/assets/ # Copy docs app cp -r apps/docs/dist/* _site/docs/ diff --git a/.gitignore b/.gitignore index b6fa3ad0..30db2d74 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,8 @@ node_modules .env.* dist/ build/ -assets/lib/* -!assets/lib/.gitkeep +apps/website/assets/lib/* +!apps/website/assets/lib/.gitkeep coverage/ .DS_Store .idea/ diff --git a/apps/website/AGENTS.md b/apps/website/AGENTS.md new file mode 100644 index 00000000..1fa097d3 --- /dev/null +++ b/apps/website/AGENTS.md @@ -0,0 +1,148 @@ +# AGENTS.md — Website App (`apps/website`) + +## What this is + +The public-facing website for `@wix/interact`, served as a static site (no client bundler). Contains two HTML entry points: + +- **Landing page** (`index.html`) — hero, performance demos, interaction showcases, and showcase gallery +- **Examples page** (`examples.html`) — live interactive gallery of animation examples built with `@wix/interact` + +Deployed to GitHub Pages via the `interactdocs.yml` workflow (landing + examples + shared assets, alongside docs, playground, and rules). + +## Local development + +```bash +# From the repo root — same as CI: document root is apps/website/ +nvm use +yarn http-server # npx http-server apps/website -p 3000 → http://localhost:3000 + +# Or: workspace dev server (runs from apps/website; same port by default) +yarn dev:website # → http://localhost:3000 +``` + +The landing page is at `/`, the examples page at `/examples.html`. + +## Tech stack + +- Vanilla HTML/JS/CSS (no framework, no Vite/webpack for the site) +- Yarn workspace package **`@wix/interact-website`** (`package.json` here) — `dev` / `build` / format scripts only; site files stay static +- Modular CSS under **`assets/css/`** — shared **`variables.css`** / **`reset.css`**, **`utilities.css`**, **`nav.css`** / **`footer.css`**, plus **`landing.css`** (landing page) and **`examples.css`** (examples gallery) +- **`@wix/interact`**, **`@wix/motion`**, and presets loaded from **`assets/lib/`** — built copies produced by `scripts/build-landing.sh` (import map on the landing page; examples page + example iframes import `/assets/lib/...` URLs) +- CodeMirror 5 for the live code editor in the examples modal +- highlight.js for code snippets on the landing page + +## Directory structure + +``` +apps/website/ + index.html Landing page + examples.html Examples gallery page + package.json Workspace @wix/interact-website (dev/build/lint scripts) + AGENTS.md Agent notes (this file) + CLAUDE.md Symlink to AGENTS.md (for Claude / tooling) + assets/ + shared/ Cross-page scripts (served as /assets/shared/…) + nav.js + footer.js + lib/ Generated — interact, motion, motion-presets ESM (gitignored except .gitkeep) + .gitkeep + main.mjs Landing page animation configs and JS + modal.js Landing page source-code modal controller + snippets.js Landing page code snippet content + css/ + variables.css Design tokens (:root) + reset.css Minimal reset + utilities.css Shared utility classes + nav.css / footer.css Shared chrome styles + landing.css Landing page components + examples.css Examples gallery layout and modal + js/ + app.js Examples page bootstrap + config.js Example registry — the main file to edit when adding examples + renderer.js Iframe cards, lazy-loading, auto-scroll + modal.js Full-screen preview + CodeMirror editor + sidebar.js Category nav with scroll sync + examples/ + basic/ Hover, click, viewEnter, scroll, pointer interactions + carousel/ Scrolling gallery layouts + layout/ Grid and list animations + ui-elements/ Buttons, inputs, toggles, nav, dropdowns + img/ Images referenced by example HTML +``` + +## Build pipeline + +The website imports packages from `assets/lib/` (e.g. `/assets/lib/interact/es/web.js`, `/assets/lib/motion/motion.js`). Those files are **not** authored by hand: run **`./scripts/build-landing.sh`** from the repo root (or **`yarn workspace @wix/interact-website run build`**), which builds all packages and copies dist output into `apps/website/assets/lib/`. This runs in CI before deployment. + +For local work, run the build script once so `assets/lib/` is populated; otherwise module imports in the landing page, `examples.html`, and example iframes will 404. + +## Deployment (GitHub Pages) + +The `interactdocs.yml` workflow: + +1. Builds packages via `./scripts/build-landing.sh` +2. Builds the docs app (`apps/docs`) +3. Builds the playground app (`apps/playground`) +4. Assembles `_site/`: + - `/` — `index.html` and `examples.html` + - `/assets/` — all website assets under `apps/website/assets/` (including `shared/`, `lib/`, examples, etc.) + - `/docs/` — docs app + - `/playground/` — playground app + - `/rules/` — interact rules markdown +5. Deploys to GitHub Pages + +## Adding a new example + +### 1. Create the HTML file + +Place a self-contained HTML file in the appropriate category folder: + +``` +assets/examples/ + basic/ Hover, click, viewEnter, scroll, pointer interactions + carousel/ Scrolling gallery layouts + layout/ Grid and list animations + ui-elements/ Buttons, inputs, toggles, nav, dropdowns +``` + +Use the same module pattern as existing demos: import from **`/assets/lib/interact/es/web.js`** (and **`/assets/lib/motion-presets/motion-presets.js`** if you need presets). If the example imports the **`@wix/motion`** bare specifier, add a matching **` + + diff --git a/apps/website/assets/examples/basic/hover.html b/apps/website/assets/examples/basic/hover.html new file mode 100644 index 00000000..2ee355fe --- /dev/null +++ b/apps/website/assets/examples/basic/hover.html @@ -0,0 +1,118 @@ + + + + + + Hover + + + + + +
+
+ +
+
+ +
+
+ + + + diff --git a/apps/website/assets/examples/basic/pointer-move.html b/apps/website/assets/examples/basic/pointer-move.html new file mode 100644 index 00000000..748b41d7 --- /dev/null +++ b/apps/website/assets/examples/basic/pointer-move.html @@ -0,0 +1,172 @@ + + + + + + + + Pointer Movement + + + +
+ + +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + diff --git a/apps/website/assets/examples/basic/scroll.html b/apps/website/assets/examples/basic/scroll.html new file mode 100644 index 00000000..42038dd1 --- /dev/null +++ b/apps/website/assets/examples/basic/scroll.html @@ -0,0 +1,144 @@ + + + + + + Scroll + + + + +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+ + + + diff --git a/apps/website/assets/examples/basic/view-enter.html b/apps/website/assets/examples/basic/view-enter.html new file mode 100644 index 00000000..0e702db0 --- /dev/null +++ b/apps/website/assets/examples/basic/view-enter.html @@ -0,0 +1,197 @@ + + + + + + View Enter + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+
+ + + + diff --git a/apps/website/assets/examples/carousel/CardSpread.html b/apps/website/assets/examples/carousel/CardSpread.html new file mode 100644 index 00000000..e7fc86dd --- /dev/null +++ b/apps/website/assets/examples/carousel/CardSpread.html @@ -0,0 +1,426 @@ + + + + + + Card Spread Animation + + + + + +
+
+ +
+ +
+ Misty mountains +
+

Serene Peaks

+

Find your calm

+
+
+
+ +
+ Green hills +
+

Rolling Hills

+

Explore the landscape

+
+
+
+ +
+ Mountain lake +
+

Alpine Lake

+

Reflect and relax

+
+
+
+ +
+ Waterfall +
+

Hidden Falls

+

Discover nature's power

+
+
+
+ +
+ Forest from above +
+

Forest Canopy

+

Breathe the fresh air

+
+
+
+
+
+
+
+
+ + + + diff --git a/apps/website/assets/examples/carousel/cards-3d-carousel.html b/apps/website/assets/examples/carousel/cards-3d-carousel.html new file mode 100644 index 00000000..1dc88bd5 --- /dev/null +++ b/apps/website/assets/examples/carousel/cards-3d-carousel.html @@ -0,0 +1,347 @@ + + + + + + 3D Carousel — @wix/interact + + + + + + + +
+
+
+ +
Scroll to explore
+
+
+
+ + + + diff --git a/apps/website/assets/examples/carousel/mouse-track-gallery.html b/apps/website/assets/examples/carousel/mouse-track-gallery.html new file mode 100644 index 00000000..5726a47a --- /dev/null +++ b/apps/website/assets/examples/carousel/mouse-track-gallery.html @@ -0,0 +1,611 @@ + + + + + + Liquidy Gallery with @wix/interact + + + +
Initializing Gallery...
+ + + + + + diff --git a/apps/website/assets/examples/carousel/sphere-interact.html b/apps/website/assets/examples/carousel/sphere-interact.html new file mode 100644 index 00000000..e597d2ef --- /dev/null +++ b/apps/website/assets/examples/carousel/sphere-interact.html @@ -0,0 +1,1430 @@ + + + + + + Immersive 3D Sphere — Interact + + + +
SCROLL TO ZOOM · HOVER TO INSPECT
+ +
+ +
+
+
+
+
+
+

CYBER CORE

+
+
+
+

SYSTEM 00

+
+
+
+
+
+
+
+

NEON FLUX

+
+
+
+

SYSTEM 01

+
+
+
+
+
+
+
+

NIGHT CITY

+
+
+
+

SYSTEM 02

+
+
+
+
+
+
+
+

MECH SUIT

+
+
+
+

SYSTEM 03

+
+
+
+
+
+
+
+

DEEP SPACE

+
+
+
+

SYSTEM 04

+
+
+
+
+
+
+
+

ABSTRACT A

+
+
+
+

SYSTEM 05

+
+
+
+
+
+
+
+

TECH NODE

+
+
+
+

SYSTEM 06

+
+
+
+
+
+
+
+

NATURE X

+
+
+
+

SYSTEM 07

+
+
+
+
+
+
+
+

SOLAR FLARE

+
+
+
+

SYSTEM 08

+
+
+
+
+
+
+
+

WARP DRIVE

+
+
+
+

SYSTEM 09

+
+
+
+
+
+
+
+

DATA MESH

+
+
+
+

SYSTEM 10

+
+
+
+
+
+
+
+

VOID STAR

+
+
+
+

SYSTEM 11

+
+
+
+
+
+
+
+

CYBER CORE

+
+
+
+

SYSTEM 12

+
+
+
+
+
+
+
+

NEON FLUX

+
+
+
+

SYSTEM 13

+
+
+
+
+
+
+
+

NIGHT CITY

+
+
+
+

SYSTEM 14

+
+
+
+
+
+
+
+

MECH SUIT

+
+
+
+

SYSTEM 15

+
+
+
+
+
+
+
+

DEEP SPACE

+
+
+
+

SYSTEM 16

+
+
+
+
+
+
+
+

ABSTRACT A

+
+
+
+

SYSTEM 17

+
+
+
+
+
+
+
+

TECH NODE

+
+
+
+

SYSTEM 18

+
+
+
+
+
+
+
+

NATURE X

+
+
+
+

SYSTEM 19

+
+
+
+
+
+
+
+

SOLAR FLARE

+
+
+
+

SYSTEM 20

+
+
+
+
+
+
+
+

WARP DRIVE

+
+
+
+

SYSTEM 21

+
+
+
+
+
+
+
+

DATA MESH

+
+
+
+

SYSTEM 22

+
+
+
+
+
+
+
+

VOID STAR

+
+
+
+

SYSTEM 23

+
+
+
+
+
+
+
+

CYBER CORE

+
+
+
+

SYSTEM 24

+
+
+
+
+
+
+
+

NEON FLUX

+
+
+
+

SYSTEM 25

+
+
+
+
+
+
+
+

NIGHT CITY

+
+
+
+

SYSTEM 26

+
+
+
+
+
+
+
+

MECH SUIT

+
+
+
+

SYSTEM 27

+
+
+
+
+
+
+
+

DEEP SPACE

+
+
+
+

SYSTEM 28

+
+
+
+
+
+
+
+

ABSTRACT A

+
+
+
+

SYSTEM 29

+
+
+
+
+
+
+
+

TECH NODE

+
+
+
+

SYSTEM 30

+
+
+
+
+
+
+
+

NATURE X

+
+
+
+

SYSTEM 31

+
+
+
+
+
+
+
+

SOLAR FLARE

+
+
+
+

SYSTEM 32

+
+
+
+
+
+
+
+

WARP DRIVE

+
+
+
+

SYSTEM 33

+
+
+
+
+
+
+
+

DATA MESH

+
+
+
+

SYSTEM 34

+
+
+
+
+
+
+
+

VOID STAR

+
+
+
+

SYSTEM 35

+
+
+
+
+
+
+
+

CYBER CORE

+
+
+
+

SYSTEM 36

+
+
+
+
+
+
+
+

NEON FLUX

+
+
+
+

SYSTEM 37

+
+
+
+
+
+
+
+

NIGHT CITY

+
+
+
+

SYSTEM 38

+
+
+
+
+
+
+
+

MECH SUIT

+
+
+
+

SYSTEM 39

+
+
+
+
+
+
+
+

DEEP SPACE

+
+
+
+

SYSTEM 40

+
+
+
+
+
+
+
+

ABSTRACT A

+
+
+
+

SYSTEM 41

+
+
+
+
+
+
+
+

TECH NODE

+
+
+
+

SYSTEM 42

+
+
+
+
+
+
+
+

NATURE X

+
+
+
+

SYSTEM 43

+
+
+
+
+
+
+
+

SOLAR FLARE

+
+
+
+

SYSTEM 44

+
+
+
+
+
+
+
+

WARP DRIVE

+
+
+
+

SYSTEM 45

+
+
+
+
+
+
+
+

DATA MESH

+
+
+
+

SYSTEM 46

+
+
+
+
+
+
+
+

VOID STAR

+
+
+
+

SYSTEM 47

+
+
+
+
+
+
+
+

CYBER CORE

+
+
+
+

SYSTEM 48

+
+
+
+
+
+
+
+

NEON FLUX

+
+
+
+

SYSTEM 49

+
+
+
+
+
+
+
+

NIGHT CITY

+
+
+
+

SYSTEM 50

+
+
+
+
+
+
+
+

MECH SUIT

+
+
+
+

SYSTEM 51

+
+
+
+
+
+
+
+

DEEP SPACE

+
+
+
+

SYSTEM 52

+
+
+
+
+
+
+
+

ABSTRACT A

+
+
+
+

SYSTEM 53

+
+
+
+
+
+
+
+

TECH NODE

+
+
+
+

SYSTEM 54

+
+
+
+
+
+
+
+

NATURE X

+
+
+
+

SYSTEM 55

+
+
+
+
+
+
+
+ + +
+
+ +
+
+ 56 CARDS +
+
+ + + + + diff --git a/apps/website/assets/examples/layout/column-shutters.html b/apps/website/assets/examples/layout/column-shutters.html new file mode 100644 index 00000000..ca680615 --- /dev/null +++ b/apps/website/assets/examples/layout/column-shutters.html @@ -0,0 +1,594 @@ + + + + + + Recent Work — Forma + + + + + + + +
+ + +
+
+
+

Recent work

+
+
+
+
+

2018/present

+
+
+
+ +
+ index + brand + discipline + date +
+ +
+ + + + +
+
+ + + + diff --git a/apps/website/assets/examples/layout/parting-list.html b/apps/website/assets/examples/layout/parting-list.html new file mode 100644 index 00000000..f271f020 --- /dev/null +++ b/apps/website/assets/examples/layout/parting-list.html @@ -0,0 +1,584 @@ + + + + + + Split List — Creative Director + + + + + + + +
+ +
+
+ ITEM (1) + ITEM (2) + ITEM (3) + ITEM (4) + ITEM (5) + ITEM (6) + ITEM (7) + ITEM (8) + ITEM (9) + ITEM (10) + ITEM (11) + ITEM (12) +
+
+ (2024) + (2024) + (2023) + (2025) + (2023) + (2024) + (2025) + (2023) + (2024) + (2025) + (2023) + (2024) +
+
+
+ + +
+ + + + + + + + + + + + + +
+
+
+ + +
+

ALEX RIVIERA

+

Creative Director

+
+
+ +
+
+ + +
+
+ +
+
+ DUSK RISINGEDITORIAL +
+
+ NEON DRIFTCOMMERCIAL +
+
+ STILL WATERSFASHION +
+
+ PAPER CRANEBRANDING +
+
+ ECHO CHAMBERMUSIC VIDEO +
+
+ GLASS HOUSEARCHITECTURE +
+
+ WILD LIGHTPHOTOGRAPHY +
+
+ SLOW BURNFILM +
+
+ DEEP CURRENTCAMPAIGN +
+
+ SILVER LINEEDITORIAL +
+
+ FIRST SNOWSHORT FILM +
+
+ OPEN FIELDBRANDING +
+
+ +
+
+ + + + diff --git a/apps/website/assets/examples/ui-elements/dropdown-light.html b/apps/website/assets/examples/ui-elements/dropdown-light.html new file mode 100644 index 00000000..b93e0d39 --- /dev/null +++ b/apps/website/assets/examples/ui-elements/dropdown-light.html @@ -0,0 +1,458 @@ + + + + + + Capsule Dropdown — Light + + + + + + + + diff --git a/apps/website/assets/examples/ui-elements/dropdown.html b/apps/website/assets/examples/ui-elements/dropdown.html new file mode 100644 index 00000000..0f715105 --- /dev/null +++ b/apps/website/assets/examples/ui-elements/dropdown.html @@ -0,0 +1,463 @@ + + + + + + Capsule Dropdown + + + + + + + + diff --git a/apps/website/assets/examples/ui-elements/label.html b/apps/website/assets/examples/ui-elements/label.html new file mode 100644 index 00000000..0403f816 --- /dev/null +++ b/apps/website/assets/examples/ui-elements/label.html @@ -0,0 +1,236 @@ + + + + + + Accessible Pill List — @wix/interact + + + + + + + + + + + diff --git a/apps/website/assets/examples/ui-elements/lock-toggle.html b/apps/website/assets/examples/ui-elements/lock-toggle.html new file mode 100644 index 00000000..fee2c4d3 --- /dev/null +++ b/apps/website/assets/examples/ui-elements/lock-toggle.html @@ -0,0 +1,328 @@ + + + + + + Lock Toggle — @wix/interact + + + + +
+ +
+ Unavailable + Available +
+
+
+ + + + diff --git a/apps/website/assets/examples/ui-elements/on-off-toggle.html b/apps/website/assets/examples/ui-elements/on-off-toggle.html new file mode 100644 index 00000000..3575f5e6 --- /dev/null +++ b/apps/website/assets/examples/ui-elements/on-off-toggle.html @@ -0,0 +1,456 @@ + + + + + + On/Off Toggle (Sun/Moon) + + + + + + + + + + diff --git a/apps/website/assets/examples/ui-elements/password-input.html b/apps/website/assets/examples/ui-elements/password-input.html new file mode 100644 index 00000000..935fb3a0 --- /dev/null +++ b/apps/website/assets/examples/ui-elements/password-input.html @@ -0,0 +1,507 @@ + + + + + + Password Input + + + +
+
+ +
+ + +
+ + + +
+
+ + Switch to English +
+
+ + + + diff --git a/apps/website/assets/examples/ui-elements/radio-buttons.html b/apps/website/assets/examples/ui-elements/radio-buttons.html new file mode 100644 index 00000000..212b4982 --- /dev/null +++ b/apps/website/assets/examples/ui-elements/radio-buttons.html @@ -0,0 +1,313 @@ + + + + + + Radio Buttons + + + +
+ Notifications + + + + + + + + + + + + + + + +
+ + + + diff --git a/apps/website/assets/examples/ui-elements/search-input-light.html b/apps/website/assets/examples/ui-elements/search-input-light.html new file mode 100644 index 00000000..e67fd2d1 --- /dev/null +++ b/apps/website/assets/examples/ui-elements/search-input-light.html @@ -0,0 +1,483 @@ + + + + + + Search Input — Light + + + +
+ +
+ + + +
+ + +
+
+
+ +
+ + + + + + + + +
No results
+
+
+ + + + diff --git a/apps/website/assets/examples/ui-elements/search-input.html b/apps/website/assets/examples/ui-elements/search-input.html new file mode 100644 index 00000000..d5c4ed78 --- /dev/null +++ b/apps/website/assets/examples/ui-elements/search-input.html @@ -0,0 +1,457 @@ + + + + + + Search Input + + + +
+ +
+ + + +
+ + +
+
+
+ +
+ + + + +
No results
+
+
+ + + + diff --git a/apps/website/assets/examples/ui-elements/smiley-nav-light.html b/apps/website/assets/examples/ui-elements/smiley-nav-light.html new file mode 100644 index 00000000..15e9f9db --- /dev/null +++ b/apps/website/assets/examples/ui-elements/smiley-nav-light.html @@ -0,0 +1,573 @@ + + + + + + Navigation Menu — Light + + + + + + + + diff --git a/apps/website/assets/examples/ui-elements/smiley-nav.html b/apps/website/assets/examples/ui-elements/smiley-nav.html new file mode 100644 index 00000000..43d408b9 --- /dev/null +++ b/apps/website/assets/examples/ui-elements/smiley-nav.html @@ -0,0 +1,575 @@ + + + + + + Navigation Menu + + + + + + + + diff --git a/apps/website/assets/img/Hero_svg.svg b/apps/website/assets/img/Hero_svg.svg new file mode 100644 index 00000000..630194c0 --- /dev/null +++ b/apps/website/assets/img/Hero_svg.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/website/assets/img/ammonite-fossil.jpg b/apps/website/assets/img/ammonite-fossil.jpg new file mode 100644 index 00000000..07e5b8ca Binary files /dev/null and b/apps/website/assets/img/ammonite-fossil.jpg differ diff --git a/apps/website/assets/img/black-sand.jpg b/apps/website/assets/img/black-sand.jpg new file mode 100644 index 00000000..57d4245c Binary files /dev/null and b/apps/website/assets/img/black-sand.jpg differ diff --git a/apps/website/assets/img/blurry-grid-dots.jpg b/apps/website/assets/img/blurry-grid-dots.jpg new file mode 100644 index 00000000..81dd52c1 Binary files /dev/null and b/apps/website/assets/img/blurry-grid-dots.jpg differ diff --git a/apps/website/assets/img/broken-tile-pattern.jpg b/apps/website/assets/img/broken-tile-pattern.jpg new file mode 100644 index 00000000..0688fcb3 Binary files /dev/null and b/apps/website/assets/img/broken-tile-pattern.jpg differ diff --git a/apps/website/assets/img/chalk-scribbles.jpg b/apps/website/assets/img/chalk-scribbles.jpg new file mode 100644 index 00000000..a08fb86a Binary files /dev/null and b/apps/website/assets/img/chalk-scribbles.jpg differ diff --git a/apps/website/assets/img/charred-wood.jpg b/apps/website/assets/img/charred-wood.jpg new file mode 100644 index 00000000..5fbc8d06 Binary files /dev/null and b/apps/website/assets/img/charred-wood.jpg differ diff --git a/apps/website/assets/img/conch-shell.jpg b/apps/website/assets/img/conch-shell.jpg new file mode 100644 index 00000000..22202103 Binary files /dev/null and b/apps/website/assets/img/conch-shell.jpg differ diff --git a/apps/website/assets/img/cracked-membrane.jpg b/apps/website/assets/img/cracked-membrane.jpg new file mode 100644 index 00000000..c21ab8b2 Binary files /dev/null and b/apps/website/assets/img/cracked-membrane.jpg differ diff --git a/apps/website/assets/img/cracked-mud.jpg b/apps/website/assets/img/cracked-mud.jpg new file mode 100644 index 00000000..6e80d204 Binary files /dev/null and b/apps/website/assets/img/cracked-mud.jpg differ diff --git a/apps/website/assets/img/cracked-rock-face.jpg b/apps/website/assets/img/cracked-rock-face.jpg new file mode 100644 index 00000000..86faa434 Binary files /dev/null and b/apps/website/assets/img/cracked-rock-face.jpg differ diff --git a/apps/website/assets/img/cracked-stone-sphere.jpg b/apps/website/assets/img/cracked-stone-sphere.jpg new file mode 100644 index 00000000..96e49df3 Binary files /dev/null and b/apps/website/assets/img/cracked-stone-sphere.jpg differ diff --git a/apps/website/assets/img/desert-boulder.jpg b/apps/website/assets/img/desert-boulder.jpg new file mode 100644 index 00000000..12b2bb10 Binary files /dev/null and b/apps/website/assets/img/desert-boulder.jpg differ diff --git a/apps/website/assets/img/desert-mirror-pool.jpg b/apps/website/assets/img/desert-mirror-pool.jpg new file mode 100644 index 00000000..71d2a1dd Binary files /dev/null and b/apps/website/assets/img/desert-mirror-pool.jpg differ diff --git a/apps/website/assets/img/dolmen-stones.jpg b/apps/website/assets/img/dolmen-stones.jpg new file mode 100644 index 00000000..24664034 Binary files /dev/null and b/apps/website/assets/img/dolmen-stones.jpg differ diff --git a/apps/website/assets/img/dry-cracked-earth.jpg b/apps/website/assets/img/dry-cracked-earth.jpg new file mode 100644 index 00000000..634ab2c1 Binary files /dev/null and b/apps/website/assets/img/dry-cracked-earth.jpg differ diff --git a/apps/website/assets/img/fern-fiddlehead.jpg b/apps/website/assets/img/fern-fiddlehead.jpg new file mode 100644 index 00000000..6e8687ed Binary files /dev/null and b/apps/website/assets/img/fern-fiddlehead.jpg differ diff --git a/apps/website/assets/img/fern-leaf.jpg b/apps/website/assets/img/fern-leaf.jpg new file mode 100644 index 00000000..08f7c2f5 Binary files /dev/null and b/apps/website/assets/img/fern-leaf.jpg differ diff --git a/apps/website/assets/img/flowing-rock.jpg b/apps/website/assets/img/flowing-rock.jpg new file mode 100644 index 00000000..2cee7af6 Binary files /dev/null and b/apps/website/assets/img/flowing-rock.jpg differ diff --git a/apps/website/assets/img/folk-horse-figurine.jpg b/apps/website/assets/img/folk-horse-figurine.jpg new file mode 100644 index 00000000..595dc74e Binary files /dev/null and b/apps/website/assets/img/folk-horse-figurine.jpg differ diff --git a/apps/website/assets/img/geometric-circle.jpg b/apps/website/assets/img/geometric-circle.jpg new file mode 100644 index 00000000..6fb5ef93 Binary files /dev/null and b/apps/website/assets/img/geometric-circle.jpg differ diff --git a/apps/website/assets/img/glyph-symbol.jpg b/apps/website/assets/img/glyph-symbol.jpg new file mode 100644 index 00000000..cfcf3e10 Binary files /dev/null and b/apps/website/assets/img/glyph-symbol.jpg differ diff --git a/apps/website/assets/img/leaf-veins.jpg b/apps/website/assets/img/leaf-veins.jpg new file mode 100644 index 00000000..2fa98512 Binary files /dev/null and b/apps/website/assets/img/leaf-veins.jpg differ diff --git a/apps/website/assets/img/leaf.jpg b/apps/website/assets/img/leaf.jpg new file mode 100644 index 00000000..f548290d Binary files /dev/null and b/apps/website/assets/img/leaf.jpg differ diff --git a/apps/website/assets/img/lily-flower.jpg b/apps/website/assets/img/lily-flower.jpg new file mode 100644 index 00000000..c6acdca4 Binary files /dev/null and b/apps/website/assets/img/lily-flower.jpg differ diff --git a/apps/website/assets/img/metal-rose.jpg b/apps/website/assets/img/metal-rose.jpg new file mode 100644 index 00000000..b48bfb7a Binary files /dev/null and b/apps/website/assets/img/metal-rose.jpg differ diff --git a/apps/website/assets/img/metal-tulip.jpg b/apps/website/assets/img/metal-tulip.jpg new file mode 100644 index 00000000..554f3d13 Binary files /dev/null and b/apps/website/assets/img/metal-tulip.jpg differ diff --git a/apps/website/assets/img/moon-surface.jpg b/apps/website/assets/img/moon-surface.jpg new file mode 100644 index 00000000..b3d3841e Binary files /dev/null and b/apps/website/assets/img/moon-surface.jpg differ diff --git a/apps/website/assets/img/mossy-lava-rocks.jpg b/apps/website/assets/img/mossy-lava-rocks.jpg new file mode 100644 index 00000000..5c2e7e9f Binary files /dev/null and b/apps/website/assets/img/mossy-lava-rocks.jpg differ diff --git a/apps/website/assets/img/motion-blur-stripes.jpg b/apps/website/assets/img/motion-blur-stripes.jpg new file mode 100644 index 00000000..851ea10a Binary files /dev/null and b/apps/website/assets/img/motion-blur-stripes.jpg differ diff --git a/apps/website/assets/img/palm-trunk.jpg b/apps/website/assets/img/palm-trunk.jpg new file mode 100644 index 00000000..2aef040a Binary files /dev/null and b/apps/website/assets/img/palm-trunk.jpg differ diff --git a/apps/website/assets/img/scallop-shell.jpg b/apps/website/assets/img/scallop-shell.jpg new file mode 100644 index 00000000..59a5c5fe Binary files /dev/null and b/apps/website/assets/img/scallop-shell.jpg differ diff --git a/apps/website/assets/img/snail-shell.jpg b/apps/website/assets/img/snail-shell.jpg new file mode 100644 index 00000000..d094c6b1 Binary files /dev/null and b/apps/website/assets/img/snail-shell.jpg differ diff --git a/apps/website/assets/img/spike-ball.jpg b/apps/website/assets/img/spike-ball.jpg new file mode 100644 index 00000000..4c7f687f Binary files /dev/null and b/apps/website/assets/img/spike-ball.jpg differ diff --git a/apps/website/assets/img/spiral-petroglyph.jpg b/apps/website/assets/img/spiral-petroglyph.jpg new file mode 100644 index 00000000..4d715120 Binary files /dev/null and b/apps/website/assets/img/spiral-petroglyph.jpg differ diff --git a/apps/website/assets/img/stone-mask.jpg b/apps/website/assets/img/stone-mask.jpg new file mode 100644 index 00000000..a60061c5 Binary files /dev/null and b/apps/website/assets/img/stone-mask.jpg differ diff --git a/apps/website/assets/img/stone-pendant.jpg b/apps/website/assets/img/stone-pendant.jpg new file mode 100644 index 00000000..f17b2409 Binary files /dev/null and b/apps/website/assets/img/stone-pendant.jpg differ diff --git a/apps/website/assets/img/stone-rosette.jpg b/apps/website/assets/img/stone-rosette.jpg new file mode 100644 index 00000000..328f4436 Binary files /dev/null and b/apps/website/assets/img/stone-rosette.jpg differ diff --git a/apps/website/assets/img/stone-statue.jpg b/apps/website/assets/img/stone-statue.jpg new file mode 100644 index 00000000..d5dcb85d Binary files /dev/null and b/apps/website/assets/img/stone-statue.jpg differ diff --git a/apps/website/assets/img/stone-sunwheel.jpg b/apps/website/assets/img/stone-sunwheel.jpg new file mode 100644 index 00000000..0f913cda Binary files /dev/null and b/apps/website/assets/img/stone-sunwheel.jpg differ diff --git a/apps/website/assets/img/stone-tool.jpg b/apps/website/assets/img/stone-tool.jpg new file mode 100644 index 00000000..325547bc Binary files /dev/null and b/apps/website/assets/img/stone-tool.jpg differ diff --git a/apps/website/assets/img/tall-grass.jpg b/apps/website/assets/img/tall-grass.jpg new file mode 100644 index 00000000..c88d166e Binary files /dev/null and b/apps/website/assets/img/tall-grass.jpg differ diff --git a/apps/website/assets/img/thistle-cluster.jpg b/apps/website/assets/img/thistle-cluster.jpg new file mode 100644 index 00000000..5a10e39d Binary files /dev/null and b/apps/website/assets/img/thistle-cluster.jpg differ diff --git a/apps/website/assets/img/thistle-flower.jpg b/apps/website/assets/img/thistle-flower.jpg new file mode 100644 index 00000000..4e13aab4 Binary files /dev/null and b/apps/website/assets/img/thistle-flower.jpg differ diff --git a/apps/website/assets/img/thorn-stem.jpg b/apps/website/assets/img/thorn-stem.jpg new file mode 100644 index 00000000..e0f8fb8a Binary files /dev/null and b/apps/website/assets/img/thorn-stem.jpg differ diff --git a/apps/website/assets/img/topographic-map.jpg b/apps/website/assets/img/topographic-map.jpg new file mode 100644 index 00000000..31dd0fca Binary files /dev/null and b/apps/website/assets/img/topographic-map.jpg differ diff --git a/apps/website/assets/img/water-ripples.jpg b/apps/website/assets/img/water-ripples.jpg new file mode 100644 index 00000000..8e21ee3b Binary files /dev/null and b/apps/website/assets/img/water-ripples.jpg differ diff --git a/apps/website/assets/js/app.js b/apps/website/assets/js/app.js new file mode 100644 index 00000000..ae811bf0 --- /dev/null +++ b/apps/website/assets/js/app.js @@ -0,0 +1,30 @@ +import { + renderSidebarItems, + renderSections, + initLazyLoading, + initDblClickExpand, +} from './renderer.js'; +import { initSidebar } from './sidebar.js'; +import { openModal, initModal } from './modal.js'; + +document.addEventListener('DOMContentLoaded', () => { + const categoryList = document.getElementById('categoryList'); + const contentScroll = document.getElementById('contentScroll'); + + renderSidebarItems(categoryList); + renderSections(contentScroll); + initLazyLoading(); + + // Reveal footer when content is scrolled to the bottom + const footer = document.querySelector('body > footer'); + if (footer && contentScroll) { + contentScroll.addEventListener('scroll', () => { + const atBottom = + contentScroll.scrollHeight - contentScroll.scrollTop - contentScroll.clientHeight < 80; + footer.classList.toggle('visible', atBottom); + }); + } + initDblClickExpand(openModal); + initSidebar(); + initModal(); +}); diff --git a/apps/website/assets/js/config.js b/apps/website/assets/js/config.js new file mode 100644 index 00000000..d344d184 --- /dev/null +++ b/apps/website/assets/js/config.js @@ -0,0 +1,167 @@ +export const categories = [ + { + id: 'basic', + title: 'Basic', + description: 'Simple, elegant animations perfect for getting started with @wix/interact.', + docsLink: '#', + docsLabel: 'docs/viewEnter', + compact: true, + examples: [ + { + id: 'hover', + title: 'Hover', + htmlPath: 'assets/examples/basic/hover.html', + }, + { + id: 'click', + title: 'Click', + htmlPath: 'assets/examples/basic/click.html', + }, + { + id: 'view-enter', + title: 'View Enter', + htmlPath: 'assets/examples/basic/view-enter.html', + autoScroll: true, + }, + { + id: 'pointer-move', + title: 'Pointer Move', + htmlPath: 'assets/examples/basic/pointer-move.html', + }, + { + id: 'scroll', + title: 'Scroll', + htmlPath: 'assets/examples/basic/scroll.html', + autoScroll: true, + viewportWidth: 665, + }, + ], + }, + { + id: 'carousel', + title: 'Gallery', + description: 'Beautiful scrolling gallery layouts that enhance visual storytelling.', + docsLink: '#', + docsLabel: 'docs/viewProgress', + layout: 'gallery-bento', + examples: [ + { + id: 'cards-3d-carousel', + title: '3D Carousel', + htmlPath: 'assets/examples/carousel/cards-3d-carousel.html', + autoScroll: true, + viewportWidth: 1400, + viewportHeight: 962, + }, + { + id: 'card-spread', + title: 'Card Spread', + htmlPath: 'assets/examples/carousel/CardSpread.html', + autoScroll: true, + viewportWidth: 1400, + }, + { + id: 'mouse-track-gallery', + title: 'Infinite Gallery', + htmlPath: 'assets/examples/carousel/mouse-track-gallery.html', + }, + { + id: 'sphere-interact', + title: '3D Sphere', + htmlPath: 'assets/examples/carousel/sphere-interact.html', + autoScroll: true, + viewportWidth: 1400, + }, + ], + }, + { + id: 'layout', + title: 'Layout', + description: 'Dynamic grid and list animations for complex content arrangements.', + docsLink: '#', + docsLabel: 'docs/lists', + examples: [ + { + id: 'column-shutters', + title: 'Column Shutters', + htmlPath: 'assets/examples/layout/column-shutters.html', + viewportWidth: 1000, + }, + { + id: 'parting-list', + title: 'Parting List', + htmlPath: 'assets/examples/layout/parting-list.html', + autoScroll: true, + viewportWidth: 1600, + }, + ], + }, + { + id: 'ui-elements', + title: 'UI Items', + description: 'Interactive micro-animations for buttons, forms, and interface components.', + docsLink: '#', + docsLabel: 'docs/pointerMove', + gridCols: 3, + gridGap: 24, + examples: [ + { + id: 'label', + title: 'Labels', + htmlPath: 'assets/examples/ui-elements/label.html', + colSpan: 2, + rowSpan: 2, + viewportWidth: 1000, + }, + { + id: 'lock-toggle', + title: 'Lock Toggle', + htmlPath: 'assets/examples/ui-elements/lock-toggle.html', + viewportWidth: 565, + }, + { + id: 'on-off-toggle', + title: 'On/Off Toggle', + htmlPath: 'assets/examples/ui-elements/on-off-toggle.html', + viewportWidth: 650, + }, + { + id: 'dropdown', + title: 'Dropdown', + htmlPath: 'assets/examples/ui-elements/dropdown.html', + viewportWidth: 670, + }, + { + id: 'password-input', + title: 'Password Input', + htmlPath: 'assets/examples/ui-elements/password-input.html', + viewportWidth: 615, + }, + { + id: 'radio-buttons', + title: 'Radio Buttons', + htmlPath: 'assets/examples/ui-elements/radio-buttons.html', + viewportWidth: 695, + }, + { + id: 'search-input', + title: 'Search Input', + htmlPath: 'assets/examples/ui-elements/search-input.html', + viewportWidth: 640, + }, + { + id: 'smiley-nav', + title: 'Navigation', + htmlPath: 'assets/examples/ui-elements/smiley-nav.html', + }, + ], + }, + { + id: 'custom-effects', + title: 'Custom', + description: 'Build your own effects with custom keyframes and pointer-driven interactions.', + docsLink: '#', + docsLabel: 'docs/customEffect', + examples: [], + }, +]; diff --git a/apps/website/assets/js/modal.js b/apps/website/assets/js/modal.js new file mode 100644 index 00000000..1cfe5fde --- /dev/null +++ b/apps/website/assets/js/modal.js @@ -0,0 +1,364 @@ +/* global CodeMirror */ + +let overlay; +let titleEl; +let iframe; +let codeBtn; +let previewPanel; +let codePanel; +let cmEditor = null; +let cmWrapper = null; +let originalSource = ''; +let htmlSource = ''; +let isCodeMode = false; + +const CLOSE_SVG = ``; +const CODE_SVG = ``; +const CHEVRON_UP_SVG = ``; +const CHEVRON_DOWN_SVG = ``; +const CLOSE_SM_SVG = ``; + +// --- Search state --- +let searchBar = null; +let searchInput = null; +let searchCount = null; +let searchMarks = []; +let searchMatches = []; +let searchIndex = -1; + +function buildModalDOM() { + overlay = document.createElement('div'); + overlay.className = 'modal-overlay'; + overlay.setAttribute('tabindex', '-1'); + + const wrapper = document.createElement('div'); + wrapper.className = 'modal-wrapper'; + + titleEl = document.createElement('div'); + titleEl.className = 'modal-title'; + + const container = document.createElement('div'); + container.className = 'modal-container'; + + previewPanel = document.createElement('div'); + previewPanel.className = 'modal-panel modal-preview'; + + iframe = document.createElement('iframe'); + iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin'); + previewPanel.appendChild(iframe); + container.appendChild(previewPanel); + + codePanel = document.createElement('div'); + codePanel.className = 'modal-panel modal-code'; + + const codeToolbar = document.createElement('div'); + codeToolbar.className = 'code-toolbar'; + + const resetBtn = document.createElement('button'); + resetBtn.className = 'code-reset-btn'; + resetBtn.textContent = 'Reset'; + resetBtn.addEventListener('click', resetCode); + codeToolbar.appendChild(resetBtn); + + codePanel.appendChild(codeToolbar); + + cmWrapper = document.createElement('div'); + cmWrapper.className = 'code-editor-wrap'; + codePanel.appendChild(cmWrapper); + + // Custom search bar + searchBar = document.createElement('div'); + searchBar.className = 'code-search-bar'; + + searchInput = document.createElement('input'); + searchInput.type = 'text'; + searchInput.placeholder = 'Find...'; + searchInput.addEventListener('input', runSearch); + searchInput.addEventListener('keydown', (e) => { + if (e.key === 'Enter') { + e.preventDefault(); + if (e.shiftKey) searchPrev(); + else searchNext(); + } + if (e.key === 'Escape') closeSearch(); + }); + + searchCount = document.createElement('span'); + searchCount.className = 'code-search-count'; + + const searchNav = document.createElement('div'); + searchNav.className = 'code-search-nav'; + + const prevBtn = document.createElement('button'); + prevBtn.innerHTML = CHEVRON_UP_SVG; + prevBtn.setAttribute('aria-label', 'Previous match'); + prevBtn.addEventListener('click', searchPrev); + + const nextBtn = document.createElement('button'); + nextBtn.innerHTML = CHEVRON_DOWN_SVG; + nextBtn.setAttribute('aria-label', 'Next match'); + nextBtn.addEventListener('click', searchNext); + + searchNav.appendChild(prevBtn); + searchNav.appendChild(nextBtn); + + const searchCloseBtn = document.createElement('button'); + searchCloseBtn.className = 'code-search-close'; + searchCloseBtn.innerHTML = CLOSE_SM_SVG; + searchCloseBtn.setAttribute('aria-label', 'Close search'); + searchCloseBtn.addEventListener('click', closeSearch); + + searchBar.appendChild(searchInput); + searchBar.appendChild(searchCount); + searchBar.appendChild(searchNav); + searchBar.appendChild(searchCloseBtn); + codePanel.appendChild(searchBar); + + container.appendChild(codePanel); + + const actions = document.createElement('div'); + actions.className = 'modal-actions'; + + const closeBtn = document.createElement('button'); + closeBtn.className = 'modal-btn modal-btn-close'; + closeBtn.innerHTML = CLOSE_SVG; + closeBtn.setAttribute('aria-label', 'Close'); + closeBtn.addEventListener('click', closeModal); + + codeBtn = document.createElement('button'); + codeBtn.className = 'modal-btn modal-btn-code'; + codeBtn.innerHTML = CODE_SVG; + codeBtn.setAttribute('aria-label', 'Toggle code'); + codeBtn.addEventListener('click', toggleCodeMode); + + actions.appendChild(closeBtn); + actions.appendChild(codeBtn); + + wrapper.appendChild(titleEl); + wrapper.appendChild(container); + wrapper.appendChild(actions); + overlay.appendChild(wrapper); + + overlay.addEventListener('click', (e) => { + if (e.target === overlay) closeModal(); + }); + + document.body.appendChild(overlay); +} + +function ensureEditor() { + if (cmEditor) return; + cmEditor = CodeMirror(cmWrapper, { + value: '', + mode: 'htmlmixed', + theme: 'material-darker', + lineNumbers: true, + lineWrapping: false, + tabSize: 2, + indentWithTabs: false, + matchBrackets: true, + autoCloseTags: true, + scrollbarStyle: 'native', + extraKeys: { + 'Cmd-F': () => openSearch(), + 'Ctrl-F': () => openSearch(), + }, + }); +} + +function resetCode() { + if (!cmEditor) return; + cmEditor.setValue(originalSource); + htmlSource = originalSource; +} + +function toggleCodeMode() { + isCodeMode = !isCodeMode; + + if (isCodeMode) { + try { + ensureEditor(); + cmEditor.setValue(htmlSource); + previewPanel.classList.add('hidden'); + codePanel.classList.add('visible'); + codeBtn.classList.add('active'); + setTimeout(() => cmEditor.refresh(), 20); + } catch (e) { + console.error('[modal] toggleCodeMode error:', e); + isCodeMode = false; // revert + } + } else { + closeSearch(); + htmlSource = cmEditor.getValue(); + iframe.removeAttribute('src'); + iframe.srcdoc = htmlSource; + previewPanel.classList.remove('hidden'); + codePanel.classList.remove('visible'); + codeBtn.classList.remove('active'); + } +} + +// --- Search functions --- +function openSearch() { + if (!searchBar) return; + searchBar.classList.add('open'); + searchInput.focus(); + searchInput.select(); +} + +function closeSearch() { + if (!searchBar) return; + searchBar.classList.remove('open'); + searchInput.value = ''; + clearSearchMarks(); + searchCount.textContent = ''; + searchCount.classList.remove('has-matches'); + if (cmEditor) cmEditor.focus(); +} + +function clearSearchMarks() { + searchMarks.forEach((m) => m.clear()); + searchMarks = []; + searchMatches = []; + searchIndex = -1; +} + +function runSearch() { + if (!cmEditor) return; + clearSearchMarks(); + + const query = searchInput.value; + if (!query) { + searchCount.textContent = ''; + searchCount.classList.remove('has-matches'); + return; + } + + const cursor = cmEditor.getSearchCursor(query, null, { caseFold: true }); + while (cursor.findNext()) { + searchMatches.push({ from: cursor.from(), to: cursor.to() }); + searchMarks.push( + cmEditor.markText(cursor.from(), cursor.to(), { className: 'cm-search-match' }), + ); + } + + if (searchMatches.length > 0) { + searchIndex = 0; + highlightCurrent(); + searchCount.classList.add('has-matches'); + } else { + searchCount.textContent = 'No results'; + searchCount.classList.remove('has-matches'); + } +} + +function highlightCurrent() { + // Reset all to base highlight + searchMarks.forEach((m) => m.clear()); + searchMarks = searchMatches.map((m, i) => + cmEditor.markText(m.from, m.to, { + className: i === searchIndex ? 'cm-search-match-current' : 'cm-search-match', + }), + ); + + searchCount.textContent = `${searchIndex + 1} of ${searchMatches.length}`; + cmEditor.scrollIntoView(searchMatches[searchIndex].from, 100); +} + +function searchNext() { + if (searchMatches.length === 0) return; + searchIndex = (searchIndex + 1) % searchMatches.length; + highlightCurrent(); +} + +function searchPrev() { + if (searchMatches.length === 0) return; + searchIndex = (searchIndex - 1 + searchMatches.length) % searchMatches.length; + highlightCurrent(); +} + +export function openModal(title, htmlPath) { + if (!overlay) buildModalDOM(); + + isCodeMode = false; + previewPanel.classList.remove('hidden'); + codePanel.classList.remove('visible'); + codeBtn.classList.remove('active'); + + titleEl.textContent = title; + iframe.src = htmlPath + `?_=${Date.now()}`; + iframe.removeAttribute('srcdoc'); + + // Listen for Escape inside the modal iframe (it captures focus on click) + iframe.addEventListener('load', function onLoad() { + try { + iframe.contentDocument.addEventListener('keydown', (e) => { + if (e.key === 'Escape') closeModal(); + }); + } catch { + /* cross-origin */ + } + iframe.removeEventListener('load', onLoad); + }); + + fetch(htmlPath) + .then((r) => r.text()) + .then((text) => { + originalSource = text; + htmlSource = text; + }) + .catch(() => { + originalSource = ''; + htmlSource = ''; + }); + + requestAnimationFrame(() => { + overlay.classList.add('open'); + overlay.focus(); + }); + + document.body.style.overflow = 'hidden'; +} + +export function closeModal() { + if (!overlay) return; + + overlay.classList.remove('open'); + document.body.style.overflow = ''; + + setTimeout(() => { + iframe.src = 'about:blank'; + iframe.removeAttribute('srcdoc'); + originalSource = ''; + htmlSource = ''; + isCodeMode = false; + previewPanel.classList.remove('hidden'); + codePanel.classList.remove('visible'); + codeBtn.classList.remove('active'); + if (cmEditor) cmEditor.setValue(''); + }, 350); +} + +export function initModal() { + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape') closeModal(); + }); + + document.addEventListener('click', (e) => { + // Expand button opens modal + const expandBtn = e.target.closest('.expand-btn'); + if (expandBtn) { + const card = expandBtn.closest('.example-card'); + if (card) openModal(card.dataset.title, card.dataset.htmlPath); + return; + } + + // Label opens modal + const label = e.target.closest('.example-label'); + if (label) { + const wrapper = label.closest('.example-card-wrapper'); + const card = wrapper?.querySelector('.example-card'); + if (card) openModal(card.dataset.title, card.dataset.htmlPath); + } + }); +} diff --git a/apps/website/assets/js/renderer.js b/apps/website/assets/js/renderer.js new file mode 100644 index 00000000..3c8fbac2 --- /dev/null +++ b/apps/website/assets/js/renderer.js @@ -0,0 +1,417 @@ +import { categories } from './config.js'; + +const EXPAND_SVG = ``; + +const IFRAME_BASE_WIDTH = 800; +const IFRAME_COMPACT_WIDTH = 800; +const CACHE_BUST = `?_=${Date.now()}`; + +// Single shared observer — computes scale for all card iframes +const scaleObserver = new ResizeObserver((entries) => { + for (const entry of entries) { + const iframe = entry.target.querySelector('.card-iframe'); + if (!iframe) continue; + const customWidth = parseInt(entry.target.dataset.viewportWidth, 10); + let baseWidth; + if (customWidth) { + baseWidth = customWidth; + } else { + const isCompact = entry.target.closest('.example-card')?.classList.contains('compact'); + baseWidth = isCompact ? IFRAME_COMPACT_WIDTH : IFRAME_BASE_WIDTH; + } + const scale = entry.contentRect.width / baseWidth; + iframe.style.transform = `translate(-50%, -50%) scale(${scale})`; + } +}); + +const PLACEHOLDER_SHAPE_COUNT = 3; + +function createPlaceholderPreview(categoryId) { + const preview = document.createElement('div'); + preview.className = `example-preview preview-${categoryId}`; + + const shapes = document.createElement('div'); + shapes.className = 'preview-shapes'; + + const count = categoryId === 'layout' ? 5 : PLACEHOLDER_SHAPE_COUNT; + for (let i = 0; i < count; i++) { + const shape = document.createElement('div'); + shape.className = 'preview-shape'; + shapes.appendChild(shape); + } + + preview.appendChild(shapes); + return preview; +} + +function createExampleCard(example, categoryId, compact) { + const wrapper = document.createElement('div'); + wrapper.className = 'example-card-wrapper'; + + const card = document.createElement('div'); + card.className = compact ? 'example-card compact' : 'example-card'; + card.dataset.htmlPath = example.htmlPath; + card.dataset.title = example.title; + + const inner = document.createElement('div'); + inner.className = 'example-card-inner'; + + // Placeholder (visible while iframe loads) + const placeholder = createPlaceholderPreview(categoryId); + inner.appendChild(placeholder); + + // Live iframe preview + const iframe = document.createElement('iframe'); + iframe.className = 'card-iframe'; + iframe.dataset.src = example.htmlPath + CACHE_BUST; + iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin'); + iframe.setAttribute('loading', 'lazy'); + iframe.setAttribute('tabindex', '-1'); + iframe.title = example.title; + + // Per-example desktop viewport — render iframe at this width so the example + // sees a real desktop viewport instead of the card's smaller width. + if (example.viewportWidth) { + const w = example.viewportWidth; + const h = example.viewportHeight ?? Math.round((w * 10) / 16); + iframe.style.width = `${w}px`; + iframe.style.height = `${h}px`; + inner.dataset.viewportWidth = String(w); + } + + iframe.addEventListener('load', () => { + if (!iframe.src || iframe.src === 'about:blank') return; + placeholder.style.display = 'none'; + iframe.classList.add('loaded'); + + // Auto-scroll for scroll-driven examples + if (example.autoScroll) { + startAutoScroll(iframe); + } + + // Double-click inside iframe opens modal, first click shows hint + try { + const doc = iframe.contentDocument; + let hintTimeout = null; + + doc.addEventListener('click', () => { + if (card.querySelector('.dblclick-hint')) return; + const hint = document.createElement('div'); + hint.className = 'dblclick-hint'; + hint.textContent = 'Double-click to expand'; + inner.appendChild(hint); + requestAnimationFrame(() => hint.classList.add('visible')); + + clearTimeout(hintTimeout); + hintTimeout = setTimeout(() => { + hint.classList.remove('visible'); + hint.addEventListener('transitionend', () => hint.remove(), { once: true }); + }, 2000); + }); + + doc.addEventListener('dblclick', () => { + if (_openModal) _openModal(card.dataset.title, card.dataset.htmlPath); + }); + } catch { + // cross-origin — ignore + } + }); + + inner.appendChild(iframe); + + // Expand button (opens modal) + const expandBtn = document.createElement('button'); + expandBtn.className = 'expand-btn'; + expandBtn.innerHTML = EXPAND_SVG; + expandBtn.setAttribute('aria-label', `Open ${example.title}`); + inner.appendChild(expandBtn); + + card.appendChild(inner); + scaleObserver.observe(inner); + wrapper.appendChild(card); + + // Label (also opens modal) + const label = document.createElement('div'); + label.className = 'example-label'; + label.textContent = example.title; + wrapper.appendChild(label); + + return wrapper; +} + +function startAutoScroll(iframe) { + let direction = 1; + + function tick() { + const rect = iframe.getBoundingClientRect(); + if (rect.bottom > -100 && rect.top < window.innerHeight + 100) { + try { + const win = iframe.contentWindow; + const max = win.document.documentElement.scrollHeight - win.innerHeight; + if (max > 0) { + if (win.scrollY >= max - 1) direction = -1; + if (win.scrollY <= 1) direction = 1; + win.scrollBy(0, direction * 0.5); + } + } catch { + return; // stop if cross-origin or detached + } + } + requestAnimationFrame(tick); + } + + requestAnimationFrame(tick); +} + +function createCarouselSliderPanel(iframe) { + const card = document.createElement('div'); + card.className = 'slider-panel-card'; + card.innerHTML = ` +
Controls
+ + + + + + `; + + const wireUp = () => { + const win = iframe.contentWindow; + const doc = iframe.contentDocument; + if (!win || !doc) return; + + const set = (sel, fn) => card.querySelector(sel).addEventListener('input', fn); + + set('[data-control="persp"]', (e) => { + doc.documentElement.style.setProperty('--perspective', e.target.value + 'px'); + card.querySelector('.val-persp').textContent = e.target.value; + }); + set('[data-control="radius"]', (e) => { + doc.documentElement.style.setProperty('--card-radius', e.target.value + 'px'); + card.querySelector('.val-radius').textContent = e.target.value; + }); + set('[data-control="loops"]', (e) => { + if (win.__cfg) win.__cfg.loops = +e.target.value; + card.querySelector('.val-loops').textContent = e.target.value; + }); + set('[data-control="dim"]', (e) => { + if (win.__cfg) win.__cfg.minB = +e.target.value / 100; + card.querySelector('.val-dim').textContent = e.target.value; + }); + set('[data-control="ring"]', (e) => { + if (win.__cfg) win.__cfg.radius = +e.target.value; + card.querySelector('.val-ring').textContent = e.target.value; + if (typeof win.__updateRadius === 'function') win.__updateRadius(); + }); + }; + + // Iframe content might already be loaded by the time we attach; + // wire up on load + try once immediately in case it's ready. + iframe.addEventListener('load', wireUp); + + return card; +} + +function createCategorySection(category) { + const section = document.createElement('section'); + section.className = 'category-section'; + section.id = category.id; + + const header = document.createElement('div'); + header.className = 'category-header'; + + const title = document.createElement('h2'); + title.className = 'category-title'; + title.textContent = category.title; + + if (category.docsLink && category.docsLabel) { + const link = document.createElement('a'); + link.className = 'docs-link'; + link.href = category.docsLink; + link.textContent = `[${category.docsLabel}]`; + title.appendChild(link); + } + + header.appendChild(title); + + const desc = document.createElement('p'); + desc.className = 'category-description'; + desc.textContent = category.description; + header.appendChild(desc); + + section.appendChild(header); + + if (category.examples.length === 0) { + const empty = document.createElement('div'); + empty.className = 'empty-category'; + empty.textContent = 'No examples yet — coming soon'; + section.appendChild(empty); + } else if (category.layout === 'gallery-bento') { + // Top row: source code card (left) + first example (right, like Labels in UI) + const top = document.createElement('div'); + top.className = 'gallery-top-row'; + + const featuredCard = createExampleCard(category.examples[0], category.id, false); + top.appendChild(featuredCard); + + const iframe = featuredCard.querySelector('.card-iframe'); + const sidePanel = createCarouselSliderPanel(iframe); + // Connector lives inside the side panel so it anchors to the panel's vertical middle + const connector = document.createElement('div'); + connector.className = 'card-connector'; + sidePanel.prepend(connector); + top.appendChild(sidePanel); + + section.appendChild(top); + + // Bottom: regular grid for remaining examples + if (category.examples.length > 1) { + const bottom = document.createElement('div'); + bottom.className = 'examples-grid gallery-bottom'; + for (let i = 1; i < category.examples.length; i++) { + bottom.appendChild(createExampleCard(category.examples[i], category.id, false)); + } + section.appendChild(bottom); + } + } else { + const grid = document.createElement('div'); + grid.className = category.compact ? 'examples-grid compact' : 'examples-grid'; + if (category.gridCols) grid.style.gridTemplateColumns = `repeat(${category.gridCols}, 1fr)`; + if (category.gridGap !== undefined) grid.style.gap = `${category.gridGap}px`; + category.examples.forEach((ex) => { + const card = createExampleCard(ex, category.id, category.compact); + if (ex.colSpan) card.style.gridColumn = `span ${ex.colSpan}`; + if (ex.rowSpan) { + card.style.gridRow = `span ${ex.rowSpan}`; + if (ex.rowSpan > 1) card.classList.add('multi-row'); + } + grid.appendChild(card); + }); + section.appendChild(grid); + } + + return section; +} + +export function renderSidebarItems(container) { + // Hero / welcome entry — sits at the top, active by default + const welcome = document.createElement('div'); + welcome.className = 'category-item active'; + welcome.dataset.target = 'welcome'; + welcome.textContent = 'Welcome'; + container.appendChild(welcome); + + categories.forEach((cat) => { + const item = document.createElement('div'); + item.className = 'category-item'; + item.dataset.target = cat.id; + item.textContent = cat.title; + container.appendChild(item); + }); +} + +function createHeroSection() { + const section = document.createElement('section'); + section.className = 'hero-section'; + section.id = 'welcome'; + + const text = document.createElement('div'); + text.className = 'hero-text'; + + const title = document.createElement('h1'); + title.className = 'hero-title'; + title.innerHTML = 'Interact
examples'; + text.appendChild(title); + + const desc = document.createElement('p'); + desc.className = 'hero-description'; + desc.textContent = + 'A live gallery of interactions. Hover, click, and scroll to play — double click any card to view source code.'; + text.appendChild(desc); + + section.appendChild(text); + + const illus = document.createElement('div'); + illus.className = 'hero-illus'; + illus.setAttribute('aria-hidden', 'true'); + illus.innerHTML = ` + + + + + + + + + + + + + + + + + + + + + + + + + + `; + section.appendChild(illus); + + return section; +} + +export function renderSections(container) { + container.appendChild(createHeroSection()); + categories.forEach((cat) => { + container.appendChild(createCategorySection(cat)); + }); +} + +export function initLazyLoading() { + const root = document.getElementById('contentScroll'); + + const observer = new IntersectionObserver( + (entries) => { + for (const entry of entries) { + if (!entry.isIntersecting) continue; + const iframe = entry.target.querySelector('.card-iframe'); + if (iframe?.dataset.src) { + iframe.src = iframe.dataset.src; + delete iframe.dataset.src; + } + observer.unobserve(entry.target); + } + }, + { root, rootMargin: '300px' }, + ); + + document.querySelectorAll('.example-card-wrapper').forEach((w) => observer.observe(w)); +} + +// Stored reference so iframe load handlers (which fire async) can call it +let _openModal = null; + +export function initDblClickExpand(openModalFn) { + _openModal = openModalFn; +} diff --git a/apps/website/assets/js/sidebar.js b/apps/website/assets/js/sidebar.js new file mode 100644 index 00000000..4e17373d --- /dev/null +++ b/apps/website/assets/js/sidebar.js @@ -0,0 +1,129 @@ +export function initSidebar() { + const categoryList = document.getElementById('categoryList'); + const contentScroll = document.getElementById('contentScroll'); + if (!categoryList || !contentScroll) return; + + const allItems = () => Array.from(document.querySelectorAll('.category-item')); + const sections = () => Array.from(document.querySelectorAll('.hero-section, .category-section')); + + let isProgrammaticScroll = false; + let currentIndex = 0; + let contentScrollTimeout; + let sidebarScrollTimeout; + + function navigateToCategory(index, fromSidebarScroll = false) { + const items = allItems(); + if (index < 0 || index >= items.length) return; + + const item = items[index]; + const targetId = item.dataset.target; + const targetSection = document.getElementById(targetId); + if (!targetSection) return; + + isProgrammaticScroll = true; + currentIndex = index; + + items.forEach((i) => i.classList.remove('active')); + item.classList.add('active'); + + if (!fromSidebarScroll) { + item.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } + + targetSection.scrollIntoView({ behavior: 'smooth', block: 'start' }); + + setTimeout(() => { + isProgrammaticScroll = false; + }, 1000); + } + + categoryList.addEventListener('click', (e) => { + const item = e.target.closest('.category-item'); + if (!item) return; + const items = allItems(); + const index = items.indexOf(item); + if (index !== -1) navigateToCategory(index); + }); + + contentScroll.addEventListener('scroll', () => { + if (isProgrammaticScroll) return; + + clearTimeout(contentScrollTimeout); + contentScrollTimeout = setTimeout(() => { + if (isProgrammaticScroll) return; + + const secs = sections(); + const items = allItems(); + let activeSection = null; + let minDistance = Infinity; + + secs.forEach((section) => { + const rect = section.getBoundingClientRect(); + const contentRect = contentScroll.getBoundingClientRect(); + const sectionTop = rect.top - contentRect.top; + const distance = Math.abs(sectionTop); + + if (distance < minDistance) { + minDistance = distance; + activeSection = section; + } + }); + + if (activeSection) { + const targetId = activeSection.id; + const newIndex = items.findIndex((item) => item.dataset.target === targetId); + + if (newIndex !== -1 && newIndex !== currentIndex) { + currentIndex = newIndex; + items.forEach((i) => i.classList.remove('active')); + items[currentIndex].classList.add('active'); + + isProgrammaticScroll = true; + items[currentIndex].scrollIntoView({ behavior: 'smooth', block: 'center' }); + setTimeout(() => { + isProgrammaticScroll = false; + }, 500); + } + } + }, 150); + }); + + categoryList.addEventListener('scroll', () => { + if (isProgrammaticScroll) return; + + clearTimeout(sidebarScrollTimeout); + sidebarScrollTimeout = setTimeout(() => { + if (isProgrammaticScroll) return; + + const items = allItems(); + const rect = categoryList.getBoundingClientRect(); + const centerY = rect.top + rect.height / 2; + let closestIndex = 0; + let minDist = Infinity; + + items.forEach((item, index) => { + const r = item.getBoundingClientRect(); + const dist = Math.abs(r.top + r.height / 2 - centerY); + if (dist < minDist) { + minDist = dist; + closestIndex = index; + } + }); + + if (closestIndex !== currentIndex) { + navigateToCategory(closestIndex, true); + } + }, 200); + }); + + document.addEventListener('keydown', (e) => { + if (document.querySelector('.modal-overlay.open')) return; + if (e.key === 'ArrowDown' || e.key === 'j') { + e.preventDefault(); + navigateToCategory(currentIndex + 1); + } else if (e.key === 'ArrowUp' || e.key === 'k') { + e.preventDefault(); + navigateToCategory(currentIndex - 1); + } + }); +} diff --git a/assets/lib/.gitkeep b/apps/website/assets/lib/.gitkeep similarity index 100% rename from assets/lib/.gitkeep rename to apps/website/assets/lib/.gitkeep diff --git a/assets/main.mjs b/apps/website/assets/main.mjs similarity index 100% rename from assets/main.mjs rename to apps/website/assets/main.mjs diff --git a/assets/modal.js b/apps/website/assets/modal.js similarity index 100% rename from assets/modal.js rename to apps/website/assets/modal.js diff --git a/apps/website/assets/shared/footer.js b/apps/website/assets/shared/footer.js new file mode 100644 index 00000000..8b09cdd8 --- /dev/null +++ b/apps/website/assets/shared/footer.js @@ -0,0 +1,46 @@ +/** + * Shared footer — single source of truth across all pages. + * + * Options via data attributes on the placeholder: + * data-interact — wrap elements in for entrance animations + */ +(function () { + var REPO = 'https://github.com/wix-incubator/interact'; + var NPM = 'https://www.npmjs.com/package/@wix/interact'; + var YEAR = new Date().getFullYear(); + + var el = document.getElementById('shared-footer'); + if (!el) return; + + var useInteract = el.hasAttribute('data-interact'); + + var brandInner = + ''; + + var linksInner = + ''; + + var brand = useInteract + ? '' + + brandInner + + '' + : brandInner; + + var links = useInteract + ? '' + + linksInner + + '' + : linksInner; + + var footer = document.createElement('footer'); + footer.className = 'site-footer'; + footer.innerHTML = brand + links; + el.replaceWith(footer); +})(); diff --git a/apps/website/assets/shared/nav.js b/apps/website/assets/shared/nav.js new file mode 100644 index 00000000..9a184bcf --- /dev/null +++ b/apps/website/assets/shared/nav.js @@ -0,0 +1,69 @@ +/** + * Shared navigation — single source of truth across all pages. + * + * Options via data attributes on the placeholder: + * data-interact — wrap logo + CTA in for entrance animations + */ +(function () { + var LINKS = [ + { label: 'Docs', href: '#' }, + { label: 'Examples', href: '/examples.html' }, + ]; + var REPO = 'https://github.com/wix-incubator/interact'; + + var el = document.getElementById('shared-nav'); + if (!el) return; + + var useInteract = el.hasAttribute('data-interact'); + var path = window.location.pathname.replace(/\/index\.html$/, '/'); + + function isActive(href) { + if (href === '/') return path === '/' || path === '/index.html'; + return path.indexOf(href) === 0; + } + + var linksHtml = ''; + for (var i = 0; i < LINKS.length; i++) { + var link = LINKS[i]; + var active = isActive(link.href); + linksHtml += + '' + + link.label + + ''; + } + + var logoHtml = ''; + var ctaHtml = + 'Go to Repository ↗'; + + var logo = useInteract + ? '' + + logoHtml + + '' + : logoHtml; + + var cta = useInteract + ? '' + + ctaHtml + + '' + : ctaHtml; + + var nav = document.createElement('nav'); + nav.className = 'site-nav'; + nav.innerHTML = + logo + + ''; + + el.replaceWith(nav); +})(); diff --git a/apps/website/assets/snippets.js b/apps/website/assets/snippets.js new file mode 100644 index 00000000..ae434169 --- /dev/null +++ b/apps/website/assets/snippets.js @@ -0,0 +1,188 @@ +// Code Snippet Logic +// Define specific snippets for each interaction +const snippets = { + hover: `[{ + trigger: 'hover', + effects: [{ + keyframeEffect: { + keyframes: [ + { transform: 'scale(1)', offset: 0 }, + { transform: 'scale(1.02)', offset: 1 } + ] + }, + duration: 400, + easing: 'ease-out', + fill: 'both' + }] +}]`, + click: `[{ + trigger: 'click', + effects: [ + { + selector: '.fill-circle', + transition: { + duration: 500, + easing: 'ease-out', + styleProperties: [{ name: 'transform', value: 'scale(1)' }] + } + }, + { + key: 'click-ripple-1', + transition: { + duration: 800, + easing: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)', + styleProperties: [ + { name: 'opacity', value: '1' }, + { name: 'transform', value: 'scale(1.7)' } + ] + } + } + ] +}]`, + entrance: `[{ + trigger: 'viewEnter', + effects: [{ + triggerType: 'alternate', + keyframeEffect: { + name: 'slideFromLeft', + keyframes: [ + { opacity: 0, transform: 'translate(-120px, -50px)' }, + { opacity: 1, transform: 'translate(0, -50px)' } + ] + }, + duration: 1000, + easing: 'cubic-bezier(0.2, 0.8, 0.2, 1)', + fill: 'both' + }] +}]`, + mouseTrack: `[{ + trigger: 'pointerMove', + params: { hitArea: 'self' }, + effects: [ + { + key: 'card-target', + namedEffect: { + type: 'Tilt3DMouse', + maxAngle: 25, + perspective: 600 + } + }, + { + key: 'content-target', + namedEffect: { + type: 'Tilt3DMouse', + maxAngle: 60, + perspective: 300 + } + } + ] +}]`, + scrollytelling: `[{ + trigger: 'viewProgress', + effects: [ + // 1. Vertical Spin + { + key: 'orbit-y', + fill: 'both', + // Updated range to cover full entry to exit + rangeStart: { name: 'entry', offset: { value: 0, unit: 'percentage' } }, + rangeEnd: { name: 'exit', offset: { value: 100, unit: 'percentage' } }, + keyframeEffect: { + name: 'rotateY', + keyframes: [ + { transform: 'rotateY(0deg)' }, + { transform: 'rotateY(360deg)' } + ] + } + }, + // 2. Horizontal Spin + { + key: 'orbit-x', + fill: 'both', + rangeStart: { name: 'entry', offset: { value: 0, unit: 'percentage' } }, + rangeEnd: { name: 'exit', offset: { value: 100, unit: 'percentage' } }, + keyframeEffect: { + name: 'rotateX', + keyframes: [ + // Added slight wobble to make rotation visible on flat plane + { transform: 'rotateX(90deg) rotateZ(0deg)' }, + { transform: 'rotateX(90deg) rotateZ(360deg)' } + ] + } + }, + // 3. Diagonal 1 (+45) + { + key: 'orbit-diag-1', + fill: 'both', + rangeStart: { name: 'entry', offset: { value: 0, unit: 'percentage' } }, + rangeEnd: { name: 'exit', offset: { value: 100, unit: 'percentage' } }, + keyframeEffect: { + name: 'rotateDiag1', + keyframes: [ + { transform: 'rotateZ(45deg) rotateY(0deg)' }, + { transform: 'rotateZ(45deg) rotateY(360deg)' } + ] + } + }, + // 4. Diagonal 2 (-45) + { + key: 'orbit-diag-2', + fill: 'both', + rangeStart: { name: 'entry', offset: { value: 0, unit: 'percentage' } }, + rangeEnd: { name: 'exit', offset: { value: 100, unit: 'percentage' } }, + keyframeEffect: { + name: 'rotateDiag2', + keyframes: [ + { transform: 'rotateZ(-45deg) rotateY(0deg)' }, + { transform: 'rotateZ(-45deg) rotateY(-360deg)' } + ] + } + } + ] +}]`, +}; + +document.addEventListener('DOMContentLoaded', () => { + // 1. Populate code blocks with plain text + const displays = document.querySelectorAll('.codeDisplay'); + displays.forEach((d) => { + const id = d.getAttribute('data-snippet'); + if (snippets[id]) { + d.textContent = snippets[id]; + // Syntax highlight (highlight.js) after injecting text + d.classList.add('language-javascript', 'whitespace-pre'); + if (window.hljs) window.hljs.highlightElement(d); + } + }); + + // 2. Updated Copy Logic: Copies the specific code for the card + document.querySelectorAll('.copy-btn').forEach((btn) => { + btn.addEventListener('click', () => { + // Find the sibling code block + const container = btn.closest('.group'); // Find parent container + const codeElement = container.querySelector('.codeDisplay'); + + if (codeElement) { + const codeText = codeElement.textContent; + + // Create temp element to copy + const textArea = document.createElement('textarea'); + textArea.value = codeText; + document.body.appendChild(textArea); + textArea.select(); + try { + document.execCommand('copy'); + const textSpan = btn.querySelector('.copy-text'); + const originalText = textSpan.innerText; + textSpan.innerText = 'COPIED!'; + setTimeout(() => { + textSpan.innerText = originalText; + }, 2000); + } catch (err) { + console.error('Failed to copy', err); + } + document.body.removeChild(textArea); + } + }); + }); +}); diff --git a/apps/website/examples.html b/apps/website/examples.html new file mode 100644 index 00000000..50a7e27f --- /dev/null +++ b/apps/website/examples.html @@ -0,0 +1,146 @@ + + + + + + @wix/interact - Examples + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + +
+
+ + + +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + diff --git a/index.html b/apps/website/index.html similarity index 52% rename from index.html rename to apps/website/index.html index f4667af4..d2a37a2d 100644 --- a/index.html +++ b/apps/website/index.html @@ -13,33 +13,6 @@ - - - - - + + + + + + + + - + - - + +
+
-
+
- -
+ +
-
- +
+
-

+

Interact

-

+

Powers the web with motion.

@@ -102,19 +73,19 @@

+
-
+

Create high-performance, beautiful web animations with Interact — from simple micro-interactions to complex, scroll-driven storytelling. Designed for designers and developers alike, with a robust, production-ready API

-
- + @@ -127,16 +98,16 @@

-
+
-
+
-
+
-

+

Unleash high performance web motion


@@ -147,8 +118,8 @@

- +
+
@@ -162,18 +133,18 @@

-
+
-
+
-
+
-

+

Motion That Fits Every Screen


@@ -185,7 +156,7 @@

+
@@ -209,7 +180,7 @@

-
+
@@ -249,8 +220,7 @@

- -

+

Beautiful Effects,
Ready @@ -259,12 +229,12 @@

-

+

Interact comes with a rich and diverse set of named effects, each designed to bring your website to life. Apply them instantly to text, images, cards, buttons, and entire sections, and create smooth, expressive animations that engage users and elevate every interaction.

-
+

Watch it move

@@ -280,11 +250,11 @@

-
+
-
+
@@ -302,69 +272,69 @@

-
- +
+
-
-

Trigger Your Motion

+
+

Trigger Your Motion


-

Animate elements based on user interactions - from micro-motion touches to large-scale expressive interactions.

-
+

Animate elements based on user interactions - from micro-motion touches to large-scale expressive interactions.

+
-
-
-
- +
+
+
+
- +
- +
-
-
+
+

Hover

Add playful hover interactions to buttons, images, or UI elements.

-
- -
- +
+
-
-
-
+
+
+
- +
- +
- +
@@ -372,85 +342,85 @@

-
+
+

Click

Trigger motion on user clicks for buttons, dialogs, or interactive components.

-
- -
+
- +
-
- -
+
+ +
- +
- +
- +
- +
-
-
+
+

View Enter (AKA Scroll Triggered Animations and Entrance Animations)

Animate elements as they enter the viewport, or pause infinite animations as they exit — perfect for cards, sections, or hero elements.

-
- -
+
- +
-
-
-
+
+
+
- + - -
+ +
- -
+ +
@@ -462,19 +432,19 @@

-
+
+

Pointer movement (AKA Trigger by Mouse)

Move elements relative to cursor position for depth and engagement.

-
- -
+
- +
@@ -482,42 +452,42 @@

-
+
+
-
+
-
+
-
+
- -
+ +
- -
+ +
- -
+ +
- -
+ +
-
+
Scroll to Spin
@@ -526,19 +496,19 @@

-
+
+

Scroll

Animate elements or sections based on scroll position to create immersive, narrative-driven experiences.

-
- -
+
- +
@@ -551,28 +521,28 @@

-
+
-
-

Effects. Tailored to Your Vision

+
+

Effects. Tailored to Your Vision


-

+

From a curated set of ready-to-use effects to fully custom motion, Interact gives you control to tailor animations precisely to your vision.

-
+
-
-

Named Effects (Built-In Library)

-

+

+

Named Effects (Built-In Library)

+

A rich collection of ready-to-use effects that bring elements to life instantly. Perfect for rapid creation while ensuring polished, consistent motion.

@@ -580,10 +550,10 @@

-
-

Custom Keyframes

+
+

Custom Keyframes


-

+

Gain control to design animations with precision. Write your own keyframes and animate any property exactly as needed.

@@ -591,9 +561,9 @@

-
-

Custom Effects (Full Extensibility)

-

+

+

Custom Effects (Full Extensibility)

+

Build reusable effects or integrate advanced motion systems — including physics-based animations, shaders, and WebGL.

@@ -606,113 +576,113 @@

-
-
-
-
-

Hey, Good Looking

+
+
+
+
+

Hey, Good Looking


-

Get inspired by these motion-forward Wix websites created with Interact

+

Get inspired by these motion-forward Wix websites created with Interact

-
-
-
-

Cosmic Scroll

+
+
+
+

Cosmic Scroll

- Go to Website ↗ + Go to Website ↗
-
-
-
-

Animal Motion

+
+
+
+

Animal Motion

- Go to Website ↗ + Go to Website ↗
-
-
-
-

Year in Review

+
+
+
+

Year in Review

- Go to Website ↗ + Go to Website ↗
-
-
-
-

Starcast Project

+
+
+
+

Starcast Project

- Go to Website ↗ + Go to Website ↗
-
-
-
-

Product Store

+
+
+
+

Product Store

- Go to Website ↗ + Go to Website ↗
-
-
-
-

Software Company

+
+
+
+

Software Company

- Go to Website ↗ + Go to Website ↗
-
-
-
-

Coming Soon Landing Page

+
+
+
+

Coming Soon Landing Page

- Go to Website ↗ + Go to Website ↗
-
-
-
-

Artist

+ -
-
-
-

Electronics Store

+
+
+
+

Electronics Store

- Go to Website ↗ + Go to Website ↗
@@ -724,43 +694,32 @@

Electronics Store

-

diff --git a/apps/website/package.json b/apps/website/package.json new file mode 100644 index 00000000..89731cd0 --- /dev/null +++ b/apps/website/package.json @@ -0,0 +1,13 @@ +{ + "name": "@wix/interact-website", + "version": "0.0.0", + "private": true, + "scripts": { + "dev": "npx http-server . -p 3000", + "dev:public": "npx http-server . -p 3000 -c-1", + "build": "../../scripts/build-landing.sh", + "preview": "npx http-server . -p 3000", + "lint": "prettier --check '**/*.{js,mjs,css,html,json}'", + "format": "prettier --write '**/*.{js,mjs,css,html,json}'" + } +} diff --git a/assets/snippets.js b/assets/snippets.js deleted file mode 100644 index 90a844f3..00000000 --- a/assets/snippets.js +++ /dev/null @@ -1,178 +0,0 @@ -// Code Snippet Logic -// Define specific snippets for each interaction -const snippets = { - hover: `trigger: 'hover', -ֿeffects: [{ - keyframeEffect: { - keyframes: [ - { transform: 'scale(1)', offset: 0 }, - { transform: 'scale(1.02)', offset: 1 } - ] - }, - duration: 400, - easing: 'ease-out', - fill: 'both' -}]`, - click: `trigger: 'click', -effects: [ - { - selector: '.fill-circle', - transition: { - duration: 500, - easing: 'ease-out', - styleProperties: [{ name: 'transform', value: 'scale(1)' }] - } - }, - { - key: 'click-ripple-1', - transition: { - duration: 800, - easing: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)', - styleProperties: [ - { name: 'opacity', value: '1' }, - { name: 'transform', value: 'scale(1.7)' } - ] - } - } -]`, - entrance: `trigger: 'viewEnter', -effects: [{ - keyframeEffect: { - name: 'slideFromLeft', - keyframes: [ - { opacity: 0, transform: 'translate(-120px, -50px)' }, - { opacity: 1, transform: 'translate(0, -50px)' } - ] - }, - duration: 1000, - easing: 'cubic-bezier(0.2, 0.8, 0.2, 1)' -}]`, - mouseTrack: `trigger: 'pointerMove', -params: { hitArea: 'self' }, -effects: [ - { - key: 'card-target', - namedEffect: { - type: 'Tilt3DMouse', - maxAngle: 25, - perspective: 600 - } - }, - { - key: 'content-target', - namedEffect: { - type: 'Tilt3DMouse', - maxAngle: 60, - perspective: 300 - } - } -]`, - scrollytelling: ` - trigger: 'viewProgress', -effects: [ -// 1. Vertical Spin -{ - key: 'orbit-y', - fill: 'both', - // Updated range to cover full entry to exit - rangeStart: { name: 'entry', offset: { value: 0, unit: 'percentage' } }, - rangeEnd: { name: 'exit', offset: { value: 100, unit: 'percentage' } }, - keyframeEffect: { - name: 'rotateY', - keyframes: [ - { transform: 'rotateY(0deg)' }, - { transform: 'rotateY(360deg)' } - ] - } -}, -// 2. Horizontal Spin -{ - key: 'orbit-x', - fill: 'both', - rangeStart: { name: 'entry', offset: { value: 0, unit: 'percentage' } }, - rangeEnd: { name: 'exit', offset: { value: 100, unit: 'percentage' } }, - keyframeEffect: { - name: 'rotateX', - keyframes: [ - // Added slight wobble to make rotation visible on flat plane - { transform: 'rotateX(90deg) rotateZ(0deg)' }, - { transform: 'rotateX(90deg) rotateZ(360deg)' } - ] - } -}, -// 3. Diagonal 1 (+45) -{ - key: 'orbit-diag-1', - fill: 'both', - rangeStart: { name: 'entry', offset: { value: 0, unit: 'percentage' } }, - rangeEnd: { name: 'exit', offset: { value: 100, unit: 'percentage' } }, - keyframeEffect: { - name: 'rotateDiag1', - keyframes: [ - { transform: 'rotateZ(45deg) rotateY(0deg)' }, - { transform: 'rotateZ(45deg) rotateY(360deg)' } - ] - } -}, -// 4. Diagonal 2 (-45) -{ - key: 'orbit-diag-2', - fill: 'both', - rangeStart: { name: 'entry', offset: { value: 0, unit: 'percentage' } }, - rangeEnd: { name: 'exit', offset: { value: 100, unit: 'percentage' } }, - keyframeEffect: { - name: 'rotateDiag2', - keyframes: [ - { transform: 'rotateZ(-45deg) rotateY(0deg)' }, - { transform: 'rotateZ(-45deg) rotateY(-360deg)' } - ] - } -} -] - `, -}; - -document.addEventListener('DOMContentLoaded', () => { - // 1. Populate code blocks with plain text - const displays = document.querySelectorAll('.codeDisplay'); - displays.forEach((d) => { - const id = d.getAttribute('data-snippet'); - if (snippets[id]) { - d.textContent = snippets[id]; - // Syntax highlight (highlight.js) after injecting text - d.classList.add('language-javascript'); - if (window.hljs) window.hljs.highlightElement(d); - } - }); - - // 2. Updated Copy Logic: Copies the specific code for the card - document.querySelectorAll('.copy-btn').forEach((btn) => { - btn.addEventListener('click', () => { - // Find the sibling code block - const container = btn.closest('.group'); // Find parent container - const codeElement = container.querySelector('.codeDisplay'); - - if (codeElement) { - const codeText = codeElement.textContent; - - // Create temp element to copy - const textArea = document.createElement('textarea'); - textArea.value = codeText; - document.body.appendChild(textArea); - textArea.select(); - try { - document.execCommand('copy'); - const textSpan = btn.querySelector('.copy-text'); - const originalText = textSpan.innerText; - textSpan.innerText = 'COPIED!'; - setTimeout(() => { - textSpan.innerText = originalText; - }, 2000); - } catch (err) { - console.error('Failed to copy', err); - } - document.body.removeChild(textArea); - } - }); - }); -}); diff --git a/assets/styles.css b/assets/styles.css deleted file mode 100644 index da1f8544..00000000 --- a/assets/styles.css +++ /dev/null @@ -1,562 +0,0 @@ -/* Register CSS custom properties for animation interpolation */ -@property --spacing { - syntax: ''; - inherits: true; - initial-value: 0px; -} - -/* Essential Overrides */ -body { - overflow-x: hidden; - background-color: #111111; /* Dark background matches hero */ - color: #ffffff; - -webkit-font-smoothing: antialiased; -} - -interact-element { - display: block; -} - -/* HERO GRID STYLES */ -.hero-grid-container { - display: grid; - /* Dense grid of small lines */ - grid-template-columns: repeat(auto-fill, minmax(40px, 1fr)); - grid-template-rows: repeat(auto-fill, minmax(40px, 1fr)); - width: 100%; - height: 100%; - /* Masking the edges slightly */ - mask-image: linear-gradient(to bottom, black 80%, transparent 100%); - -webkit-mask-image: linear-gradient(to bottom, black 80%, transparent 100%); -} - -.text-stroke-black { - -webkit-text-stroke-width: 12px; - -webkit-text-stroke-color: #111111; - paint-order: stroke fill; - filter: drop-shadow(0px 0px 2px #111111); -} - -.grid-cell { - display: flex; - align-items: center; - justify-content: center; - width: 100%; - height: 100%; -} - -.grid-line { - width: 2px; - height: 24px; - background: linear-gradient(to bottom, rgba(255, 255, 255, 0.05) 0%, #ffffff 100%); - transform-origin: top; - will-change: transform; - border-radius: 2px; -} - -@media (max-width: 1000px) { - .grid-line { - height: 12px; - } -} - -/* CRITICAL ANIMATION STYLES (Preserved for other sections) */ -.sticky-wrapper { - position: sticky; - top: 0; - height: 100vh; - overflow: clip; /* Required for ViewTimeline */ - display: flex; - align-items: center; - justify-content: center; -} - -/* Card Spread Styles */ -.spread-card { - position: absolute; - top: 50%; - left: 50%; - width: 350px; - height: 500px; - background-size: contain; - background-repeat: no-repeat; - background-position: center; - will-change: transform; - filter: drop-shadow(0 25px 25px rgba(0, 0, 0, 0.15)); -} - -.spread-card-init { - transform: translate(-50%, -50%); -} -/* Z-Index desktop card spread */ -[data-interact-key='spread-card-0'] .spread-card { - z-index: 5; -} -[data-interact-key='spread-card-1'] .spread-card { - z-index: 4; -} -[data-interact-key='spread-card-2'] .spread-card { - z-index: 3; -} -[data-interact-key='spread-card-3'] .spread-card { - z-index: 2; -} -[data-interact-key='spread-card-4'] .spread-card { - z-index: 1; -} - -/* Mobile overrides for card spread */ -@media (max-width: 1000px) { - #spread-section { - height: 500vh !important; - } - - .spread-card { - top: 15vh; - left: 50%; - width: 85vw; - height: 65vh; - max-width: 400px; - background-size: contain; - box-shadow: none; - border-radius: 0; - transform: translateX(-50%); - } - [data-interact-key='spread-card-0'] .spread-card { - transform: translateX(-50%) translateY(0); - z-index: 1; - } - [data-interact-key='spread-card-1'] .spread-card { - transform: translateX(-50%) translateY(100vh); - z-index: 2; - } - [data-interact-key='spread-card-2'] .spread-card { - transform: translateX(-50%) translateY(100vh); - z-index: 3; - } - [data-interact-key='spread-card-3'] .spread-card { - transform: translateX(-50%) translateY(100vh); - z-index: 4; - } - [data-interact-key='spread-card-4'] .spread-card { - transform: translateX(-50%) translateY(100vh); - z-index: 5; - } -} - -/* TUNNEL EFFECT STYLES */ -.tunnel-container { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - /* pointer-events: none; */ - z-index: 0; - display: flex; - justify-content: center; - align-items: center; -} - -/* Updated Tunnel Circle Styles */ -.tunnel-circle { - display: flex; - justify-content: center; - align-items: center; - - border-radius: 50%; - /* The border color is now set dynamically in JS for the depth/gradient effect */ - border: 1px solid rgba(255, 255, 255, 1); - will-change: transform; - - opacity: 0.6; - - flex-shrink: 0; - max-width: none; - max-height: none; - - width: 93%; - height: 93%; - - /* Enhanced 3D shadow effect */ - box-shadow: - inset 3px 3px 6px rgba(255, 255, 255, 0.3), - inset -3px -3px 6px rgba(0, 0, 0, 0.8); -} - -/* Updated constraints for the tunnel circles inside the new container */ -.tunnel-container > .tunnel-circle { - width: 50vmax; - height: 50vmax; - margin: auto; -} - -/* Horizontal Scroll Styles */ -.horizontal-track { - display: flex; - gap: 8rem; - align-items: flex-end; - padding-left: calc(50vw - 300px); - padding-right: 50vw; - will-change: transform; - width: max-content; -} - -.h-card { - width: 600px; - height: 400px; - flex-shrink: 0; - background-size: cover; - background-position: center; - border-radius: 4px; - transform-origin: center center; - will-change: transform; - /* Added for hover content */ - position: relative; - overflow: hidden; - border: 1px solid rgba(255, 255, 255, 0.8); -} - -/* Mobile responsive for horizontal scroll cards */ -@media (max-width: 1000px) { - .horizontal-track { - gap: 4vw; - padding-left: 2.5vw; /* Centers 95vw card: 50vw - 47.5vw */ - padding-right: 50vw; - } - - .h-card { - width: 95vw; - height: 63.33vw; /* Maintains 1.5:1 aspect ratio */ - border-radius: 12px; - } -} - -/* NEW COMPONENT STYLES */ -.custom-scrollbar::-webkit-scrollbar { - width: 3px; - height: 3px; -} -.custom-scrollbar::-webkit-scrollbar-track { - background: transparent; -} -.custom-scrollbar::-webkit-scrollbar-thumb { - background: #bde1ff; - border-radius: 3px; -} -.custom-scrollbar::-webkit-scrollbar-thumb:hover { - background: #bde1ff; -} - -/* Existing Circle Style for Primitives */ -.circle { - width: 16rem; /* w-64 */ - height: 16rem; /* h-64 */ - background-color: transparent; - border: 1px solid white; /* Ensures white outline on white bg too? No, needs contrast */ - border-radius: 50%; - position: relative; - overflow: hidden; - will-change: transform; -} - -.fill-circle { - width: 100%; - height: 100%; - /* Reverted back to solid white */ - background-color: white; - border-radius: 50%; - transform: scale(0); - will-change: transform; -} - -/* Utility */ -.card-shadow { - box-shadow: 0 20px 40px -10px rgba(0, 0, 0, 0.15); -} - -.hover-target { - box-shadow: inset 0 0 0 160px #f3f4f6; -} - -/* Initial states for ripples: hidden and slightly smaller */ -.ripple-init { - opacity: 0; - transform: scale(0.8); -} - -/* --- MOUSE TRACK TUNNEL STYLES --- */ -.preserve-3d { - transform-style: preserve-3d; -} - -.ring-base { - display: flex; - justify-content: center; - align-items: center; - border-radius: 50%; - border: 1px solid rgba(255, 255, 255, 0.8); - /* pointer-events: none; */ - position: absolute; -} - -.tunnel-static { - width: 14rem; /* Scaled to fit card */ - height: 14rem; - border-width: 2px; - border-color: rgba(255, 255, 255, 0.4); - z-index: 10; -} - -.tunnel-moving { - width: 85%; - height: 85%; - position: relative; - display: flex; - justify-content: center; - align-items: center; -} - -/* --- PYRAMID STYLES (NEW) --- */ -.pyramid-wrapper { - position: relative; - width: 400px; - height: 400px; - transform-style: preserve-3d; - z-index: 20; - transform: scale(0.76); - transform-origin: center; -} -@media (max-width: 740px) { - .pyramid-wrapper { - transform-origin: top center; - transform: scale(0.45); - } -} - -/* Target Element for Rotation */ -.pyramid-rotator { - width: 100%; - height: 100%; - position: relative; - transform-style: preserve-3d; - transform-origin: 50% 57%; - /* Starts at 0, driven by scroll */ - transform: rotateX(0deg) rotateY(0deg); -} - -.face { - position: absolute; - width: 400px; - height: 400px; - box-sizing: border-box; - pointer-events: none; - display: flex; - justify-content: center; - align-items: flex-end; - backface-visibility: visible; - transform-origin: bottom center; -} - -.face svg { - width: 100%; - height: 100%; - overflow: visible; -} - -.face polygon, -.face rect { - vector-effect: non-scaling-stroke; - stroke: #ffffff; - stroke-width: 1.5; -} - -/* Geometry */ -.face.front { - transform: translateZ(200px) rotateX(30deg); -} -.face.back { - transform: rotateY(180deg) translateZ(200px) rotateX(30deg); -} -.face.right { - transform: rotateY(90deg) translateZ(200px) rotateX(30deg); -} -.face.left { - transform: rotateY(-90deg) translateZ(200px) rotateX(30deg); -} -.face.base { - transform-origin: center center; - transform: rotateX(90deg) translateZ(-200px); - align-items: center; -} - -/* --- TEXT/CONTENT STYLES (NEW) --- */ -.content-layer { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - z-index: 10; - text-align: center; - mix-blend-mode: normal; - width: 100%; -} - -.scene-container { - flex-direction: column; - perspective: 500px; - gap: 2rem; -} - -/* Modal Styles */ -.modal-overlay { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(255, 255, 255, 0.8); - backdrop-filter: blur(12px); - z-index: 100; - display: none; - justify-content: center; - align-items: center; - opacity: 0; - transition: opacity 0.3s ease; -} - -.modal-overlay.active { - display: flex; - opacity: 1; -} - -.modal-content { - background: white; - border: 1px solid #eee; - width: 90%; - max-width: 800px; - max-height: 80vh; - overflow-y: auto; - position: relative; - box-shadow: 0 30px 60px rgba(0, 0, 0, 0.1); - border-radius: 16px; -} - -.code-block { - background: #f9f9f9; - padding: 1.5rem; - font-family: 'Menlo', 'Monaco', monospace; - font-size: 0.8rem; - line-height: 1.5; - color: #333; - overflow-x: auto; - border-left: 2px solid #111; - white-space: pre; -} - -.hljs { - background: transparent !important; -} - -/* --- MENGER SPONGE STYLES --- */ - -/* Mobile fix: Give sections more vertical space to prevent clipping */ -@media (max-width: 1000px) { - /* Cube section */ - [data-interact-key='cube-section'] > section { - min-height: 100vh; - height: auto; - padding-bottom: 6rem; - } - - [data-interact-key='cube-section'] .sticky-wrapper { - position: relative; - height: auto; - min-height: 100vh; - padding-top: 2rem; - padding-bottom: 6rem; - } - - /* Performance section */ - [data-interact-key='performance-section'] > section { - min-height: 100vh; - height: auto; - padding-bottom: 4rem; - } - - [data-interact-key='performance-section'] .sticky-wrapper { - position: relative; - height: auto; - min-height: 100vh; - padding-top: 2rem; - padding-bottom: 6rem; - } -} - -.scene-container-2 { - perspective: 1200px; - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - transform-style: preserve-3d; - /* Removed overflow: hidden to allow the sponge to explode outside its box */ -} - -/* Sponge interaction wrapper */ -.interact-sponge-wrapper { - width: 320px; - height: 320px; - transform-style: preserve-3d; - position: relative; - cursor: pointer; - z-index: 10; -} - -.sponge-root { - position: absolute; - top: 50%; - left: 50%; - width: 0; - height: 0; - transform-style: preserve-3d; - will-change: transform; -} - -.voxel { - position: absolute; - top: 0; - left: 0; - width: 60px; - height: 60px; - transform-style: preserve-3d; - will-change: transform; - margin-left: -30px; - margin-top: -30px; - pointer-events: none; - - /* CSS Var --spacing is animated by Interact via WAAPI */ - transform: translate3d( - calc(var(--x) * (60px + var(--spacing))), - calc(var(--y) * (60px + var(--spacing))), - calc(var(--z) * (60px + var(--spacing))) - ); -} - -.cube-face { - position: absolute; - box-sizing: border-box; - width: 60px; - height: 60px; - /* Fills are now 70% opacity */ - background-color: rgba(23, 23, 23, 0.7); - border: 1px solid rgba(255, 255, 255, 0.9); - backface-visibility: hidden; - transform-origin: center center; - pointer-events: none; -} - -::-webkit-scrollbar { - width: 0px; -} diff --git a/eslint.config.mjs b/eslint.config.mjs index e21083db..424d89b5 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -13,6 +13,8 @@ export default [ ignores: [ '**/dist/**', '**/build/**', + // Website copies of package builds (see scripts/build-landing.sh) — not authored here + 'apps/website/assets/lib/**', '**/node_modules/**', '**/*.d.ts', '**/coverage/**', diff --git a/package.json b/package.json index d2b6edd9..70889503 100644 --- a/package.json +++ b/package.json @@ -8,12 +8,13 @@ "build": "yarn workspaces foreach --all --topological --include 'packages/*' run build && yarn workspaces foreach --all --include 'apps/*' run build", "lint": "eslint . --max-warnings=0", "test": "yarn workspaces foreach --all --include 'packages/*' run test", + "dev:website": "yarn workspace @wix/interact-website run dev", "dev:docs": "yarn workspace @wix/interact-docs run dev", "dev:demo": "yarn workspace @wix/interact-demo run dev", "format": "prettier . --write", "format:check": "prettier . --check", - "serve": "npx serve . -l 3000", - "serve:public": "npx serve . -l tcp://0.0.0.0:3000" + "serve": "npx serve apps/website -l 3000", + "serve:public": "npx serve apps/website -l tcp://0.0.0.0:3000" }, "repository": { "type": "git", diff --git a/scripts/build-landing.sh b/scripts/build-landing.sh index 260961c8..cfb9027d 100755 --- a/scripts/build-landing.sh +++ b/scripts/build-landing.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Builds all packages and copies the dist files into assets/lib/ +# Builds all packages and copies the dist files into apps/website/assets/lib/ # so that index.html can load Interact and Presets from local bundles. # # Usage: @@ -9,7 +9,7 @@ set -euo pipefail REPO_ROOT="$(git rev-parse --show-toplevel)" -LIB_DIR="$REPO_ROOT/assets/lib" +LIB_DIR="$REPO_ROOT/apps/website/assets/lib" yarn workspaces foreach --all --topological --include 'packages/*' run build @@ -24,4 +24,4 @@ cp "$REPO_ROOT/packages/interact/dist"/index-*.mjs "$LIB_DIR/interact/" cp "$REPO_ROOT/packages/motion-presets/dist/es/motion-presets.js" "$LIB_DIR/motion-presets/" cp "$REPO_ROOT/packages/motion/dist/es/motion.js" "$LIB_DIR/motion/" -echo "Landing page libraries copied to assets/lib/" +echo "Landing page libraries copied to apps/website/assets/lib/" diff --git a/yarn.lock b/yarn.lock index 4bd98bd3..bd3db145 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1379,6 +1379,12 @@ __metadata: languageName: unknown linkType: soft +"@wix/interact-website@workspace:apps/website": + version: 0.0.0-use.local + resolution: "@wix/interact-website@workspace:apps/website" + languageName: unknown + linkType: soft + "@wix/interact@npm:^2.2.2, @wix/interact@workspace:packages/interact": version: 0.0.0-use.local resolution: "@wix/interact@workspace:packages/interact"