fancoolo-fx 1.0.2 → 1.2.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fancoolo-fx",
3
- "version": "1.0.2",
3
+ "version": "1.2.0",
4
4
  "description": "A class-driven GSAP animation wrapper for WordPress and static sites.",
5
5
  "main": "src/fx.js",
6
6
  "keywords": ["gsap", "animation", "scrolltrigger", "splittext", "wordpress", "gutenberg"],
package/src/fx.js CHANGED
@@ -84,6 +84,11 @@
84
84
  spinReveal: { duration: 1.4, ease: 'power3.out' },
85
85
  bgReveal: { duration: 1, ease: 'power3.out' },
86
86
  scaleIn: { duration: 1, ease: 'power3.out' },
87
+ fadeIn: { duration: 1.4, ease: 'power1.out' },
88
+ blurIn: { duration: 1.2, ease: 'power2.out' },
89
+ clipUp: { duration: 1, ease: 'power3.inOut' },
90
+ clipDown: { duration: 1, ease: 'power3.inOut' },
91
+ tiltIn: { duration: 1.4, ease: 'power3.out' },
87
92
  };
88
93
 
89
94
  // ── Helpers ──────────────────────────────────
@@ -241,6 +246,105 @@
241
246
  gsap.from(el, tweenVars);
242
247
  }
243
248
 
249
+ function fadeIn(el, opts) {
250
+ opts = opts || {};
251
+ var o = resolveOptions(el, 'fadeIn', opts);
252
+
253
+ var tweenVars = {
254
+ opacity: 0,
255
+ scale: opts.scale != null ? opts.scale : 0.95,
256
+ duration: o.duration,
257
+ ease: o.ease,
258
+ delay: o.delay,
259
+ };
260
+
261
+ if (opts.trigger === 'scroll' || opts.scrollTrigger) {
262
+ tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
263
+ }
264
+
265
+ gsap.from(el, tweenVars);
266
+ }
267
+
268
+ function blurIn(el, opts) {
269
+ opts = opts || {};
270
+ var o = resolveOptions(el, 'blurIn', opts);
271
+
272
+ var tweenVars = {
273
+ filter: 'blur(' + (opts.blur != null ? opts.blur : 12) + 'px)',
274
+ opacity: 0,
275
+ duration: o.duration,
276
+ ease: o.ease,
277
+ delay: o.delay,
278
+ };
279
+
280
+ if (opts.trigger === 'scroll' || opts.scrollTrigger) {
281
+ tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
282
+ }
283
+
284
+ gsap.from(el, tweenVars);
285
+ }
286
+
287
+ function clipUp(el, opts) {
288
+ opts = opts || {};
289
+ var o = resolveOptions(el, 'clipUp', opts);
290
+
291
+ var tweenVars = {
292
+ clipPath: 'inset(100% 0 0 0)',
293
+ duration: o.duration,
294
+ ease: o.ease,
295
+ delay: o.delay,
296
+ };
297
+
298
+ if (opts.trigger === 'scroll' || opts.scrollTrigger) {
299
+ tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
300
+ }
301
+
302
+ gsap.from(el, tweenVars);
303
+ }
304
+
305
+ function clipDown(el, opts) {
306
+ opts = opts || {};
307
+ var o = resolveOptions(el, 'clipDown', opts);
308
+
309
+ var tweenVars = {
310
+ clipPath: 'inset(0 0 100% 0)',
311
+ duration: o.duration,
312
+ ease: o.ease,
313
+ delay: o.delay,
314
+ };
315
+
316
+ if (opts.trigger === 'scroll' || opts.scrollTrigger) {
317
+ tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
318
+ }
319
+
320
+ gsap.from(el, tweenVars);
321
+ }
322
+
323
+ function tiltIn(el, opts) {
324
+ opts = opts || {};
325
+ var o = resolveOptions(el, 'tiltIn', opts);
326
+
327
+ gsap.fromTo(el, {
328
+ rotationX: opts.rotationX != null ? opts.rotationX : 45,
329
+ scale: opts.scale != null ? opts.scale : 0.8,
330
+ opacity: opts.opacity != null ? opts.opacity : 0,
331
+ transformPerspective: opts.perspective != null ? opts.perspective : 1000,
332
+ transformOrigin: opts.transformOrigin || 'center bottom',
333
+ }, {
334
+ rotationX: 0,
335
+ scale: 1,
336
+ opacity: 1,
337
+ transformPerspective: 1000,
338
+ ease: o.ease,
339
+ scrollTrigger: {
340
+ trigger: (opts.scrollTrigger && opts.scrollTrigger.trigger) || el,
341
+ start: config.scrollStart || 'top 85%',
342
+ end: opts.end || 'top 20%',
343
+ scrub: opts.scrub != null ? opts.scrub : 0.6,
344
+ },
345
+ });
346
+ }
347
+
244
348
  // ── Class-to-effect mapping ─────────────────
245
349
 
246
350
  var effects = {
@@ -249,6 +353,10 @@
249
353
  'fx-spin-reveal': spinReveal,
250
354
  'fx-bg-reveal': bgReveal,
251
355
  'fx-scale-in': scaleIn,
356
+ 'fx-fade-in': fadeIn,
357
+ 'fx-blur-in': blurIn,
358
+ 'fx-clip-up': clipUp,
359
+ 'fx-clip-down': clipDown,
252
360
  };
253
361
 
254
362
  var effectsByName = {
@@ -257,6 +365,11 @@
257
365
  spinReveal: spinReveal,
258
366
  bgReveal: bgReveal,
259
367
  scaleIn: scaleIn,
368
+ fadeIn: fadeIn,
369
+ blurIn: blurIn,
370
+ clipUp: clipUp,
371
+ clipDown: clipDown,
372
+ tiltIn: tiltIn,
260
373
  };
261
374
 
262
375
  // ── Helpers ──────────────────────────────────
@@ -321,6 +434,8 @@
321
434
  });
322
435
 
323
436
  // 3. Bare class inside a section: .fx-<name> (no suffix)
437
+ // Only elements inside a matching section are picked up,
438
+ // but each element triggers itself (same as -st).
324
439
  if (config.sectionSelector) {
325
440
  document.querySelectorAll(config.sectionSelector).forEach(function (section) {
326
441
  var bareEls = Array.from(section.querySelectorAll('.' + name))
@@ -329,14 +444,29 @@
329
444
 
330
445
  var groups = groupByParent(bareEls);
331
446
  groups.forEach(function (group) {
332
- applyScrollGroup(fn, group, section);
333
- group.forEach(function (el) { processed.add(el); });
447
+ group.forEach(function (el, i) {
448
+ fn(el, {
449
+ trigger: 'scroll',
450
+ delay: i * 0.15,
451
+ scrollTrigger: { trigger: el },
452
+ });
453
+ processed.add(el);
454
+ });
334
455
  });
335
456
  });
336
457
  }
337
458
  });
338
459
 
339
- // 4. Tag-based auto-animation inside sections
460
+ // 4. fx-tilt-in — scrub-based 3D perspective (always scroll-linked)
461
+ // Processed before tagMap so tilt elements aren't grabbed by tagMap first.
462
+ document.querySelectorAll('.fx-tilt-in-st, .fx-tilt-in-pl, .fx-tilt-in').forEach(function (el) {
463
+ if (!processed.has(el)) {
464
+ tiltIn(el);
465
+ processed.add(el);
466
+ }
467
+ });
468
+
469
+ // 5. Tag-based auto-animation inside sections
340
470
  if (config.tagMap && config.sectionSelector) {
341
471
  document.querySelectorAll(config.sectionSelector).forEach(function (section) {
342
472
  Object.keys(config.tagMap).forEach(function (selector) {
@@ -356,6 +486,50 @@
356
486
  });
357
487
  });
358
488
  }
489
+ // 5. fx-stagger-all-[selector] — target children, effect from sibling class
490
+ // Requires an effect class on the same element (e.g. fx-reveal-st).
491
+ document.querySelectorAll('[class*="fx-stagger-all-"]').forEach(function (container) {
492
+ // Parse selector from fx-stagger-all-[img,p]
493
+ var childSelector = null;
494
+ for (var ci = 0; ci < container.classList.length; ci++) {
495
+ var cls = container.classList[ci];
496
+ if (cls.indexOf('fx-stagger-all-[') === 0 && cls.charAt(cls.length - 1) === ']') {
497
+ childSelector = cls.slice('fx-stagger-all-['.length, -1);
498
+ break;
499
+ }
500
+ }
501
+ if (!childSelector) return;
502
+
503
+ // Find which effect class is on this container
504
+ var effectFn = null;
505
+ var effectName = null;
506
+ Object.keys(effects).forEach(function (name) {
507
+ if (container.classList.contains(name + '-st') ||
508
+ container.classList.contains(name + '-pl') ||
509
+ container.classList.contains(name)) {
510
+ effectFn = effects[name];
511
+ effectName = name;
512
+ }
513
+ });
514
+ if (!effectFn) return; // No effect class paired — do nothing
515
+
516
+ var isScroll = container.classList.contains(effectName + '-st') ||
517
+ container.classList.contains(effectName);
518
+ var children = Array.from(container.querySelectorAll(childSelector))
519
+ .filter(function (el) { return !processed.has(el); });
520
+ if (children.length === 0) return;
521
+
522
+ children.forEach(function (child, i) {
523
+ var opts = { delay: i * 0.15 };
524
+ if (isScroll) {
525
+ opts.trigger = 'scroll';
526
+ opts.scrollTrigger = { trigger: child };
527
+ }
528
+ effectFn(child, opts);
529
+ processed.add(child);
530
+ });
531
+ });
532
+
359
533
  }
360
534
 
361
535
  // ── Boot ────────────────────────────────────
@@ -389,6 +563,11 @@
389
563
  spinReveal: spinReveal,
390
564
  bgReveal: bgReveal,
391
565
  scaleIn: scaleIn,
566
+ fadeIn: fadeIn,
567
+ blurIn: blurIn,
568
+ clipUp: clipUp,
569
+ clipDown: clipDown,
570
+ tiltIn: tiltIn,
392
571
  init: init,
393
572
  };
394
573
  })();
@@ -84,6 +84,11 @@
84
84
  spinReveal: { duration: 1.4, ease: 'power3.out' },
85
85
  bgReveal: { duration: 1, ease: 'power3.out' },
86
86
  scaleIn: { duration: 1, ease: 'power3.out' },
87
+ fadeIn: { duration: 1.4, ease: 'power1.out' },
88
+ blurIn: { duration: 1.2, ease: 'power2.out' },
89
+ clipUp: { duration: 1, ease: 'power3.inOut' },
90
+ clipDown: { duration: 1, ease: 'power3.inOut' },
91
+ tiltIn: { duration: 1.4, ease: 'power3.out' },
87
92
  };
88
93
 
89
94
  // ── Helpers ──────────────────────────────────
@@ -241,6 +246,105 @@
241
246
  gsap.from(el, tweenVars);
242
247
  }
243
248
 
249
+ function fadeIn(el, opts) {
250
+ opts = opts || {};
251
+ var o = resolveOptions(el, 'fadeIn', opts);
252
+
253
+ var tweenVars = {
254
+ opacity: 0,
255
+ scale: opts.scale != null ? opts.scale : 0.95,
256
+ duration: o.duration,
257
+ ease: o.ease,
258
+ delay: o.delay,
259
+ };
260
+
261
+ if (opts.trigger === 'scroll' || opts.scrollTrigger) {
262
+ tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
263
+ }
264
+
265
+ gsap.from(el, tweenVars);
266
+ }
267
+
268
+ function blurIn(el, opts) {
269
+ opts = opts || {};
270
+ var o = resolveOptions(el, 'blurIn', opts);
271
+
272
+ var tweenVars = {
273
+ filter: 'blur(' + (opts.blur != null ? opts.blur : 12) + 'px)',
274
+ opacity: 0,
275
+ duration: o.duration,
276
+ ease: o.ease,
277
+ delay: o.delay,
278
+ };
279
+
280
+ if (opts.trigger === 'scroll' || opts.scrollTrigger) {
281
+ tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
282
+ }
283
+
284
+ gsap.from(el, tweenVars);
285
+ }
286
+
287
+ function clipUp(el, opts) {
288
+ opts = opts || {};
289
+ var o = resolveOptions(el, 'clipUp', opts);
290
+
291
+ var tweenVars = {
292
+ clipPath: 'inset(100% 0 0 0)',
293
+ duration: o.duration,
294
+ ease: o.ease,
295
+ delay: o.delay,
296
+ };
297
+
298
+ if (opts.trigger === 'scroll' || opts.scrollTrigger) {
299
+ tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
300
+ }
301
+
302
+ gsap.from(el, tweenVars);
303
+ }
304
+
305
+ function clipDown(el, opts) {
306
+ opts = opts || {};
307
+ var o = resolveOptions(el, 'clipDown', opts);
308
+
309
+ var tweenVars = {
310
+ clipPath: 'inset(0 0 100% 0)',
311
+ duration: o.duration,
312
+ ease: o.ease,
313
+ delay: o.delay,
314
+ };
315
+
316
+ if (opts.trigger === 'scroll' || opts.scrollTrigger) {
317
+ tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
318
+ }
319
+
320
+ gsap.from(el, tweenVars);
321
+ }
322
+
323
+ function tiltIn(el, opts) {
324
+ opts = opts || {};
325
+ var o = resolveOptions(el, 'tiltIn', opts);
326
+
327
+ gsap.fromTo(el, {
328
+ rotationX: opts.rotationX != null ? opts.rotationX : 45,
329
+ scale: opts.scale != null ? opts.scale : 0.8,
330
+ opacity: opts.opacity != null ? opts.opacity : 0,
331
+ transformPerspective: opts.perspective != null ? opts.perspective : 1000,
332
+ transformOrigin: opts.transformOrigin || 'center bottom',
333
+ }, {
334
+ rotationX: 0,
335
+ scale: 1,
336
+ opacity: 1,
337
+ transformPerspective: 1000,
338
+ ease: o.ease,
339
+ scrollTrigger: {
340
+ trigger: (opts.scrollTrigger && opts.scrollTrigger.trigger) || el,
341
+ start: config.scrollStart || 'top 85%',
342
+ end: opts.end || 'top 20%',
343
+ scrub: opts.scrub != null ? opts.scrub : 0.6,
344
+ },
345
+ });
346
+ }
347
+
244
348
  // ── Class-to-effect mapping ─────────────────
245
349
 
246
350
  var effects = {
@@ -249,6 +353,10 @@
249
353
  'fx-spin-reveal': spinReveal,
250
354
  'fx-bg-reveal': bgReveal,
251
355
  'fx-scale-in': scaleIn,
356
+ 'fx-fade-in': fadeIn,
357
+ 'fx-blur-in': blurIn,
358
+ 'fx-clip-up': clipUp,
359
+ 'fx-clip-down': clipDown,
252
360
  };
253
361
 
254
362
  var effectsByName = {
@@ -257,6 +365,11 @@
257
365
  spinReveal: spinReveal,
258
366
  bgReveal: bgReveal,
259
367
  scaleIn: scaleIn,
368
+ fadeIn: fadeIn,
369
+ blurIn: blurIn,
370
+ clipUp: clipUp,
371
+ clipDown: clipDown,
372
+ tiltIn: tiltIn,
260
373
  };
261
374
 
262
375
  // ── Helpers ──────────────────────────────────
@@ -321,6 +434,8 @@
321
434
  });
322
435
 
323
436
  // 3. Bare class inside a section: .fx-<name> (no suffix)
437
+ // Only elements inside a matching section are picked up,
438
+ // but each element triggers itself (same as -st).
324
439
  if (config.sectionSelector) {
325
440
  document.querySelectorAll(config.sectionSelector).forEach(function (section) {
326
441
  var bareEls = Array.from(section.querySelectorAll('.' + name))
@@ -329,14 +444,29 @@
329
444
 
330
445
  var groups = groupByParent(bareEls);
331
446
  groups.forEach(function (group) {
332
- applyScrollGroup(fn, group, section);
333
- group.forEach(function (el) { processed.add(el); });
447
+ group.forEach(function (el, i) {
448
+ fn(el, {
449
+ trigger: 'scroll',
450
+ delay: i * 0.15,
451
+ scrollTrigger: { trigger: el },
452
+ });
453
+ processed.add(el);
454
+ });
334
455
  });
335
456
  });
336
457
  }
337
458
  });
338
459
 
339
- // 4. Tag-based auto-animation inside sections
460
+ // 4. fx-tilt-in — scrub-based 3D perspective (always scroll-linked)
461
+ // Processed before tagMap so tilt elements aren't grabbed by tagMap first.
462
+ document.querySelectorAll('.fx-tilt-in-st, .fx-tilt-in-pl, .fx-tilt-in').forEach(function (el) {
463
+ if (!processed.has(el)) {
464
+ tiltIn(el);
465
+ processed.add(el);
466
+ }
467
+ });
468
+
469
+ // 5. Tag-based auto-animation inside sections
340
470
  if (config.tagMap && config.sectionSelector) {
341
471
  document.querySelectorAll(config.sectionSelector).forEach(function (section) {
342
472
  Object.keys(config.tagMap).forEach(function (selector) {
@@ -356,6 +486,50 @@
356
486
  });
357
487
  });
358
488
  }
489
+ // 5. fx-stagger-all-[selector] — target children, effect from sibling class
490
+ // Requires an effect class on the same element (e.g. fx-reveal-st).
491
+ document.querySelectorAll('[class*="fx-stagger-all-"]').forEach(function (container) {
492
+ // Parse selector from fx-stagger-all-[img,p]
493
+ var childSelector = null;
494
+ for (var ci = 0; ci < container.classList.length; ci++) {
495
+ var cls = container.classList[ci];
496
+ if (cls.indexOf('fx-stagger-all-[') === 0 && cls.charAt(cls.length - 1) === ']') {
497
+ childSelector = cls.slice('fx-stagger-all-['.length, -1);
498
+ break;
499
+ }
500
+ }
501
+ if (!childSelector) return;
502
+
503
+ // Find which effect class is on this container
504
+ var effectFn = null;
505
+ var effectName = null;
506
+ Object.keys(effects).forEach(function (name) {
507
+ if (container.classList.contains(name + '-st') ||
508
+ container.classList.contains(name + '-pl') ||
509
+ container.classList.contains(name)) {
510
+ effectFn = effects[name];
511
+ effectName = name;
512
+ }
513
+ });
514
+ if (!effectFn) return; // No effect class paired — do nothing
515
+
516
+ var isScroll = container.classList.contains(effectName + '-st') ||
517
+ container.classList.contains(effectName);
518
+ var children = Array.from(container.querySelectorAll(childSelector))
519
+ .filter(function (el) { return !processed.has(el); });
520
+ if (children.length === 0) return;
521
+
522
+ children.forEach(function (child, i) {
523
+ var opts = { delay: i * 0.15 };
524
+ if (isScroll) {
525
+ opts.trigger = 'scroll';
526
+ opts.scrollTrigger = { trigger: child };
527
+ }
528
+ effectFn(child, opts);
529
+ processed.add(child);
530
+ });
531
+ });
532
+
359
533
  }
360
534
 
361
535
  // ── Boot ────────────────────────────────────
@@ -389,6 +563,11 @@
389
563
  spinReveal: spinReveal,
390
564
  bgReveal: bgReveal,
391
565
  scaleIn: scaleIn,
566
+ fadeIn: fadeIn,
567
+ blurIn: blurIn,
568
+ clipUp: clipUp,
569
+ clipDown: clipDown,
570
+ tiltIn: tiltIn,
392
571
  init: init,
393
572
  };
394
573
  })();
@@ -1,9 +1,9 @@
1
1
  <?php
2
2
  /**
3
3
  * Plugin Name: Fancoolo FX
4
- * Plugin URI: https://github.com/krstivoja/gsap-animations-template
4
+ * Plugin URI: https://github.com/krstivoja/fancoolo-fx
5
5
  * Description: A class-driven GSAP animation wrapper. Add CSS classes in Gutenberg and get animations — no JavaScript needed.
6
- * Version: 1.0.2
6
+ * Version: 1.2.0
7
7
  * Author: Fancoolo
8
8
  * Author URI: https://github.com/krstivoja
9
9
  * License: ISC
@@ -14,7 +14,7 @@ if ( ! defined( 'ABSPATH' ) ) {
14
14
  exit;
15
15
  }
16
16
 
17
- define( 'FANCOOLO_FX_VERSION', '1.0.2' );
17
+ define( 'FANCOOLO_FX_VERSION', '1.2.0' );
18
18
  define( 'FANCOOLO_FX_PATH', plugin_dir_path( __FILE__ ) );
19
19
  define( 'FANCOOLO_FX_URL', plugin_dir_url( __FILE__ ) );
20
20
 
@@ -253,6 +253,11 @@ function fancoolo_fx_render_admin_page() {
253
253
  <tr><td><code>FX.spinReveal(el, opts)</code></td><td>Rotate and scale in</td></tr>
254
254
  <tr><td><code>FX.bgReveal(el, opts)</code></td><td>Background slide up</td></tr>
255
255
  <tr><td><code>FX.scaleIn(el, opts)</code></td><td>Scale up with fade</td></tr>
256
+ <tr><td><code>FX.fadeIn(el, opts)</code></td><td>Opacity + subtle scale, no movement</td></tr>
257
+ <tr><td><code>FX.blurIn(el, opts)</code></td><td>Fade in while deblurring</td></tr>
258
+ <tr><td><code>FX.clipUp(el, opts)</code></td><td>Clip-path wipe from bottom</td></tr>
259
+ <tr><td><code>FX.clipDown(el, opts)</code></td><td>Clip-path wipe from top</td></tr>
260
+ <tr><td><code>FX.tiltIn(el, opts)</code></td><td>3D perspective reveal (scrub-based)</td></tr>
256
261
  <tr><td><code>FX.init()</code></td><td>Re-scan DOM — call after changing any config</td></tr>
257
262
  </tbody>
258
263
  </table>
@@ -346,6 +351,38 @@ FX.init();</pre>
346
351
  <div class="ffx-class-row"><code data-copy>fx-scale-in-st</code><span class="ffx-desc">Scroll triggered</span></div>
347
352
  <div class="ffx-class-row"><code data-copy>fx-scale-in</code><span class="ffx-desc">Auto triggered inside a section</span></div>
348
353
 
354
+ <!-- Fade In -->
355
+ <div class="ffx-group-title">Fade In</div>
356
+ <div class="ffx-class-row"><code data-copy>fx-fade-in-pl</code><span class="ffx-desc">Page load — opacity only, no movement</span></div>
357
+ <div class="ffx-class-row"><code data-copy>fx-fade-in-st</code><span class="ffx-desc">Scroll triggered</span></div>
358
+ <div class="ffx-class-row"><code data-copy>fx-fade-in</code><span class="ffx-desc">Auto triggered inside a section</span></div>
359
+
360
+ <!-- Blur In -->
361
+ <div class="ffx-group-title">Blur In</div>
362
+ <div class="ffx-class-row"><code data-copy>fx-blur-in-pl</code><span class="ffx-desc">Page load — fade in while deblurring</span></div>
363
+ <div class="ffx-class-row"><code data-copy>fx-blur-in-st</code><span class="ffx-desc">Scroll triggered</span></div>
364
+ <div class="ffx-class-row"><code data-copy>fx-blur-in</code><span class="ffx-desc">Auto triggered inside a section</span></div>
365
+
366
+ <!-- Clip Up -->
367
+ <div class="ffx-group-title">Clip Reveal</div>
368
+ <div class="ffx-class-row"><code data-copy>fx-clip-up-pl</code><span class="ffx-desc">Page load — clip-path wipe from bottom</span></div>
369
+ <div class="ffx-class-row"><code data-copy>fx-clip-up-st</code><span class="ffx-desc">Scroll triggered</span></div>
370
+ <div class="ffx-class-row"><code data-copy>fx-clip-up</code><span class="ffx-desc">Auto triggered inside a section</span></div>
371
+ <div class="ffx-class-row"><code data-copy>fx-clip-down-pl</code><span class="ffx-desc">Page load — clip-path wipe from top</span></div>
372
+ <div class="ffx-class-row"><code data-copy>fx-clip-down-st</code><span class="ffx-desc">Scroll triggered</span></div>
373
+ <div class="ffx-class-row"><code data-copy>fx-clip-down</code><span class="ffx-desc">Auto triggered inside a section</span></div>
374
+
375
+ <!-- Tilt In -->
376
+ <div class="ffx-group-title">Tilt In <span style="font-weight:normal;color:#646970;font-size:12px;">(scrub — tied to scroll position)</span></div>
377
+ <div class="ffx-class-row"><code data-copy>fx-tilt-in-st</code><span class="ffx-desc">3D perspective reveal linked to scroll</span></div>
378
+ <div class="ffx-class-row"><code data-copy>fx-tilt-in</code><span class="ffx-desc">Auto triggered inside a section</span></div>
379
+
380
+ <!-- Stagger All -->
381
+ <div class="ffx-group-title" style="margin-top: 20px;">Stagger Children <span style="font-weight:normal;color:#646970;font-size:12px;">(pair with an effect class)</span></div>
382
+ <div class="ffx-class-row"><code data-copy>fx-stagger-all-[img]</code><span class="ffx-desc">Target all img children — requires effect class</span></div>
383
+ <div class="ffx-class-row"><code data-copy>fx-stagger-all-[img,p]</code><span class="ffx-desc">Target img and p children</span></div>
384
+ <div class="ffx-class-row"><code data-copy>fx-stagger-all-[.card]</code><span class="ffx-desc">Target children by CSS class</span></div>
385
+
349
386
  <!-- Modifiers -->
350
387
  <div class="ffx-group-title" style="margin-top: 20px;">Modifiers <span style="font-weight:normal;color:#646970;font-size:12px;">(combine with any effect class)</span></div>
351
388
  <div class="ffx-class-row"><code data-copy>fx-duration-[1.5]</code><span class="ffx-desc">Custom duration (seconds)</span></div>
@@ -360,7 +397,7 @@ FX.init();</pre>
360
397
  <strong style="color:#1d2327;">Important:</strong> Always add <code>FX.init();</code> at the end when changing config — it re-scans the page with your new settings.
361
398
  </p>
362
399
  <p style="margin-top: 12px;">
363
- <a href="https://krstivoja.github.io/gsap-animations-template/documentation/" target="_blank">
400
+ <a href="https://krstivoja.github.io/fancoolo-fx/documentation/" target="_blank">
364
401
  Full Documentation &rarr;
365
402
  </a>
366
403
  </p>