gclass-anims 1.0.0-beta.22 → 1.0.0-beta.22.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  All notable changes to `gclass-anims` will be documented in this file.
4
4
 
5
+ ## [1.0.0-beta.22.2] - 2026-09-06
6
+ - Fixed `m:order` (and any `bp:order` / `bp:priority-*` / `bp:ease-*` / `bp:time-*`) disabling the entire `scroll` animation on smaller screens — `Listeners.js:1200,1232,1262` previously wrapped `setupScroll` / `playText` scroll variants in generic `runWithBreakpoint(el, ...)` which gated on *any* `bp:*` class on the element (so `m:order` gated `scroll`). Now uses per-animation `qAllAllVariants` + `runWithBreakpointForSel` (`".scroll"`, `".scroll-progress"`, `tSel`) so only a true `m:scroll` / `m:spawn-*` gates its own animation; unrelated modifiers like `m:order` only affect `hasGClass("order")` / `readTiming` (stagger) and `spawn-*` + `scroll` still play on all sizes (without stagger on <m). Keeps live `gsap.matchMedia` handling for true breakpoint-gated scroll. Reverts `HowWorkSection.jsx` workaround from `m:order` back to plain `order` for the default staggered landing.
7
+
8
+ ## [1.0.0-beta.22.1] - 2026-09-06
9
+ - Fixed `.order` stagger grouping bug — `Listeners.js:76` `wrapQAll` now preserves DOM order via `body *` filter + `elementMatchesSel` (was `Set([...spawn-up], [...spawn-down])` grouping by type, now top-to-bottom as `qAll` does). Fixes `doc` `spawn` and landing `order` appearing all over.
10
+ - No API change from `beta.22`.
11
+
5
12
  ## [1.0.0-beta.22] - 2026-09-06
6
13
  - Added responsive breakpoints — `Config.js:75` `defaults.breakpoints {xs:475,s:640,m:768,l:1024,xl:1280}` (single-letter `s/m/l` avoids Tailwind `sm/md/lg` collision). Usage `m:spawn-up`, `l:float`, `xs:spawn-up`. Gating is live via `gsap.matchMedia` (`Listeners.js:26,202`).
7
14
  - Added breakpoint-aware modifiers — `m:amount-20`, `m:time-2`, `m:ease-bounce`, `m:priority-3`, `m:chars-[...]`, `m:spawn-num-10` etc. Mobile-first largest active wins (`Listeners.js:237` `getActivePrefixedClass`, `Animations.js:13` lazy helpers). Covers `amount-`, `time-`, `priority-`, `edelay-`, `etime-`, `stagger-`, `fill-time-`, `reveal-delay-`, `spawn-num-`, `progress-start-`, etc.
@@ -9,6 +16,7 @@ All notable changes to `gclass-anims` will be documented in this file.
9
16
  - Added ESM + CJS dual build — `vite.lib.config.js` (Vite lib) builds `dist/gclass.esm.js` + `dist/gclass.cjs` (`gsap` external); `package.json:4` bumped to `beta.22`, `main/module` point to `dist/`, `exports: {import, require}`, `sideEffects:false`, `prepublishOnly: build`.
10
17
  - Fixed CJS `gsap` interop — `Animations.js:2`/`Listeners.js:1`/`AnimToggle.js:3`/`CustomAnims.js:1` now `import {gsap} from 'gsap'` (named import) for correct `require('gsap').gsap` interop.
11
18
  - Added `dev-react-strict` test harness — `vitest` + `jsdom` + `src/__tests__/breakpoints.test.jsx` (14 tests: gating, modifiers, Tailwind coexistence, StrictMode) + `BreakpointHarness.jsx` visual; `vite.config.js` test config.
19
+ - Documented breakpoints — `doc/src/app/documentation/responsive-design/page.js` (was duplicate `optimization`).
12
20
 
13
21
  ## [1.0.0-beta.21] - 2026-9-3
14
22
  - Added a `gclassOpts()` function that controls the animation fps and observer throttling
package/Listeners.js CHANGED
@@ -73,32 +73,17 @@ const qAllBp = (sel, qAll) => {
73
73
  })())
74
74
  }
75
75
  // For generic qAll(sel) calls that should be breakpoint-aware, use this wrapper.
76
+ // Preserves DOM order (critical for .order stagger) and respects root scoping (boot screen).
76
77
  const wrapQAll = (qAll) => (sel) => {
77
- // if sel is a comma list (orderSelector), split and union
78
- if (sel.includes(',')) {
79
- const parts = sel.split(',').map(s => s.trim()).filter(Boolean)
80
- const set = new Set()
81
- parts.forEach(p => wrapQAll(qAll)(p).forEach(e => set.add(e)))
82
- return [...set]
83
- }
84
- // "body *" passthrough
85
78
  if (sel === "body *") return qAll(sel)
86
- const hasBpInSel = bpNames.some(bp => sel.includes(`${bp}:`))
87
- if (hasBpInSel) {
88
- // sel already contains bp: variant - filter by active
89
- return qAll("body *").filter(el => sel.split(',').some(s => elementMatchesSel(el, s.trim())))
90
- }
91
- // normal sel - include base + active variants
92
- try {
93
- const baseEls = qAll(sel)
94
- // also scan for active variants that wouldn't be found by qAll(sel)
95
- const variantEls = qAll("body *").filter(el => {
96
- for (const bp of bpNames) if (el.classList.contains(`${bp}:${sel.slice(1)}`) && isBreakpointActive(bp)) return true
97
- return false
98
- })
99
- const set = new Set([...baseEls, ...variantEls])
100
- return [...set]
101
- } catch { return qAll(sel) }
79
+ const parts = sel.split(',').map(s => s.trim()).filter(Boolean)
80
+ // Filter all candidates in DOM order, matching base or active bp: variant via elementMatchesSel
81
+ // This keeps .order sequence top-to-bottom instead of grouping by sel type.
82
+ return qAll("body *").filter(el => parts.some(p => {
83
+ // p is like ".spawn-up" or ".spawn-text-spawn-up"
84
+ // elementMatchesSel handles both base and bp:base when active
85
+ try { return elementMatchesSel(el, p) } catch { return false }
86
+ }))
102
87
  }
103
88
 
104
89
  // --- Named onComplete handler registry -------------------------------------
@@ -1212,7 +1197,18 @@ export default function initListeners(root = document, throttlePerFrame) {
1212
1197
  })
1213
1198
  scrollTriggers.push(st)
1214
1199
  }
1215
- qAll("body *").filter(el => hasGClass(el, "scroll") || hasGClass(el, "scroll-progress")).forEach(el => runWithBreakpoint(el, () => setupScroll(el)))
1200
+ // scroll / scroll-progress are breakpoint-gated per-animation, not per-element generic
1201
+ // (m:order must not disable a plain `scroll` + `spawn-*` on <m)
1202
+ const scrollSel = ".scroll"
1203
+ const progressSel = ".scroll-progress"
1204
+ qAllAllVariants(scrollSel).forEach(el => runWithBreakpointForSel(el, scrollSel, () => {
1205
+ if (!hasGClass(el, "scroll")) return
1206
+ setupScroll(el)
1207
+ }))
1208
+ qAllAllVariants(progressSel).forEach(el => runWithBreakpointForSel(el, progressSel, () => {
1209
+ if (!hasGClass(el, "scroll-progress")) return
1210
+ setupScroll(el)
1211
+ }))
1216
1212
 
1217
1213
  // SplitText scroll variants: `.spawn-text-<spawn>.scroll` plays the per-part
1218
1214
  // tween when the element enters the viewport and reverses on exit.
@@ -1220,8 +1216,9 @@ export default function initListeners(root = document, throttlePerFrame) {
1220
1216
  if (isTypewriter || text === false) return
1221
1217
  const tSel = "." + TEXT_PREFIX + sel.slice(1)
1222
1218
 
1223
- qAll("body *").filter(el => elementMatchesSel(el, tSel) && hasGClass(el, "scroll") && !hasGClass(el, "scroll-progress")).forEach((el) => {
1219
+ qAllAllVariants(tSel).filter(el => hasGClass(el, "scroll") && !hasGClass(el, "scroll-progress")).forEach((el) => {
1224
1220
  const run = () => {
1221
+ if (!elementMatchesSel(el, tSel)) return
1225
1222
  if (isReduced(el)) return
1226
1223
  const { delay, duration } = readTiming(el)
1227
1224
  const ease = getEase(el)
@@ -1244,10 +1241,10 @@ export default function initListeners(root = document, throttlePerFrame) {
1244
1241
  onLeave: reverseToStart,
1245
1242
  onLeaveBack: reverseToStart,
1246
1243
  }))
1247
- }; runWithBreakpoint(el, run)
1244
+ }; runWithBreakpointForSel(el, tSel, run)
1248
1245
  })
1249
1246
 
1250
- qAll("body *").filter(el => elementMatchesSel(el, tSel) && hasGClass(el, "scroll-progress")).forEach((el) => {
1247
+ qAllAllVariants(tSel).filter(el => hasGClass(el, "scroll-progress")).forEach((el) => {
1251
1248
  const run = () => {
1252
1249
  if (isReduced(el)) return
1253
1250
  const ease = getEase(el)
@@ -1274,7 +1271,7 @@ export default function initListeners(root = document, throttlePerFrame) {
1274
1271
  tl.fromTo(parts, { ...(reverse ? to : from), ...rnd },
1275
1272
  { ...(rnd ? randomEnds(Object.keys(rnd)) : null), ...(reverse ? from : to), ease })
1276
1273
  scrollTriggers.push(tl.scrollTrigger)
1277
- }; runWithBreakpoint(el, run)
1274
+ }; runWithBreakpointForSel(el, tSel, run)
1278
1275
  })
1279
1276
  })
1280
1277
  ScrollTrigger.refresh()
package/dist/gclass.cjs CHANGED
@@ -1325,24 +1325,15 @@ var hasGClass = (el, name) => {
1325
1325
  return false;
1326
1326
  };
1327
1327
  var wrapQAll = (qAll) => (sel) => {
1328
- if (sel.includes(",")) {
1329
- const parts = sel.split(",").map((s) => s.trim()).filter(Boolean);
1330
- const set = /* @__PURE__ */ new Set();
1331
- parts.forEach((p) => wrapQAll(qAll)(p).forEach((e) => set.add(e)));
1332
- return [...set];
1333
- }
1334
1328
  if (sel === "body *") return qAll(sel);
1335
- if (bpNames.some((bp) => sel.includes(`${bp}:`))) return qAll("body *").filter((el) => sel.split(",").some((s) => elementMatchesSel(el, s.trim())));
1336
- try {
1337
- const baseEls = qAll(sel);
1338
- const variantEls = qAll("body *").filter((el) => {
1339
- for (const bp of bpNames) if (el.classList.contains(`${bp}:${sel.slice(1)}`) && isBreakpointActive(bp)) return true;
1329
+ const parts = sel.split(",").map((s) => s.trim()).filter(Boolean);
1330
+ return qAll("body *").filter((el) => parts.some((p) => {
1331
+ try {
1332
+ return elementMatchesSel(el, p);
1333
+ } catch {
1340
1334
  return false;
1341
- });
1342
- return [.../* @__PURE__ */ new Set([...baseEls, ...variantEls])];
1343
- } catch {
1344
- return qAll(sel);
1345
- }
1335
+ }
1336
+ }));
1346
1337
  };
1347
1338
  var completeHandlers = /* @__PURE__ */ new Map();
1348
1339
  function registerComplete(name, fn) {
@@ -1411,23 +1402,6 @@ function initListeners(root = document, throttlePerFrame) {
1411
1402
  for (const bp of bpNames) if (el.classList.contains(`${bp}:${base}`)) return bp;
1412
1403
  return null;
1413
1404
  };
1414
- const getElGateBp = (el) => {
1415
- for (const c of el.classList) {
1416
- const m = c.match(bpPrefixRE);
1417
- if (m) return m[1];
1418
- }
1419
- return null;
1420
- };
1421
- const runWithBreakpoint = (el, fn) => {
1422
- const bp = getElGateBp(el);
1423
- if (!bp) {
1424
- fn();
1425
- return;
1426
- }
1427
- const mq = mqForBp(bp);
1428
- const ret = mm.add(mq, fn);
1429
- breakpointContexts.push(ret);
1430
- };
1431
1405
  const runWithBreakpointForSel = (el, sel, fn) => {
1432
1406
  const bp = getGateBpForSel(el, sel);
1433
1407
  if (!bp) {
@@ -2205,12 +2179,22 @@ function initListeners(root = document, throttlePerFrame) {
2205
2179
  });
2206
2180
  scrollTriggers.push(st);
2207
2181
  };
2208
- qAll("body *").filter((el) => hasGClass(el, "scroll") || hasGClass(el, "scroll-progress")).forEach((el) => runWithBreakpoint(el, () => setupScroll(el)));
2182
+ const scrollSel = ".scroll";
2183
+ const progressSel = ".scroll-progress";
2184
+ qAllAllVariants(scrollSel).forEach((el) => runWithBreakpointForSel(el, scrollSel, () => {
2185
+ if (!hasGClass(el, "scroll")) return;
2186
+ setupScroll(el);
2187
+ }));
2188
+ qAllAllVariants(progressSel).forEach((el) => runWithBreakpointForSel(el, progressSel, () => {
2189
+ if (!hasGClass(el, "scroll-progress")) return;
2190
+ setupScroll(el);
2191
+ }));
2209
2192
  spawnConfigs.forEach(({ sel, from, typewriter: isTypewriter, text }) => {
2210
2193
  if (isTypewriter || text === false) return;
2211
2194
  const tSel = ".spawn-text-" + sel.slice(1);
2212
- qAll("body *").filter((el) => elementMatchesSel(el, tSel) && hasGClass(el, "scroll") && !hasGClass(el, "scroll-progress")).forEach((el) => {
2195
+ qAllAllVariants(tSel).filter((el) => hasGClass(el, "scroll") && !hasGClass(el, "scroll-progress")).forEach((el) => {
2213
2196
  const run = () => {
2197
+ if (!elementMatchesSel(el, tSel)) return;
2214
2198
  if (isReduced(el)) return;
2215
2199
  const { delay, duration } = readTiming(el);
2216
2200
  const ease = getEase(el);
@@ -2238,9 +2222,9 @@ function initListeners(root = document, throttlePerFrame) {
2238
2222
  onLeaveBack: reverseToStart
2239
2223
  }));
2240
2224
  };
2241
- runWithBreakpoint(el, run);
2225
+ runWithBreakpointForSel(el, tSel, run);
2242
2226
  });
2243
- qAll("body *").filter((el) => elementMatchesSel(el, tSel) && hasGClass(el, "scroll-progress")).forEach((el) => {
2227
+ qAllAllVariants(tSel).filter((el) => hasGClass(el, "scroll-progress")).forEach((el) => {
2244
2228
  const run = () => {
2245
2229
  if (isReduced(el)) return;
2246
2230
  const ease = getEase(el);
@@ -2269,7 +2253,7 @@ function initListeners(root = document, throttlePerFrame) {
2269
2253
  });
2270
2254
  scrollTriggers.push(tl.scrollTrigger);
2271
2255
  };
2272
- runWithBreakpoint(el, run);
2256
+ runWithBreakpointForSel(el, tSel, run);
2273
2257
  });
2274
2258
  });
2275
2259
  gsap_all.ScrollTrigger.refresh();
@@ -1324,24 +1324,15 @@ var hasGClass = (el, name) => {
1324
1324
  return false;
1325
1325
  };
1326
1326
  var wrapQAll = (qAll) => (sel) => {
1327
- if (sel.includes(",")) {
1328
- const parts = sel.split(",").map((s) => s.trim()).filter(Boolean);
1329
- const set = /* @__PURE__ */ new Set();
1330
- parts.forEach((p) => wrapQAll(qAll)(p).forEach((e) => set.add(e)));
1331
- return [...set];
1332
- }
1333
1327
  if (sel === "body *") return qAll(sel);
1334
- if (bpNames.some((bp) => sel.includes(`${bp}:`))) return qAll("body *").filter((el) => sel.split(",").some((s) => elementMatchesSel(el, s.trim())));
1335
- try {
1336
- const baseEls = qAll(sel);
1337
- const variantEls = qAll("body *").filter((el) => {
1338
- for (const bp of bpNames) if (el.classList.contains(`${bp}:${sel.slice(1)}`) && isBreakpointActive(bp)) return true;
1328
+ const parts = sel.split(",").map((s) => s.trim()).filter(Boolean);
1329
+ return qAll("body *").filter((el) => parts.some((p) => {
1330
+ try {
1331
+ return elementMatchesSel(el, p);
1332
+ } catch {
1339
1333
  return false;
1340
- });
1341
- return [.../* @__PURE__ */ new Set([...baseEls, ...variantEls])];
1342
- } catch {
1343
- return qAll(sel);
1344
- }
1334
+ }
1335
+ }));
1345
1336
  };
1346
1337
  var completeHandlers = /* @__PURE__ */ new Map();
1347
1338
  function registerComplete(name, fn) {
@@ -1410,23 +1401,6 @@ function initListeners(root = document, throttlePerFrame) {
1410
1401
  for (const bp of bpNames) if (el.classList.contains(`${bp}:${base}`)) return bp;
1411
1402
  return null;
1412
1403
  };
1413
- const getElGateBp = (el) => {
1414
- for (const c of el.classList) {
1415
- const m = c.match(bpPrefixRE);
1416
- if (m) return m[1];
1417
- }
1418
- return null;
1419
- };
1420
- const runWithBreakpoint = (el, fn) => {
1421
- const bp = getElGateBp(el);
1422
- if (!bp) {
1423
- fn();
1424
- return;
1425
- }
1426
- const mq = mqForBp(bp);
1427
- const ret = mm.add(mq, fn);
1428
- breakpointContexts.push(ret);
1429
- };
1430
1404
  const runWithBreakpointForSel = (el, sel, fn) => {
1431
1405
  const bp = getGateBpForSel(el, sel);
1432
1406
  if (!bp) {
@@ -2204,12 +2178,22 @@ function initListeners(root = document, throttlePerFrame) {
2204
2178
  });
2205
2179
  scrollTriggers.push(st);
2206
2180
  };
2207
- qAll("body *").filter((el) => hasGClass(el, "scroll") || hasGClass(el, "scroll-progress")).forEach((el) => runWithBreakpoint(el, () => setupScroll(el)));
2181
+ const scrollSel = ".scroll";
2182
+ const progressSel = ".scroll-progress";
2183
+ qAllAllVariants(scrollSel).forEach((el) => runWithBreakpointForSel(el, scrollSel, () => {
2184
+ if (!hasGClass(el, "scroll")) return;
2185
+ setupScroll(el);
2186
+ }));
2187
+ qAllAllVariants(progressSel).forEach((el) => runWithBreakpointForSel(el, progressSel, () => {
2188
+ if (!hasGClass(el, "scroll-progress")) return;
2189
+ setupScroll(el);
2190
+ }));
2208
2191
  spawnConfigs.forEach(({ sel, from, typewriter: isTypewriter, text }) => {
2209
2192
  if (isTypewriter || text === false) return;
2210
2193
  const tSel = ".spawn-text-" + sel.slice(1);
2211
- qAll("body *").filter((el) => elementMatchesSel(el, tSel) && hasGClass(el, "scroll") && !hasGClass(el, "scroll-progress")).forEach((el) => {
2194
+ qAllAllVariants(tSel).filter((el) => hasGClass(el, "scroll") && !hasGClass(el, "scroll-progress")).forEach((el) => {
2212
2195
  const run = () => {
2196
+ if (!elementMatchesSel(el, tSel)) return;
2213
2197
  if (isReduced(el)) return;
2214
2198
  const { delay, duration } = readTiming(el);
2215
2199
  const ease = getEase(el);
@@ -2237,9 +2221,9 @@ function initListeners(root = document, throttlePerFrame) {
2237
2221
  onLeaveBack: reverseToStart
2238
2222
  }));
2239
2223
  };
2240
- runWithBreakpoint(el, run);
2224
+ runWithBreakpointForSel(el, tSel, run);
2241
2225
  });
2242
- qAll("body *").filter((el) => elementMatchesSel(el, tSel) && hasGClass(el, "scroll-progress")).forEach((el) => {
2226
+ qAllAllVariants(tSel).filter((el) => hasGClass(el, "scroll-progress")).forEach((el) => {
2243
2227
  const run = () => {
2244
2228
  if (isReduced(el)) return;
2245
2229
  const ease = getEase(el);
@@ -2268,7 +2252,7 @@ function initListeners(root = document, throttlePerFrame) {
2268
2252
  });
2269
2253
  scrollTriggers.push(tl.scrollTrigger);
2270
2254
  };
2271
- runWithBreakpoint(el, run);
2255
+ runWithBreakpointForSel(el, tSel, run);
2272
2256
  });
2273
2257
  });
2274
2258
  ScrollTrigger.refresh();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gclass-anims",
3
- "version": "1.0.0-beta.22",
3
+ "version": "1.0.0-beta.22.2",
4
4
  "description": "A Tailwind-style utility layer on top of GSAP. Framework-agnostic - works in vanilla JS, React, Vue, Svelte, or any bundler.",
5
5
  "type": "module",
6
6
  "main": "./dist/gclass.cjs",