gclass-anims 1.0.0-beta.15 → 1.0.0-beta.16

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 CHANGED
@@ -1,4 +1,6 @@
1
1
  import initListeners from './Listeners'
2
+ import { defaults, animations } from './Config.js'
3
+ import gsap from 'gsap'
2
4
 
3
5
  // localStorage key controlling whether the GSAP animation system is mounted.
4
6
  const STORAGE_KEY = 'gclass-animations-enabled'
@@ -111,12 +113,106 @@ export function disableReducedMotion() {
111
113
  }
112
114
 
113
115
  let cleanup = null
116
+ let bootCleanup = null
117
+ let bootTimeout = null
118
+ let bootStyle = null
119
+
120
+ const readBootTime = (els, fallback) => {
121
+ let max = null
122
+ for (const el of els) {
123
+ const cls = [...el.classList].find(c => c.startsWith('boot-time-'))
124
+ if (cls) {
125
+ const n = Number(cls.slice('boot-time-'.length))
126
+ if (!Number.isNaN(n)) max = max === null ? n : Math.max(max, n)
127
+ }
128
+ }
129
+ return max ?? fallback
130
+ }
114
131
 
115
132
  // Boots the GSAP animation system unless animations are disabled (stored "off"
116
133
  // or reduced-motion fallback with no explicit choice). Idempotent: calling it
117
134
  // again tears down any previous run first.
135
+ // Now also handles boot screen: any HTML/JSX with `.boot-up` anywhere is treated as the boot overlay.
136
+ // No separate initBoot needed - just call initAnimations().
137
+ // Boot stops all DOM rendering for defaults.bootTime (overwritten by boot-time-N class).
118
138
  export function initAnimations() {
119
139
  if (typeof window === 'undefined' || !getEnabled()) return
120
- if (cleanup) cleanup()
140
+ if (cleanup) { cleanup(); cleanup = null }
141
+ if (bootCleanup) { bootCleanup(); bootCleanup = null }
142
+ if (bootTimeout) { clearTimeout(bootTimeout); bootTimeout = null }
143
+ if (bootStyle) { bootStyle.remove(); bootStyle = null; document.documentElement.classList.remove('gclass-booting') }
144
+
145
+ const bootEls = typeof document !== 'undefined' ? document.querySelectorAll(".boot-up") : []
146
+ if (!bootEls.length) {
147
+ // no .boot-up -> completely skip boot
148
+ } else if (bootEls.length > 1) {
149
+ console.error(`[initAnimations] Multiple .boot-up elements detected (${bootEls.length}) - skipping all boot animations`, bootEls)
150
+ bootEls.forEach(el => el.remove())
151
+ // fall through to normal initListeners without pausing DOM
152
+ } else {
153
+ const bootEl = bootEls[0]
154
+ const hasBootEnd = [...bootEl.classList].some(c => c.startsWith('boot-end-'))
155
+ if (!hasBootEnd) {
156
+ // no boot-end-* -> completely skip boot animation (still pause? spec says skip it)
157
+ // spec: skip boot-end animation if no class, but still do boot pause? user said "completely skip it if no .boot-end-<name> class is present"
158
+ // interpret as skip the exit animation only, still pause for bootTime
159
+ // To match "completely skip it" for boot-end, we just don't play exit tween
160
+ }
161
+ const bootTime = readBootTime(bootEls, defaults.bootTime ?? 2)
162
+ console.log(`[initAnimations] .boot-up found: ${bootEls.length} - pausing DOM for ${bootTime}s`, bootEls)
163
+ // stop all DOM rendering except .boot-up
164
+ bootStyle = document.createElement('style')
165
+ bootStyle.id = 'gclass-boot-style'
166
+ bootStyle.textContent = `html.gclass-booting{visibility:hidden} html.gclass-booting .boot-up,html.gclass-booting .boot-up *{visibility:visible} html.gclass-booting .boot-up{position:fixed;inset:0;z-index:9999;display:grid;place-items:center}`
167
+ document.head.appendChild(bootStyle)
168
+ document.documentElement.classList.add('gclass-booting')
169
+ // ensure boot els are visible even if nested inside hidden ancestors
170
+ bootEls.forEach(el => { el.style.visibility = 'visible' })
171
+
172
+ // animations inside boot screen must play while rest of DOM is hidden - init scoped to boot-up
173
+ bootCleanup = initListeners(bootEl)
174
+
175
+ bootTimeout = setTimeout(() => {
176
+ const bootEndCls = [...bootEl.classList].find(c => c.startsWith('boot-end-'))
177
+ if (!bootEndCls) {
178
+ // no boot-end -> skip exit animation, just remove
179
+ bootCleanup?.(); bootCleanup = null
180
+ document.documentElement.classList.remove('gclass-booting')
181
+ bootStyle?.remove(); bootStyle = null
182
+ bootEls.forEach(el => el.remove())
183
+ bootTimeout = null
184
+ cleanup = initListeners()
185
+ return
186
+ }
187
+ const name = bootEndCls.slice('boot-end-'.length) // e.g. spawn-blur
188
+ const cfg = animations.find(a => a.sel === '.' + name)
189
+ const from = cfg?.from
190
+ const easeCl = [...bootEl.classList].find(c => c.startsWith('ease-'))
191
+ const ease = easeCl ? easeCl.split('-')[1] : defaults.ease
192
+ const dur = readBootTime([bootEl], defaults.effectDuration ?? 1) // reuse boot-time- or fallback to effectDuration; if boot-time used for pause, reuse same value for exit unless overridden
193
+ // Actually use boot-end-time-N if present, else effectDuration
194
+ const endTimeCls = [...bootEl.classList].find(c => c.startsWith('boot-end-time-'))
195
+ const endDur = endTimeCls ? Number(endTimeCls.slice('boot-end-time-'.length)) : dur
196
+
197
+ const finish = () => {
198
+ bootCleanup?.(); bootCleanup = null
199
+ document.documentElement.classList.remove('gclass-booting')
200
+ bootStyle?.remove(); bootStyle = null
201
+ bootEls.forEach(el => el.remove())
202
+ bootTimeout = null
203
+ cleanup = initListeners()
204
+ }
205
+
206
+ if (!cfg || !from) {
207
+ console.warn(`[initAnimations] boot-end-${name} has no from state - removing without animation`)
208
+ finish()
209
+ return
210
+ }
211
+ // play spawn in reverse (visible -> hidden) before removing
212
+ gsap.to(bootEl, { ...from, duration: endDur, ease, onComplete: finish })
213
+ }, bootTime * 1000)
214
+ return
215
+ }
216
+
121
217
  cleanup = initListeners()
122
218
  }
package/Animations.js CHANGED
@@ -79,7 +79,11 @@ export function expandA (target , delay , dur , ease){
79
79
  }
80
80
 
81
81
  export function typewriter (target , text , dur , delay , ease){
82
- return gsap.fromTo(target , {text:""} , {ease:easeOf(ease) , duration:dur , delay:delay , text:text})
82
+ // Any element using TextPlugin gets pre-wrap so "\n" from VSCode Enter or JS strings
83
+ // renders as a real line break and any innerHTML formatting is preserved.
84
+ gsap.utils.toArray(target).forEach(el => { if (el.style) el.style.whiteSpace = "pre-wrap" })
85
+ const value = String(text).replace(/\n/g, "<br>")
86
+ return gsap.fromTo(target , {text:""} , {ease:easeOf(ease) , duration:dur , delay:delay , text:{value}})
83
87
  }
84
88
 
85
89
  export function spawnSpinCCW (target , delay , dur , ease){
package/Boot.js CHANGED
@@ -1,145 +1,12 @@
1
1
  import { initAnimations } from './AnimToggle.js'
2
2
 
3
- // Raw JS boot overlay — framework-agnostic, no React.
4
- // Shows #boot-overlay once per session, delays main DOM wiring until finished,
5
- // and keeps Config.js animations usable inside the overlay.
6
- // Works with any framework (vanilla, React, Vue, Svelte, Next, Nuxt, Angular) and with/without Tailwind.
7
-
8
- let hasBooted = false
9
- const BOOT_KEY = 'gclass-boot-done' // kept for optional persistence, not used for gate
10
-
11
- const readBootTime = (el, fallback) => {
12
- const cls = [...el.classList].find(c => c.startsWith('boot-time-'))
13
- return cls ? Number(cls.slice('boot-time-'.length)) : fallback
14
- }
15
-
16
- export async function initBoot({ enabled = false, time = 2, id = 'boot-overlay', onDone } = {}) {
17
- if (typeof window === 'undefined') return () => {}
18
- if (!enabled) {
19
- initAnimations()
20
- return () => {}
21
- }
22
- if (hasBooted) {
23
- initAnimations()
24
- return () => {}
25
- }
26
-
27
- let overlay = document.getElementById(id)
28
- if (!overlay) {
29
- const tryFetch = async (url) => {
30
- try {
31
- const res = await fetch(url)
32
- if (res.ok) return await res.text()
33
- } catch {}
34
- return null
35
- }
36
- let html = await tryFetch(new URL('./Boot.html', import.meta.url).href)
37
- if (!html) html = await tryFetch('/Boot.html')
38
- if (!html) html = await tryFetch('./Boot.html')
39
- if (html) {
40
- const tpl = document.createElement('template')
41
- tpl.innerHTML = html.trim()
42
- const node = tpl.content.firstElementChild
43
- if (node) {
44
- document.body.prepend(node)
45
- overlay = document.getElementById(id) ?? node
46
- }
47
- }
48
- }
49
- if (!overlay) {
50
- initAnimations()
51
- return () => {}
52
- }
53
-
54
- hasBooted = true
55
- overlay.style.position = 'fixed'
56
- overlay.style.inset = '0'
57
- overlay.style.zIndex = '9999'
58
- overlay.style.display = 'grid'
59
- overlay.style.placeItems = 'center'
60
- if (!overlay.style.background && !overlay.style.backgroundColor) {
61
- overlay.style.background = '#020617'
62
- }
63
-
64
- // Isolate: detach all body content except overlay so first initAnimations() only wires overlay
65
- // This works for vanilla <main>, React #root, Vue #app, Next #__next, Nuxt, Svelte, Angular
66
- // Listeners.js scans body * regardless of display:none, so must physically detach
67
- const toIsolate = [...document.body.children].filter(el => {
68
- if (el.id === id) return false
69
- if (el.tagName === 'SCRIPT' || el.tagName === 'STYLE' || el.tagName === 'TEMPLATE' || el.tagName === 'LINK') return false
70
- if (el.id === 'next-route-announcer' || el.hasAttribute('data-nextjs')) return false
71
- return true
72
- })
73
- const placements = toIsolate.map(el => ({ el, parent: el.parentNode, next: el.nextSibling }))
74
- placements.forEach(({ el }) => el.remove())
75
- const prevOverflow = document.body.style.overflow
76
- document.body.style.overflow = 'hidden'
77
-
78
- initAnimations()
79
- placements.forEach(({ parent, next, el }) => {
80
- if (!parent || el.isConnected) return
81
- if (next && next.isConnected && next.parentNode === parent) {
82
- parent.insertBefore(el, next)
83
- } else {
84
- parent.appendChild(el)
85
- }
86
- })
87
-
88
- const duration = readBootTime(overlay, time)
89
-
90
- const timer = setTimeout(() => {
91
- overlay.style.transition = 'opacity 0.4s ease'
92
- overlay.style.opacity = '0'
93
- setTimeout(() => {
94
- overlay.remove()
95
- document.body.style.removeProperty('overflow')
96
- if (prevOverflow) document.body.style.overflow = prevOverflow
97
- initAnimations()
98
- onDone?.()
99
- }, 400)
100
- }, duration * 1000)
101
-
102
- return () => {
103
- clearTimeout(timer)
104
- document.body.style.removeProperty('overflow')
105
- if (prevOverflow) document.body.style.overflow = prevOverflow
106
- placements.forEach(({ el, parent, next }) => {
107
- if (!el.isConnected && parent) {
108
- if (next && next.isConnected && next.parentNode === parent) parent.insertBefore(el, next)
109
- else parent.appendChild(el)
110
- }
111
- })
112
- }
3
+ export function Boot(){
4
+ const els = document.querySelectorAll(".boot-up")
5
+ console.log(`[Boot -> initAnimations] .boot-up count: ${els.length}`, els)
6
+ // rewired: Boot now just delegates to initAnimations (single entry point)
7
+ return initAnimations()
113
8
  }
114
-
115
- export async function createBootOverlay(html, opts = {}) {
116
- if (document.getElementById(opts.id ?? 'boot-overlay')) return initBoot(opts)
117
- if (!html) {
118
- const tryFetch = async (url) => {
119
- try { const r = await fetch(url); if (r.ok) return await r.text() } catch {}
120
- return null
121
- }
122
- html = await tryFetch(new URL('./Boot.html', import.meta.url).href)
123
- if (!html) html = await tryFetch('/Boot.html')
124
- }
125
- const div = document.createElement('div')
126
- div.id = opts.id ?? 'boot-overlay'
127
- if (!html) {
128
- div.className = `boot-time-${opts.time ?? 2}`
129
- div.style.cssText = 'position:fixed;inset:0;z-index:9999;display:grid;place-items:center;background:#020617;'
130
- div.innerHTML = '<h1 class="spawn-up time-1">Loading...</h1>'
131
- } else {
132
- const tpl = document.createElement('template')
133
- tpl.innerHTML = html.trim()
134
- const node = tpl.content.firstElementChild
135
- if (node) {
136
- document.body.prepend(node)
137
- return initBoot(opts)
138
- }
139
- div.innerHTML = html
140
- }
141
- document.body.prepend(div)
142
- return initBoot(opts)
143
- }
144
-
145
- export default initBoot
9
+ // keep named exports wired so index.js / dev don't break during test
10
+ export const initBoot = Boot
11
+ export const createBootOverlay = Boot
12
+ export default Boot
package/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  All notable changes to `gclass-anims` will be documented in this file.
4
4
 
5
+ ## [1.0.0-beta.16] - 2026-09-1
6
+ - Added a new `.boot-up` class for boot up animations
7
+ - Fixed text animations not taking formatting into account
8
+
9
+
5
10
  ## [1.0.0-beta.13] - 2026-08-27
6
11
  - Added a new `.fill-svg` modifier for the `.draw` and `.draw-split` classes that fills the SVG after it has been drawn.
7
12
 
package/Config.js CHANGED
@@ -70,7 +70,8 @@ export const defaults = {
70
70
  typewriterSplitCharDuration: 0.05,
71
71
  minTextPartDuration: 0.3,
72
72
  revealDelay:0,
73
- characterlist:"AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"
73
+ characterlist:"AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz",
74
+ bootTime: 5,
74
75
  }
75
76
 
76
77
  export const animations = [
package/Listeners.js CHANGED
@@ -93,9 +93,21 @@ const invokePlay = (config, el, delay, dur, ease) => {
93
93
  }
94
94
  }
95
95
 
96
- export default function initListeners() {
96
+ export default function initListeners(root = document) {
97
97
  gsap.registerPlugin(TextPlugin, ScrollTrigger, SplitText)
98
98
 
99
+ // helper to scope queries to root (for boot screen: only boot-up subtree animates during boot)
100
+ const qAll = (sel) => {
101
+ if (root === document || root === document.documentElement || root === document.body) return gsap.utils.toArray(sel)
102
+ if (sel === "body *") return gsap.utils.toArray(root.querySelectorAll("*"))
103
+ try {
104
+ const els = [...(root.querySelectorAll ? root.querySelectorAll(sel) : [])]
105
+ if (root.matches?.(sel)) els.unshift(root)
106
+ // handle comma selectors where root itself may match one part
107
+ return els
108
+ } catch { return gsap.utils.toArray(sel) }
109
+ }
110
+
99
111
  const registeredListeners = []
100
112
  const onCompleteTweens = []
101
113
  const addListener = (el, type, fn) => {
@@ -358,7 +370,7 @@ export default function initListeners() {
358
370
  spawnConfigs.map(({ sel }) => "." + TEXT_PREFIX + sel.slice(1)).join(",")
359
371
 
360
372
  const getOrderDelay = (el, priority) => {
361
- const samepri = gsap.utils.toArray(orderSelector())
373
+ const samepri = qAll(orderSelector())
362
374
  .filter((e) => {
363
375
  if (!e.classList.contains("order")) return false
364
376
  const match = [...e.classList].find(p => p.startsWith("priority-"))
@@ -472,7 +484,10 @@ export default function initListeners() {
472
484
  // would render it in its connecting form instead of its correct END form),
473
485
  // but it should still be split into its own span so it animates too.
474
486
  const RTL_LETTER = /[\u0621-\u064A\u066E-\u06D5\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]/
475
- const getRTLCharSplit = (el) => new SplitText(el, {
487
+ const getRTLCharSplit = (el) => {
488
+ // Any element using SplitText gets pre-wrap so "\n" and formatting is preserved
489
+ if (el.style) el.style.whiteSpace = "pre-wrap"
490
+ return new SplitText(el, {
476
491
  type: "words",
477
492
  linesClass: "gsap-line",
478
493
  wordsClass: "gsap-word",
@@ -555,6 +570,7 @@ export default function initListeners() {
555
570
  self.words.length = 0
556
571
  },
557
572
  })
573
+ }
558
574
 
559
575
  // Flex targets can't be split directly: the split parts would become
560
576
  // flex ITEMS, so justify-content/gap would apply per letter, whitespace-
@@ -595,6 +611,7 @@ export default function initListeners() {
595
611
  }
596
612
 
597
613
  const getSplit = (el, gran) => {
614
+ if (el.style) el.style.whiteSpace = "pre-wrap"
598
615
  let s = splitCache.get(el)
599
616
  const rtlChars = gran === "chars" && isRTLText(el)
600
617
  const key = rtlChars ? "rtl-chars" : gran
@@ -604,6 +621,9 @@ export default function initListeners() {
604
621
  ? getRTLCharSplit(el)
605
622
  : new SplitText(el, {
606
623
  type: gran,
624
+ tag: "span",
625
+ reduceWhiteSpace: false,
626
+
607
627
  linesClass: "gsap-line",
608
628
  wordsClass: "gsap-word",
609
629
  charsClass: "gsap-char",
@@ -739,7 +759,7 @@ export default function initListeners() {
739
759
  // created before any scroll/scroll-progress trigger measures its position.
740
760
  // Setting them up here (before the trigger pass below) keeps offsets correct
741
761
  // and lets the single ScrollTrigger.refresh() at the end reconcile layout.
742
- gsap.utils.toArray(".pin").forEach(setupPin)
762
+ qAll(".pin").forEach(setupPin)
743
763
 
744
764
  // Scroll-driven extras - class-driven ScrollTrigger behaviours that don't
745
765
  // fit the spawn/loop machinery (no `play`/`build`), handled like `.pin`:
@@ -832,7 +852,7 @@ export default function initListeners() {
832
852
  scrollTriggers.push(t.scrollTrigger)
833
853
  }
834
854
  }
835
- gsap.utils.toArray('[class^="parallax-"],[class*=" parallax-"], .progress-bar, .scroll-fill, .scroll-fade-bg, .scroll-horizontal').forEach(setupScrollDriven)
855
+ qAll('[class^="parallax-"],[class*=" parallax-"], .progress-bar, .scroll-fill, .scroll-fade-bg, .scroll-horizontal').forEach(setupScrollDriven)
836
856
 
837
857
  // Scroller resolution: a `.scroll`/`.scroll-progress` element inside a
838
858
  // `.scroll-frame` container binds its trigger to THAT box instead of the
@@ -1001,7 +1021,7 @@ export default function initListeners() {
1001
1021
  })
1002
1022
  scrollTriggers.push(st)
1003
1023
  }
1004
- gsap.utils.toArray(".scroll, .scroll-progress").forEach(setupScroll)
1024
+ qAll(".scroll, .scroll-progress").forEach(setupScroll)
1005
1025
 
1006
1026
  // SplitText scroll variants: `.spawn-text-<spawn>.scroll` plays the per-part
1007
1027
  // tween when the element enters the viewport and reverses on exit.
@@ -1009,7 +1029,7 @@ export default function initListeners() {
1009
1029
  if (isTypewriter || text === false) return
1010
1030
  const tSel = "." + TEXT_PREFIX + sel.slice(1)
1011
1031
 
1012
- gsap.utils.toArray(tSel + ".scroll:not(.scroll-progress)").forEach((el) => {
1032
+ qAll(tSel + ".scroll:not(.scroll-progress)").forEach((el) => {
1013
1033
  if (isReduced(el)) return
1014
1034
  const { delay, duration } = readTiming(el)
1015
1035
  const ease = getEase(el)
@@ -1034,7 +1054,7 @@ export default function initListeners() {
1034
1054
  }))
1035
1055
  })
1036
1056
 
1037
- gsap.utils.toArray(tSel + ".scroll-progress").forEach((el) => {
1057
+ qAll(tSel + ".scroll-progress").forEach((el) => {
1038
1058
  if (isReduced(el)) return
1039
1059
  const ease = getEase(el)
1040
1060
  const parts = getParts(el, getGranularity(el))
@@ -1087,7 +1107,7 @@ export default function initListeners() {
1087
1107
 
1088
1108
  spawnConfigs.forEach((config) => {
1089
1109
  const { sel, typewriter: isTypewriter, typewriterSplit } = config
1090
- gsap.utils.toArray(sel).forEach((el) => {
1110
+ qAll(sel).forEach((el) => {
1091
1111
  if (el.classList.contains("scroll") || el.classList.contains("scroll-progress")) return
1092
1112
  if (isPreserved(el)) return
1093
1113
  if (isReduced(el)) return
@@ -1122,7 +1142,7 @@ export default function initListeners() {
1122
1142
  spawnConfigs.forEach(({ sel, from, typewriter: isTypewriter, text }) => {
1123
1143
  if (isTypewriter || text === false) return
1124
1144
  const tSel = "." + TEXT_PREFIX + sel.slice(1)
1125
- gsap.utils.toArray(tSel).forEach((el) => {
1145
+ qAll(tSel).forEach((el) => {
1126
1146
  if (el.classList.contains("scroll") || el.classList.contains("scroll-progress")) return
1127
1147
  if (isPreserved(el)) return
1128
1148
  if (isReduced(el)) return
@@ -1487,7 +1507,7 @@ export default function initListeners() {
1487
1507
 
1488
1508
  // Bind click + loop animations to every element present at load, tagging
1489
1509
  // them so the MutationObserver below never double-binds a dynamic one.
1490
- gsap.utils.toArray("body *").forEach((el) => {
1510
+ qAll("body *").forEach((el) => {
1491
1511
  if (el.dataset?.gsapSetup) return
1492
1512
  el.dataset.gsapSetup = "1"
1493
1513
  setupClicks(el)
@@ -1608,7 +1628,7 @@ export default function initListeners() {
1608
1628
  appearObserver.observe(document.body, { childList: true, subtree: true })
1609
1629
 
1610
1630
  // Capture any .leave elements already present so they can exit later
1611
- gsap.utils.toArray(".leave").forEach(captureLeave)
1631
+ qAll(".leave").forEach(captureLeave)
1612
1632
 
1613
1633
  const leaveObserver = new MutationObserver((mutations) => {
1614
1634
  mutations.forEach((mutation) => {
@@ -1625,7 +1645,7 @@ export default function initListeners() {
1625
1645
  if (positionTick) return
1626
1646
  positionTick = true
1627
1647
  requestAnimationFrame(() => {
1628
- gsap.utils.toArray(".leave").forEach((el) => {
1648
+ qAll(".leave").forEach((el) => {
1629
1649
  const s = leaveStates.get(el)
1630
1650
  if (s) s.rect = el.getBoundingClientRect()
1631
1651
  })
@@ -1666,7 +1686,7 @@ export default function initListeners() {
1666
1686
  // so dynamically-added .appear elements can animate under the new
1667
1687
  // engine run. data-gsap-preserved (cross-reset memory) and
1668
1688
  // data-gsap-ghost (detached .leave clones) are intentionally KEPT.
1669
- gsap.utils.toArray('[data-gsap-scroll],[data-gsap-setup],[data-gsap-pinned],[data-gsap-scroll-driven]').forEach((el) => {
1689
+ qAll('[data-gsap-scroll],[data-gsap-setup],[data-gsap-pinned],[data-gsap-scroll-driven]').forEach((el) => {
1670
1690
  delete el.dataset.gsapScroll
1671
1691
  delete el.dataset.gsapSetup
1672
1692
  delete el.dataset.gsapPinned
@@ -1681,7 +1701,7 @@ export default function initListeners() {
1681
1701
  document.querySelectorAll('[data-gsap-radiate]').forEach((n) => n.remove())
1682
1702
  cssTweens.forEach((t) => t?.kill())
1683
1703
  cssTweens.length = 0
1684
- gsap.utils.toArray(".typewriter, .scramble").forEach(el => {
1704
+ qAll(".typewriter, .scramble").forEach(el => {
1685
1705
  // Preserved-region typewriters/scrambles stay as-is (already at
1686
1706
  // their end state); finalize the rest so a mid-type/mid-scramble
1687
1707
  // kill can't strand partial or garbled text where the next run's
package/index.d.ts CHANGED
@@ -220,15 +220,7 @@ export function hover(delay: number, target: TweenTarget, amount: number, dur: n
220
220
  export function marquee(target: TweenTarget, dir: string, duration: number, xOffset?: number, yOffset?: number, noRepeat?: boolean): any
221
221
  export function flip(state: any, ease: string, dur: number): any
222
222
  export function animatecss(target: TweenTarget, dur: number, delay: number, ease: string, propertyS: any, propertySValue: any, propertyE: any, propertyEValue: any): any
223
- // --- Boot ----------------------------------------------------------------
224
223
 
225
- export interface BootOptions {
226
- enabled?: boolean
227
- time?: number
228
- id?: string
229
- onDone?: () => void
230
- }
231
- export function initBoot(opts?: BootOptions): Promise<() => void>
232
- export function createBootOverlay(html?: string, opts?: BootOptions): Promise<() => void>
233
- export default function Boot(opts?: BootOptions): Promise<() => void>
224
+
225
+ export default function Boot()
234
226
 
package/index.js CHANGED
@@ -2,5 +2,5 @@ export { initAnimations, toggleAnimations, enableReducedMotion, disableReducedMo
2
2
  export { default as initListeners, registerComplete } from './Listeners.js'
3
3
  export { customAnims } from './CustomAnims.js'
4
4
  export { defaults, animations, normalize } from './Config.js'
5
- export { default as Boot, initBoot, createBootOverlay } from './Boot.js'
5
+ export { Boot } from './Boot.js'
6
6
  export * from './Animations.js'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gclass-anims",
3
- "version": "1.0.0-beta.15",
3
+ "version": "1.0.0-beta.16",
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": "index.js",
@@ -22,7 +22,6 @@
22
22
  "Config.js",
23
23
  "CustomAnims.js",
24
24
  "Boot.js",
25
- "Boot.html",
26
25
  "README.md",
27
26
  "LICENSE",
28
27
  "CHANGELOG.md"
package/Boot.html DELETED
@@ -1,18 +0,0 @@
1
- <!-- Boot.html — GClass boot overlay template -->
2
- <!-- 1. Put your boot HTML here. This file is scanned by Tailwind (v4 auto-detects *.html, v3 add "./Boot.html" to content[]) -->
3
- <!-- so any Tailwind utility + gclass-anims class (e.g. .spawn-*, .time-*) placed here is preserved in the CSS build. -->
4
- <!-- 2. Boot.js will fetch and inject this file if no #boot-overlay exists in the DOM. -->
5
- <!-- 3. Keep the outer id="boot-overlay" and .boot-time-N (N=seconds visible) — Boot.js reads it via readBootTime(). -->
6
-
7
- <div id="boot-overlay" class="boot-time-2 fixed inset-0 z-[9999] grid place-items-center bg-slate-950" style="position:fixed;inset:0;z-index:9999;display:grid;place-items:center;background:#020617;">
8
- <div class="flex flex-col items-center gap-4 p-8 text-center">
9
- <!-- gclass-anims classes work here — Config.js:76 wiring runs inside the overlay -->
10
- <h1 class="spawn-up time-1 text-4xl font-bold tracking-tight text-cyan-200 radiate radiate-z-9999 compatibility">GClass</h1>
11
- <p class="spawn-fade time-1 text-sm text-slate-400">GSAP utilities</p>
12
-
13
- <!-- Tailwind / other CSS lib classes also work — they are detected because this *.html is in tailwind content -->
14
- <div class="mt-2 h-1 w-32 overflow-hidden rounded-full bg-slate-800">
15
-
16
- </div>
17
- </div>
18
- </div>