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 +8 -0
- package/Listeners.js +26 -29
- package/dist/gclass.cjs +22 -38
- package/dist/gclass.esm.js +22 -38
- package/package.json +1 -1
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
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
};
|
|
1244
|
+
}; runWithBreakpointForSel(el, tSel, run)
|
|
1248
1245
|
})
|
|
1249
1246
|
|
|
1250
|
-
|
|
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
|
-
};
|
|
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
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2225
|
+
runWithBreakpointForSel(el, tSel, run);
|
|
2242
2226
|
});
|
|
2243
|
-
|
|
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
|
-
|
|
2256
|
+
runWithBreakpointForSel(el, tSel, run);
|
|
2273
2257
|
});
|
|
2274
2258
|
});
|
|
2275
2259
|
gsap_all.ScrollTrigger.refresh();
|
package/dist/gclass.esm.js
CHANGED
|
@@ -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
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2224
|
+
runWithBreakpointForSel(el, tSel, run);
|
|
2241
2225
|
});
|
|
2242
|
-
|
|
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
|
-
|
|
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",
|