@hortonstudio/main 1.9.7 → 1.9.8

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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## **Overview**
4
4
 
5
- The accessibility system provides 11 modular functions to enhance website accessibility and functionality. Each function operates independently and can be customized through data attributes.
5
+ The accessibility system provides 7 modular functions to enhance website accessibility and functionality. Each function operates independently and can be customized through data attributes.
6
6
 
7
7
  **Note:** This module auto-initializes and loads all functions on page load.
8
8
 
@@ -24,63 +24,35 @@ Adds proper ARIA `role="list"` and `role="listitem"` to custom-styled lists.
24
24
 
25
25
  ---
26
26
 
27
- ### **3. Remove List Accessibility**
28
- Removes list semantics by stripping ARIA roles and converting `<ul>/<ol>/<li>` to divs.
29
-
30
- **Use case:** Decorative list layouts that shouldn't be announced as lists.
31
-
32
- ---
33
-
34
- ### **4. Convert to Span**
35
- Converts most HTML elements to span elements while preserving attributes and content.
36
-
37
- **Use case:** Removing semantic meaning from purely decorative wrapper elements.
38
-
39
- ---
40
-
41
- ### **5. Year Replacement**
27
+ ### **3. Year Replacement**
42
28
  Replaces `{{year}}` and `{{month}}` placeholders with current year and month.
43
29
 
44
30
  **Use case:** Auto-updating copyright years and date-based content.
45
31
 
46
32
  ---
47
33
 
48
- ### **6. Prevent Default**
49
- Prevents default behavior on elements including clicks and keyboard activation.
50
-
51
- **Use case:** Decorative buttons or preventing anchor link scrolling.
52
-
53
- ---
54
-
55
- ### **7. Custom Values Replacement**
56
- Collects custom name-value pairs and replaces `{{name}}` placeholders throughout the page.
57
-
58
- **Use case:** Dynamic content replacement for user-defined values.
59
-
60
- ---
61
-
62
- ### **8. Click Forwarding**
34
+ ### **4. Click Forwarding**
63
35
  Forwards clicks from decorative wrapper elements to actual interactive trigger elements.
64
36
 
65
37
  **Use case:** Making entire card areas clickable while maintaining semantic structure.
66
38
 
67
39
  ---
68
40
 
69
- ### **9. Text Synchronization**
41
+ ### **5. Text Synchronization**
70
42
  Synchronizes text content and aria-labels from original element to multiple match elements in real-time.
71
43
 
72
44
  **Use case:** Keeping duplicate content in sync across multiple locations.
73
45
 
74
46
  ---
75
47
 
76
- ### **10. Table of Contents (TOC)**
48
+ ### **6. Table of Contents (TOC)**
77
49
  Automatically generates a table of contents from H2 headings with smooth scrolling, focus management, and active state tracking.
78
50
 
79
51
  **Use case:** Auto-generated TOC navigation for blog posts and documentation pages.
80
52
 
81
53
  ---
82
54
 
83
- ### **11. Dropdown**
55
+ ### **7. Dropdown**
84
56
  Universal dropdown system for FAQ, summary/read-more, and general toggle components. Syncs ARIA with Webflow interactions and optionally updates text content.
85
57
 
86
58
  **Use case:** All dropdown/accordion/toggle patterns with unified ARIA management and optional text swapping.
@@ -95,11 +67,7 @@ Each function has detailed documentation in its respective folder:
95
67
 
96
68
  - `functions/blog-remover/README.md`
97
69
  - `functions/list-accessibility/README.md`
98
- - `functions/remove-list-accessibility/README.md`
99
- - `functions/convert-to-span/README.md`
100
70
  - `functions/year-replacement/README.md`
101
- - `functions/prevent-default/README.md`
102
- - `functions/custom-values-replacement/README.md`
103
71
  - `functions/click-forwarding/README.md`
104
72
  - `functions/text-synchronization/README.md`
105
73
  - `functions/toc/README.md`
@@ -8,11 +8,7 @@ export async function init() {
8
8
  const functionMap = {
9
9
  "blog-remover": () => import("./functions/blog-remover/blog-remover.js"),
10
10
  "list-accessibility": () => import("./functions/list-accessibility/list-accessibility.js"),
11
- "remove-list-accessibility": () => import("./functions/remove-list-accessibility/remove-list-accessibility.js"),
12
- "convert-to-span": () => import("./functions/convert-to-span/convert-to-span.js"),
13
11
  "year-replacement": () => import("./functions/year-replacement/year-replacement.js"),
14
- "prevent-default": () => import("./functions/prevent-default/prevent-default.js"),
15
- "custom-values-replacement": () => import("./functions/custom-values-replacement/custom-values-replacement.js"),
16
12
  "click-forwarding": () => import("./functions/click-forwarding/click-forwarding.js"),
17
13
  "text-synchronization": () => import("./functions/text-synchronization/text-synchronization.js"),
18
14
  "toc": () => import("./functions/toc/toc.js"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hortonstudio/main",
3
- "version": "1.9.7",
3
+ "version": "1.9.8",
4
4
  "description": "Animation and utility library for client websites",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -1,59 +0,0 @@
1
- # **Convert to Span**
2
-
3
- ## **Overview**
4
-
5
- Converts most HTML elements to span elements while preserving attributes and content. Useful for removing semantic meaning from decorative elements.
6
-
7
- ---
8
-
9
- ## **Required Elements**
10
-
11
- **Container**
12
- * data-hs-a11y="convert-span"
13
-
14
- ---
15
-
16
- ## **What It Does**
17
-
18
- 1. Finds all `[data-hs-a11y="convert-span"]` containers
19
- 2. Converts all descendant elements to `<span>` (except skip list)
20
- 3. Converts container itself to `<span>`
21
- 4. Preserves all attributes except `data-hs-a11y`
22
-
23
- **Skip List:** `span`, `a`, `button`, `input`, `textarea`, `select`, `img`, `video`, `audio`, `iframe`, `object`, `embed`, `canvas`, `svg`, `form`, `table`, semantic tags, headings, `script`, `style`
24
-
25
- ---
26
-
27
- ## **Usage Example**
28
-
29
- ```html
30
- <!-- Before -->
31
- <div data-hs-a11y="convert-span">
32
- <p>Text here</p>
33
- <section>Content</section>
34
- </div>
35
-
36
- <!-- After -->
37
- <span>
38
- <span>Text here</span>
39
- <span>Content</span>
40
- </span>
41
- ```
42
-
43
- **Result:** All elements become spans, removing semantic meaning.
44
-
45
- ---
46
-
47
- ## **Key Attributes**
48
-
49
- | Attribute | Purpose |
50
- | ----- | ----- |
51
- | `data-hs-a11y="convert-span"` | Container to convert |
52
-
53
- ---
54
-
55
- ## **Notes**
56
-
57
- * One-time DOM transformation
58
- * Skips interactive and media elements
59
- * Use for purely decorative wrappers
@@ -1,70 +0,0 @@
1
- export function init() {
2
- function setupConvertToSpan() {
3
- const containers = document.querySelectorAll('[data-hs-a11y="convert-span"]');
4
-
5
- containers.forEach(container => {
6
- const skipTags = [
7
- 'span', 'a', 'button', 'input', 'textarea', 'select', 'img', 'video', 'audio',
8
- 'iframe', 'object', 'embed', 'canvas', 'svg', 'form', 'table', 'thead', 'tbody',
9
- 'tr', 'td', 'th', 'ul', 'ol', 'li', 'dl', 'dt', 'dd', 'h1', 'h2', 'h3', 'h4',
10
- 'h5', 'h6', 'script', 'style', 'link', 'meta', 'title', 'head', 'html', 'body'
11
- ];
12
-
13
- // Convert all child elements first
14
- const elementsToConvert = container.querySelectorAll('*');
15
-
16
- elementsToConvert.forEach(element => {
17
- const tagName = element.tagName.toLowerCase();
18
-
19
- if (!skipTags.includes(tagName)) {
20
- const newSpan = document.createElement('span');
21
-
22
- // Copy all attributes except data-hs-a11y
23
- Array.from(element.attributes).forEach(attr => {
24
- if (attr.name !== 'data-hs-a11y') {
25
- newSpan.setAttribute(attr.name, attr.value);
26
- }
27
- });
28
-
29
- // Move all child nodes
30
- while (element.firstChild) {
31
- newSpan.appendChild(element.firstChild);
32
- }
33
-
34
- // Replace the element
35
- element.parentNode.replaceChild(newSpan, element);
36
- }
37
- });
38
-
39
- // Convert the container itself to span
40
- const containerTagName = container.tagName.toLowerCase();
41
- if (!skipTags.includes(containerTagName)) {
42
- const newSpan = document.createElement('span');
43
-
44
- // Copy all attributes except data-hs-a11y
45
- Array.from(container.attributes).forEach(attr => {
46
- if (attr.name !== 'data-hs-a11y') {
47
- newSpan.setAttribute(attr.name, attr.value);
48
- }
49
- });
50
-
51
- // Move all child nodes
52
- while (container.firstChild) {
53
- newSpan.appendChild(container.firstChild);
54
- }
55
-
56
- // Replace the container
57
- container.parentNode.replaceChild(newSpan, container);
58
- }
59
- });
60
- }
61
-
62
- setupConvertToSpan();
63
-
64
- return {
65
- result: "convert-to-span initialized",
66
- destroy: () => {
67
- // No cleanup needed - this is a one-time DOM operation
68
- }
69
- };
70
- }
@@ -1,71 +0,0 @@
1
- # **Custom Values Replacement**
2
-
3
- ## **Overview**
4
-
5
- Collects custom name-value pairs from a list and replaces `{{name}}` placeholders throughout the page. Similar to site-settings but for user-defined values.
6
-
7
- ---
8
-
9
- ## **Required Elements**
10
-
11
- **Custom Values List**
12
- * data-hs-a11y="custom-values-list"
13
-
14
- **Name Element** *(within list descendants)*
15
- * data-hs-a11y="custom-values-name"
16
-
17
- **Value Element** *(within list descendants)*
18
- * data-hs-a11y="custom-values-value"
19
-
20
- ---
21
-
22
- ## **What It Does**
23
-
24
- 1. Finds custom values list
25
- 2. Collects name-value pairs from descendants
26
- 3. Replaces `{{name}}` placeholders in all text nodes
27
- 4. Replaces `{{name}}` placeholders in link hrefs
28
-
29
- ---
30
-
31
- ## **Usage Example**
32
-
33
- ```html
34
- <div data-hs-a11y="custom-values-list" style="display: none;">
35
- <div>
36
- <span data-hs-a11y="custom-values-name">support-email</span>
37
- <span data-hs-a11y="custom-values-value">help@example.com</span>
38
- </div>
39
- <div>
40
- <span data-hs-a11y="custom-values-name">phone</span>
41
- <span data-hs-a11y="custom-values-value">555-1234</span>
42
- </div>
43
- </div>
44
-
45
- <p>Contact us at {{support-email}} or call {{phone}}</p>
46
- <a href="mailto:{{support-email}}">Email Us</a>
47
- ```
48
-
49
- **Result:**
50
- ```html
51
- <p>Contact us at help@example.com or call 555-1234</p>
52
- <a href="mailto:help@example.com">Email Us</a>
53
- ```
54
-
55
- ---
56
-
57
- ## **Key Attributes**
58
-
59
- | Attribute | Purpose |
60
- | ----- | ----- |
61
- | `data-hs-a11y="custom-values-list"` | Container for values |
62
- | `data-hs-a11y="custom-values-name"` | Placeholder name |
63
- | `data-hs-a11y="custom-values-value"` | Replacement value |
64
-
65
- ---
66
-
67
- ## **Notes**
68
-
69
- * One-time text replacement
70
- * List should be hidden with CSS
71
- * No cleanup needed
@@ -1,102 +0,0 @@
1
- export function init() {
2
- function setupCustomValuesReplacement() {
3
- const customValuesList = document.querySelector('[data-hs-a11y="custom-values-list"]');
4
-
5
- if (!customValuesList) {
6
- return;
7
- }
8
-
9
- // Collect all custom values data
10
- const customValues = {};
11
- const descendants = customValuesList.getElementsByTagName('*');
12
-
13
- Array.from(descendants).forEach(descendant => {
14
- const nameElement = descendant.querySelector('[data-hs-a11y="custom-values-name"]');
15
- const valueElement = descendant.querySelector('[data-hs-a11y="custom-values-value"]');
16
-
17
- if (nameElement && valueElement) {
18
- const name = nameElement.textContent.trim();
19
- const value = valueElement.textContent.trim();
20
-
21
- if (name && value) {
22
- customValues[name] = value;
23
- }
24
- }
25
- });
26
-
27
- // If no custom values found, exit early
28
- if (Object.keys(customValues).length === 0) {
29
- return;
30
- }
31
-
32
- // Replace text content efficiently using TreeWalker
33
- const walker = document.createTreeWalker(
34
- document.body,
35
- NodeFilter.SHOW_TEXT,
36
- {
37
- acceptNode: (node) => {
38
- // Check if any custom value names exist in the text content
39
- const text = node.textContent;
40
- for (const name in customValues) {
41
- if (text.includes(`{{${name}}}`)) {
42
- return NodeFilter.FILTER_ACCEPT;
43
- }
44
- }
45
- return NodeFilter.FILTER_SKIP;
46
- }
47
- }
48
- );
49
-
50
- const textNodes = [];
51
- let node;
52
- while (node = walker.nextNode()) {
53
- textNodes.push(node);
54
- }
55
-
56
- // Replace text in collected nodes
57
- textNodes.forEach(textNode => {
58
- let newText = textNode.textContent;
59
- let hasChanges = false;
60
-
61
- for (const name in customValues) {
62
- const placeholder = `{{${name}}}`;
63
- if (newText.includes(placeholder)) {
64
- newText = newText.replace(new RegExp(placeholder, 'g'), customValues[name]);
65
- hasChanges = true;
66
- }
67
- }
68
-
69
- if (hasChanges) {
70
- textNode.textContent = newText;
71
- }
72
- });
73
-
74
- // Replace link hrefs
75
- const links = document.querySelectorAll('a[href]');
76
- links.forEach(link => {
77
- let href = link.getAttribute('href');
78
- let hasChanges = false;
79
-
80
- for (const name in customValues) {
81
- const placeholder = `{{${name}}}`;
82
- if (href.includes(placeholder)) {
83
- href = href.replace(new RegExp(placeholder, 'g'), customValues[name]);
84
- hasChanges = true;
85
- }
86
- }
87
-
88
- if (hasChanges) {
89
- link.setAttribute('href', href);
90
- }
91
- });
92
- }
93
-
94
- setupCustomValuesReplacement();
95
-
96
- return {
97
- result: "custom-values-replacement initialized",
98
- destroy: () => {
99
- // No cleanup needed - this is a one-time text replacement
100
- }
101
- };
102
- }
@@ -1,58 +0,0 @@
1
- # **Prevent Default**
2
-
3
- ## **Overview**
4
-
5
- Prevents default behavior on elements, including clicks and keyboard activation. Useful for decorative buttons or preventing anchor scrolling.
6
-
7
- ---
8
-
9
- ## **Required Elements**
10
-
11
- **Element to Disable**
12
- * data-hs-a11y="prevent-default"
13
-
14
- ---
15
-
16
- ## **What It Does**
17
-
18
- 1. Finds all `[data-hs-a11y="prevent-default"]` elements
19
- 2. Prevents click events
20
- 3. Prevents Enter/Space keyboard activation
21
- 4. For anchor links with `#` hrefs:
22
- - Removes href attribute
23
- - Sets `role="button"`
24
- - Sets `tabindex="0"`
25
-
26
- ---
27
-
28
- ## **Usage Example**
29
-
30
- ```html
31
- <!-- Decorative button (no action) -->
32
- <button data-hs-a11y="prevent-default">
33
- Disabled Action
34
- </button>
35
-
36
- <!-- Anchor link without scroll -->
37
- <a href="#" data-hs-a11y="prevent-default">
38
- Click me (won't scroll)
39
- </a>
40
- ```
41
-
42
- **Result:** Elements receive focus but perform no action when clicked or activated.
43
-
44
- ---
45
-
46
- ## **Key Attributes**
47
-
48
- | Attribute | Purpose |
49
- | ----- | ----- |
50
- | `data-hs-a11y="prevent-default"` | Element to disable |
51
-
52
- ---
53
-
54
- ## **Notes**
55
-
56
- * Event listeners cleaned up on destroy
57
- * Useful with Webflow IX for custom behaviors
58
- * Anchor links converted to role="button"
@@ -1,58 +0,0 @@
1
- export function init() {
2
- const cleanup = {
3
- handlers: []
4
- };
5
-
6
- const addHandler = (element, event, handler, options) => {
7
- element.addEventListener(event, handler, options);
8
- cleanup.handlers.push({ element, event, handler, options });
9
- };
10
-
11
- function setupPreventDefault(addHandler) {
12
- const elements = document.querySelectorAll('[data-hs-a11y="prevent-default"]');
13
-
14
- elements.forEach(element => {
15
- // Prevent click
16
- const clickHandler = (e) => {
17
- e.preventDefault();
18
- e.stopPropagation();
19
- return false;
20
- };
21
- addHandler(element, 'click', clickHandler);
22
-
23
- // Prevent keyboard activation
24
- const keydownHandler = (e) => {
25
- if (e.key === 'Enter' || e.key === ' ') {
26
- e.preventDefault();
27
- e.stopPropagation();
28
- return false;
29
- }
30
- };
31
- addHandler(element, 'keydown', keydownHandler);
32
-
33
- // Additional prevention for anchor links
34
- if (element.tagName.toLowerCase() === 'a') {
35
- // Remove or modify href to prevent scroll
36
- const originalHref = element.getAttribute('href');
37
- if (originalHref && (originalHref === '#' || originalHref.startsWith('#'))) {
38
- element.setAttribute('data-original-href', originalHref);
39
- element.removeAttribute('href');
40
- element.setAttribute('role', 'button');
41
- element.setAttribute('tabindex', '0');
42
- }
43
- }
44
- });
45
- }
46
-
47
- setupPreventDefault(addHandler);
48
-
49
- return {
50
- result: "prevent-default initialized",
51
- destroy: () => {
52
- cleanup.handlers.forEach(({ element, event, handler, options }) => {
53
- element.removeEventListener(event, handler, options);
54
- });
55
- cleanup.handlers.length = 0;
56
- }
57
- };
58
- }
@@ -1,57 +0,0 @@
1
- # **Remove List Accessibility**
2
-
3
- ## **Overview**
4
-
5
- Removes list semantics from elements by stripping ARIA roles and converting semantic list tags to divs. Useful when you need list styling without list semantics.
6
-
7
- ---
8
-
9
- ## **Required Elements**
10
-
11
- **Container**
12
- * data-hs-a11y="remove-list"
13
-
14
- ---
15
-
16
- ## **What It Does**
17
-
18
- 1. Finds all `[data-hs-a11y="remove-list"]` containers
19
- 2. Removes `role="list"` and `role="listitem"` from container and descendants
20
- 3. Converts all `<ul>`, `<ol>`, `<li>` tags to `<div>` elements
21
- 4. Preserves all other attributes and content
22
-
23
- ---
24
-
25
- ## **Usage Example**
26
-
27
- ```html
28
- <!-- Before -->
29
- <ul data-hs-a11y="remove-list">
30
- <li>Item 1</li>
31
- <li>Item 2</li>
32
- </ul>
33
-
34
- <!-- After -->
35
- <div>
36
- <div>Item 1</div>
37
- <div>Item 2</div>
38
- </div>
39
- ```
40
-
41
- **Result:** Visual list styling without list semantics for screen readers.
42
-
43
- ---
44
-
45
- ## **Key Attributes**
46
-
47
- | Attribute | Purpose |
48
- | ----- | ----- |
49
- | `data-hs-a11y="remove-list"` | Container to de-semanticize |
50
-
51
- ---
52
-
53
- ## **Notes**
54
-
55
- * One-time DOM transformation
56
- * Useful for decorative list layouts
57
- * Use sparingly - semantic lists are preferred for actual lists
@@ -1,68 +0,0 @@
1
- export function init() {
2
- function setupRemoveListAccessibility() {
3
- const containers = document.querySelectorAll('[data-hs-a11y="remove-list"]');
4
-
5
- containers.forEach(container => {
6
- // Remove role="list" and role="listitem" from container and all descendants
7
- const elementsWithListRoles = container.querySelectorAll('[role="list"], [role="listitem"]');
8
- elementsWithListRoles.forEach(element => {
9
- element.removeAttribute('role');
10
- });
11
-
12
- // Also remove from container itself if it has these roles
13
- if (container.getAttribute('role') === 'list' || container.getAttribute('role') === 'listitem') {
14
- container.removeAttribute('role');
15
- }
16
-
17
- // Convert semantic lists to divs in container and all descendants
18
- const listsToConvert = container.querySelectorAll('ul, ol, li');
19
- listsToConvert.forEach(listElement => {
20
- const newDiv = document.createElement('div');
21
-
22
- // Copy all attributes except role
23
- Array.from(listElement.attributes).forEach(attr => {
24
- if (attr.name !== 'role') {
25
- newDiv.setAttribute(attr.name, attr.value);
26
- }
27
- });
28
-
29
- // Move all child nodes
30
- while (listElement.firstChild) {
31
- newDiv.appendChild(listElement.firstChild);
32
- }
33
-
34
- // Replace the element
35
- listElement.parentNode.replaceChild(newDiv, listElement);
36
- });
37
-
38
- // Convert container itself if it's a semantic list
39
- if (container.tagName.toLowerCase() === 'ul' || container.tagName.toLowerCase() === 'ol' || container.tagName.toLowerCase() === 'li') {
40
- const newDiv = document.createElement('div');
41
-
42
- // Copy all attributes except data-hs-a11y and role
43
- Array.from(container.attributes).forEach(attr => {
44
- if (attr.name !== 'data-hs-a11y' && attr.name !== 'role') {
45
- newDiv.setAttribute(attr.name, attr.value);
46
- }
47
- });
48
-
49
- // Move all child nodes
50
- while (container.firstChild) {
51
- newDiv.appendChild(container.firstChild);
52
- }
53
-
54
- // Replace the container
55
- container.parentNode.replaceChild(newDiv, container);
56
- }
57
- });
58
- }
59
-
60
- setupRemoveListAccessibility();
61
-
62
- return {
63
- result: "remove-list-accessibility initialized",
64
- destroy: () => {
65
- // No cleanup needed - this is a one-time DOM operation
66
- }
67
- };
68
- }