@hortonstudio/main 1.2.35 → 1.4.0
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/.claude/settings.local.json +22 -1
- package/TEMP-before-after-attributes.md +158 -0
- package/animations/hero.js +741 -611
- package/animations/text.js +505 -317
- package/animations/transition.js +36 -21
- package/autoInit/accessibility.js +7 -67
- package/autoInit/counter.js +338 -0
- package/autoInit/custom-values.js +266 -0
- package/autoInit/form.js +471 -0
- package/autoInit/modal.js +43 -38
- package/autoInit/navbar.js +484 -371
- package/autoInit/smooth-scroll.js +86 -84
- package/index.js +140 -88
- package/package.json +1 -1
- package/utils/before-after.js +279 -146
- package/utils/scroll-progress.js +26 -21
- package/utils/toc.js +73 -66
- package/CLAUDE.md +0 -45
- package/debug-version.html +0 -37
package/utils/scroll-progress.js
CHANGED
@@ -1,29 +1,34 @@
|
|
1
1
|
export async function init() {
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
if (!progressBar || !progressContent) {
|
7
|
-
return {
|
8
|
-
result: 'util-scroll-progress initialized'
|
9
|
-
};
|
10
|
-
}
|
2
|
+
const progressBar = document.querySelector('[data-hs-progress="bar"]');
|
3
|
+
const progressContent = document.querySelector(
|
4
|
+
'[data-hs-progress="wrapper"]',
|
5
|
+
);
|
11
6
|
|
7
|
+
// Check if elements exist before using them
|
8
|
+
if (!progressBar || !progressContent) {
|
9
|
+
return {
|
10
|
+
result: "util-scroll-progress initialized",
|
11
|
+
};
|
12
|
+
}
|
13
|
+
|
14
|
+
// Check if gsap exists before using it
|
15
|
+
if (typeof gsap !== "undefined") {
|
12
16
|
gsap.set(progressBar, { width: "0%" });
|
13
17
|
|
14
18
|
// Create the scroll progress animation
|
15
19
|
gsap.to(progressBar, {
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
width: "100%",
|
21
|
+
ease: "none",
|
22
|
+
scrollTrigger: {
|
23
|
+
trigger: progressContent,
|
24
|
+
start: "top bottom",
|
25
|
+
end: "bottom bottom",
|
26
|
+
scrub: true,
|
27
|
+
},
|
24
28
|
});
|
29
|
+
}
|
25
30
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
}
|
31
|
+
return {
|
32
|
+
result: "util-scroll-progress initialized",
|
33
|
+
};
|
34
|
+
}
|
package/utils/toc.js
CHANGED
@@ -1,77 +1,84 @@
|
|
1
1
|
export async function init() {
|
2
|
-
|
3
|
-
|
2
|
+
const contentArea = document.querySelector('[data-hs-toc="content"]');
|
3
|
+
const tocList = document.querySelector('[data-hs-toc="list"]');
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
// Check main elements
|
6
|
+
if (!contentArea) {
|
7
|
+
return;
|
8
|
+
}
|
9
|
+
if (!tocList) {
|
10
|
+
return;
|
11
|
+
}
|
12
|
+
if (tocList.children.length === 0) {
|
13
|
+
return;
|
14
|
+
}
|
9
15
|
|
10
|
-
|
11
|
-
|
12
|
-
|
16
|
+
const template = tocList.children[0];
|
17
|
+
tocList.innerHTML = "";
|
18
|
+
const h2Headings = contentArea.querySelectorAll("h2");
|
13
19
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
}
|
20
|
+
// Create sections and wrap content
|
21
|
+
h2Headings.forEach((heading, index) => {
|
22
|
+
const sectionId = heading.textContent
|
23
|
+
.toLowerCase()
|
24
|
+
.replace(/[^a-z0-9]+/g, "-")
|
25
|
+
.replace(/(^-|-$)/g, "");
|
26
|
+
const section = document.createElement("div");
|
27
|
+
section.id = sectionId;
|
28
|
+
heading.parentNode.insertBefore(section, heading);
|
29
|
+
section.appendChild(heading);
|
30
|
+
let nextElement = section.nextElementSibling;
|
31
|
+
while (nextElement && nextElement.tagName !== "H2") {
|
32
|
+
const elementToMove = nextElement;
|
33
|
+
nextElement = nextElement.nextElementSibling;
|
34
|
+
section.appendChild(elementToMove);
|
35
|
+
}
|
36
|
+
});
|
30
37
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
38
|
+
// Create TOC entries
|
39
|
+
h2Headings.forEach((heading, index) => {
|
40
|
+
const tocItem = template.cloneNode(true);
|
41
|
+
const link = tocItem.querySelector("a");
|
42
|
+
const sectionId = heading.parentElement.id;
|
43
|
+
link.href = "#" + sectionId;
|
37
44
|
|
38
|
-
|
39
|
-
|
40
|
-
|
45
|
+
// Bold numbered text
|
46
|
+
const number = document.createElement("strong");
|
47
|
+
number.textContent = index + 1 + ". ";
|
41
48
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
49
|
+
// Clear the link and add the number + text
|
50
|
+
link.innerHTML = "";
|
51
|
+
link.appendChild(number);
|
52
|
+
link.appendChild(document.createTextNode(heading.textContent));
|
46
53
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
const targetSection = document.getElementById(sectionId);
|
52
|
-
if (targetSection) {
|
53
|
-
targetSection.scrollIntoView({ behavior: 'smooth' });
|
54
|
-
// Focus on the section for accessibility (will only show outline for keyboard users due to CSS)
|
55
|
-
setTimeout(() => {
|
56
|
-
targetSection.focus();
|
57
|
-
}, 100);
|
58
|
-
}
|
59
|
-
});
|
60
|
-
|
61
|
-
// Ensure sections are focusable for keyboard users but use CSS to control focus visibility
|
62
|
-
const targetSection = document.getElementById(sectionId);
|
63
|
-
if (targetSection) {
|
64
|
-
targetSection.setAttribute('tabindex', '-1');
|
65
|
-
// Use focus-visible to only show outline for keyboard focus
|
66
|
-
targetSection.style.outline = 'none';
|
67
|
-
targetSection.style.setProperty('outline', 'none', 'important');
|
68
|
-
}
|
54
|
+
// Add click handler for smooth scrolling
|
55
|
+
link.addEventListener("click", (e) => {
|
56
|
+
e.preventDefault();
|
69
57
|
|
70
|
-
|
71
|
-
|
58
|
+
const targetSection = document.getElementById(sectionId);
|
59
|
+
if (targetSection) {
|
60
|
+
targetSection.scrollIntoView({ behavior: "smooth" });
|
61
|
+
// Focus on the section for accessibility (will only show outline for keyboard users due to CSS)
|
62
|
+
setTimeout(() => {
|
63
|
+
targetSection.focus();
|
64
|
+
}, 100);
|
65
|
+
}
|
72
66
|
});
|
73
67
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
68
|
+
// Ensure sections are focusable for keyboard users but use CSS to control focus visibility
|
69
|
+
const targetSection = document.getElementById(sectionId);
|
70
|
+
if (targetSection) {
|
71
|
+
targetSection.setAttribute("tabindex", "-1");
|
72
|
+
// Use focus-visible to only show outline for keyboard focus
|
73
|
+
targetSection.style.outline = "none";
|
74
|
+
targetSection.style.setProperty("outline", "none", "important");
|
75
|
+
}
|
76
|
+
|
77
|
+
// Add item to the TOC list
|
78
|
+
tocList.appendChild(tocItem);
|
79
|
+
});
|
80
|
+
|
81
|
+
return {
|
82
|
+
result: "util-toc initialized",
|
83
|
+
};
|
84
|
+
}
|
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/debug-version.html
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
<!DOCTYPE html>
|
2
|
-
<html>
|
3
|
-
<head>
|
4
|
-
<title>CDN Version Debug Test</title>
|
5
|
-
</head>
|
6
|
-
<body>
|
7
|
-
<h1>CDN Version Debug Test</h1>
|
8
|
-
<div id="results"></div>
|
9
|
-
|
10
|
-
<!-- Test with major version @1 -->
|
11
|
-
<script type="module" src="https://cdn.jsdelivr.net/npm/@hortonstudio/main@1/index.js" data-hs-main data-hs-anim-hero></script>
|
12
|
-
|
13
|
-
<script>
|
14
|
-
setTimeout(() => {
|
15
|
-
const results = document.getElementById('results');
|
16
|
-
|
17
|
-
// Check what scripts are found
|
18
|
-
const scripts = [...document.querySelectorAll('script[type="module"]')];
|
19
|
-
results.innerHTML += `<h2>Found Scripts:</h2>`;
|
20
|
-
scripts.forEach((script, i) => {
|
21
|
-
results.innerHTML += `<p>Script ${i}: ${script.src}</p>`;
|
22
|
-
});
|
23
|
-
|
24
|
-
// Check if hsmain loaded and what scripts it found
|
25
|
-
if (window.hsmain) {
|
26
|
-
results.innerHTML += `<h2>HSMAIN Scripts Found:</h2>`;
|
27
|
-
results.innerHTML += `<p>Found ${window.hsmain.scripts.length} matching scripts</p>`;
|
28
|
-
window.hsmain.scripts.forEach((script, i) => {
|
29
|
-
results.innerHTML += `<p>HSMAIN Script ${i}: ${script.src}</p>`;
|
30
|
-
});
|
31
|
-
} else {
|
32
|
-
results.innerHTML += `<p>HSMAIN not loaded</p>`;
|
33
|
-
}
|
34
|
-
}, 2000);
|
35
|
-
</script>
|
36
|
-
</body>
|
37
|
-
</html>
|