@hortonstudio/main 1.2.13 → 1.2.15

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/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ var n="hsmain",S=async()=>{if(window[n]&&!Array.isArray(window[n])&&window[n].loaded)return;let w=Array.isArray(window[n])?window[n]:[],h={"data-hs-anim-text":!0,"data-hs-anim-hero":!0,"data-hs-anim-transition":!0},f={"data-hs-util-toc":!0,"data-hs-util-progress":!0,"data-hs-util-navbar":!0},m={"smooth-scroll":!0},y={...h,...f},{loadModule:p}=await import("./src-load-3UYE3H3N.js"),g=async()=>new Promise(t=>{window.Webflow||(window.Webflow=[]),window.Webflow.push(t)}),A=async()=>new Promise(t=>{document.readyState==="loading"?document.addEventListener("DOMContentLoaded",t):t()}),a=[...document.querySelectorAll(`script[type="module"][src="${import.meta.url}"]`)];a.length===0&&(a=[...document.querySelectorAll('script[type="module"][src*="@hortonstudio/main"]')].filter(e=>{let o=e.src,s=import.meta.url,i=o.match(/@hortonstudio\/main(@[\d.]+)?/)?.[0],r=s.match(/@hortonstudio\/main(@[\d.]+)?/)?.[0];return i&&r&&i.split("@")[0]===r.split("@")[0]}));let l=async t=>{let e=window[n];if(e.process.has(t))return e.modules[t]?.loading;e.process.add(t);let o=e.modules[t]||{};e.modules[t]=o,o.loading=new Promise((s,i)=>{o.resolve=s,o.reject=i});try{let{init:s,version:i}=await p(t),r=await s(),{result:d,destroy:c}=r||{};return o.version=i,o.destroy=()=>{c?.(),e.process.delete(t)},o.restart=()=>(o.destroy?.(),e.load(t)),o.resolve?.(d),delete o.resolve,delete o.reject,d}catch(s){throw o.reject?.(s),e.process.delete(t),s}},u=[];window[n]={scripts:a,modules:{},process:new Set,load:l,loaded:!1,push(...t){for(let[e,o]of t)typeof o=="function"?this.modules[e]?.loading?.then(o):this.load(e)},destroy(){for(let t in this.modules)this.modules[t]?.destroy?.()},afterReady(t){typeof t=="function"&&(this.loaded?t():u.push(t))},afterWebflowReady(t){typeof t=="function"&&(this.loaded?t():u.push(t))},status(t){return t?{loaded:!!this.modules[t],loading:this.process.has(t)}:{loaded:Object.keys(this.modules),loading:[...this.process],animations:Object.keys(h),utilities:Object.keys(f),autoInit:Object.keys(m)}}};let b=()=>{for(let e of a){let o=e.getAttribute("data-hs-auto")==="true";for(let s of Object.keys(y))e.hasAttribute(s)&&l(s);o&&A().then(()=>{let s=new Set,i=document.querySelectorAll("*");for(let r of i)for(let d of r.getAttributeNames()){let c=d.match(/^(data-hs-(?:anim|util)-[^-=]+)/)?.[1];c&&y[c]&&s.add(c)}for(let r of s)l(r)})}for(let e of Object.keys(m))l(e);a.some(e=>e.hasAttribute("data-hs-anim-transition"))||document.querySelectorAll(".transition").forEach(o=>{o.style.display="none"})};document.querySelectorAll(".w-richtext").forEach(t=>{t.querySelectorAll("img").forEach(o=>{o.loading="eager"})});let M=async()=>{b(),await g(),window[n].loaded=!0,u.forEach(t=>{try{t()}catch{}})};window[n].push(...w),M().catch(()=>{})};S();
@@ -0,0 +1 @@
1
+ var m="hsmain",D=()=>window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches,c={announce:0,nav:.1,navLogo:.3,navList:.35,navMenu:.35,navButton:.5,tag:.1,heading:.15,subheading:.25,button:.35,image:.5,appear:.6},r=null,y=[],C=[],x=null,e={global:{animationDelay:.2},headingSplit:{duration:1.5,stagger:.1,yPercent:110,ease:"power4.out"},subheadingSplit:{duration:1.5,stagger:.1,yPercent:110,ease:"power4.out"},appear:{y:50,duration:1.5,ease:"power3.out"},navStagger:{duration:1.5,stagger:.1,ease:"power3.out"},nav:{duration:1,ease:"power3.out"}};function B(p){function i(t,s){for(let d in s)s[d]&&typeof s[d]=="object"&&!Array.isArray(s[d])?(t[d]=t[d]||{},i(t[d],s[d])):t[d]=s[d];return t}i(e,p)}function w(){x&&(clearTimeout(x),x=null),r&&(r.kill(),r=null),y.forEach(t=>{t&&t.revert&&t.revert()}),y=[],C.forEach(t=>{t&&t.revert&&t.revert()}),C=[],document.querySelectorAll("[data-original-tabindex]").forEach(t=>{t.style.pointerEvents="";let s=t.getAttribute("data-original-tabindex");s==="0"?t.removeAttribute("tabindex"):t.setAttribute("tabindex",s),t.removeAttribute("data-original-tabindex")});let i=document.querySelector('[data-hs-hero="nav"]');i&&(i.style.pointerEvents="")}function P(){w(),j()}function N(){[...document.querySelectorAll('[data-hs-hero="announce"]'),...document.querySelectorAll('[data-hs-hero="nav"]'),...document.querySelectorAll('[data-hs-hero="nav-menu"]'),...document.querySelectorAll('[data-hs-hero="nav-logo"]'),...document.querySelectorAll('[data-hs-hero="nav-button"] > *:first-child'),...document.querySelectorAll('[data-hs-hero="nav-list"] > * > *:first-child'),...document.querySelectorAll('[data-hs-hero="heading"] > *:first-child'),...document.querySelectorAll('[data-hs-hero="subheading"] > *:first-child'),...document.querySelectorAll('[data-hs-hero="tag"] > *:first-child'),...document.querySelectorAll('[data-hs-hero="button"] > *'),...document.querySelectorAll('[data-hs-hero="image"]'),...document.querySelectorAll('[data-hs-hero="appear"]')].forEach(t=>{t&&(gsap.set(t,{autoAlpha:1,opacity:1,y:0,yPercent:0}),t.style.pointerEvents="")}),document.querySelectorAll("[data-original-tabindex]").forEach(t=>{t.style.pointerEvents="";let s=t.getAttribute("data-original-tabindex");s==="0"?t.removeAttribute("tabindex"):t.setAttribute("tabindex",s),t.removeAttribute("data-original-tabindex")})}async function j(){if(typeof window.gsap>"u"){console.error("GSAP not found - hero animations disabled");return}if(D())return N(),window[m]=window[m]||{},window[m].heroAnimations={config:e,updateConfig:B,start:P,kill:w,restart:()=>{w(),P()}},{result:"anim-hero initialized (reduced motion)"};gsap.registerPlugin(ScrollTrigger,SplitText);let p=document.querySelectorAll('[data-hs-hero="announce"]'),i=document.querySelector('[data-hs-hero="nav"]'),t=document.querySelectorAll('[data-hs-hero="nav-menu"]'),s=document.querySelectorAll('[data-hs-hero="nav-logo"]'),d=document.querySelectorAll('[data-hs-hero="image"]'),T=document.querySelectorAll('[data-hs-hero="appear"]'),g=i&&i.hasAttribute("data-hs-heroconfig")&&i.getAttribute("data-hs-heroconfig")==="advanced",b=[];g&&document.querySelectorAll('[data-hs-hero="nav-button"]').forEach(n=>{n.firstElementChild&&b.push(n.firstElementChild)});let L=[],v=[],H=document.querySelectorAll('[data-hs-hero="subheading"]'),F=[];H.forEach(a=>{a.firstElementChild&&((a.getAttribute("data-hs-heroconfig")||"appear")==="appear"?v.push(a.firstElementChild):(L.push(a.firstElementChild),F.push(a)))});let k=[],q=[],z=document.querySelectorAll('[data-hs-hero="heading"]'),M=[];z.forEach(a=>{a.firstElementChild&&((a.getAttribute("data-hs-heroconfig")||"word")==="appear"?q.push(a.firstElementChild):(k.push(a.firstElementChild),M.push(a)))});let A=[];document.querySelectorAll('[data-hs-hero="tag"]').forEach(a=>{a.firstElementChild&&A.push(a.firstElementChild)});let E=[];document.querySelectorAll('[data-hs-hero="button"]').forEach(a=>{let n=Array.from(a.children);E.push(...n)});let S=[];return g&&document.querySelectorAll('[data-hs-hero="nav-list"]').forEach(n=>{Array.from(n.children).forEach(l=>{l.classList.add("u-overflow-clip"),l.firstElementChild&&S.push(l.firstElementChild)})}),p.length>0&&gsap.set(p,{opacity:0,y:-50}),i&&(gsap.set(i,{opacity:0,y:-50}),i.style.pointerEvents="none"),g&&S.length>0&&gsap.set(S,{opacity:0,yPercent:110}),g&&t.length>0&&gsap.set(t,{opacity:0}),g&&b.length>0&&gsap.set(b,{opacity:0}),g&&s.length>0&&gsap.set(s,{opacity:0}),v.length>0&&gsap.set(v,{y:e.appear.y,opacity:0}),A.length>0&&gsap.set(A,{y:e.appear.y,opacity:0}),E.length>0&&gsap.set(E,{y:e.appear.y,opacity:0}),d.length>0&&gsap.set(d,{opacity:0}),T.length>0&&gsap.set(T,{y:e.appear.y,opacity:0}),q.length>0&&gsap.set(q,{y:e.appear.y,opacity:0}),document.querySelectorAll('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])').forEach(a=>{a.style.pointerEvents="none",a.setAttribute("data-original-tabindex",a.getAttribute("tabindex")||"0"),a.setAttribute("tabindex","-1")}),document.fonts.ready.then(()=>{y=[],k.length>0&&M.forEach((a,n)=>{let o=k[n],l=a.getAttribute("data-hs-heroconfig")||"word",u={},h="";l==="char"?(u={type:"words,chars",mask:"chars",charsClass:"char"},h="chars"):l==="line"?(u={type:"lines",mask:"lines",linesClass:"line"},h="lines"):(u={type:"words",mask:"words",wordsClass:"word"},h="words");let f=new SplitText(o,u);f.elementsClass=h,y.push(f),gsap.set(f[h],{yPercent:e.headingSplit.yPercent}),gsap.set(o,{autoAlpha:1})}),L.length>0&&F.forEach((a,n)=>{let o=L[n],l=a.getAttribute("data-hs-heroconfig")||"word",u={},h="";l==="char"?(u={type:"words,chars",mask:"chars",charsClass:"char"},h="chars"):l==="line"?(u={type:"lines",mask:"lines",linesClass:"line"},h="lines"):(u={type:"words",mask:"words",wordsClass:"word"},h="words");let f=new SplitText(o,u);f.elementsClass=h,C.push(f),gsap.set(f[h],{yPercent:e.subheadingSplit.yPercent}),gsap.set(o,{autoAlpha:1})}),x=setTimeout(()=>{r=gsap.timeline(),p.length>0&&r.to(p,{opacity:1,y:0,duration:e.nav.duration,ease:e.nav.ease},c.announce),i&&r.to(i,{opacity:1,y:0,duration:e.nav.duration,ease:e.nav.ease},c.nav),g&&s.length>0&&r.to(s,{opacity:1,duration:.5,ease:e.nav.ease},c.navLogo),g&&S.length>0&&r.to(S,{opacity:1,yPercent:0,duration:e.nav.duration,stagger:.05,ease:e.nav.ease,onComplete:()=>{document.querySelectorAll('[data-hs-hero="nav-list"]').forEach(o=>{let l=o.children;Array.from(l).forEach(u=>{u.classList.remove("u-overflow-clip")})})}},c.navList),g&&t.length>0&&r.to(t,{opacity:1,duration:e.nav.duration,ease:e.nav.ease},c.navMenu),g&&b.length>0&&r.to(b,{opacity:1,duration:e.nav.duration,ease:e.nav.ease},c.navButton),y.length>0&&y.forEach(n=>{r.to(n[n.elementsClass],{yPercent:0,duration:e.headingSplit.duration,stagger:e.headingSplit.stagger,ease:e.headingSplit.ease,onComplete:()=>{n&&n.revert}},c.heading)}),C.length>0&&C.forEach(n=>{r.to(n[n.elementsClass],{yPercent:0,duration:e.subheadingSplit.duration,stagger:e.subheadingSplit.stagger,ease:e.subheadingSplit.ease,onComplete:()=>{n&&n.revert}},c.subheading)}),v.length>0&&r.to(v,{y:0,opacity:1,duration:e.appear.duration,ease:e.appear.ease},c.subheading),A.length>0&&r.to(A,{y:0,opacity:1,duration:e.appear.duration,ease:e.appear.ease},c.tag),E.length>0&&r.to(E,{y:0,opacity:1,duration:e.navStagger.duration,stagger:e.navStagger.stagger,ease:e.navStagger.ease},c.button),d.length>0&&r.to(d,{opacity:1,duration:e.appear.duration,ease:e.appear.ease},c.image);let a=[...T,...q];a.length>0?r.to(a,{y:0,opacity:1,duration:e.appear.duration,ease:e.appear.ease,onComplete:()=>{document.querySelectorAll("[data-original-tabindex]").forEach(o=>{o.style.pointerEvents="";let l=o.getAttribute("data-original-tabindex");l==="0"?o.removeAttribute("tabindex"):o.setAttribute("tabindex",l),o.removeAttribute("data-original-tabindex")}),i&&(i.style.pointerEvents="")}},c.appear):r.call(()=>{document.querySelectorAll("[data-original-tabindex]").forEach(o=>{o.style.pointerEvents="";let l=o.getAttribute("data-original-tabindex");l==="0"?o.removeAttribute("tabindex"):o.setAttribute("tabindex",l),o.removeAttribute("data-original-tabindex")}),i&&(i.style.pointerEvents="")}),x=null},e.global.animationDelay*1e3)}),window[m]=window[m]||{},window[m].heroAnimations={config:e,updateConfig:B,start:P,kill:w,restart:()=>{w(),P()}},{result:"anim-hero initialized"}}export{j as init};
@@ -0,0 +1 @@
1
+ var r=async t=>{switch(t){case"data-hs-anim-text":return import("./src-text-BZVCCQJP.js");case"data-hs-anim-hero":return import("./src-hero-UYNS76KU.js");case"data-hs-anim-transition":return import("./src-transition-UBAPWTIU.js");case"data-hs-util-toc":return import("./src-toc-SHTDW6BF.js");case"data-hs-util-progress":return import("./src-scroll-progress-EX62FWYD.js");case"data-hs-util-navbar":return import("./src-navbar-763V2PWX.js");case"smooth-scroll":return import("./src-smooth-scroll-TG4SEEIC.js");default:throw new Error(`hsmain module "${t}" is not supported.`)}};export{r as loadModule};
@@ -0,0 +1 @@
1
+ var w=()=>{let v=document.querySelectorAll('[data-hs-nav-dropdown="wrapper"]'),u=[],y=(n=null)=>{u.forEach(a=>{a.wrapper!==n&&a.isOpen&&a.closeDropdown()})};return v.forEach(n=>{let r=n.querySelector("a"),t=n.querySelector('[data-hs-nav-dropdown="list"]'),d=t.querySelector('[data-hs-nav-dropdown="container"]'),f=r.querySelector('[data-hs-nav-dropdown="arrow"]'),p=r.querySelector('[data-hs-nav-dropdown="text"]');gsap.set(d,{yPercent:-110}),gsap.set(t,{display:"none"}),gsap.set(f,{rotation:0,scale:1,x:0,color:""}),gsap.set(p,{scale:1,color:""});let i=!1,s=null;function m(){i||(s&&s.kill(),y(n),i=!0,r.setAttribute("aria-expanded","true"),t.setAttribute("aria-hidden","false"),s=gsap.timeline(),s.set(t,{display:"flex"}).to(d,{yPercent:0,duration:.3,ease:"ease"},0).to(f,{rotation:90,scale:1.2,x:4,color:"var(--swatch--brand)",duration:.3,ease:"ease"},0).to(p,{scale:1.1,color:"var(--swatch--brand)",duration:.3,ease:"ease"},0))}function l(){if(!i)return;s&&s.kill();let e=t.contains(document.activeElement);i=!1,o=-1,r.setAttribute("aria-expanded","false"),t.setAttribute("aria-hidden","true");let D=t.getAttribute("role");t.removeAttribute("role"),s=gsap.timeline(),s.to(d,{yPercent:-110,duration:.3,ease:"ease"},0).to(f,{rotation:0,scale:1,x:0,color:"",duration:.3,ease:"ease"},0).to(p,{scale:1,color:"",duration:.3,ease:"ease"},0).set(t,{display:"none"}).call(()=>{t.setAttribute("role",D||"menu")}),e&&setTimeout(()=>{r.focus()},50)}let c=t.querySelectorAll('a, button, [role="menuitem"]'),o=-1;r.addEventListener("mouseenter",m),n.addEventListener("mouseleave",l),t.addEventListener("keydown",function(e){i&&(e.key==="ArrowDown"?(e.preventDefault(),o=(o+1)%c.length,c[o].focus()):e.key==="ArrowUp"?(e.preventDefault(),o=o<=0?c.length-1:o-1,c[o].focus()):e.key==="Escape"&&(e.preventDefault(),l(),r.focus()))}),r.addEventListener("keydown",function(e){e.key==="ArrowDown"?(e.preventDefault(),m(),c.length>0&&(o=0,setTimeout(()=>c[0].focus(),50))):e.key===" "?(e.preventDefault(),i?l():m()):(e.key==="ArrowUp"||e.key==="Escape")&&(e.preventDefault(),l())}),document.addEventListener("click",function(e){!n.contains(e.target)&&i&&l()}),u.push({wrapper:n,isOpen:()=>i,closeDropdown:l})}),document.addEventListener("focusin",function(n){u.forEach(a=>{a.isOpen()&&!a.wrapper.contains(n.target)&&a.closeDropdown()})}),{result:"navbar initialized"}};export{w as init};
@@ -0,0 +1 @@
1
+ async function e(){let r=document.querySelector('[data-hs-progress="bar"]'),t=document.querySelector('[data-hs-progress="wrapper"]');return!r||!t?{result:"util-scroll-progress initialized"}:(gsap.set(r,{width:"0%"}),gsap.to(r,{width:"100%",ease:"none",scrollTrigger:{trigger:t,start:"top bottom",end:"bottom bottom",scrub:!0}}),{result:"util-scroll-progress initialized"})}export{e as init};
@@ -0,0 +1 @@
1
+ var f="hsmain";async function m(){window[f].afterWebflowReady(()=>{typeof $<"u"&&$(document).off("click.wf-scroll")}),document.documentElement.style.scrollBehavior="auto",document.body.style.scrollBehavior="auto";function i(){return window.matchMedia("(prefers-reduced-motion: reduce)").matches}function c(){let t=getComputedStyle(document.documentElement).getPropertyValue("--misc--scroll-offset").trim();return parseInt(t)||0}function l(t,e=0){if(t){if(i()){let n=t.getBoundingClientRect().top+window.scrollY-e;window.scrollTo(0,n),t.setAttribute("tabindex","-1"),t.focus({preventScroll:!0});return}gsap.to(window,{duration:1,scrollTo:{y:t,offsetY:e},ease:"power2.out",onComplete:function(){t.setAttribute("tabindex","-1"),t.focus({preventScroll:!0})}})}}function s(){document.addEventListener("click",r),document.addEventListener("keydown",function(t){(t.key==="Enter"||t.key===" ")&&r(t)})}function r(t){let e=t.target.closest('a[href^="#"]');if(!e)return;let n=e.getAttribute("href");if(!n||n==="#")return;let u=n.substring(1),o=document.getElementById(u);if(o){t.preventDefault(),history.replaceState&&history.replaceState(null,null,`#${o.id}`);let d=c();l(o,d)}}return s(),{result:"autoInit-smooth-scroll initialized"}}export{m as init};
@@ -0,0 +1 @@
1
+ var w="hsmain",r=()=>window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches,a={global:{animationDelay:0},wordSplit:{duration:1.5,stagger:.075,yPercent:110,ease:"power4.out",start:"top 97%"},lineSplit:{duration:1.5,stagger:.1,yPercent:110,ease:"power4.out",start:"top 97%"},charSplit:{duration:1.2,stagger:.03,yPercent:110,ease:"power4.out",start:"top 97%"},appear:{y:50,duration:1.5,ease:"power3.out",start:"top 97%"}};function S(e){function t(i,l){for(let n in l)l[n]&&typeof l[n]=="object"&&!Array.isArray(l[n])?(i[n]=i[n]||{},t(i[n],l[n])):i[n]=l[n];return i}t(a,e)}function c(){o.forEach(({timeline:e,element:t})=>{e&&e.kill(),t?.splitTextInstance&&t.splitTextInstance.revert()}),o.length=0}function p(){if(r()){u();return}f().then(()=>{g()})}var o=[];function s(){return document.fonts.ready}function u(){[...document.querySelectorAll(".a-char-split > *:first-child"),...document.querySelectorAll(".a-word-split > *:first-child"),...document.querySelectorAll(".a-line-split > *:first-child"),...document.querySelectorAll(".a-appear")].forEach(t=>{gsap.set(t,{autoAlpha:1,y:0,yPercent:0,opacity:1})})}var d={async initial(){if(await s(),r())return;document.querySelectorAll(".a-char-split > *:first-child").forEach(t=>{let i=SplitText.create(t,{type:"chars",mask:"chars",charsClass:"char"});t.splitTextInstance=i,gsap.set(i.chars,{yPercent:a.charSplit.yPercent}),gsap.set(t,{autoAlpha:1})})},async animate(){await s(),!r()&&document.querySelectorAll(".a-char-split > *:first-child").forEach(e=>{let t=e.querySelectorAll(".char"),i=gsap.timeline({scrollTrigger:{trigger:e,start:a.charSplit.start,invalidateOnRefresh:!0},onComplete:()=>{}});i.to(t,{yPercent:0,duration:a.charSplit.duration,stagger:a.charSplit.stagger,ease:a.charSplit.ease}),o.push({timeline:i,element:e})})}},y={async initial(){if(await s(),r())return;document.querySelectorAll(".a-word-split > *:first-child").forEach(t=>{let i=SplitText.create(t,{type:"words",mask:"words",wordsClass:"word"});t.splitTextInstance=i,gsap.set(i.words,{yPercent:a.wordSplit.yPercent}),gsap.set(t,{autoAlpha:1})})},async animate(){await s(),!r()&&document.querySelectorAll(".a-word-split > *:first-child").forEach(e=>{let t=e.querySelectorAll(".word"),i=gsap.timeline({scrollTrigger:{trigger:e,start:a.wordSplit.start,invalidateOnRefresh:!0},onComplete:()=>{}});i.to(t,{yPercent:0,duration:a.wordSplit.duration,stagger:a.wordSplit.stagger,ease:a.wordSplit.ease}),o.push({timeline:i,element:e})})}},h={async initial(){if(await s(),r())return;document.querySelectorAll(".a-line-split > *:first-child").forEach(t=>{let i=SplitText.create(t,{type:"lines",mask:"lines",linesClass:"line"});t.splitTextInstance=i,gsap.set(i.lines,{yPercent:a.lineSplit.yPercent}),gsap.set(t,{autoAlpha:1})})},async animate(){await s(),!r()&&document.querySelectorAll(".a-line-split > *:first-child").forEach(e=>{let t=e.querySelectorAll(".line"),i=gsap.timeline({scrollTrigger:{trigger:e,start:a.lineSplit.start,invalidateOnRefresh:!0},onComplete:()=>{}});i.to(t,{yPercent:0,duration:a.lineSplit.duration,stagger:a.lineSplit.stagger,ease:a.lineSplit.ease}),o.push({timeline:i,element:e})})}},m={async initial(){if(await s(),r())return;document.querySelectorAll(".a-appear").forEach(t=>{gsap.set(t,{y:a.appear.y,opacity:0})})},async animate(){await s(),!r()&&document.querySelectorAll(".a-appear").forEach(e=>{let t=gsap.timeline({scrollTrigger:{trigger:e,start:a.appear.start,invalidateOnRefresh:!0}});t.to(e,{y:0,opacity:1,duration:a.appear.duration,ease:a.appear.ease}),o.push({timeline:t,element:e})})}};async function f(){await Promise.all([d.initial(),y.initial(),h.initial(),m.initial()])}async function g(){a.global.animationDelay>0&&await new Promise(e=>setTimeout(e,a.global.animationDelay*1e3)),await Promise.all([d.animate(),y.animate(),h.animate(),m.animate()])}async function A(){r()?u():(await f(),g()),window.addEventListener("resize",ScrollTrigger.refresh());let e=window[w]||{};return e.textAnimations={config:a,updateConfig:S,start:p,kill:c,restart:()=>{c(),p()}},{result:"anim-text initialized"}}export{A as init};
@@ -0,0 +1 @@
1
+ async function p(){let s=document.querySelector('[data-hs-toc="content"]'),o=document.querySelector('[data-hs-toc="list"]');if(!s||!o||o.children.length===0)return;let u=o.children[0];o.innerHTML="";let d=s.querySelectorAll("h2");return d.forEach((n,a)=>{let c=n.textContent.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/(^-|-$)/g,""),e=document.createElement("div");e.id=c,n.parentNode.insertBefore(e,n),e.appendChild(n);let t=e.nextElementSibling;for(;t&&t.tagName!=="H2";){let r=t;t=t.nextElementSibling,e.appendChild(r)}}),d.forEach((n,a)=>{let c=u.cloneNode(!0),e=c.querySelector("a"),t=n.parentElement.id;e.href="#"+t;let r=document.createElement("strong");r.textContent=a+1+". ",e.innerHTML="",e.appendChild(r),e.appendChild(document.createTextNode(n.textContent)),e.addEventListener("click",m=>{m.preventDefault();let l=document.getElementById(t);l&&(l.scrollIntoView({behavior:"smooth"}),setTimeout(()=>{l.focus()},100))});let i=document.getElementById(t);i&&(i.setAttribute("tabindex","-1"),i.style.outline="none",i.style.setProperty("outline","none","important")),o.appendChild(c)}),{result:"util-toc initialized"}}export{p as init};
@@ -0,0 +1 @@
1
+ var a="hsmain";async function l(){return window[a].afterReady(()=>{typeof $<"u"&&r()}),{result:"anim-transition initialized"}}function r(){let i=$(".transition-trigger"),n=800,o=400,e="no-transition";i.length>0&&(window.Webflow&&window.Webflow.push?Webflow.push(function(){i.click()}):setTimeout(()=>{i.click()},100),$("body").addClass("no-scroll-transition"),setTimeout(()=>{$("body").removeClass("no-scroll-transition")},n)),$("a").on("click",function(t){if($(this).prop("hostname")==window.location.host&&$(this).attr("href").indexOf("#")===-1&&!$(this).hasClass(e)&&$(this).attr("target")!=="_blank"&&i.length>0){t.preventDefault(),$("body").addClass("no-scroll-transition");let s=$(this).attr("href");i.click(),setTimeout(function(){window.location=s},o)}}),window.onpageshow=function(t){t.persisted&&window.location.reload()},setTimeout(()=>{$(window).on("resize",function(){setTimeout(()=>{$(".transition").css("display","none")},50)})},n)}export{l as init};
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "@hortonstudio/main",
3
- "version": "1.2.13",
3
+ "version": "1.2.15",
4
4
  "description": "Core animation and utility library for client websites",
5
- "main": "index.js",
5
+ "main": "dist/index.js",
6
6
  "type": "module",
7
+ "files": [
8
+ "dist/**/*"
9
+ ],
7
10
  "keywords": [
8
11
  "animation",
9
12
  "gsap",
@@ -12,6 +15,12 @@
12
15
  ],
13
16
  "author": "Horton Studio",
14
17
  "license": "ISC",
18
+ "scripts": {
19
+ "build": "tsx bin/build.ts",
20
+ "build:dev": "tsx bin/build.ts --dev",
21
+ "dev": "tsx bin/build.ts --dev --watch",
22
+ "prepublishOnly": "npm run build"
23
+ },
15
24
  "dependencies": {
16
25
  "@hortonstudio/main-anim-hero": "^1.0.0",
17
26
  "@hortonstudio/main-anim-text": "^1.0.0",
@@ -20,5 +29,10 @@
20
29
  "@hortonstudio/main-util-toc": "^1.0.0",
21
30
  "@hortonstudio/main-util-progress": "^1.0.0",
22
31
  "@hortonstudio/main-smooth-scroll": "^1.0.0"
32
+ },
33
+ "devDependencies": {
34
+ "esbuild": "^0.19.0",
35
+ "tsx": "^4.0.0",
36
+ "typescript": "^5.0.0"
23
37
  }
24
38
  }
package/CLAUDE.md DELETED
@@ -1,45 +0,0 @@
1
- # CLAUDE.md
2
-
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
-
5
- ## Project Overview
6
-
7
- This is `@hortonstudio/main` - an animation and utility library for client websites, primarily designed for Webflow integration. The library uses a modular ES6 system with dynamic loading based on HTML data attributes.
8
-
9
- ## Architecture
10
-
11
- **Main API**: `window.hsmain` (configurable via `API_NAME` constant in index.js)
12
-
13
- **Module Categories**:
14
- - Animation modules: `data-hs-anim-*` attributes trigger loading
15
- - Utility modules: `data-hs-util-*` attributes trigger loading
16
- - Auto-init modules: Always loaded automatically
17
-
18
- **Dependencies**: Requires GSAP with ScrollTrigger and SplitText plugins
19
-
20
- **Integration**: Designed for Webflow with `Webflow.ready()` callback system
21
-
22
- ## Module Loading System
23
-
24
- Modules are loaded via script tag attributes:
25
- ```html
26
- <script src="index.js" data-hs-main data-hs-anim-hero data-hs-util-toc></script>
27
- ```
28
-
29
- Each module exports an `init()` function returning `{ result: 'module-name initialized' }`.
30
-
31
- ## Key Animation Patterns
32
-
33
- **Hero animations** (`hero.js`): Orchestrated timeline with navigation reveals, split text headings, and staggered element appearances. Uses data attributes like `data-hs-hero="heading"` and `data-hs-split="word|line|char"`.
34
-
35
- **Text animations** (`text.js`): Scroll-triggered animations using CSS classes `.a-word-split`, `.a-line-split`, `.a-char-split`, `.a-appear` on parent elements, targeting first child for animation.
36
-
37
- **Configuration**: All modules expose config objects via `window.hsmain.moduleAnimations.config` with `updateConfig()` methods for runtime modification.
38
-
39
- ## Important Implementation Details
40
-
41
- - All animations wait for `document.fonts.ready` before initialization
42
- - Split text instances are automatically cleaned up after animations complete
43
- - CSS utility classes like `.u-overflow-clip` are dynamically added/removed for animation masking
44
- - The library handles Webflow DOM changes by calling `Webflow.ready()` after module loading
45
- - Navigation accessibility is temporarily disabled during hero animations then restored
@@ -1,567 +0,0 @@
1
- # @hortonstudio/main - Complete Usage Guide
2
-
3
- This is a comprehensive guide for using the `@hortonstudio/main` animation and utility library, designed for modern web applications including Webflow, Next.js, React, and vanilla JavaScript projects.
4
-
5
- ## Table of Contents
6
- 1. [Installation & Setup](#installation--setup)
7
- 2. [Framework Integration](#framework-integration)
8
- 3. [Hero Animations](#hero-animations)
9
- 4. [Text Animations](#text-animations)
10
- 5. [Page Transitions](#page-transitions)
11
- 6. [Utility Modules](#utility-modules)
12
- 7. [Auto-Init Modules](#auto-init-modules)
13
- 8. [Configuration](#configuration)
14
- 9. [API Reference](#api-reference)
15
-
16
- ---
17
-
18
- ## Installation & Setup
19
-
20
- ### 1. Include the Script
21
- Add this script tag to your page (preferably in the `<head>` or before closing `</body>`):
22
-
23
- ```html
24
- <script src="https://cdn.jsdelivr.net/npm/@hortonstudio/main@latest/index.js"
25
- data-hs-main
26
- data-hs-anim-hero
27
- data-hs-util-toc></script>
28
- ```
29
-
30
- ### 2. Required Dependencies
31
- Ensure GSAP with required plugins is loaded before the main script:
32
-
33
- ```html
34
- <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
35
- <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script>
36
- <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/SplitText.min.js"></script>
37
- ```
38
-
39
- ### 3. Module Loading
40
- Modules are loaded based on data attributes on the script tag:
41
-
42
- - `data-hs-main`: **Required** - Core library identifier
43
- - `data-hs-anim-*`: Animation modules
44
- - `data-hs-util-*`: Utility modules
45
-
46
- **Example:**
47
- ```html
48
- <script src="index.js"
49
- data-hs-main
50
- data-hs-anim-hero
51
- data-hs-anim-text
52
- data-hs-util-navbar
53
- data-hs-util-progress></script>
54
- ```
55
-
56
- ---
57
-
58
- ## Framework Integration
59
-
60
- ### Webflow
61
- The library works seamlessly with Webflow with full integration support:
62
-
63
- ```html
64
- <script src="https://cdn.jsdelivr.net/npm/@hortonstudio/main@latest/index.js"
65
- data-hs-main
66
- data-hs-anim-hero
67
- data-hs-util-toc></script>
68
- ```
69
-
70
- ### Next.js / React
71
- For Next.js applications, install and use as an ES module:
72
-
73
- ```bash
74
- npm install @hortonstudio/main
75
- ```
76
-
77
- ```javascript
78
- // In your component or _app.js
79
- import { useEffect } from 'react'
80
-
81
- export default function MyApp({ Component, pageProps }) {
82
- useEffect(() => {
83
- // Load the library after component mounts
84
- import('@hortonstudio/main').then((lib) => {
85
- // Library automatically initializes
86
- console.log('HS Main loaded:', window.hsmain)
87
- })
88
- }, [])
89
-
90
- return <Component {...pageProps} />
91
- }
92
- ```
93
-
94
- ### Vanilla JavaScript
95
- For standard JavaScript projects:
96
-
97
- ```javascript
98
- // Import as ES module
99
- import '@hortonstudio/main'
100
-
101
- // Or use CDN with script tag
102
- // <script src="https://cdn.jsdelivr.net/npm/@hortonstudio/main@latest/index.js" data-hs-main></script>
103
-
104
- // Access via global API
105
- console.log(window.hsmain.status())
106
- ```
107
-
108
- ### Vue.js
109
- ```javascript
110
- // In main.js or component
111
- import { onMounted } from 'vue'
112
- import '@hortonstudio/main'
113
-
114
- export default {
115
- setup() {
116
- onMounted(() => {
117
- // Library ready after DOM mount
118
- window.hsmain.afterReady(() => {
119
- console.log('Animations ready!')
120
- })
121
- })
122
- }
123
- }
124
- ```
125
-
126
- ### Framework-Agnostic Initialization
127
- The library detects the environment and initializes appropriately:
128
-
129
- - **With Webflow**: Uses `Webflow.ready()` and `Webflow.push()`
130
- - **Without Webflow**: Uses standard DOM events and timeouts
131
- - **Universal**: All animations and utilities work in any environment
132
-
133
- ---
134
-
135
- ## Hero Animations
136
-
137
- Hero animations create orchestrated entrance animations for landing page elements.
138
-
139
- ### Basic Setup
140
-
141
- #### 1. Navigation
142
- ```html
143
- <nav data-hs-hero="nav">
144
- <!-- Your navigation content -->
145
- </nav>
146
- ```
147
-
148
- #### 2. Advanced Navigation (with staggered animations)
149
- ```html
150
- <nav data-hs-hero="nav" data-hs-heroconfig="advanced">
151
- <div data-hs-hero="nav-logo">
152
- <!-- Logo content -->
153
- </div>
154
- <ul data-hs-hero="nav-list">
155
- <li><a href="#">Link 1</a></li>
156
- <li><a href="#">Link 2</a></li>
157
- </ul>
158
- <div data-hs-hero="nav-menu">
159
- <!-- Menu button -->
160
- </div>
161
- <div data-hs-hero="nav-button">
162
- <!-- CTA button -->
163
- </div>
164
- </nav>
165
- ```
166
-
167
- #### 3. Headings with Text Splitting
168
- ```html
169
- <div data-hs-hero="heading" data-hs-heroconfig="word">
170
- <h1>Your Main Heading</h1>
171
- </div>
172
-
173
- <div data-hs-hero="heading" data-hs-heroconfig="line">
174
- <h1>Multi-line Heading That Splits by Lines</h1>
175
- </div>
176
-
177
- <div data-hs-hero="heading" data-hs-heroconfig="char">
178
- <h1>Character Split Animation</h1>
179
- </div>
180
- ```
181
-
182
- #### 4. Subheadings
183
- ```html
184
- <div data-hs-hero="subheading" data-hs-heroconfig="appear">
185
- <h2>Simple fade-in subheading</h2>
186
- </div>
187
-
188
- <div data-hs-hero="subheading" data-hs-heroconfig="word">
189
- <h2>Word-split subheading</h2>
190
- </div>
191
- ```
192
-
193
- #### 5. Other Hero Elements
194
- ```html
195
- <!-- Announcement banner -->
196
- <div data-hs-hero="announce">
197
- <p>Special announcement</p>
198
- </div>
199
-
200
- <!-- Tag/category -->
201
- <div data-hs-hero="tag">
202
- <span>Category Tag</span>
203
- </div>
204
-
205
- <!-- Buttons (all children animate with stagger) -->
206
- <div data-hs-hero="button">
207
- <a href="#" class="btn">Primary Button</a>
208
- <a href="#" class="btn">Secondary Button</a>
209
- </div>
210
-
211
- <!-- Images -->
212
- <img data-hs-hero="image" src="hero-image.jpg" alt="Hero Image">
213
-
214
- <!-- General appear elements -->
215
- <div data-hs-hero="appear">
216
- <p>Any content that should fade in</p>
217
- </div>
218
- ```
219
-
220
- ### Hero Configuration Options
221
-
222
- | Attribute Value | Description | Usage |
223
- |----------------|-------------|--------|
224
- | `word` | Split text by words | `data-hs-heroconfig="word"` |
225
- | `line` | Split text by lines | `data-hs-heroconfig="line"` |
226
- | `char` | Split text by characters | `data-hs-heroconfig="char"` |
227
- | `appear` | Simple fade-in animation | `data-hs-heroconfig="appear"` |
228
- | `advanced` | Enable advanced nav animations | `data-hs-heroconfig="advanced"` |
229
-
230
- ---
231
-
232
- ## Text Animations
233
-
234
- Text animations provide scroll-triggered text splitting animations for content sections.
235
-
236
- ### Setup Classes
237
-
238
- Add these classes to parent elements, with the actual text element as the first child:
239
-
240
- #### 1. Word Split Animation
241
- ```html
242
- <div class="a-word-split">
243
- <h2>Text that splits by words on scroll</h2>
244
- </div>
245
- ```
246
-
247
- #### 2. Line Split Animation
248
- ```html
249
- <div class="a-line-split">
250
- <h2>Multi-line text that splits by lines</h2>
251
- </div>
252
- ```
253
-
254
- #### 3. Character Split Animation
255
- ```html
256
- <div class="a-char-split">
257
- <h1>Text split by individual characters</h1>
258
- </div>
259
- ```
260
-
261
- #### 4. Simple Appear Animation
262
- ```html
263
- <div class="a-appear">
264
- <p>Content that fades in on scroll</p>
265
- </div>
266
- ```
267
-
268
- ### Important Notes
269
- - **First child targeting**: The animation targets the **first child element** of the element with the class
270
- - **Scroll triggered**: All text animations trigger when the element enters the viewport
271
- - **Auto cleanup**: SplitText instances are automatically cleaned up after animation completes
272
-
273
- ---
274
-
275
- ## Page Transitions
276
-
277
- Page transitions create smooth animations between page loads.
278
-
279
- ### Setup
280
-
281
- #### 1. Add Transition Trigger Element
282
- ```html
283
- <!-- This element triggers the transition animation -->
284
- <div class="transition-trigger" style="display: none;"></div>
285
- ```
286
-
287
- #### 2. Add Transition Overlay
288
- ```html
289
- <!-- This element covers the screen during transitions -->
290
- <div class="transition">
291
- <!-- Your transition content/animation -->
292
- </div>
293
- ```
294
-
295
- #### 3. Exclude Links from Transitions
296
- ```html
297
- <!-- Links that should NOT trigger transitions -->
298
- <a href="/page" class="no-transition">Direct Link</a>
299
- <a href="/external" target="_blank">External Link</a>
300
- <a href="#section">Anchor Link</a>
301
- ```
302
-
303
- ### Configuration
304
- - **Intro Duration**: 800ms (time for page load transition)
305
- - **Exit Duration**: 400ms (time for page exit transition)
306
- - **Excluded**: Links with `no-transition` class, external links, anchor links
307
- - **Auto-disable**: Transitions disable on window resize for mobile compatibility
308
-
309
- ---
310
-
311
- ## Utility Modules
312
-
313
- ### Table of Contents (TOC)
314
- Auto-generates navigation from page headings.
315
-
316
- ```html
317
- <!-- Container where TOC will be inserted -->
318
- <nav data-hs-toc="container">
319
- <!-- TOC items will be auto-generated here -->
320
- </nav>
321
-
322
- <!-- Headings that should appear in TOC -->
323
- <h2 data-hs-toc="item">Section 1</h2>
324
- <h3 data-hs-toc="item">Subsection 1.1</h3>
325
- <h2 data-hs-toc="item">Section 2</h2>
326
- ```
327
-
328
- ### Scroll Progress Indicator
329
- Shows page scroll progress.
330
-
331
- ```html
332
- <!-- Progress bar element -->
333
- <div data-hs-progress="bar"></div>
334
- ```
335
-
336
- ### Smart Navbar
337
- Navbar that hides/shows based on scroll direction.
338
-
339
- ```html
340
- <nav data-hs-navbar="container">
341
- <!-- Your navigation content -->
342
- </nav>
343
- ```
344
-
345
- **Behavior:**
346
- - Hides when scrolling down
347
- - Shows when scrolling up
348
- - Always visible at the top of the page
349
- - Smooth transitions with CSS transforms
350
-
351
- ---
352
-
353
- ## Auto-Init Modules
354
-
355
- These modules load automatically without requiring data attributes.
356
-
357
- ### Smooth Scroll
358
- - **Automatically enabled** for all anchor links
359
- - **Smooth scrolling** to page sections
360
- - **Offset support** for fixed headers
361
- - **No setup required**
362
-
363
- ---
364
-
365
- ## Configuration
366
-
367
- ### Global API Access
368
- All modules expose configuration through the global API:
369
-
370
- ```javascript
371
- // Access the API (default name: 'hsmain')
372
- window.hsmain
373
-
374
- // Check module status
375
- window.hsmain.status() // All modules
376
- window.hsmain.status('data-hs-anim-hero') // Specific module
377
-
378
- // Restart a module
379
- window.hsmain.modules['data-hs-anim-hero'].restart()
380
- ```
381
-
382
- ### Hero Animation Configuration
383
- ```javascript
384
- // Update hero animation settings
385
- window.hsmain.heroAnimations.updateConfig({
386
- global: {
387
- animationDelay: 0.5 // Delay before animations start
388
- },
389
- headingSplit: {
390
- duration: 2.0, // Animation duration
391
- stagger: 0.15, // Delay between elements
392
- yPercent: 120, // Initial position offset
393
- ease: "power3.out" // Easing function
394
- }
395
- })
396
-
397
- // Restart hero animations with new config
398
- window.hsmain.heroAnimations.restart()
399
- ```
400
-
401
- ### Module Animation Configuration
402
- ```javascript
403
- // Update text animation settings
404
- window.hsmain.moduleAnimations.config.updateConfig({
405
- wordSplit: {
406
- duration: 1.8,
407
- stagger: 0.1,
408
- start: "top 90%" // ScrollTrigger start position
409
- }
410
- })
411
- ```
412
-
413
- ---
414
-
415
- ## API Reference
416
-
417
- ### Core API Methods
418
-
419
- ```javascript
420
- // Load a module manually
421
- window.hsmain.load('data-hs-util-navbar')
422
-
423
- // Check if modules are loaded/loading
424
- window.hsmain.status()
425
- // Returns: { loaded: [...], loading: [...], animations: [...], utilities: [...] }
426
-
427
- // Register callback for after library initialization
428
- window.hsmain.afterReady(() => {
429
- console.log('Library is ready!')
430
- })
431
-
432
- // Legacy Webflow callback (still supported)
433
- window.hsmain.afterWebflowReady(() => {
434
- console.log('Webflow compatibility mode')
435
- })
436
- ```
437
-
438
- ### Hero Animations API
439
-
440
- ```javascript
441
- // Available methods
442
- window.hsmain.heroAnimations.start() // Start animations
443
- window.hsmain.heroAnimations.kill() // Stop animations
444
- window.hsmain.heroAnimations.restart() // Restart animations
445
- window.hsmain.heroAnimations.config // Current configuration
446
- window.hsmain.heroAnimations.updateConfig(newConfig) // Update settings
447
- ```
448
-
449
- ### Timing Configuration
450
-
451
- #### Hero Animation Timing (in seconds)
452
- ```javascript
453
- const timing = {
454
- announce: 0, // Announcement elements
455
- nav: 0.1, // Main navigation
456
- navLogo: 0.3, // Navigation logo
457
- navList: 0.35, // Navigation links
458
- navMenu: 0.35, // Menu button
459
- navButton: 0.5, // CTA button
460
- tag: 0.1, // Category tags
461
- heading: 0.15, // Main headings
462
- subheading: 0.25, // Subheadings
463
- button: 0.35, // Action buttons
464
- image: 0.5, // Images
465
- appear: 0.6 // General appear elements
466
- }
467
- ```
468
-
469
- ---
470
-
471
- ## CSS Utility Classes
472
-
473
- The library includes these utility classes:
474
-
475
- ```css
476
- /* Transition styles */
477
- .transition { display: block; }
478
- .w-editor .transition { display: none; }
479
- .no-scroll-transition { overflow: hidden; position: relative; }
480
-
481
- /* Split text masks */
482
- .line-mask, .word-mask, .char-mask {
483
- padding-bottom: .1em;
484
- margin-bottom: -.1em;
485
- padding-inline: .1em;
486
- margin-inline: -.1em;
487
- }
488
-
489
- /* Scroll improvements */
490
- html, body {
491
- overscroll-behavior: none;
492
- scrollbar-gutter: stable;
493
- }
494
- ```
495
-
496
- ---
497
-
498
- ## Best Practices
499
-
500
- ### 1. Module Loading
501
- - Only load modules you actually use
502
- - Include required dependencies before the main script
503
- - Use `data-hs-main` on the script tag
504
-
505
- ### 2. Hero Animations
506
- - Structure your HTML with proper parent/child relationships
507
- - Use semantic HTML elements (h1, h2, nav, etc.)
508
- - Test animations on different screen sizes
509
-
510
- ### 3. Text Animations
511
- - Place classes on parent containers, not the text elements directly
512
- - Ensure the text element is the first child
513
- - Consider animation timing with page scroll speed
514
-
515
- ### 4. Performance
516
- - Animations wait for fonts to load (`document.fonts.ready`)
517
- - SplitText instances are cleaned up automatically
518
- - Modules only initialize when needed
519
-
520
- ### 5. Framework Integration
521
- - **Webflow**: Full integration with Webflow's responsive design and interactions
522
- - **React/Next.js**: Works with SSR and client-side rendering
523
- - **Vue.js**: Compatible with reactive data and component lifecycle
524
- - **Universal**: Framework-agnostic core ensures compatibility
525
-
526
- ---
527
-
528
- ## Troubleshooting
529
-
530
- ### Common Issues
531
-
532
- 1. **Animations not starting**
533
- - Check that GSAP and plugins are loaded
534
- - Verify data attributes are correct
535
- - Check browser console for errors
536
-
537
- 2. **Text splitting not working**
538
- - Ensure SplitText plugin is loaded
539
- - Check that target element is the first child
540
- - Verify class names are correct
541
-
542
- 3. **Module not loading**
543
- - Check script tag has `data-hs-main`
544
- - Verify module attribute name
545
- - Check `window.hsmain.status()` for loading state
546
-
547
- 4. **Webflow conflicts**
548
- - Ensure script loads after Webflow
549
- - Check for conflicting CSS
550
- - Test without other custom scripts
551
-
552
- ### Debug Commands
553
-
554
- ```javascript
555
- // Check what's loaded
556
- console.log(window.hsmain.status())
557
-
558
- // Check for errors
559
- window.hsmain.modules
560
-
561
- // Restart problematic modules
562
- window.hsmain.modules['module-name'].restart()
563
- ```
564
-
565
- ---
566
-
567
- *This documentation covers version 1.1.27 of @hortonstudio/main*
package/index.js DELETED
@@ -1,305 +0,0 @@
1
- // ver 1.2.13
2
-
3
- const API_NAME = 'hsmain';
4
-
5
- // Main initialization function
6
- const initializeHsMain = () => {
7
- // Handle existing API
8
- if (window[API_NAME] && !Array.isArray(window[API_NAME]) && window[API_NAME].loaded) {
9
- return;
10
- }
11
-
12
- // Store any early API calls
13
- const existingRequests = Array.isArray(window[API_NAME]) ? window[API_NAME] : [];
14
-
15
- // Module definitions
16
- const animationModules = {
17
- "data-hs-anim-text": true,
18
- "data-hs-anim-hero": true,
19
- "data-hs-anim-transition": true
20
- };
21
-
22
- const utilityModules = {
23
- "data-hs-util-toc": true,
24
- "data-hs-util-progress": true,
25
- "data-hs-util-navbar": true
26
- };
27
-
28
- const autoInitModules = {
29
- "smooth-scroll": true
30
- };
31
-
32
- // All available modules
33
- const allModules = { ...animationModules, ...utilityModules };
34
-
35
- // Dynamic module loader (like Finsweet)
36
- const loadModule = async (moduleName) => {
37
- switch (moduleName) {
38
- case "data-hs-anim-text":
39
- return import('@hortonstudio/main-anim-text');
40
- case "data-hs-anim-hero":
41
- return import('@hortonstudio/main-anim-hero');
42
- case "data-hs-anim-transition":
43
- return import('@hortonstudio/main-anim-transition');
44
- case "data-hs-util-toc":
45
- return import('@hortonstudio/main-util-toc');
46
- case "data-hs-util-progress":
47
- return import('@hortonstudio/main-util-progress');
48
- case "data-hs-util-navbar":
49
- return import('@hortonstudio/main-util-navbar');
50
- case "smooth-scroll":
51
- return import('@hortonstudio/main-smooth-scroll');
52
- default:
53
- throw new Error(`${API_NAME} module "${moduleName}" is not supported.`);
54
- }
55
- };
56
-
57
- // Webflow ready helper (like Finsweet)
58
- const waitWebflowReady = async () => {
59
- return new Promise((resolve) => {
60
- window.Webflow ||= [];
61
- window.Webflow.push(resolve);
62
- });
63
- };
64
-
65
- // DOM ready helper (like Finsweet)
66
- const waitDOMReady = async () => {
67
- return new Promise((resolve) => {
68
- if (document.readyState === 'loading') {
69
- document.addEventListener('DOMContentLoaded', resolve);
70
- } else {
71
- resolve();
72
- }
73
- });
74
- };
75
-
76
- // Find script tags (with CDN version redirect support)
77
- let scripts = [...document.querySelectorAll(`script[type="module"][src="${import.meta.url}"]`)];
78
-
79
- // Handle CDN version redirects (e.g., @1 -> @1.2.11)
80
- if (scripts.length === 0) {
81
- const allScripts = [...document.querySelectorAll('script[type="module"][src*="@hortonstudio/main"]')];
82
- scripts = allScripts.filter(script => {
83
- const src = script.src;
84
- const metaUrl = import.meta.url;
85
- // Extract package name and check if they match (ignoring version differences)
86
- const srcPackage = src.match(/@hortonstudio\/main(@[\d.]+)?/)?.[0];
87
- const metaPackage = metaUrl.match(/@hortonstudio\/main(@[\d.]+)?/)?.[0];
88
- return srcPackage && metaPackage && srcPackage.split('@')[0] === metaPackage.split('@')[0];
89
- });
90
- }
91
-
92
- // Module loading function
93
- const loadHsModule = async (moduleName) => {
94
- const apiInstance = window[API_NAME];
95
-
96
- // Check if already processing
97
- if (apiInstance.process.has(moduleName)) {
98
- return apiInstance.modules[moduleName]?.loading;
99
- }
100
-
101
- // Add to processing set
102
- apiInstance.process.add(moduleName);
103
-
104
- // Create module object
105
- const moduleObj = apiInstance.modules[moduleName] || {};
106
- apiInstance.modules[moduleName] = moduleObj;
107
-
108
- // Create loading promise
109
- moduleObj.loading = new Promise((resolve, reject) => {
110
- moduleObj.resolve = resolve;
111
- moduleObj.reject = reject;
112
- });
113
-
114
- try {
115
- const { init, version } = await loadModule(moduleName);
116
- const initResult = await init();
117
- const { result, destroy } = initResult || {};
118
-
119
- moduleObj.version = version;
120
-
121
- // Add destroy and restart methods
122
- moduleObj.destroy = () => {
123
- destroy?.();
124
- apiInstance.process.delete(moduleName);
125
- };
126
-
127
- moduleObj.restart = () => {
128
- moduleObj.destroy?.();
129
- return apiInstance.load(moduleName);
130
- };
131
-
132
- moduleObj.resolve?.(result);
133
- delete moduleObj.resolve;
134
- delete moduleObj.reject;
135
-
136
- return result;
137
-
138
- } catch (error) {
139
- moduleObj.reject?.(error);
140
- apiInstance.process.delete(moduleName);
141
- throw error;
142
- }
143
- };
144
-
145
- // Store callbacks to run after initialization
146
- const postWebflowCallbacks = [];
147
-
148
- // Initialize API
149
- window[API_NAME] = {
150
- scripts,
151
- modules: {},
152
- process: new Set(),
153
- load: loadHsModule,
154
- loaded: false,
155
-
156
- // Push method for queuing
157
- push(...requests) {
158
- for (let [moduleName, callback] of requests) {
159
- if (typeof callback === 'function') {
160
- this.modules[moduleName]?.loading?.then(callback);
161
- } else {
162
- this.load(moduleName);
163
- }
164
- }
165
- },
166
-
167
- // Destroy all modules
168
- destroy() {
169
- for (let moduleName in this.modules) {
170
- this.modules[moduleName]?.destroy?.();
171
- }
172
- },
173
-
174
- // API function for scripts to register post-initialization callbacks
175
- afterReady(callback) {
176
- if (typeof callback === 'function') {
177
- if (this.loaded) {
178
- callback();
179
- } else {
180
- postWebflowCallbacks.push(callback);
181
- }
182
- }
183
- },
184
-
185
- // Legacy alias for Webflow compatibility
186
- afterWebflowReady(callback) {
187
- if (typeof callback === 'function') {
188
- if (this.loaded) {
189
- callback();
190
- } else {
191
- postWebflowCallbacks.push(callback);
192
- }
193
- }
194
- },
195
-
196
- // Status method
197
- status(moduleName) {
198
- if (!moduleName) {
199
- return {
200
- loaded: Object.keys(this.modules),
201
- loading: [...this.process],
202
- animations: Object.keys(animationModules),
203
- utilities: Object.keys(utilityModules),
204
- autoInit: Object.keys(autoInitModules)
205
- };
206
- }
207
- return {
208
- loaded: !!this.modules[moduleName],
209
- loading: this.process.has(moduleName)
210
- };
211
- }
212
- };
213
-
214
- // Process modules from script tags
215
- const processScriptModules = () => {
216
- for (const script of scripts) {
217
- // Check for auto mode
218
- const autoMode = script.getAttribute('data-hs-auto') === 'true';
219
-
220
- // Load modules based on script attributes
221
- for (const moduleName of Object.keys(allModules)) {
222
- if (script.hasAttribute(moduleName)) {
223
- loadHsModule(moduleName);
224
- }
225
- }
226
-
227
- // Auto-discovery mode
228
- if (autoMode) {
229
- waitDOMReady().then(() => {
230
- const foundModules = new Set();
231
- const allElements = document.querySelectorAll('*');
232
-
233
- for (const element of allElements) {
234
- for (const attrName of element.getAttributeNames()) {
235
- // Look for data-hs-* attributes
236
- const match = attrName.match(/^(data-hs-(?:anim|util)-[^-=]+)/)?.[1];
237
- if (match && allModules[match]) {
238
- foundModules.add(match);
239
- }
240
- }
241
- }
242
-
243
- for (const moduleName of foundModules) {
244
- loadHsModule(moduleName);
245
- }
246
- });
247
- }
248
- }
249
-
250
- // Always load auto-init modules
251
- for (const moduleName of Object.keys(autoInitModules)) {
252
- loadHsModule(moduleName);
253
- }
254
-
255
- // Hide .transition elements if transition module is not loaded
256
- const hasTransition = scripts.some(script => script.hasAttribute('data-hs-anim-transition'));
257
- if (!hasTransition) {
258
- const transitionElements = document.querySelectorAll('.transition');
259
- transitionElements.forEach(element => {
260
- element.style.display = 'none';
261
- });
262
- }
263
- };
264
-
265
- // Handle rich text blocks
266
- const richTextBlocks = document.querySelectorAll('.w-richtext');
267
- richTextBlocks.forEach(block => {
268
- const images = block.querySelectorAll('img');
269
- images.forEach(img => {
270
- img.loading = 'eager';
271
- });
272
- });
273
-
274
- // Main initialization
275
- const initializeMain = async () => {
276
- // Process script modules
277
- processScriptModules();
278
-
279
- // Wait for Webflow
280
- await waitWebflowReady();
281
-
282
- // Mark as loaded
283
- window[API_NAME].loaded = true;
284
-
285
- // Run all registered post-Webflow callbacks
286
- postWebflowCallbacks.forEach((callback) => {
287
- try {
288
- callback();
289
- } catch (error) {
290
- // Silent fail for callbacks
291
- }
292
- });
293
- };
294
-
295
- // Process any early requests
296
- window[API_NAME].push(...existingRequests);
297
-
298
- // Start initialization
299
- initializeMain().catch(() => {
300
- // Silent fail for initialization
301
- });
302
- };
303
-
304
- // Run initialization
305
- initializeHsMain();
package/styles.css DELETED
@@ -1,29 +0,0 @@
1
- /* transition */
2
- body .transition {display: block}
3
- .w-editor .transition {display: none;}
4
- .no-scroll-transition {overflow: hidden; position: relative;}
5
-
6
- /* splittext */
7
- .line-mask, .word-mask, .char-mask {
8
- padding-bottom: .1em;
9
- margin-bottom: -.1em;
10
- padding-inline: .1em;
11
- margin-inline: -.1em;
12
- }
13
-
14
-
15
- /* scroll cleanliness */
16
- html, body {
17
- overscroll-behavior: none;
18
- scrollbar-gutter: stable;
19
- }
20
-
21
- /* TOC focus handling - only show focus outline for keyboard navigation */
22
- [data-hs-toc] div[id]:focus:not(:focus-visible) {
23
- outline: none !important;
24
- }
25
-
26
- [data-hs-toc] div[id]:focus-visible {
27
- outline: 2px solid #007bff !important;
28
- outline-offset: 2px;
29
- }