No description
  • TypeScript 81.8%
  • JavaScript 13.9%
  • Dockerfile 1.8%
  • Liquid 1.3%
  • Shell 0.7%
  • Other 0.5%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-03 09:59:52 +03:00
app feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00
deploy chore: prepare VPS deployment and document validation 2026-10-03 09:59:52 +03:00
extensions/hover-swap feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00
prisma feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00
public feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00
scripts chore: prepare VPS deployment and document validation 2026-10-03 09:59:52 +03:00
storefront/src feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00
tests chore: prepare VPS deployment and document validation 2026-10-03 09:59:52 +03:00
.dockerignore chore: prepare VPS deployment and document validation 2026-10-03 09:59:52 +03:00
.editorconfig feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00
.env.example feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00
.eslintignore feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00
.eslintrc.cjs feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00
.gitattributes feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00
.gitignore feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00
.prettierignore feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00
.prettierrc.json feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00
DEPLOYMENT.md chore: prepare VPS deployment and document validation 2026-10-03 09:59:52 +03:00
docker-compose.yml chore: prepare VPS deployment and document validation 2026-10-03 09:59:52 +03:00
Dockerfile chore: prepare VPS deployment and document validation 2026-10-03 09:59:52 +03:00
env.d.ts feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00
LICENSE Initial commit 2026-10-03 06:01:05 +00:00
package-lock.json feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00
package.json feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00
playwright.config.ts feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00
README.md chore: prepare VPS deployment and document validation 2026-10-03 09:59:52 +03:00
shopify.app.toml feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00
shopify.web.toml feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00
TASK.md chore: prepare VPS deployment and document validation 2026-10-03 09:59:52 +03:00
THIRD_PARTY_LICENSES.md feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00
tsconfig.json feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00
VALIDATION.md chore: prepare VPS deployment and document validation 2026-10-03 09:59:52 +03:00
vite.config.ts feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00
vitest.config.ts feat: implement Hover Swap app and storefront extension 2026-10-03 09:56:25 +03:00

Hover Swap

Hover Swap displays the next product photo when a mouse or trackpad hovers over a product-card image, and restores the original view on leave. It works on collection/search grids and recommendation cards, including cards on a product page. The main PDP gallery is excluded. Touch-only devices perform no application product/image preparation.

Architecture

  • Official Shopify React Router template architecture, React, App Bridge and Polaris web components. Authentication/token exchange and webhook HMAC verification use @shopify/shopify-app-react-router.
  • Prisma stores Shopify sessions only. Local SQLite and production PostgreSQL have separate schemas/migrations with the same Session fields, including expiring offline token support.
  • Theme App Extension App Embed serves assets through Shopify CDN. Enable/disable, Fade/Instant and 0–1000 ms duration are configured in Theme Editor. Shopify loads the schema JavaScript asset non-blockingly.
  • Framework-free TypeScript storefront, compiled by esbuild. No product synchronization, Storefront API token, image proxy or page-view backend requests. No Admin API product/theme scopes are required.
  • Authenticated uninstall, scopes-update and mandatory privacy webhooks. No shopper data is collected. Shop redaction/uninstall removes sessions; repeated deliveries are safe.

The initial scaffold came from Shopify's official React Router template, with demo routes removed. Platform references: extension configuration and editor deep links, Ajax Product API, and Polaris web components.

Project structure

app/                         Embedded admin, auth, webhook routes, health
extensions/hover-swap/        Liquid embed, locales, generated CDN assets
storefront/src/               Detection, cache, loading, rendering, observers
prisma/schema.prisma         Local SQLite sessions
prisma/postgresql/            Production schema and PostgreSQL migrations
scripts/                     Storefront build, fixture server, consistency checks
tests/                       Unit and Chromium storefront tests
deploy/                      VPS reverse proxy and deployment helper
Dockerfile, docker-compose.yml

Requirements and Windows local setup

Node.js 24, npm, Git, a Shopify Dev Dashboard organization, a development app and development store with an Online Store theme. Docker is only needed for production-container testing. Work in this repository; commands below use PowerShell.

npm ci
Copy-Item .env.example .env
npm run setup
npm run build:storefront

setup generates Prisma Client and applies SQLite migrations. DATABASE_URL=file:dev.sqlite resolves relative to prisma/schema.prisma. Do not commit .env, databases or tokens.

For a local backend smoke test, placeholder API key/secret from .env.example are sufficient; they cannot authenticate a Shopify store:

npm run dev:local
# http://localhost:8012 and http://localhost:8012/health

Ports 8012 (backend), 8013 (fixture) and 64999 (local Vite HMR) are separate from Bagira 8000 and Ygen 8001. Check availability before starting. In production, Compose publishes only loopback 8012. Use HOVER_SWAP_PORT to change the host port.

Connect a Shopify development app/store

Shopify CLI is installed as a project dev dependency; run it with npm run shopify -- ... or npx shopify .... This repository intentionally ships a zero placeholder client_id and example.com URLs. They must be linked before Shopify development or release:

  1. Sign in at Dev Dashboard, create/select an organization and development app, and create/select a development store. An account without an organization causes Cannot find a valid organization in CLI.
  2. Run npm run config:link and select that app. Review changes to shopify.app.toml; preserve the empty access scopes and webhook subscriptions.
  3. Set SHOPIFY_API_KEY, SHOPIFY_API_SECRET and development URL in your ignored .env from that app's credentials. npm run shopify -- app env show can display environment values locally; never paste secrets into tickets or Git.
  4. Run npm run dev -- --store your-development-store.myshopify.com. CLI provides a public tunnel, adjusts development URLs and previews the extension. Follow its install link for the development store.
  5. Open Hover Swap in Shopify admin. Click Enable in Theme Editor (documented activateAppId=API_KEY/hover-swap deep link), enable the embed and Save. Verify the switch in Theme Editor; admin reports installation, not a guessed activation state.
  6. Add two or more photos to a test product and hover its card on a collection page. Check PDP gallery and PDP recommendations separately.

For storefront changes, keep npm run watch:storefront running in a second terminal while npm run dev watches the extension assets. No manual theme edits are needed. Do not install in a live store or release the extension without approval.

Detection and safe fallback

Discovery starts from same-origin product links, including localized and collection-scoped routes. CardDetector combines known card shortcuts with structural scoring (semantic card/item names, repeated siblings, grid/list/search/recommendation context). Mixed-product ancestors and navigation/cart/gallery containers are rejected. ThemeAdapter is a small extension point for future compatibility improvements; generic detection remains the fallback.

ImageResolver searches the card media wrapper for a distinct, non-icon/swatches secondary image. It preloads a decorative overlay using that URL, preserving theme-owned DOM and image sources. If unavailable, ProductCache fetches window.Shopify.routes.root + products/HANDLE.js, shares pending promises and caches the second image or safe null result. At most four Ajax requests run concurrently, with an eight-second timeout. Failed products/images and one-photo products are skipped.

VisibilityObserver starts preparation within 300 px of the viewport. With no IntersectionObserver, preparation waits for first hover. HoverRenderer adds an absolutely positioned, pointer-transparent, empty-alt/aria-hidden overlay, follows image dimensions with ResizeObserver and respects reduced motion. CardRegistry prevents duplicate overlays and releases removed/replaced cards. MutationWatcher batches added subtrees and href changes instead of rescanning the document. Shopify section-load events are supported. Destroy/reinitialization removes owned overlays and listeners.

Conservative detection deliberately skips ambiguous structures, oversized hero media, swatches, mixed-product containers and media wrappers with unrelated text/controls. Unknown themes cannot be guaranteed compatible without store testing. Native theme hover effects remain theme-owned; test their interaction in the selected theme. No paid-plan/billing behavior is included in this free MVP; future billing can be isolated behind authenticated backend routes.

Add ?hswap_debug=1 (or &hswap_debug=1) to a storefront URL for [HoverSwap] console diagnostics. Production logging is silent by default.

Commands and validation

npm run check                 # config consistency, typecheck, lint, format, unit tests, build
npx playwright install chromium
npm run test:browser          # automatic fixture server on 127.0.0.1:8013
npm run preview:storefront    # inspect offline fixture with colored image placeholders on port 8013
npm run shopify -- app config validate
npm run shopify -- app build  # local Theme Check and extension packaging

npm run build builds both storefront CDN assets and React Router server/client. Commit the generated extension assets; no source maps or framework dependencies are sent to the storefront. Initial minified storefront JavaScript is approximately 9.98 KB / 3.90 KB gzip, CSS 0.26 KB / 0.19 KB gzip; see VALIDATION.md for measured results.

Unit tests cover URL/image validation, generic detection, PDP exclusion, request deduplication/concurrency, safe errors and visibility gating. Chromium tests cover actual rendering, card geometry, clicks, original source preservation, dynamic cards, gallery/recommendation distinction, lazy Ajax and touch-only startup. Fixtures simulate Shopify responses; real OAuth, store installation and theme compatibility require a linked development store.

Environment

Variable Purpose
SHOPIFY_API_KEY App client ID, also used for App Bridge/editor link
SHOPIFY_API_SECRET Shopify app secret used by the official auth framework
SHOPIFY_APP_URL Public tunnel URL in development; HTTPS app origin in production
DATABASE_URL SQLite locally; PostgreSQL in production
NODE_ENV development or production
PORT Node server port, default deployment port 8012
POSTGRES_PASSWORD Compose database password, production only
HOVER_SWAP_PORT Compose loopback host port, default 8012

There is no custom session-cookie signing implementation, so no invented SESSION_SECRET is needed. Shopify manages auth through its official library. Keep scopes in app TOML; this MVP requests none. Server logs contain event, level, shop/topic and timestamp, never webhook bodies or tokens. /health checks database connectivity and returns only ok or unavailable.

Production

The multi-stage Node 24 Docker image uses PostgreSQL Prisma Client, runs migrations before starting, and runs as an unprivileged user. Compose contains only app and database, with a private database network/volume. Host Nginx terminates HTTPS and proxies to the loopback app port. See DEPLOYMENT.md for VPS preparation, releases, backups and rollback. shopify app deploy releases app configuration/extensions; it does not host this backend. No external deployment is performed by this task.