@hortonstudio/main 1.2.14 → 1.2.18

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.
@@ -0,0 +1,40 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "mcp__webflow__pages_get_content",
5
+ "WebFetch(domain:compassfacilities.webflow.io)",
6
+ "mcp__webflow__collections_list",
7
+ "mcp__webflow__collections_get",
8
+ "mcp__webflow__sites_list",
9
+ "mcp__webflow__site_applied_scripts_list",
10
+ "mcp__webflow__site_registered_scripts_list",
11
+ "mcp__webflow__add_inline_site_script",
12
+ "Bash(mv:*)",
13
+ "WebFetch(domain:www.compassfacilities.com)",
14
+ "mcp__webflow__pages_list",
15
+ "Bash(npm publish:*)",
16
+ "Bash(npm version:*)",
17
+ "mcp__ide__getDiagnostics",
18
+ "Bash(git init:*)",
19
+ "Bash(git branch:*)",
20
+ "Bash(mkdir:*)",
21
+ "Bash(rm:*)",
22
+ "Bash(git add:*)",
23
+ "Bash(git commit:*)",
24
+ "Bash(git remote add:*)",
25
+ "Bash(git push:*)",
26
+ "Bash(find:*)",
27
+ "WebFetch(domain:github.com)",
28
+ "WebFetch(domain:raw.githubusercontent.com)",
29
+ "WebFetch(domain:www.jsdelivr.com)",
30
+ "WebFetch(domain:cdn.jsdelivr.net)",
31
+ "Bash(npm install)",
32
+ "Bash(npm run build:*)",
33
+ "Bash(ls:*)",
34
+ "Bash(cp:*)",
35
+ "Bash(git remote remove:*)",
36
+ "Bash(tree:*)"
37
+ ],
38
+ "deny": []
39
+ }
40
+ }
@@ -0,0 +1,599 @@
1
+ const API_NAME = 'hsmain';
2
+
3
+ // Check for reduced motion preference
4
+ const prefersReducedMotion = () => {
5
+ return window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
6
+ };
7
+
8
+ // Animation timing (in seconds)
9
+ const timing = {
10
+ announce: 0,
11
+ nav: 0.1,
12
+ navLogo: 0.3,
13
+ navList: 0.35,
14
+ navMenu: 0.35,
15
+ navButton: 0.5,
16
+ tag: 0.1,
17
+ heading: 0.15,
18
+ subheading: 0.25,
19
+ button: 0.35,
20
+ image: 0.5,
21
+ appear: 0.6
22
+ };
23
+
24
+ // Hero Animations Module
25
+ let heroTimeline = null;
26
+ let headingSplits = [];
27
+ let subheadingSplits = [];
28
+ let heroTimeout = null; // Track the setTimeout
29
+
30
+ const config = {
31
+ global: {
32
+ animationDelay: 0.2
33
+ },
34
+ headingSplit: {
35
+ duration: 1.5,
36
+ stagger: 0.1,
37
+ yPercent: 110,
38
+ ease: "power4.out"
39
+ },
40
+ subheadingSplit: {
41
+ duration: 1.5,
42
+ stagger: 0.1,
43
+ yPercent: 110,
44
+ ease: "power4.out"
45
+ },
46
+ appear: {
47
+ y: 50,
48
+ duration: 1.5,
49
+ ease: "power3.out"
50
+ },
51
+ navStagger: {
52
+ duration: 1.5,
53
+ stagger: 0.1,
54
+ ease: "power3.out"
55
+ },
56
+ nav: {
57
+ duration: 1,
58
+ ease: "power3.out"
59
+ }
60
+ };
61
+
62
+ function updateConfig(newConfig) {
63
+ function deepMerge(target, source) {
64
+ for (const key in source) {
65
+ if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
66
+ target[key] = target[key] || {};
67
+ deepMerge(target[key], source[key]);
68
+ } else {
69
+ target[key] = source[key];
70
+ }
71
+ }
72
+ return target;
73
+ }
74
+
75
+ deepMerge(config, newConfig);
76
+ }
77
+
78
+ function killHeroAnimations() {
79
+ if (heroTimeout) {
80
+ clearTimeout(heroTimeout);
81
+ heroTimeout = null;
82
+ }
83
+
84
+ if (heroTimeline) {
85
+ heroTimeline.kill();
86
+ heroTimeline = null;
87
+ }
88
+
89
+ headingSplits.forEach(split => {
90
+ if (split && split.revert) {
91
+ split.revert();
92
+ }
93
+ });
94
+ headingSplits = [];
95
+
96
+ subheadingSplits.forEach(split => {
97
+ if (split && split.revert) {
98
+ split.revert();
99
+ }
100
+ });
101
+ subheadingSplits = [];
102
+
103
+ // Restore page-wide tabbing if animation is killed
104
+ const allFocusableElements = document.querySelectorAll('[data-original-tabindex]');
105
+ allFocusableElements.forEach(el => {
106
+ el.style.pointerEvents = '';
107
+ const originalTabindex = el.getAttribute('data-original-tabindex');
108
+ if (originalTabindex === '0') {
109
+ el.removeAttribute('tabindex');
110
+ } else {
111
+ el.setAttribute('tabindex', originalTabindex);
112
+ }
113
+ el.removeAttribute('data-original-tabindex');
114
+ });
115
+ // Restore nav pointer events if animation is killed
116
+ const navElement = document.querySelector('[data-hs-hero="nav"]');
117
+ if (navElement) {
118
+ navElement.style.pointerEvents = '';
119
+ }
120
+ }
121
+
122
+ function startHeroAnimations() {
123
+ killHeroAnimations();
124
+ init();
125
+ }
126
+
127
+ function showHeroElementsWithoutAnimation() {
128
+ // Simply show all hero elements without any animation or split text
129
+ const allHeroElements = [
130
+ ...document.querySelectorAll('[data-hs-hero="announce"]'),
131
+ ...document.querySelectorAll('[data-hs-hero="nav"]'),
132
+ ...document.querySelectorAll('[data-hs-hero="nav-menu"]'),
133
+ ...document.querySelectorAll('[data-hs-hero="nav-logo"]'),
134
+ ...document.querySelectorAll('[data-hs-hero="nav-button"] > *:first-child'),
135
+ ...document.querySelectorAll('[data-hs-hero="nav-list"] > * > *:first-child'),
136
+ ...document.querySelectorAll('[data-hs-hero="heading"] > *:first-child'),
137
+ ...document.querySelectorAll('[data-hs-hero="subheading"] > *:first-child'),
138
+ ...document.querySelectorAll('[data-hs-hero="tag"] > *:first-child'),
139
+ ...document.querySelectorAll('[data-hs-hero="button"] > *'),
140
+ ...document.querySelectorAll('[data-hs-hero="image"]'),
141
+ ...document.querySelectorAll('[data-hs-hero="appear"]')
142
+ ];
143
+
144
+ allHeroElements.forEach(element => {
145
+ if (element) {
146
+ gsap.set(element, {
147
+ autoAlpha: 1,
148
+ opacity: 1,
149
+ y: 0,
150
+ yPercent: 0
151
+ });
152
+ // Remove any pointer-events restrictions
153
+ element.style.pointerEvents = '';
154
+ }
155
+ });
156
+
157
+ // Restore page-wide tabbing
158
+ const allFocusableElements = document.querySelectorAll('[data-original-tabindex]');
159
+ allFocusableElements.forEach(el => {
160
+ el.style.pointerEvents = '';
161
+ const originalTabindex = el.getAttribute('data-original-tabindex');
162
+ if (originalTabindex === '0') {
163
+ el.removeAttribute('tabindex');
164
+ } else {
165
+ el.setAttribute('tabindex', originalTabindex);
166
+ }
167
+ el.removeAttribute('data-original-tabindex');
168
+ });
169
+ }
170
+
171
+ export async function init() {
172
+ if (typeof window.gsap === "undefined") {
173
+ console.error('GSAP not found - hero animations disabled');
174
+ return;
175
+ }
176
+
177
+ if (prefersReducedMotion()) {
178
+ // For reduced motion, just show elements without animation
179
+ showHeroElementsWithoutAnimation();
180
+
181
+ // Still expose the API for consistency
182
+ window[API_NAME] = window[API_NAME] || {};
183
+ window[API_NAME].heroAnimations = {
184
+ config: config,
185
+ updateConfig: updateConfig,
186
+ start: startHeroAnimations,
187
+ kill: killHeroAnimations,
188
+ restart: () => {
189
+ killHeroAnimations();
190
+ startHeroAnimations();
191
+ }
192
+ };
193
+
194
+ return { result: 'anim-hero initialized (reduced motion)' };
195
+ }
196
+
197
+ gsap.registerPlugin(ScrollTrigger, SplitText);
198
+
199
+ // Element selection
200
+ const announceElements = document.querySelectorAll('[data-hs-hero="announce"]');
201
+ const navElement = document.querySelector('[data-hs-hero="nav"]');
202
+ const navMenuElements = document.querySelectorAll('[data-hs-hero="nav-menu"]');
203
+ const navLogoElements = document.querySelectorAll('[data-hs-hero="nav-logo"]');
204
+ const imageElements = document.querySelectorAll('[data-hs-hero="image"]');
205
+ const appearElements = document.querySelectorAll('[data-hs-hero="appear"]');
206
+
207
+ // Check if nav has advanced config
208
+ const hasAdvancedNav = navElement && navElement.hasAttribute('data-hs-heroconfig') && navElement.getAttribute('data-hs-heroconfig') === 'advanced';
209
+
210
+ // First child elements - only select if advanced nav is enabled
211
+ const navButton = [];
212
+ if (hasAdvancedNav) {
213
+ const navButtonParents = document.querySelectorAll('[data-hs-hero="nav-button"]');
214
+ navButtonParents.forEach(el => {
215
+ if (el.firstElementChild) navButton.push(el.firstElementChild);
216
+ });
217
+ }
218
+
219
+ const subheading = [];
220
+ const subheadingAppearElements = [];
221
+ const subheadingElements = document.querySelectorAll('[data-hs-hero="subheading"]');
222
+ const subheadingSplitElements = [];
223
+
224
+ subheadingElements.forEach(el => {
225
+ if (el.firstElementChild) {
226
+ // Get the heroconfig attribute to determine animation type (default to appear)
227
+ const heroConfig = el.getAttribute('data-hs-heroconfig') || 'appear';
228
+
229
+ if (heroConfig === 'appear') {
230
+ subheadingAppearElements.push(el.firstElementChild);
231
+ } else {
232
+ subheading.push(el.firstElementChild);
233
+ subheadingSplitElements.push(el);
234
+ }
235
+ }
236
+ });
237
+
238
+ const heading = [];
239
+ const headingAppearElements = [];
240
+ const headingElements = document.querySelectorAll('[data-hs-hero="heading"]');
241
+ const headingSplitElements = [];
242
+
243
+ headingElements.forEach(el => {
244
+ if (el.firstElementChild) {
245
+ // Get the heroconfig attribute to determine animation type
246
+ const heroConfig = el.getAttribute('data-hs-heroconfig') || 'word'; // default to word if not specified
247
+
248
+ if (heroConfig === 'appear') {
249
+ headingAppearElements.push(el.firstElementChild);
250
+ } else {
251
+ heading.push(el.firstElementChild);
252
+ headingSplitElements.push(el);
253
+ }
254
+ }
255
+ });
256
+
257
+ const tag = [];
258
+ const tagParents = document.querySelectorAll('[data-hs-hero="tag"]');
259
+ tagParents.forEach(el => {
260
+ if (el.firstElementChild) tag.push(el.firstElementChild);
261
+ });
262
+
263
+ // All children elements
264
+ const buttonAllChildren = [];
265
+ const buttonParents = document.querySelectorAll('[data-hs-hero="button"]');
266
+ buttonParents.forEach(el => {
267
+ const children = Array.from(el.children);
268
+ buttonAllChildren.push(...children);
269
+ });
270
+
271
+ const navListAllChildren = [];
272
+ if (hasAdvancedNav) {
273
+ const navListParents = document.querySelectorAll('[data-hs-hero="nav-list"]');
274
+ navListParents.forEach(el => {
275
+ const children = Array.from(el.children);
276
+
277
+ // Add overflow clip class to each child and collect their first child for animation
278
+ children.forEach(child => {
279
+ child.classList.add('u-overflow-clip');
280
+ if (child.firstElementChild) {
281
+ navListAllChildren.push(child.firstElementChild);
282
+ }
283
+ });
284
+ });
285
+ }
286
+
287
+ // Initial states
288
+ if (announceElements.length > 0) gsap.set(announceElements, { opacity: 0, y: -50 });
289
+ if (navElement) {
290
+ gsap.set(navElement, { opacity: 0, y: -50 });
291
+ // Disable nav pointer events until animation completes
292
+ navElement.style.pointerEvents = 'none';
293
+ }
294
+ if (hasAdvancedNav && navListAllChildren.length > 0) gsap.set(navListAllChildren, { opacity: 0, yPercent: 110 });
295
+ if (hasAdvancedNav && navMenuElements.length > 0) gsap.set(navMenuElements, { opacity: 0 });
296
+ if (hasAdvancedNav && navButton.length > 0) gsap.set(navButton, { opacity: 0 });
297
+ if (hasAdvancedNav && navLogoElements.length > 0) gsap.set(navLogoElements, { opacity: 0 });
298
+ if (subheadingAppearElements.length > 0) gsap.set(subheadingAppearElements, { y: config.appear.y, opacity: 0 });
299
+ if (tag.length > 0) gsap.set(tag, { y: config.appear.y, opacity: 0 });
300
+ if (buttonAllChildren.length > 0) gsap.set(buttonAllChildren, { y: config.appear.y, opacity: 0 });
301
+ if (imageElements.length > 0) gsap.set(imageElements, { opacity: 0 });
302
+ if (appearElements.length > 0) gsap.set(appearElements, { y: config.appear.y, opacity: 0 });
303
+ if (headingAppearElements.length > 0) gsap.set(headingAppearElements, { y: config.appear.y, opacity: 0 });
304
+
305
+ // Disable page-wide tabbing and interactions until animation completes
306
+ const allFocusableElements = document.querySelectorAll('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])');
307
+ allFocusableElements.forEach(el => {
308
+ el.style.pointerEvents = 'none';
309
+ el.setAttribute('data-original-tabindex', el.getAttribute('tabindex') || '0');
310
+ el.setAttribute('tabindex', '-1');
311
+ });
312
+
313
+ // Animation timeline
314
+ document.fonts.ready.then(() => {
315
+
316
+ // Split text setup (after fonts are loaded)
317
+ headingSplits = [];
318
+
319
+ if (heading.length > 0) {
320
+ headingSplitElements.forEach((parent, index) => {
321
+ const textElement = heading[index];
322
+ const splitType = parent.getAttribute('data-hs-heroconfig') || 'word';
323
+
324
+ let splitConfig = {};
325
+ let elementsClass = '';
326
+
327
+ if (splitType === 'char') {
328
+ splitConfig = {
329
+ type: "words,chars",
330
+ mask: "chars",
331
+ charsClass: "char"
332
+ };
333
+ elementsClass = 'chars';
334
+ } else if (splitType === 'line') {
335
+ splitConfig = {
336
+ type: "lines",
337
+ mask: "lines",
338
+ linesClass: "line"
339
+ };
340
+ elementsClass = 'lines';
341
+ } else {
342
+ splitConfig = {
343
+ type: "words",
344
+ mask: "words",
345
+ wordsClass: "word"
346
+ };
347
+ elementsClass = 'words';
348
+ }
349
+
350
+ const split = new SplitText(textElement, splitConfig);
351
+ split.elementsClass = elementsClass;
352
+ headingSplits.push(split);
353
+
354
+ gsap.set(split[elementsClass], { yPercent: config.headingSplit.yPercent });
355
+ gsap.set(textElement, { autoAlpha: 1 });
356
+ });
357
+ }
358
+
359
+ // Split text setup for subheadings
360
+ if (subheading.length > 0) {
361
+ subheadingSplitElements.forEach((parent, index) => {
362
+ const textElement = subheading[index];
363
+ const splitType = parent.getAttribute('data-hs-heroconfig') || 'word';
364
+
365
+ let splitConfig = {};
366
+ let elementsClass = '';
367
+
368
+ if (splitType === 'char') {
369
+ splitConfig = {
370
+ type: "words,chars",
371
+ mask: "chars",
372
+ charsClass: "char"
373
+ };
374
+ elementsClass = 'chars';
375
+ } else if (splitType === 'line') {
376
+ splitConfig = {
377
+ type: "lines",
378
+ mask: "lines",
379
+ linesClass: "line"
380
+ };
381
+ elementsClass = 'lines';
382
+ } else {
383
+ splitConfig = {
384
+ type: "words",
385
+ mask: "words",
386
+ wordsClass: "word"
387
+ };
388
+ elementsClass = 'words';
389
+ }
390
+
391
+ const split = new SplitText(textElement, splitConfig);
392
+ split.elementsClass = elementsClass;
393
+ subheadingSplits.push(split);
394
+
395
+ gsap.set(split[elementsClass], { yPercent: config.subheadingSplit.yPercent });
396
+ gsap.set(textElement, { autoAlpha: 1 });
397
+ });
398
+ }
399
+
400
+ heroTimeout = setTimeout(() => {
401
+ heroTimeline = gsap.timeline();
402
+
403
+ if (announceElements.length > 0) {
404
+ heroTimeline.to(announceElements,
405
+ { opacity: 1, y: 0, duration: config.nav.duration, ease: config.nav.ease },
406
+ timing.announce
407
+ );
408
+ }
409
+
410
+ if (navElement) {
411
+ heroTimeline.to(navElement,
412
+ { opacity: 1, y: 0, duration: config.nav.duration, ease: config.nav.ease },
413
+ timing.nav
414
+ );
415
+ }
416
+
417
+ if (hasAdvancedNav && navLogoElements.length > 0) {
418
+ heroTimeline.to(navLogoElements,
419
+ { opacity: 1, duration: .5, ease: config.nav.ease },
420
+ timing.navLogo
421
+ );
422
+ }
423
+
424
+ if (hasAdvancedNav && navListAllChildren.length > 0) {
425
+ heroTimeline.to(navListAllChildren,
426
+ {
427
+ opacity: 1,
428
+ yPercent: 0,
429
+ duration: config.nav.duration,
430
+ stagger: 0.05,
431
+ ease: config.nav.ease,
432
+ onComplete: () => {
433
+ // Remove u-overflow-clip class from list children
434
+ const navListParents = document.querySelectorAll('[data-hs-hero="nav-list"]');
435
+ navListParents.forEach(parent => {
436
+ const children = parent.children;
437
+ Array.from(children).forEach(child => {
438
+ child.classList.remove('u-overflow-clip');
439
+ });
440
+ });
441
+ }
442
+ },
443
+ timing.navList
444
+ );
445
+ }
446
+
447
+ if (hasAdvancedNav && navMenuElements.length > 0) {
448
+ heroTimeline.to(navMenuElements,
449
+ { opacity: 1, duration: config.nav.duration, ease: config.nav.ease },
450
+ timing.navMenu
451
+ );
452
+ }
453
+
454
+ if (hasAdvancedNav && navButton.length > 0) {
455
+ heroTimeline.to(navButton,
456
+ { opacity: 1, duration: config.nav.duration, ease: config.nav.ease },
457
+ timing.navButton
458
+ );
459
+ }
460
+
461
+ if (headingSplits.length > 0) {
462
+ headingSplits.forEach(split => {
463
+ heroTimeline.to(split[split.elementsClass],
464
+ {
465
+ yPercent: 0,
466
+ duration: config.headingSplit.duration,
467
+ stagger: config.headingSplit.stagger,
468
+ ease: config.headingSplit.ease,
469
+ onComplete: () => {
470
+ if (split && split.revert) {
471
+
472
+ }
473
+ }
474
+ },
475
+ timing.heading
476
+ );
477
+ });
478
+ }
479
+
480
+ if (subheadingSplits.length > 0) {
481
+ subheadingSplits.forEach(split => {
482
+ heroTimeline.to(split[split.elementsClass],
483
+ {
484
+ yPercent: 0,
485
+ duration: config.subheadingSplit.duration,
486
+ stagger: config.subheadingSplit.stagger,
487
+ ease: config.subheadingSplit.ease,
488
+ onComplete: () => {
489
+ if (split && split.revert) {
490
+
491
+ }
492
+ }
493
+ },
494
+ timing.subheading
495
+ );
496
+ });
497
+ }
498
+
499
+ if (subheadingAppearElements.length > 0) {
500
+ heroTimeline.to(subheadingAppearElements,
501
+ { y: 0, opacity: 1, duration: config.appear.duration, ease: config.appear.ease },
502
+ timing.subheading
503
+ );
504
+ }
505
+
506
+ if (tag.length > 0) {
507
+ heroTimeline.to(tag,
508
+ { y: 0, opacity: 1, duration: config.appear.duration, ease: config.appear.ease },
509
+ timing.tag
510
+ );
511
+ }
512
+
513
+ if (buttonAllChildren.length > 0) {
514
+ heroTimeline.to(buttonAllChildren,
515
+ { y: 0, opacity: 1, duration: config.navStagger.duration, stagger: config.navStagger.stagger, ease: config.navStagger.ease },
516
+ timing.button
517
+ );
518
+ }
519
+
520
+ if (imageElements.length > 0) {
521
+ heroTimeline.to(imageElements,
522
+ { opacity: 1, duration: config.appear.duration, ease: config.appear.ease },
523
+ timing.image
524
+ );
525
+ }
526
+
527
+ // Combine appear elements and heading appear elements
528
+ const allAppearElements = [...appearElements, ...headingAppearElements];
529
+
530
+ if (allAppearElements.length > 0) {
531
+ heroTimeline.to(allAppearElements,
532
+ {
533
+ y: 0,
534
+ opacity: 1,
535
+ duration: config.appear.duration,
536
+ ease: config.appear.ease,
537
+ onComplete: () => {
538
+ // Restore page-wide tabbing and interactions after hero animation completes
539
+ const allFocusableElements = document.querySelectorAll('[data-original-tabindex]');
540
+ allFocusableElements.forEach(el => {
541
+ el.style.pointerEvents = '';
542
+ const originalTabindex = el.getAttribute('data-original-tabindex');
543
+ if (originalTabindex === '0') {
544
+ el.removeAttribute('tabindex');
545
+ } else {
546
+ el.setAttribute('tabindex', originalTabindex);
547
+ }
548
+ el.removeAttribute('data-original-tabindex');
549
+ });
550
+ // Restore nav pointer events
551
+ if (navElement) {
552
+ navElement.style.pointerEvents = '';
553
+ }
554
+ }
555
+ },
556
+ timing.appear
557
+ );
558
+ } else {
559
+ // If no appear elements, restore tabbing when timeline completes
560
+ heroTimeline.call(() => {
561
+ const allFocusableElements = document.querySelectorAll('[data-original-tabindex]');
562
+ allFocusableElements.forEach(el => {
563
+ el.style.pointerEvents = '';
564
+ const originalTabindex = el.getAttribute('data-original-tabindex');
565
+ if (originalTabindex === '0') {
566
+ el.removeAttribute('tabindex');
567
+ } else {
568
+ el.setAttribute('tabindex', originalTabindex);
569
+ }
570
+ el.removeAttribute('data-original-tabindex');
571
+ });
572
+ // Restore nav pointer events
573
+ if (navElement) {
574
+ navElement.style.pointerEvents = '';
575
+ }
576
+ });
577
+ }
578
+
579
+ heroTimeout = null;
580
+
581
+ }, config.global.animationDelay * 1000);
582
+
583
+ });
584
+
585
+ // API exposure
586
+ window[API_NAME] = window[API_NAME] || {};
587
+ window[API_NAME].heroAnimations = {
588
+ config: config,
589
+ updateConfig: updateConfig,
590
+ start: startHeroAnimations,
591
+ kill: killHeroAnimations,
592
+ restart: () => {
593
+ killHeroAnimations();
594
+ startHeroAnimations();
595
+ }
596
+ };
597
+
598
+ return { result: 'anim-hero initialized' };
599
+ }