gclass-anims 1.0.0-beta.22.2 → 1.0.0-beta.24
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/AnimToggle.js +61 -15
- package/Animations.js +33 -6
- package/CHANGELOG.md +47 -19
- package/Config.js +0 -1
- package/Listeners.js +421 -37
- package/README.md +27 -0
- package/dist/gclass.cjs +7 -3252
- package/dist/gclass.cjs.map +1 -0
- package/dist/gclass.esm.js +1829 -2410
- package/dist/gclass.esm.js.map +1 -0
- package/index.d.ts +63 -12
- package/index.js +1 -1
- package/package.json +2 -1
- package/vite.lib.config.js +35 -0
package/AnimToggle.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import initListeners from './Listeners.js'
|
|
2
2
|
import { defaults, animations } from './Config.js'
|
|
3
3
|
import { gsap } from 'gsap'
|
|
4
|
-
|
|
4
|
+
import { GSDevTools } from 'gsap/all'
|
|
5
|
+
// GSDevTools is intentionally NOT registered at module scope. This module is
|
|
6
|
+
// imported by index.js, and package.json declares "sideEffects": false-a
|
|
7
|
+
// bundler is then free to drop the registration, leaving gclassDev() to call
|
|
8
|
+
// GSDevTools.create() against an unregistered plugin. It is also unnecessary:
|
|
9
|
+
// gclassDev() is usable standalone (the docs site and dev-react-strict both
|
|
10
|
+
// call it without ever calling initAnimations()), so registration must happen
|
|
11
|
+
// at the point of use instead of at import time.
|
|
5
12
|
// localStorage key controlling whether the GSAP animation system is mounted.
|
|
6
13
|
const STORAGE_KEY = 'gclass-animations-enabled'
|
|
7
14
|
// localStorage key for a forced reduced-motion override (see
|
|
@@ -46,7 +53,7 @@ function readReducedOverride() {
|
|
|
46
53
|
// - Otherwise, if the user HAS an explicit stored choice, respect it
|
|
47
54
|
// (override wins), even under reduced motion.
|
|
48
55
|
// - Otherwise (no stored value) fall back to the default, which is ON unless
|
|
49
|
-
// reduced motion is detected
|
|
56
|
+
// reduced motion is detected-in which case animations are off.
|
|
50
57
|
function getEnabled() {
|
|
51
58
|
if (forcedReduced) return false
|
|
52
59
|
return stored === null ? !reduced : stored
|
|
@@ -118,7 +125,7 @@ let bootTimeout = null
|
|
|
118
125
|
let bootStyle = null
|
|
119
126
|
let hasBooted = false // true after first hard-load boot, skips boot on SPA path changes (remains false until first boot, resets on hard reload)
|
|
120
127
|
|
|
121
|
-
// Runtime config for gclassOpts
|
|
128
|
+
// Runtime config for gclassOpts-0 = defaults (no throttle, default GSAP ticker)
|
|
122
129
|
let currentThrottle = 0
|
|
123
130
|
let currentFps = 0
|
|
124
131
|
const configSubscribers = new Set()
|
|
@@ -144,7 +151,7 @@ function normalizeGclassArgs(throttlePerFrame, fps) {
|
|
|
144
151
|
|
|
145
152
|
/**
|
|
146
153
|
* Change GClass runtime options on the fly without reload.
|
|
147
|
-
* gclassOpts(throttlePerFrame, fps)
|
|
154
|
+
* gclassOpts(throttlePerFrame, fps)-both optional numbers.
|
|
148
155
|
* gclassOpts() or gclassOpts(undefined, undefined) resets to defaults (no throttle, default ticker).
|
|
149
156
|
* Also accepts gclassOpts({throttlePerFrame, fps}).
|
|
150
157
|
* Example low-end button: onClick={() => gclassOpts(1, 30)}
|
|
@@ -181,16 +188,55 @@ const readBootTime = (els, fallback) => {
|
|
|
181
188
|
return max ?? fallback
|
|
182
189
|
}
|
|
183
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Create GSDevTools UI styled like doc/ (Header.jsx:8 slate-800/50 ring + layout.js:85 slate-950).
|
|
193
|
+
* Accepts: gclassDev() | gclassDev("width:50%; bottom:30px") | gclassDev({width:"50%"}) | gclassDev({css:{}, minimal:true, animation:tl})
|
|
194
|
+
*/
|
|
195
|
+
export function gclassDev(cssOrOpts){
|
|
196
|
+
// Register at point of use-see the note at the top of this file.
|
|
197
|
+
gsap.registerPlugin(GSDevTools)
|
|
198
|
+
const docCss = {
|
|
199
|
+
backgroundColor: "rgba(15,23,42,0.96)", // slate-900 ~ doc bg-slate-950/900
|
|
200
|
+
border: "1px solid rgba(51,65,85,0.8)", // ring-slate-700 Header.jsx:8
|
|
201
|
+
borderRadius: "12px", // rounded-xl
|
|
202
|
+
boxShadow: "0 0 0 1px rgba(51,65,85,0.5), 0 8px 32px rgba(0,0,0,0.45)",
|
|
203
|
+
backdropFilter: "blur(8px)",
|
|
204
|
+
color: "#e2e8f0", // slate-200
|
|
205
|
+
bottom: "16px",
|
|
206
|
+
width: "92%",
|
|
207
|
+
maxWidth: "860px",
|
|
208
|
+
left: "50%",
|
|
209
|
+
transform: "translateX(-50%)",
|
|
210
|
+
}
|
|
211
|
+
let opts = {}
|
|
212
|
+
let css = docCss
|
|
213
|
+
if (typeof cssOrOpts === "string") {
|
|
214
|
+
css = cssOrOpts // verbatim string: "width:50%; bottom:30px"
|
|
215
|
+
} else if (cssOrOpts && typeof cssOrOpts === "object") {
|
|
216
|
+
// {width:"50%", bottom:"30px"} shorthand OR {css:{}, animation, minimal,...} full opts
|
|
217
|
+
const hasFullOpts = "css" in cssOrOpts || "animation" in cssOrOpts || "container" in cssOrOpts || "minimal" in cssOrOpts || "id" in cssOrOpts
|
|
218
|
+
if (hasFullOpts) {
|
|
219
|
+
const { css: userCss, ...rest } = cssOrOpts
|
|
220
|
+
opts = rest
|
|
221
|
+
if (typeof userCss === "string") css = userCss
|
|
222
|
+
else if (userCss && typeof userCss === "object") css = { ...docCss, ...userCss }
|
|
223
|
+
} else {
|
|
224
|
+
css = { ...docCss, ...cssOrOpts }
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return GSDevTools.create({ css, ...opts })
|
|
228
|
+
}
|
|
229
|
+
|
|
184
230
|
// Boots the GSAP animation system unless animations are disabled (stored "off"
|
|
185
231
|
// or reduced-motion fallback with no explicit choice). Idempotent: calling it
|
|
186
232
|
// again tears down any previous run first.
|
|
187
233
|
// Now also handles boot screen: any HTML/JSX with `.boot-up` anywhere is treated as the boot overlay.
|
|
188
|
-
// No separate initBoot needed
|
|
234
|
+
// No separate initBoot needed-just call initAnimations().
|
|
189
235
|
// Boot stops all DOM rendering for defaults.bootTime (overwritten by boot-time-N class).
|
|
190
236
|
// throttlePerFrame / fps: forwarded to gclassOpts-equivalent runtime.
|
|
191
|
-
// initAnimations(throttlePerFrame, fps)
|
|
192
|
-
// initAnimations({throttlePerFrame, fps})
|
|
193
|
-
// initAnimations()
|
|
237
|
+
// initAnimations(throttlePerFrame, fps)-positional numbers, 0/undefined = defaults
|
|
238
|
+
// initAnimations({throttlePerFrame, fps})-object overload
|
|
239
|
+
// initAnimations()-uses last gclassOpts values (defaults on first call)
|
|
194
240
|
export function initAnimations(throttlePerFrame, fps) {
|
|
195
241
|
// gclassOpts-style normalization: (throttle, fps) positional or {throttlePerFrame, fps} object
|
|
196
242
|
// no args -> fallback to last gclassOpts values (defaults 0 = no throttle, default ticker)
|
|
@@ -210,9 +256,9 @@ export function initAnimations(throttlePerFrame, fps) {
|
|
|
210
256
|
else gsap.ticker.fps(0)
|
|
211
257
|
}
|
|
212
258
|
if (typeof window === 'undefined' || !getEnabled()) return
|
|
213
|
-
// boot already in progress (first mount in StrictMode)
|
|
259
|
+
// boot already in progress (first mount in StrictMode)-ignore second mount
|
|
214
260
|
if (bootTimeout) {
|
|
215
|
-
console.log(`[initAnimations] boot already in progress
|
|
261
|
+
console.log(`[initAnimations] boot already in progress-ignoring duplicate call`)
|
|
216
262
|
return
|
|
217
263
|
}
|
|
218
264
|
if (cleanup) { cleanup(); cleanup = null }
|
|
@@ -220,7 +266,7 @@ export function initAnimations(throttlePerFrame, fps) {
|
|
|
220
266
|
if (bootStyle) { bootStyle.remove(); bootStyle = null; document.documentElement.classList.remove('gclass-booting') }
|
|
221
267
|
|
|
222
268
|
const hideBootEls = (els) => {
|
|
223
|
-
// React-safe: don't el.remove()
|
|
269
|
+
// React-safe: don't el.remove()-React owns the nodes and will throw
|
|
224
270
|
// insertBefore/removeChild on next commit if we mutate outside React.
|
|
225
271
|
// Hiding keeps React's tree intact but visually removes boot screen.
|
|
226
272
|
els.forEach(el => {
|
|
@@ -234,12 +280,12 @@ export function initAnimations(throttlePerFrame, fps) {
|
|
|
234
280
|
if (!bootEls.length) {
|
|
235
281
|
// no .boot-up -> completely skip boot
|
|
236
282
|
} else if (bootEls.length > 1) {
|
|
237
|
-
console.error(`[initAnimations] Multiple .boot-up elements detected (${bootEls.length})
|
|
283
|
+
console.error(`[initAnimations] Multiple .boot-up elements detected (${bootEls.length})-skipping all boot animations`, bootEls)
|
|
238
284
|
hideBootEls(bootEls)
|
|
239
285
|
hasBooted = true
|
|
240
286
|
// fall through to normal initListeners without pausing DOM
|
|
241
287
|
} else if (hasBooted && !bootTimeout) {
|
|
242
|
-
// path change after already booted (SPA navigation)
|
|
288
|
+
// path change after already booted (SPA navigation)-skip boot, hard reload resets hasBooted
|
|
243
289
|
console.log(`[initAnimations] skipping boot on path change (already booted)`, bootEls)
|
|
244
290
|
hideBootEls(bootEls)
|
|
245
291
|
hasBooted = true
|
|
@@ -265,7 +311,7 @@ export function initAnimations(throttlePerFrame, fps) {
|
|
|
265
311
|
// ensure boot els are visible even if nested inside hidden ancestors
|
|
266
312
|
bootEls.forEach(el => { el.style.visibility = 'visible' })
|
|
267
313
|
|
|
268
|
-
// animations inside boot screen must play while rest of DOM is hidden
|
|
314
|
+
// animations inside boot screen must play while rest of DOM is hidden-init scoped to boot-up
|
|
269
315
|
bootCleanup = initListeners(bootEl, effThrottle)
|
|
270
316
|
|
|
271
317
|
bootTimeout = setTimeout(() => {
|
|
@@ -300,7 +346,7 @@ export function initAnimations(throttlePerFrame, fps) {
|
|
|
300
346
|
}
|
|
301
347
|
|
|
302
348
|
if (!cfg || !from) {
|
|
303
|
-
console.warn(`[initAnimations] boot-end-${name} has no from state
|
|
349
|
+
console.warn(`[initAnimations] boot-end-${name} has no from state-removing without animation`)
|
|
304
350
|
finish()
|
|
305
351
|
return
|
|
306
352
|
}
|
package/Animations.js
CHANGED
|
@@ -2,12 +2,32 @@ import { DrawSVGPlugin, Flip, MotionPathPlugin, ScrambleTextPlugin, SplitText, T
|
|
|
2
2
|
import { gsap } from "gsap";
|
|
3
3
|
import { defaults } from './Config.js'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
// Plugins are deliberately NOT registered here at module scope.
|
|
6
|
+
//
|
|
7
|
+
// package.json declares "sideEffects": false, which tells a consumer's
|
|
8
|
+
// bundler that importing this module has no observable side effects and that
|
|
9
|
+
// it may be dropped entirely when nothing is imported from it. Registering
|
|
10
|
+
// plugins on import contradicts that promise: a bundler that tree-shakes this
|
|
11
|
+
// module away silently un-registers SplitText / DrawSVG / ScrambleText and the
|
|
12
|
+
// corresponding classes then throw at runtime, with a stack trace that points
|
|
13
|
+
// nowhere useful.
|
|
14
|
+
//
|
|
15
|
+
// Registration is therefore deferred to registerPlugins() below, which
|
|
16
|
+
// initListeners() calls (Listeners.js). This module stays safe to import for
|
|
17
|
+
// side-effect-free helpers, and the whole set is registered exactly once.
|
|
18
|
+
let pluginsRegistered = false
|
|
19
|
+
export function registerPlugins() {
|
|
20
|
+
if (pluginsRegistered) return
|
|
21
|
+
pluginsRegistered = true
|
|
22
|
+
gsap.registerPlugin(
|
|
23
|
+
Flip,
|
|
24
|
+
SplitText,
|
|
25
|
+
TextPlugin,
|
|
26
|
+
DrawSVGPlugin,
|
|
27
|
+
MotionPathPlugin,
|
|
28
|
+
ScrambleTextPlugin,
|
|
29
|
+
)
|
|
30
|
+
}
|
|
11
31
|
|
|
12
32
|
// --- Breakpoint helpers for modifiers (xs/s/m/l/xl) -------------------------
|
|
13
33
|
// Lazy to avoid circular init (Config.js <-> Animations.js)
|
|
@@ -322,7 +342,14 @@ export function splitPaths (paths){
|
|
|
322
342
|
const path = toSplit[0]
|
|
323
343
|
if (!path) return newPaths
|
|
324
344
|
if (path._gcSplitPaths?.[0]?.isConnected) return path._gcSplitPaths
|
|
345
|
+
// Only SVG <path> elements can be split. A non-path target (or a detached
|
|
346
|
+
// node) makes MotionPathPlugin.getRawPath return undefined, and the
|
|
347
|
+
// .map() below then throws and takes the whole engine init down with it.
|
|
348
|
+
// Bail out quietly instead: draw-split is a decorative extra and a
|
|
349
|
+
// misapplied class must never be fatal.
|
|
350
|
+
if (path.nodeType !== 1 || path.tagName?.toLowerCase() !== "path" || !path.isConnected) return newPaths
|
|
325
351
|
const rawPath = MotionPathPlugin.getRawPath(path)
|
|
352
|
+
if (!rawPath || !Array.isArray(rawPath)) return newPaths
|
|
326
353
|
const parent = path.parentNode
|
|
327
354
|
const attributes = [...path.attributes]
|
|
328
355
|
newPaths = rawPath.map(segment => {
|
package/CHANGELOG.md
CHANGED
|
@@ -2,21 +2,49 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `gclass-anims` will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.0.0-beta.24] - 2026-10-05
|
|
6
|
+
### Added
|
|
7
|
+
- **`.gc-tween-[...]` escape hatch** - write GSAP vars inline when the menu has no class for it. `class="gc-tween-[y:40,opacity:0]"` plays **from** those values **to** the element's live resting state (read via `getComputedStyle`), so end values follow CSS / dark mode / breakpoints instead of being frozen into the class. Multiple `gc-tween-[...]` classes on one element **merge** (both properties end up on one tween). Modifiers work as usual: `.time-N`, `.ease-*`, `.order`, `.priority-N`, `.delay-N`, `.reduced`, `.preserve`, and `bp:` gating (`m:gc-tween-[...]`). Trigger prefixes mirror the `css-*` grammar: `hover-gc-tween-[...]`, `click-gc-tween-[...]`.
|
|
8
|
+
- **`.gc-tl` timeline host + `.gc-<n>-[...]` ordered steps** - `class="gc-tl gc-1-[y:40] gc-2-[opacity:1] gc-3-[scale:0.8]"` builds a `gsap.timeline()` with one tween per step, sorted ascending by the numeric prefix regardless of attribute order. Numbered steps imply a timeline, so `.gc-tl` is optional but expresses intent.
|
|
9
|
+
- **Per-step control keys inside the bracket** - `time:` / `dur:` overrides that step's duration (class-level `.time-N` still sets the default for steps that don't), `ease:` overrides the step's ease, and `at:` sets the GSAP **position** parameter (`at:0`, `at:">"`, `at:"+=0.2"`, `at:"<"`) for overlapping steps. These are consumed by the engine and never reach GSAP as properties.
|
|
10
|
+
- `.delay-N` is now a real modifier-a plain additive offset applied **after** the `.order` / `.priority-N` delay, so `order delay-2` shifts the stagger and `priority-3 delay-1` composes. `Config.js:23` previously promised `delay` support that no code implemented.
|
|
11
|
+
- `registerPlugins()` is exported, so a consumer can register the plugin set explicitly without booting the engine.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- **Consumer-package corruption via `sideEffects: false`.** All GSAP plugin registration moved out of module scope into `initListeners()` (`registerPlugins()` in `Animations.js`, plus the existing `TextPlugin`/`ScrollTrigger`/`SplitText` call). `package.json` declares `sideEffects: false`, which tells a consumer's bundler it may drop a module that only registers plugins on import-silently un-registering `SplitText` / `DrawSVG` / `ScrambleText` and making the matching classes throw at runtime inside someone else's app. Importing the package for helpers is now genuinely side-effect free. This also removes the duplicate `SplitText` / `TextPlugin` registration that existed at both module scope and init time.
|
|
15
|
+
- **`gclassDev()` no longer depends on import order.** `GSDevTools` registration moved from `AnimToggle.js` module scope into the body of `gclassDev()`, so it works whether or not `initAnimations()` has run. The docs site and `dev-react-strict` both call `gclassDev()` standalone and were relying on the module-scope registration.
|
|
16
|
+
- **Breakpoint tables are no longer frozen at import.** `Listeners.js` computed `bpNames` / `bpMap` / `bpPrefixRE` as module-scope `const`s, so assigning `defaults.breakpoints` after import was silently ignored. They are now derived lazily and memoised on the identity of the `defaults.breakpoints` object (port of the lazy `getBpDataMod()` approach `Animations.js` already used).
|
|
17
|
+
- **`css-<prop>-<from>-<to>` now merges instead of first-match-wins.** `parseCssAnim` returned on the first matching class, so an element could carry exactly one `css-*` animation and any further classes were ignored. All matching classes now merge into a single tween; a later class for the same property wins, and the first trigger prefix seen decides spawn/hover/click/loop for the whole element.
|
|
18
|
+
- **`.appear` on dynamically inserted `.gc` elements.** `setupGcAnims` is registered in the MutationObserver appear batch as well as the initial load pass, matching `setupCssAnims`.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- `dist/` is now minified with source maps (`minify: true`, `sourcemap: true` in `vite.lib.config.js`). ESM 95.7 kB → 74.7 kB (gzip 19.6 kB), CJS 97.7 kB → 57.7 kB (gzip 17.5 kB). Source maps keep stack traces resolvable to the original `Listeners.js` / `Animations.js` lines. Note: `minify: 'esbuild'` fails on a clean install here because this repo has no direct `esbuild` dependency (Vite 8 delegates to rolldown)-`minify: true` is the portable form.
|
|
22
|
+
- **Removed `defaults.textStagger`.** Declared in `Config.js`, typed in `index.d.ts`, and documented on the defaults page, but read by nothing-stagger is derived arithmetically in `playText`. Deleted from all three rather than wired up, because making it the default would silently change the timing of every existing text animation.
|
|
23
|
+
- `index.d.ts` drift fixed: `initListeners` was declared as a **default** export while `index.js` re-exports it **named**, so `import initListeners from 'gclass-anims'` type-checked and then returned `undefined` at runtime; `Example` was typed but never exported (`index.js` omits it) and is now removed; `Defaults` now declares `breakpoints`, without which `defaults.breakpoints = {...}` was a type error and the whole `m:` system was unconfigurable in TypeScript; `finalOpacity`, `stashText`, `spawnClipReveal`, `curtainHorizontal`, `curtainVertical` and `scrambleSegments` now have declarations (all six ship in the bundle).
|
|
24
|
+
|
|
25
|
+
### Notes for consumers
|
|
26
|
+
- **One bracket group per `.gc` class.** Tailwind re-lexes bracket contents as standalone candidates, so a second bracket group leaks real CSS into your stylesheet: `gc-tween-[y:40]` emits nothing, but `gc-tween-[y:40]-[y:0]` makes Tailwind emit `.[y:0] { y: 0 }`. The parser only ever accepts one group, and `dev-react-strict/src/__tests__/gc-tween.test.jsx` asserts this against real Tailwind (with a control case proving the harness can detect a leak at all). For a two-value tween use two numbered steps, or the existing `css-<prop>-<from>-<to>`.
|
|
27
|
+
- `.randomize-*` does not reach `.gc` tweens-it is injected by monkey-patching `gsap.fromTo` inside `play()`-built tweens only.
|
|
28
|
+
- `demo/` is legacy: those static pages predate the docs site and are not wired into anything. Their `gclass-bundle.js` is a stale prebuilt bundle (dated 2026-08-27) and nothing in the repo regenerates it, so treat that directory as historical rather than a live example. `dist/` is the current build.
|
|
29
|
+
|
|
30
|
+
## [1.0.0-beta.23] - 2026-09-09
|
|
31
|
+
- Added `gclassDev()` helper-`AnimToggle.js:185` `gclassDev(cssOrOpts)` wraps `GSDevTools.create()` with doc-styled defaults (`slate-900` `rgba(15,23,42,0.96)`, `slate-700` border, `rounded-xl`, `backdrop-blur`). Accepts `gclassDev()` (defaults), `gclassDev("width:50%; bottom:30px")` (CSS string), `gclassDev({width:"50%"})` (style object shorthand), or `gclassDev({css, animation, minimal, ...})` (full `GSDevTools` opts). Registers `GSDevTools` via `gsap.registerPlugin(GSDevTools)` (`AnimToggle.js:4`), re-exported from `index.js:1` and typed in `index.d.ts:35`.
|
|
32
|
+
|
|
5
33
|
## [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
|
|
34
|
+
- 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
35
|
|
|
8
36
|
## [1.0.0-beta.22.1] - 2026-09-06
|
|
9
|
-
- Fixed `.order` stagger grouping bug
|
|
37
|
+
- 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
38
|
- No API change from `beta.22`.
|
|
11
39
|
|
|
12
40
|
## [1.0.0-beta.22] - 2026-09-06
|
|
13
|
-
- Added responsive breakpoints
|
|
14
|
-
- Added breakpoint-aware modifiers
|
|
15
|
-
- Fixed runtime `customAnims` re-normalization
|
|
16
|
-
- Added ESM + CJS dual build
|
|
17
|
-
- Fixed CJS `gsap` interop
|
|
18
|
-
- Added `dev-react-strict` test harness
|
|
19
|
-
- Documented breakpoints
|
|
41
|
+
- 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`).
|
|
42
|
+
- 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.
|
|
43
|
+
- Fixed runtime `customAnims` re-normalization-`Listeners.js:190` now calls `normalize(customAnims)` inside `initListeners()` so `customAnims.push()` before next `init` is picked up.
|
|
44
|
+
- 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`.
|
|
45
|
+
- 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.
|
|
46
|
+
- 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.
|
|
47
|
+
- Documented breakpoints-`doc/src/app/documentation/responsive-design/page.js` (was duplicate `optimization`).
|
|
20
48
|
|
|
21
49
|
## [1.0.0-beta.21] - 2026-9-3
|
|
22
50
|
- Added a `gclassOpts()` function that controls the animation fps and observer throttling
|
|
@@ -48,22 +76,22 @@ All notable changes to `gclass-anims` will be documented in this file.
|
|
|
48
76
|
|
|
49
77
|
## [1.0.0-beta.12] - 2026-08-26
|
|
50
78
|
|
|
51
|
-
- Fixed `scramble` with `scroll-progress` throwing `can't convert undefined to object
|
|
79
|
+
- Fixed `scramble` with `scroll-progress` throwing `can't convert undefined to object`-`computeTo` (`Listeners.js:424`) and scrub `to` builder (`Listeners.js:867`) now guard `from` (`scramble` has no `from`).
|
|
52
80
|
|
|
53
81
|
## [1.0.0-beta.11] - 2026-08-26
|
|
54
82
|
|
|
55
|
-
- Fixed `.draw-split` infinite loop when paired with `.appear
|
|
83
|
+
- Fixed `.draw-split` infinite loop when paired with `.appear`-`splitPaths` (`Animations.js:244`) now strips `appear`/`scroll`/`scroll-progress`/`draw`/`draw-split`/`data-gsap-*` from cloned segments, marks children with `data-gsap-split` + `contain:paint`/`will-change:transform` isolation, and prevents `appearObserver` (`Listeners.js:1559`) re-triggering. Also isolated `draw-split` demos in docs.
|
|
56
84
|
|
|
57
85
|
## [1.0.0-beta.10] - 2026-08-26
|
|
58
86
|
|
|
59
|
-
- Added `.randomize-<prop>-[min]-[max]
|
|
60
|
-
- Added `.draw
|
|
61
|
-
- Added `.draw-split
|
|
62
|
-
- Added `.scramble
|
|
63
|
-
- Added `.scramble-all
|
|
64
|
-
- Added `.scroll-frame
|
|
65
|
-
- Fixed `spawn-text-*` (SplitText) not working correctly on flex containers
|
|
87
|
+
- Added `.randomize-<prop>-[min]-[max]`-randomize spawn start values per element (e.g. `randomize-rotation-[-90]-[90]`, `randomize-x-[-40]-[40]`). Re-rolls on every replay (`.scroll` re-enter, `.appear`).
|
|
88
|
+
- Added `.draw`-stroke-draw reveal for SVG paths using DrawSVGPlugin (`drawSVG: 0% → 100%`).
|
|
89
|
+
- Added `.draw-split`-draws multi-segment SVG paths sequentially at constant pen speed (splits paths with multiple `M` commands into individual strokes).
|
|
90
|
+
- Added `.scramble`-text resolves from empty through scrambled characters into real content (ScrambleTextPlugin). Supports `.reveal-delay-N`, `.chars-[...]`, `.amount-N`, `.scramble-rtl`.
|
|
91
|
+
- Added `.scramble-all`-variant of scramble with no empty start; the finished string flips to garbage as a whole then sweeps back.
|
|
92
|
+
- Added `.scroll-frame`-use a scrollable container as the ScrollTrigger scroller for nested `.scroll` / `.scroll-progress` elements (innermost `.scroll-frame` ancestor wins).
|
|
93
|
+
- Fixed `spawn-text-*` (SplitText) not working correctly on flex containers-text runs are now wrapped in block containers before splitting to preserve flex layout, spacing, and line grouping.
|
|
66
94
|
|
|
67
|
-
## [1.0.0-beta.9]
|
|
95
|
+
## [1.0.0-beta.9]-Previous release
|
|
68
96
|
|
|
69
97
|
- See git history for earlier changes.
|