@widelab-nc/widehue 1.0.44 → 1.0.45

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": "@widelab-nc/widehue",
3
- "version": "1.0.44",
3
+ "version": "1.0.45",
4
4
  "description": "Widelab starter template based on Finsweet template + add-ons.",
5
5
  "homepage": "https://widelab.co",
6
6
  "license": "ISC",
package/src/index.js CHANGED
@@ -1479,16 +1479,50 @@ window.animationTest4 = function(root) {
1479
1479
 
1480
1480
 
1481
1481
  window.pricingInteraction = function(e) {
1482
+ console.log('[pricingInteraction] Function called');
1483
+ console.log('[pricingInteraction] Window width:', window.innerWidth);
1484
+
1482
1485
  // Check if the screen width is 992px or wider
1483
1486
  if (window.innerWidth >= 992) {
1487
+ console.log('[pricingInteraction] Desktop mode - creating pin');
1488
+
1489
+ const tabsContent = document.querySelector('.tabs-content');
1490
+ const tabsMenu = document.querySelector('.tabs-menu');
1491
+
1492
+ console.log('[pricingInteraction] .tabs-content found:', tabsContent);
1493
+ console.log('[pricingInteraction] .tabs-menu found:', tabsMenu);
1494
+
1495
+ if (!tabsContent || !tabsMenu) {
1496
+ console.error('[pricingInteraction] Required elements not found!');
1497
+ return;
1498
+ }
1499
+
1500
+ // Debug computed styles
1501
+ const tabsContentStyles = window.getComputedStyle(tabsContent);
1502
+ const tabsMenuStyles = window.getComputedStyle(tabsMenu);
1503
+ console.log('[pricingInteraction] .tabs-content styles:', {
1504
+ position: tabsContentStyles.position,
1505
+ height: tabsContentStyles.height,
1506
+ display: tabsContentStyles.display,
1507
+ });
1508
+ console.log('[pricingInteraction] .tabs-menu styles:', {
1509
+ position: tabsMenuStyles.position,
1510
+ height: tabsMenuStyles.height,
1511
+ display: tabsMenuStyles.display,
1512
+ });
1513
+
1484
1514
  // Create the pinning effect
1485
- ScrollTrigger.create({
1515
+ const trigger = ScrollTrigger.create({
1486
1516
  trigger: ".tabs-content",
1487
- start: "top-=48px top", // Start pinning 48px after the top of the .tabs-content hits the top of the viewport
1488
- end: "bottom bottom", // Stop pinning when the bottom of the .tabs-content hits the bottom of the viewport
1489
- pin: ".tabs-menu", // The element to pin
1490
- pinSpacing: false // Maintain space for pinned element (optional)
1517
+ start: "top-=48px top",
1518
+ end: "bottom bottom",
1519
+ pin: ".tabs-menu",
1520
+
1491
1521
  });
1522
+
1523
+ console.log('[pricingInteraction] ScrollTrigger created:', trigger);
1524
+ } else {
1525
+ console.log('[pricingInteraction] Mobile mode - skipping pin');
1492
1526
  }
1493
1527
  };
1494
1528
 
@@ -2527,6 +2561,42 @@ window.formSubmitAnimation = function(e) {
2527
2561
  observeDisplayChanges();
2528
2562
  }
2529
2563
 
2564
+ // Scrubbed timeline: progress tied to scroll progress
2565
+ window.initBentoRevealScrub = function (container) {
2566
+ // Respect reduced motion
2567
+ if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
2568
+ container.querySelectorAll(':scope > *').forEach(el => {
2569
+ el.style.opacity = 1;
2570
+ el.style.transform = 'none';
2571
+ });
2572
+ return;
2573
+ }
2574
+
2575
+ const items = Array.from(container.children); // direct children only
2576
+
2577
+ // Important: define from state in timeline for scrub to interpolate smoothly
2578
+ const tl = gsap.timeline({
2579
+ scrollTrigger: {
2580
+ trigger: container,
2581
+ start: 'top 90%', // start easing in slightly later
2582
+ end: 'bottom 65%', // full reveal by near the end of container
2583
+ scrub: 0.5, // smoothing; use true for no smoothing
2584
+ markers: false, // uncomment for debugging
2585
+ }
2586
+ });
2587
+
2588
+ tl.fromTo(items,
2589
+ { opacity: 0, y: 28 },
2590
+ {
2591
+ opacity: 1,
2592
+ y: 0,
2593
+ ease: 'power2.out',
2594
+ // Duration is ignored by scrub; timeline length maps to scroll distance.
2595
+ // Stagger distributes the items across the timeline for a nice cascade.
2596
+ stagger: { each: 0.12 }
2597
+ }
2598
+ );
2599
+ };
2530
2600
 
2531
2601
 
2532
2602
  // Refresh Gsap scrollTriggers
@@ -2563,6 +2633,283 @@ window.closeInfoBar = function() {
2563
2633
  }
2564
2634
 
2565
2635
 
2636
+ // Hero 2026 animation - navbar letters on scroll
2637
+ // EXPERIMENTAL: play/reverse version (to revert: replace this whole function with scrub version)
2638
+ window.heroAnimation2026 = function(e) {
2639
+ const wideletters = document.querySelectorAll('.wideletter');
2640
+ const introSection = document.querySelector('[x-init*="introAnimation"]');
2641
+
2642
+ if (!introSection) return;
2643
+
2644
+ // Wideletters animation timeline (paused)
2645
+ const widelettersTimeline = gsap.timeline({ paused: true });
2646
+ widelettersTimeline.fromTo(wideletters,
2647
+ { y: '-150%' },
2648
+ {
2649
+ y: '0%',
2650
+ duration: 0.8,
2651
+ ease: 'power2.out',
2652
+ stagger: 0.05,
2653
+ }
2654
+ );
2655
+
2656
+ // Trigger for wideletters
2657
+ ScrollTrigger.create({
2658
+ trigger: introSection,
2659
+ start: 'bottom bottom-=25%',
2660
+ onEnter: () => widelettersTimeline.play(),
2661
+ onLeaveBack: () => widelettersTimeline.reverse(),
2662
+ });
2663
+
2664
+ // Partner section animation
2665
+ const partnerSection = document.querySelector('#partner');
2666
+ const heroFlex = document.querySelector('.hero-flex');
2667
+ const coverBg = document.querySelector('.cover-bg');
2668
+
2669
+ if (!partnerSection || !heroFlex || !coverBg) return;
2670
+
2671
+ // Partner timeline (cover-bg + wideletters reverse)
2672
+ const partnerTimeline = gsap.timeline({ paused: true });
2673
+
2674
+ // Cover-bg fade-in
2675
+ partnerTimeline.fromTo(coverBg,
2676
+ { opacity: 0 },
2677
+ { opacity: 1, duration: 0.6, ease: 'power2.out' },
2678
+ 0
2679
+ );
2680
+
2681
+ // Reverse wideletters
2682
+ partnerTimeline.to(wideletters, {
2683
+ y: '-150%',
2684
+ duration: 0.6,
2685
+ ease: 'power2.in',
2686
+ stagger: 0.03,
2687
+ }, 0);
2688
+
2689
+ // Trigger for partner timeline
2690
+ ScrollTrigger.create({
2691
+ trigger: partnerSection,
2692
+ start: 'top 80%',
2693
+ onEnter: () => partnerTimeline.play(),
2694
+ onLeaveBack: () => partnerTimeline.reverse(),
2695
+ });
2696
+ };
2697
+
2698
+ // Intro animation on page load
2699
+ window.introAnimation = function(e) {
2700
+ const introBrand = e.querySelector('[intro-brand]');
2701
+ const introSubtitle = e.querySelector('[intro-subtitle]');
2702
+ const widelettersIntro = e.querySelectorAll('.wideletter-intro');
2703
+
2704
+ // Set initial states
2705
+ gsap.set([introBrand, introSubtitle], { opacity: 0 });
2706
+ gsap.set(widelettersIntro, { y: '-150%' });
2707
+
2708
+ // Prevent browser scroll restoration and force scroll to top
2709
+ history.scrollRestoration = 'manual';
2710
+ window.scrollTo(0, 0);
2711
+
2712
+ // Lock scroll: position fixed keeps scrollbar visible but prevents scrolling
2713
+ document.body.style.position = 'fixed';
2714
+ document.body.style.top = '0px';
2715
+ document.body.style.width = '100%';
2716
+
2717
+ // Page load animation - scroll timeline is created AFTER this completes
2718
+ const introTimeline = gsap.timeline({
2719
+ onComplete: () => {
2720
+ // Unlock scroll
2721
+ document.body.style.position = '';
2722
+ document.body.style.top = '';
2723
+ document.body.style.width = '';
2724
+ window.scrollTo(0, 0);
2725
+
2726
+ // Force all letters to final state before creating ScrollTrigger
2727
+ gsap.set(widelettersIntro, { y: '0%', opacity: 1 });
2728
+ gsap.set(introBrand, { opacity: 1 });
2729
+
2730
+ // Small delay to ensure DOM is ready, then create scroll timeline and refresh ScrollTrigger
2731
+ requestAnimationFrame(() => {
2732
+ createScrollTimeline();
2733
+ ScrollTrigger.refresh();
2734
+ });
2735
+ }
2736
+ });
2737
+
2738
+ introTimeline.to(introBrand, {
2739
+ opacity: 1,
2740
+ duration: 0.1,
2741
+ });
2742
+
2743
+ introTimeline.to(widelettersIntro, {
2744
+ y: '0%',
2745
+ duration: 1,
2746
+ ease: 'loader2',
2747
+ stagger: 0.1,
2748
+ });
2749
+
2750
+ // EXPERIMENTAL: play/reverse version (no parallax)
2751
+ function createScrollTimeline() {
2752
+ // Timeline 1: Letters out
2753
+ const lettersOutTimeline = gsap.timeline({ paused: true });
2754
+ lettersOutTimeline.to(widelettersIntro, {
2755
+ y: '-150%',
2756
+ opacity: 0,
2757
+ duration: 0.6,
2758
+ ease: 'power2.in',
2759
+ stagger: 0.03,
2760
+ });
2761
+
2762
+ // Trigger 1: Letters out (at 20% of section)
2763
+ ScrollTrigger.create({
2764
+ trigger: e,
2765
+ start: 'top top-=20%',
2766
+ onEnter: () => lettersOutTimeline.play(),
2767
+ onLeaveBack: () => lettersOutTimeline.reverse(),
2768
+ });
2769
+
2770
+ // Timeline 2: Subtitle in
2771
+ const subtitleInTimeline = gsap.timeline({ paused: true });
2772
+ subtitleInTimeline.fromTo(introSubtitle,
2773
+ { y: '50%', opacity: 0 },
2774
+ { y: '0%', opacity: 1, duration: 0.6, ease: 'power2.out' }
2775
+ );
2776
+
2777
+ // Trigger 2: Subtitle in (at 90% of section)
2778
+ ScrollTrigger.create({
2779
+ trigger: e,
2780
+ start: 'top top-=90%',
2781
+ onEnter: () => subtitleInTimeline.play(),
2782
+ onLeaveBack: () => subtitleInTimeline.reverse(),
2783
+ });
2784
+
2785
+ // Timeline 3: Subtitle out
2786
+ const subtitleOutTimeline = gsap.timeline({ paused: true });
2787
+ subtitleOutTimeline.to(introSubtitle, {
2788
+ opacity: 0,
2789
+ duration: 0.5,
2790
+ ease: 'power2.out',
2791
+ });
2792
+
2793
+ // Trigger 3: Subtitle out (at 140% - well into section scroll)
2794
+ ScrollTrigger.create({
2795
+ trigger: e,
2796
+ start: 'top top-=140%',
2797
+ onEnter: () => subtitleOutTimeline.play(),
2798
+ onLeaveBack: () => subtitleOutTimeline.reverse(),
2799
+ });
2800
+ }
2801
+ };
2802
+
2803
+ // Global GSAP title animation - animates [gsap-title] elements on scroll
2804
+ function gsapTitleAnimation() {
2805
+ const gsapTitles = document.querySelectorAll('[gsap-title]');
2806
+
2807
+ if (gsapTitles.length === 0) return;
2808
+
2809
+ gsapTitles.forEach((titleElement) => {
2810
+ // Split text into lines
2811
+ const splitTitle = new SplitText(titleElement, {
2812
+ type: 'lines',
2813
+ linesClass: 'gsap-title-line',
2814
+ });
2815
+
2816
+ // Create mask wrapper for each line
2817
+ const splitTitleMask = new SplitText(titleElement, {
2818
+ type: 'lines',
2819
+ linesClass: 'gsap-title-line-mask',
2820
+ });
2821
+
2822
+ const lines = titleElement.querySelectorAll('.gsap-title-line');
2823
+
2824
+ if (lines.length === 0) return;
2825
+
2826
+ // Set initial state - lines hidden below
2827
+ gsap.set(lines, { y: '100%' });
2828
+
2829
+ // Create paused timeline
2830
+ const titleTimeline = gsap.timeline({ paused: true });
2831
+
2832
+ titleTimeline.to(lines, {
2833
+ y: '0%',
2834
+ duration: 0.75,
2835
+ stagger: 0.2,
2836
+ ease: 'loader2',
2837
+ });
2838
+
2839
+ // Create ScrollTrigger with play/reverse
2840
+ ScrollTrigger.create({
2841
+ trigger: titleElement,
2842
+ start: 'top 80%',
2843
+ onEnter: () => titleTimeline.play(),
2844
+ onLeaveBack: () => titleTimeline.reverse(),
2845
+ });
2846
+ });
2847
+ }
2848
+
2849
+ // Global GSAP container animation - animates direct children of [gsap-container]
2850
+ function gsapContainerAnimation() {
2851
+ const gsapContainers = document.querySelectorAll('[gsap-container]');
2852
+
2853
+ if (gsapContainers.length === 0) return;
2854
+
2855
+ gsapContainers.forEach((container) => {
2856
+ const children = container.children;
2857
+
2858
+ if (children.length === 0) return;
2859
+
2860
+ // Set initial state
2861
+ gsap.set(children, { y: '4rem', opacity: 0 });
2862
+
2863
+ // Create paused timeline
2864
+ const containerTimeline = gsap.timeline({ paused: true });
2865
+
2866
+ containerTimeline.to(children, {
2867
+ y: '0rem',
2868
+ opacity: 1,
2869
+ duration: 1,
2870
+ stagger: 0.1,
2871
+ ease: 'power3.out',
2872
+ });
2873
+
2874
+ // Create ScrollTrigger with play/reverse
2875
+ ScrollTrigger.create({
2876
+ trigger: container,
2877
+ start: 'top 80%',
2878
+ onEnter: () => containerTimeline.play(),
2879
+ onLeaveBack: () => containerTimeline.reverse(),
2880
+ });
2881
+ });
2882
+ }
2883
+
2884
+ // Global GSAP fade animation - fades in [gsap-fade] elements on scroll
2885
+ function gsapFadeAnimation() {
2886
+ const gsapFadeElements = document.querySelectorAll('[gsap-fade]');
2887
+
2888
+ if (gsapFadeElements.length === 0) return;
2889
+
2890
+ gsapFadeElements.forEach((element) => {
2891
+ // Set initial state
2892
+ gsap.set(element, { opacity: 0 });
2893
+
2894
+ // Create paused timeline
2895
+ const fadeTimeline = gsap.timeline({ paused: true });
2896
+
2897
+ fadeTimeline.to(element, {
2898
+ opacity: 1,
2899
+ duration: 2,
2900
+ ease: 'power3.out',
2901
+ });
2902
+
2903
+ // Create ScrollTrigger with play/reverse
2904
+ ScrollTrigger.create({
2905
+ trigger: element,
2906
+ start: 'top 65%',
2907
+ onEnter: () => fadeTimeline.play(),
2908
+ onLeaveBack: () => fadeTimeline.reverse(),
2909
+ });
2910
+ });
2911
+ }
2912
+
2566
2913
  // !!!!!!!!! Keep this Alpine init at the end !!!!!!!!!
2567
2914
  window.Webflow ||= [];
2568
2915
  window.Webflow.push(() => {
@@ -2586,6 +2933,9 @@ window.Webflow.push(() => {
2586
2933
  contactFormOpenAnimation();
2587
2934
  openContactFormFromURL();
2588
2935
  disableScrollMenu();
2936
+ gsapTitleAnimation();
2937
+ gsapContainerAnimation();
2938
+ gsapFadeAnimation();
2589
2939
  refreshScrollTriggersAfterLoad();
2590
2940
 
2591
2941
  });