@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 +1 -0
- package/dist/src-hero-UYNS76KU.js +1 -0
- package/dist/src-load-3UYE3H3N.js +1 -0
- package/dist/src-navbar-763V2PWX.js +1 -0
- package/dist/src-scroll-progress-EX62FWYD.js +1 -0
- package/dist/src-smooth-scroll-TG4SEEIC.js +1 -0
- package/dist/src-text-BZVCCQJP.js +1 -0
- package/dist/src-toc-SHTDW6BF.js +1 -0
- package/dist/src-transition-UBAPWTIU.js +1 -0
- package/package.json +16 -2
- package/CLAUDE.md +0 -45
- package/HS-MAIN-DOCUMENTATION.md +0 -567
- package/index.js +0 -305
- package/styles.css +0 -29
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.
|
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
|
package/HS-MAIN-DOCUMENTATION.md
DELETED
@@ -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
|
-
}
|