@hortonstudio/main 1.9.20 → 1.9.22

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.
Files changed (100) hide show
  1. package/dist/assets/animations-igIF6V0K.js +3 -0
  2. package/dist/assets/animations-igIF6V0K.js. +3 -0
  3. package/dist/assets/animations-igIF6V0K.js.gz +0 -0
  4. package/dist/assets/animations-igIF6V0K.js.map +1 -0
  5. package/dist/assets/counter-B9xmgh8V.js +2 -0
  6. package/dist/assets/counter-B9xmgh8V.js. +2 -0
  7. package/dist/assets/counter-B9xmgh8V.js.gz +0 -0
  8. package/dist/assets/counter-B9xmgh8V.js.map +1 -0
  9. package/dist/assets/cssVariables-n9wQSSYb.js +2 -0
  10. package/dist/assets/cssVariables-n9wQSSYb.js.map +1 -0
  11. package/dist/assets/default-CZ6vle49.js +2 -0
  12. package/dist/assets/default-CZ6vle49.js. +2 -0
  13. package/dist/assets/default-CZ6vle49.js.gz +0 -0
  14. package/dist/assets/default-CZ6vle49.js.map +1 -0
  15. package/dist/assets/modalManager-LtDi9OJz.js +2 -0
  16. package/dist/assets/modalManager-LtDi9OJz.js. +2 -0
  17. package/dist/assets/modalManager-LtDi9OJz.js.gz +0 -0
  18. package/dist/assets/modalManager-LtDi9OJz.js.map +1 -0
  19. package/dist/assets/normalize-DWI4olFS.js +2 -0
  20. package/dist/assets/normalize-DWI4olFS.js. +2 -0
  21. package/dist/assets/normalize-DWI4olFS.js.gz +0 -0
  22. package/dist/assets/normalize-DWI4olFS.js.map +1 -0
  23. package/dist/assets/structure--7b3v7AH.js +2 -0
  24. package/dist/assets/structure--7b3v7AH.js. +2 -0
  25. package/dist/assets/structure--7b3v7AH.js.gz +0 -0
  26. package/dist/assets/structure--7b3v7AH.js.map +1 -0
  27. package/dist/assets/utils-DA-PANmk.js +2 -0
  28. package/dist/assets/utils-DA-PANmk.js.map +1 -0
  29. package/dist/bootstrap.js +2 -0
  30. package/dist/bootstrap.js.map +1 -0
  31. package/dist/main.js +3 -0
  32. package/dist/main.js. +3 -0
  33. package/dist/main.js.gz +0 -0
  34. package/dist/main.js.map +1 -0
  35. package/package.json +5 -2
  36. package/.prettierrc +0 -8
  37. package/eslint.config.js +0 -32
  38. package/index.ts +0 -275
  39. package/public/bootstrap.js +0 -16
  40. package/src/animations/animations.ts +0 -93
  41. package/src/animations/functions/counter/counter.ts +0 -137
  42. package/src/config.json +0 -570
  43. package/src/config.ts +0 -105
  44. package/src/modules/default/README.md +0 -167
  45. package/src/modules/default/default.ts +0 -71
  46. package/src/modules/default/functions/accessibility/README.md +0 -134
  47. package/src/modules/default/functions/accessibility/accessibility.ts +0 -54
  48. package/src/modules/default/functions/accordion/README.md +0 -451
  49. package/src/modules/default/functions/accordion/accordion.ts +0 -189
  50. package/src/modules/default/functions/comparison/comparison.ts +0 -424
  51. package/src/modules/default/functions/marquee/marquee.ts +0 -206
  52. package/src/modules/default/functions/navbar/README.md +0 -393
  53. package/src/modules/default/functions/navbar/functions/arrow-navigation/arrow-navigation.ts +0 -183
  54. package/src/modules/default/functions/navbar/functions/dropdown/dropdown.ts +0 -313
  55. package/src/modules/default/functions/navbar/functions/menu/menu.ts +0 -315
  56. package/src/modules/default/functions/navbar/navbar.ts +0 -51
  57. package/src/modules/default/functions/smooth-scroll/README.md +0 -417
  58. package/src/modules/default/functions/smooth-scroll/smooth-scroll.ts +0 -115
  59. package/src/modules/default/functions/transition/README.md +0 -328
  60. package/src/modules/default/functions/transition/transition.ts +0 -290
  61. package/src/modules/normalize/README.md +0 -172
  62. package/src/modules/normalize/functions/clickable/README.md +0 -84
  63. package/src/modules/normalize/functions/clickable/clickable.ts +0 -43
  64. package/src/modules/normalize/functions/clickable/functions/normalize/README.md +0 -213
  65. package/src/modules/normalize/functions/clickable/functions/normalize/normalize.ts +0 -68
  66. package/src/modules/normalize/functions/dupe/README.md +0 -405
  67. package/src/modules/normalize/functions/dupe/dupe.ts +0 -197
  68. package/src/modules/normalize/functions/sync/sync.ts +0 -378
  69. package/src/modules/normalize/normalize.ts +0 -58
  70. package/src/modules/structure/README.md +0 -190
  71. package/src/modules/structure/functions/form/README.md +0 -94
  72. package/src/modules/structure/functions/form/form.ts +0 -54
  73. package/src/modules/structure/functions/form/functions/honeypot/README.md +0 -77
  74. package/src/modules/structure/functions/form/functions/honeypot/honeypot.ts +0 -37
  75. package/src/modules/structure/functions/form/functions/range/README.md +0 -410
  76. package/src/modules/structure/functions/form/functions/range/range.ts +0 -92
  77. package/src/modules/structure/functions/form/functions/select/README.md +0 -393
  78. package/src/modules/structure/functions/form/functions/select/functions/custom-select/custom-select.ts +0 -637
  79. package/src/modules/structure/functions/form/functions/select/functions/states/states.ts +0 -118
  80. package/src/modules/structure/functions/form/functions/select/select.ts +0 -48
  81. package/src/modules/structure/functions/form/functions/test/test.ts +0 -132
  82. package/src/modules/structure/functions/pagination/README.md +0 -527
  83. package/src/modules/structure/functions/pagination/pagination.ts +0 -493
  84. package/src/modules/structure/functions/site-settings/README.md +0 -395
  85. package/src/modules/structure/functions/site-settings/site-settings.ts +0 -158
  86. package/src/modules/structure/functions/toc/README.md +0 -82
  87. package/src/modules/structure/functions/toc/functions/heading-links/heading-links.ts +0 -171
  88. package/src/modules/structure/functions/toc/functions/progress-bar/progress-bar.ts +0 -101
  89. package/src/modules/structure/functions/toc/toc.ts +0 -35
  90. package/src/modules/structure/functions/year-replacement/README.md +0 -55
  91. package/src/modules/structure/functions/year-replacement/year-replacement.ts +0 -59
  92. package/src/modules/structure/structure.ts +0 -59
  93. package/src/utils/attributeSelector.ts +0 -78
  94. package/src/utils/cssVariables.ts +0 -24
  95. package/src/utils/gsap.ts +0 -198
  96. package/src/utils/heightAnimator.ts +0 -130
  97. package/src/utils/modalManager.ts +0 -150
  98. package/src/utils.ts +0 -54
  99. package/tsconfig.json +0 -24
  100. package/vite.config.js +0 -45
@@ -1,378 +0,0 @@
1
- /**
2
- * List Sync Module
3
- *
4
- * Syncs collection list data to static lists with matching structure.
5
- * Supports two modes: simple syncing and field-based syncing.
6
- *
7
- * SIMPLE SYNCING:
8
- * Extracts first text/href from source, applies to ALL matching elements in target.
9
- * Use case: Footer service links.
10
- *
11
- * Source (collection list):
12
- * <div data-hs-sync="source" data-hs-sync-id="services">
13
- * <a data-hs-sync="item" href="/service-1">Service 1</a>
14
- * <a data-hs-sync="item" href="/service-2">Service 2</a>
15
- * </div>
16
- *
17
- * Target (footer):
18
- * <div data-hs-sync="target" data-hs-sync-id="services">
19
- * <a data-hs-sync="item" href="#">Placeholder</a>
20
- * </div>
21
- *
22
- * FIELD-BASED SYNCING:
23
- * Maps specific fields between source and target by field name.
24
- * Use case: Before/after sliders, complex components with multiple images/text.
25
- *
26
- * Source (collection list):
27
- * <div data-hs-sync="source" data-hs-sync-id="projects">
28
- * <div data-hs-sync="item">
29
- * <img data-hs-sync-field="before" src="/before1.jpg">
30
- * <img data-hs-sync-field="after" src="/after1.jpg">
31
- * <h3 data-hs-sync-field="title">Project 1</h3>
32
- * <a data-hs-sync-field="link" href="/project-1">View</a>
33
- * </div>
34
- * </div>
35
- *
36
- * Target (before-after component):
37
- * <div data-hs-sync="target" data-hs-sync-id="projects">
38
- * <div data-hs-sync="item">
39
- * <img data-hs-sync-field="before" src="/placeholder-before.jpg">
40
- * <img data-hs-sync-field="after" src="/placeholder-after.jpg">
41
- * <h3 data-hs-sync-field="title">Placeholder</h3>
42
- * <a data-hs-sync-field="link" href="#">View</a>
43
- * </div>
44
- * </div>
45
- *
46
- * CHILD MODE SYNCING:
47
- * Use when the item element is a wrapper and its child should be the template.
48
- * Use case: Components where data-hs-sync="item" is on a parent wrapper.
49
- *
50
- * Target (marquee with child mode):
51
- * <div data-hs-sync="target" data-hs-sync-id="marquee-logos">
52
- * <div data-hs-sync="item" data-hs-sync-mode="child">
53
- * <div class="marquee_list">
54
- * <img data-hs-sync-field="image" src="/placeholder.jpg">
55
- * </div>
56
- * </div>
57
- * </div>
58
- *
59
- * Result: The .marquee_list element (child) becomes the template, not the wrapper.
60
- *
61
- * IGNORE ELEMENTS:
62
- * Preserve specific elements during sync (e.g., "All Services" button).
63
- *
64
- * <div data-hs-sync="target" data-hs-sync-id="services">
65
- * <a data-hs-sync="ignore" href="/services">All Services</a>
66
- * <a data-hs-sync="item" href="#">Placeholder</a>
67
- * </div>
68
- *
69
- * Process:
70
- * 1. Find source by sync-id
71
- * 2. Find matching target by sync-id (skip if no match)
72
- * 3. Clone target item template (or child if sync-mode="child")
73
- * 4. Detect ignore element and its position relative to template
74
- * 5. Clear target list (preserve ignore element)
75
- * 6. For each source item: clone template, sync data (field-based or simple)
76
- * 7. Insert synced items before or after ignore based on template position
77
- *
78
- * @version 2.0.0
79
- */
80
-
81
- import { querySelectorAll, querySelector } from '@utils';
82
-
83
- /**
84
- * Get all text nodes within an element
85
- */
86
- function getTextNodes(element) {
87
- const textNodes = [];
88
- const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null);
89
-
90
- let node;
91
- while ((node = walker.nextNode())) {
92
- // Skip empty/whitespace-only nodes
93
- if (node.nodeValue.trim()) {
94
- textNodes.push(node);
95
- }
96
- }
97
-
98
- return textNodes;
99
- }
100
-
101
- /**
102
- * Get all elements with href attribute (including the element itself)
103
- */
104
- function getHrefElements(element) {
105
- const hrefs = [];
106
-
107
- // Check if element itself has href
108
- if (element.hasAttribute('href')) {
109
- hrefs.push(element);
110
- }
111
-
112
- // Check descendants
113
- hrefs.push(...Array.from(element.querySelectorAll('[href]')));
114
-
115
- return hrefs;
116
- }
117
-
118
- /**
119
- * Check if item uses field-based syncing
120
- */
121
- function usesFieldBasedSync(item, config) {
122
- const fieldAttr = config.attributes.properties.syncField;
123
- return item.querySelector(`[${fieldAttr}]`) !== null;
124
- }
125
-
126
- /**
127
- * Find element with src attribute (check element itself, then descendants)
128
- */
129
- function findElementWithSrc(element) {
130
- // Check element itself
131
- if (element.hasAttribute('src')) {
132
- const src = element.getAttribute('src');
133
- if (src) return element;
134
- }
135
-
136
- // Check descendants
137
- const descendant = element.querySelector('[src]');
138
- if (descendant) {
139
- const src = descendant.getAttribute('src');
140
- if (src) return descendant;
141
- }
142
-
143
- return null;
144
- }
145
-
146
- /**
147
- * Sync data using field-based mapping
148
- * Maps fields by name between source and target
149
- */
150
- function syncFieldBasedData(sourceItem, targetClone, config) {
151
- const fieldAttr = config.attributes.properties.syncField;
152
-
153
- // Get all fields in source
154
- const sourceFields = sourceItem.querySelectorAll(`[${fieldAttr}]`);
155
-
156
- sourceFields.forEach((sourceField) => {
157
- const fieldName = sourceField.getAttribute(fieldAttr);
158
-
159
- // Find matching field in target
160
- const targetField = targetClone.querySelector(`[${fieldAttr}="${fieldName}"]`);
161
- if (!targetField) return;
162
-
163
- // Try to find elements with src (for images)
164
- const sourceImgElement = findElementWithSrc(sourceField);
165
- const targetImgElement = findElementWithSrc(targetField);
166
-
167
- if (sourceImgElement && targetImgElement) {
168
- // Sync src attribute
169
- const src = sourceImgElement.getAttribute('src');
170
- if (src) {
171
- targetImgElement.setAttribute('src', src);
172
- }
173
- }
174
- // Link elements: sync href
175
- else if (sourceField.hasAttribute('href') && targetField.hasAttribute('href')) {
176
- const href = sourceField.getAttribute('href');
177
- if (href) {
178
- targetField.setAttribute('href', href);
179
- }
180
- }
181
- // Text elements: sync text content
182
- else {
183
- const sourceTextNodes = getTextNodes(sourceField);
184
- const sourceText =
185
- sourceTextNodes.length > 0 ? sourceTextNodes[0].nodeValue : sourceField.textContent.trim();
186
-
187
- if (sourceText) {
188
- const targetTextNodes = getTextNodes(targetField);
189
- if (targetTextNodes.length > 0) {
190
- targetTextNodes[0].nodeValue = sourceText;
191
- } else {
192
- targetField.textContent = sourceText;
193
- }
194
- }
195
- }
196
- });
197
- }
198
-
199
- /**
200
- * Sync data from source item to target clone
201
- * Extracts text and href from source, applies to ALL matching elements in target
202
- */
203
- function syncItemData(sourceItem, targetClone, config) {
204
- // Check if using field-based syncing
205
- if (usesFieldBasedSync(targetClone, config)) {
206
- syncFieldBasedData(sourceItem, targetClone, config);
207
- return;
208
- }
209
-
210
- // Fallback to simple syncing (existing behavior)
211
- // Get text content from source (first non-empty text node)
212
- const sourceTextNodes = getTextNodes(sourceItem);
213
- const sourceText = sourceTextNodes.length > 0 ? sourceTextNodes[0].nodeValue : '';
214
-
215
- // Get href from source (first href element)
216
- const sourceHrefs = getHrefElements(sourceItem);
217
- const sourceHref = sourceHrefs.length > 0 ? sourceHrefs[0].getAttribute('href') : '';
218
-
219
- // Apply source text to ALL text nodes in target
220
- if (sourceText) {
221
- const targetTextNodes = getTextNodes(targetClone);
222
- targetTextNodes.forEach((node) => {
223
- node.nodeValue = sourceText;
224
- });
225
- }
226
-
227
- // Apply source href to ALL href elements in target
228
- if (sourceHref) {
229
- const targetHrefs = getHrefElements(targetClone);
230
- targetHrefs.forEach((el) => {
231
- el.setAttribute('href', sourceHref);
232
- });
233
- }
234
- }
235
-
236
- export async function init(config) {
237
- // Find all source lists and build map by sync-id
238
- const sourceLists = querySelectorAll(config, 'source');
239
- const sourceMap = new Map();
240
-
241
- sourceLists.forEach((source) => {
242
- const syncId = source.getAttribute(config.attributes.properties.syncId);
243
- if (syncId) {
244
- sourceMap.set(syncId, source);
245
- }
246
- });
247
-
248
- // Find all target lists
249
- const targetLists = querySelectorAll(config, 'target');
250
- let syncedCount = 0;
251
- let skippedCount = 0;
252
-
253
- targetLists.forEach((target) => {
254
- const syncId = target.getAttribute(config.attributes.properties.syncId);
255
-
256
- // Skip if no sync-id or no matching source
257
- if (!syncId || !sourceMap.has(syncId)) {
258
- skippedCount++;
259
- return;
260
- }
261
-
262
- const source = sourceMap.get(syncId);
263
-
264
- // Find item template in target (element to clone)
265
- const targetTemplateWrapper = querySelector(config, 'item', target);
266
- if (!targetTemplateWrapper) {
267
- console.warn(`[sync] Target list "${syncId}" missing item template`);
268
- return;
269
- }
270
-
271
- // Check sync mode - determines which element to use as template
272
- const syncMode = targetTemplateWrapper.getAttribute(config.attributes.properties.syncMode);
273
- const isChildMode = syncMode === 'child';
274
-
275
- // Find ignore element in target (if exists)
276
- const ignoreElement = querySelector(config, 'ignore', target);
277
-
278
- // Find all source items
279
- const sourceItems = querySelectorAll(config, 'item', source);
280
- if (sourceItems.length === 0) {
281
- console.warn(`[sync] Source list "${syncId}" has no items`);
282
- return;
283
- }
284
-
285
- // Determine template based on mode
286
- let template: Element;
287
- if (isChildMode) {
288
- // Child mode: clone the wrapper, we'll replace its child content
289
- const firstChild = targetTemplateWrapper.firstElementChild;
290
- if (!firstChild) {
291
- console.warn(
292
- `[sync] Target list "${syncId}" has sync-mode="child" but item wrapper has no children`
293
- );
294
- return;
295
- }
296
- // Clone the child element to use for syncing data
297
- template = firstChild.cloneNode(true) as Element;
298
- } else {
299
- // Default mode: clone the item element itself
300
- template = targetTemplateWrapper.cloneNode(true) as Element;
301
- template.removeAttribute(config.attributes.elements.item.primary.split('=')[0]);
302
- }
303
-
304
- // Determine insertion strategy if ignore exists
305
- let insertAfterIgnore = false;
306
- if (ignoreElement) {
307
- // Check if template wrapper comes after ignore element
308
- let currentElement = ignoreElement.nextElementSibling;
309
- while (currentElement) {
310
- if (currentElement === targetTemplateWrapper) {
311
- insertAfterIgnore = true;
312
- break;
313
- }
314
- currentElement = currentElement.nextElementSibling;
315
- }
316
- }
317
-
318
- if (isChildMode) {
319
- // CHILD MODE: Keep wrapper as single element, duplicate children inside it
320
- // Clear the wrapper's children
321
- while (targetTemplateWrapper.firstChild) {
322
- targetTemplateWrapper.removeChild(targetTemplateWrapper.firstChild);
323
- }
324
-
325
- // Create synced children and append to the wrapper
326
- sourceItems.forEach((sourceItem) => {
327
- const childClone = template.cloneNode(true) as Element;
328
- syncItemData(sourceItem, childClone, config);
329
- targetTemplateWrapper.appendChild(childClone);
330
- });
331
-
332
- // Remove data-hs-sync="item" attribute from wrapper
333
- targetTemplateWrapper.removeAttribute(config.attributes.elements.item.primary.split('=')[0]);
334
- } else {
335
- // DEFAULT MODE: Duplicate the item element itself
336
- // Clear target list, but preserve ignore element
337
- const children = Array.from(target.children);
338
- children.forEach((child) => {
339
- if (child !== ignoreElement) {
340
- child.remove();
341
- }
342
- });
343
-
344
- // Create synced items
345
- const syncedItems = Array.from(sourceItems).map((sourceItem) => {
346
- const clone = template.cloneNode(true);
347
- syncItemData(sourceItem, clone, config);
348
- return clone;
349
- });
350
-
351
- // Insert synced items based on strategy
352
- if (ignoreElement) {
353
- if (insertAfterIgnore) {
354
- // Insert all items after ignore
355
- syncedItems.forEach((item) => {
356
- target.appendChild(item);
357
- });
358
- } else {
359
- // Insert all items before ignore
360
- syncedItems.forEach((item) => {
361
- target.insertBefore(item, ignoreElement);
362
- });
363
- }
364
- } else {
365
- // No ignore element, just append all
366
- syncedItems.forEach((item) => {
367
- target.appendChild(item);
368
- });
369
- }
370
- }
371
-
372
- syncedCount++;
373
- });
374
-
375
- return {
376
- result: `sync initialized (${syncedCount} synced, ${skippedCount} skipped)`,
377
- };
378
- }
@@ -1,58 +0,0 @@
1
- /**
2
- * Normalize Orchestrator
3
- * Manages DOM normalization functions in sequence
4
- *
5
- * Uses static imports and passes config down to functions
6
- * @version 2.0.0
7
- */
8
- import config from '@config';
9
- import { init as syncInit } from './functions/sync/sync.ts';
10
- import { init as clickableInit } from './functions/clickable/clickable.ts';
11
- import { init as dupeInit } from './functions/dupe/dupe.ts';
12
-
13
- const CONFIG_ROOT = 'normalize';
14
-
15
- export async function init() {
16
- const cleanup = { destroyFunctions: [] };
17
- const moduleConfig = config[CONFIG_ROOT];
18
-
19
- try {
20
- // Phase 1a: Sync (populates lists with collection data) - must complete first
21
- const syncResult = await syncInit(moduleConfig.sync);
22
- if (syncResult?.destroy) cleanup.destroyFunctions.push(syncResult.destroy);
23
-
24
- // Phase 1b: Clickable (normalizes button/link structure) - runs after sync creates content
25
- const clickableResult = await clickableInit(moduleConfig.clickable);
26
- if (clickableResult?.destroy) cleanup.destroyFunctions.push(clickableResult.destroy);
27
-
28
- // Phase 1c: Dupe (duplicates normalized elements)
29
- const dupeResult = await dupeInit(moduleConfig.dupe);
30
- if (dupeResult?.destroy) cleanup.destroyFunctions.push(dupeResult.destroy);
31
-
32
- return {
33
- result: 'normalize initialized',
34
- destroy: () => {
35
- // Call all destroy functions in reverse order
36
- cleanup.destroyFunctions.reverse().forEach((destroyFn) => {
37
- try {
38
- destroyFn();
39
- } catch (error) {
40
- console.error('[normalize] Error during cleanup:', error);
41
- }
42
- });
43
- cleanup.destroyFunctions.length = 0;
44
- },
45
- };
46
- } catch (error) {
47
- console.error('[normalize] Initialization failed:', error);
48
- // Cleanup any partial initialization
49
- cleanup.destroyFunctions.reverse().forEach((fn) => {
50
- try {
51
- fn();
52
- } catch (cleanupError) {
53
- console.error('[normalize] Error during error cleanup:', cleanupError);
54
- }
55
- });
56
- throw error;
57
- }
58
- }
@@ -1,190 +0,0 @@
1
- # **Structure System Documentation**
2
-
3
- ## **Overview**
4
-
5
- The structure module provides functions that modify DOM structure, content, and visual appearance before the page is revealed. These are critical functions that run in Phase 2 (visual-dom) of the phased initialization system.
6
-
7
- **Purpose**: Ensure all structural and visual DOM changes are complete before the transition overlay dismisses, preventing flash of unstyled content (FOUC).
8
-
9
- **Phase**: Visual-DOM (Phase 2) - runs after clickable normalization, before transition
10
-
11
- ---
12
-
13
- ## **Functions**
14
-
15
- ### **1. Year Replacement**
16
-
17
- Replaces `{{year}}` placeholder text with the current year.
18
-
19
- **Use case:** Dynamic copyright notices, "© 2024-{{year}}" updates automatically
20
-
21
- ### **2. Text Synchronization**
22
-
23
- Synchronizes text content between multiple elements in real-time.
24
-
25
- **Use case:** Keep headings, labels, or form fields in sync across the page
26
-
27
- ### **3. Table of Contents (TOC)**
28
-
29
- Generates interactive table of contents from H2 headings with smooth scroll navigation.
30
-
31
- **Use case:** Blog posts, documentation, long-form content navigation
32
-
33
- ### **4. Pagination**
34
-
35
- Creates pagination controls for multi-page content.
36
-
37
- **Use case:** Blog archives, product listings, search results
38
-
39
- ### **5. Site Settings**
40
-
41
- Global site configuration and settings management.
42
-
43
- **Use case:** Theme toggles, language selection, persistent user preferences
44
-
45
- ---
46
-
47
- ## **Documentation**
48
-
49
- Each function has detailed documentation in its respective folder:
50
-
51
- - `functions/year-replacement/README.md`
52
- - `functions/toc/README.md`
53
- - `functions/pagination/README.md`
54
- - `functions/site-settings/README.md`
55
-
56
- ---
57
-
58
- ## **How It Works**
59
-
60
- The structure system uses a dynamic loader pattern:
61
-
62
- 1. Main `structure.js` imports all function modules
63
- 2. Each function is loaded in parallel via `Promise.allSettled()`
64
- 3. All destroy functions are collected for cleanup
65
- 4. On destroy, all functions are cleaned up properly
66
-
67
- **Initialization Flow**:
68
-
69
- ```
70
- Page Load
71
-
72
- Phase 1: Normalize (clickable)
73
-
74
- Phase 2: Visual-DOM (structure + form) ← WE ARE HERE
75
-
76
- Phase 3: Transition reveals page
77
-
78
- Phase 4: Default modules (accessibility, navbar, etc.)
79
- ```
80
-
81
- ---
82
-
83
- ## **Phased Initialization**
84
-
85
- ### **Why Phase 2?**
86
-
87
- These functions modify the DOM in ways that affect visual appearance:
88
-
89
- - Year replacement changes text content
90
- - TOC adds entire navigation structures
91
- - Marquee creates scrolling elements
92
- - Site settings might toggle themes/classes
93
-
94
- **Without phasing**: Transition reveals page before these changes complete → FOUC
95
-
96
- **With phasing**: All visual changes complete → then transition reveals page → smooth UX
97
-
98
- ### **Phase Characteristics**
99
-
100
- - **Timing**: Runs after clickable (Phase 1), before transition (Phase 3)
101
- - **Parallelization**: All functions load in parallel within Phase 2
102
- - **Resilience**: Uses `Promise.allSettled` - individual failures don't block phase
103
- - **No timeout**: If phase hangs, 3s safety timeout in index.js will force transition anyway
104
-
105
- ---
106
-
107
- ## **Barba.js / SPA Compatibility**
108
-
109
- The structure system is fully compatible with Barba.js and other SPA frameworks:
110
-
111
- ### **v2.0.0 Improvements:**
112
-
113
- - **Resilient loading with Promise.allSettled** - Individual function failures won't break other functions
114
- - **Graceful error handling** - Failed functions are logged but don't prevent successful ones from working
115
- - **Complete cleanup on destroy** - All functions properly cleaned up for page transitions
116
- - **Phased initialization** - Same clean flow on page load and SPA transitions
117
-
118
- ### **Resilient Loading:**
119
-
120
- - If one function fails (missing element, error, etc.), others continue working
121
- - Logs helpful warnings showing which functions succeeded/failed
122
- - System remains functional even with partial failures
123
-
124
- ### **On Destroy:**
125
-
126
- 1. Calls destroy() on all successfully loaded functions
127
- 2. Errors during cleanup are caught and logged
128
- 3. Cleanup tracking arrays are reset
129
- 4. Safe for Barba.js page transitions
130
-
131
- ### **On Reinitialize:**
132
-
133
- 1. Attempts to load all functions again
134
- 2. Works like fresh page load on new DOM
135
- 3. Independent function loading ensures maximum resilience
136
- 4. Runs in same Phase 2 timing for consistent UX
137
-
138
- ---
139
-
140
- ## **Performance**
141
-
142
- **Expected load time**: ~30-60ms for all functions in parallel
143
-
144
- Individual timing varies by function:
145
-
146
- - Year replacement: ~1-5ms (text replacement)
147
- - Text synchronization: ~5-10ms (event listeners)
148
- - TOC: ~10-20ms (DOM generation, IntersectionObserver)
149
- - Pagination: ~5-15ms (button generation)
150
- - Site settings: ~5-15ms (localStorage, class toggles)
151
-
152
- **Total Phase 2 timing**: structure + form loading in parallel, typically completes in 50-100ms
153
-
154
- ---
155
-
156
- ## **Migration from v1.x**
157
-
158
- ### **What Changed:**
159
-
160
- - **Previously**: Functions scattered across accessibility module and autoInit root
161
- - **Now**: Centralized in structure module for clear organization
162
-
163
- ### **Moved from accessibility:**
164
-
165
- - year-replacement
166
- - toc
167
- - pagination
168
-
169
- ### **Moved from autoInit root:**
170
-
171
- - site-settings
172
-
173
- ### **Why the change?**
174
-
175
- 1. **Clearer mental model**: "Structure" = DOM/visual changes that happen before page reveal
176
- 2. **Better organization**: Related functions grouped together
177
- 3. **Simpler phasing**: One module in Phase 2 instead of tracking individual functions
178
- 4. **Accessibility focus**: accessibility module now focuses on true a11y features (accordion)
179
-
180
- ---
181
-
182
- ## **Notes**
183
-
184
- - All functions auto-initialize on page load
185
- - Each function operates independently
186
- - Barba.js compatible with proper cleanup
187
- - No configuration required at structure level - each function uses its own config
188
- - Runs in Phase 2 (visual-dom) of initialization system
189
- - Uses Promise.allSettled for maximum reliability
190
- - Part of v2.0.0 phased initialization architecture