@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.
@@ -3,26 +3,38 @@ let supportsInert = null;
3
3
  let screenReaderLiveRegion = null;
4
4
 
5
5
  export const init = () => {
6
+ // Ensure DOM is ready before initializing
7
+ if (document.readyState === "loading") {
8
+ document.addEventListener("DOMContentLoaded", initializeNavbar);
9
+ } else {
10
+ initializeNavbar();
11
+ }
12
+ return { result: "navbar initialized" };
13
+ };
14
+
15
+ function initializeNavbar() {
6
16
  setupAccessibilityFeatures();
7
17
  setupDynamicDropdowns();
8
18
  setupMobileMenuButton();
9
19
  setupMobileMenuARIA();
10
20
  setupMobileMenuBreakpointHandler();
11
- return { result: 'navbar initialized' };
12
- };
21
+ }
13
22
 
14
23
  // Accessibility features setup
15
24
  function setupAccessibilityFeatures() {
16
25
  // Check inert support once
17
- supportsInert = 'inert' in HTMLElement.prototype;
18
-
19
- // Create screen reader live region
20
- screenReaderLiveRegion = document.createElement('div');
21
- screenReaderLiveRegion.setAttribute('aria-live', 'polite');
22
- screenReaderLiveRegion.setAttribute('aria-atomic', 'true');
23
- screenReaderLiveRegion.className = 'u-sr-only';
24
- screenReaderLiveRegion.style.cssText = 'position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;';
25
- document.body.appendChild(screenReaderLiveRegion);
26
+ supportsInert = "inert" in HTMLElement.prototype;
27
+
28
+ // Create screen reader live region only if body exists
29
+ if (document.body) {
30
+ screenReaderLiveRegion = document.createElement("div");
31
+ screenReaderLiveRegion.setAttribute("aria-live", "polite");
32
+ screenReaderLiveRegion.setAttribute("aria-atomic", "true");
33
+ screenReaderLiveRegion.className = "u-sr-only";
34
+ screenReaderLiveRegion.style.cssText =
35
+ "position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;";
36
+ document.body.appendChild(screenReaderLiveRegion);
37
+ }
26
38
  }
27
39
 
28
40
  // Inert polyfill for browsers that don't support it
@@ -31,29 +43,30 @@ function setElementInert(element, isInert) {
31
43
  element.inert = isInert;
32
44
  } else {
33
45
  // Polyfill: manage tabindex for all focusable elements
34
- const focusableSelectors = 'a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])';
46
+ const focusableSelectors =
47
+ 'a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])';
35
48
  const focusableElements = element.querySelectorAll(focusableSelectors);
36
-
49
+
37
50
  if (isInert) {
38
51
  // Store original tabindex values and disable
39
- focusableElements.forEach(el => {
40
- const currentTabindex = el.getAttribute('tabindex');
41
- el.setAttribute('data-inert-tabindex', currentTabindex || '0');
42
- el.setAttribute('tabindex', '-1');
52
+ focusableElements.forEach((el) => {
53
+ const currentTabindex = el.getAttribute("tabindex");
54
+ el.setAttribute("data-inert-tabindex", currentTabindex || "0");
55
+ el.setAttribute("tabindex", "-1");
43
56
  });
44
- element.setAttribute('data-inert', 'true');
57
+ element.setAttribute("data-inert", "true");
45
58
  } else {
46
59
  // Restore original tabindex values
47
- focusableElements.forEach(el => {
48
- const originalTabindex = el.getAttribute('data-inert-tabindex');
49
- if (originalTabindex === '0') {
50
- el.removeAttribute('tabindex');
60
+ focusableElements.forEach((el) => {
61
+ const originalTabindex = el.getAttribute("data-inert-tabindex");
62
+ if (originalTabindex === "0") {
63
+ el.removeAttribute("tabindex");
51
64
  } else if (originalTabindex) {
52
- el.setAttribute('tabindex', originalTabindex);
65
+ el.setAttribute("tabindex", originalTabindex);
53
66
  }
54
- el.removeAttribute('data-inert-tabindex');
67
+ el.removeAttribute("data-inert-tabindex");
55
68
  });
56
- element.removeAttribute('data-inert');
69
+ element.removeAttribute("data-inert");
57
70
  }
58
71
  }
59
72
  }
@@ -62,97 +75,103 @@ function setElementInert(element, isInert) {
62
75
  function announceToScreenReader(message) {
63
76
  if (screenReaderLiveRegion) {
64
77
  screenReaderLiveRegion.textContent = message;
65
-
78
+
66
79
  // Clear after a delay to allow for repeat announcements
67
80
  setTimeout(() => {
68
- screenReaderLiveRegion.textContent = '';
81
+ screenReaderLiveRegion.textContent = "";
69
82
  }, 1000);
70
83
  }
71
84
  }
72
85
 
73
86
  // Extract menu name from element text or aria-label
74
87
  function getMenuName(element) {
75
- const text = element.textContent?.trim() ||
76
- element.getAttribute('aria-label') ||
77
- 'menu';
78
- return text.replace(/^(Open|Close)\s+/i, '').replace(/\s+(menu|navigation)$/i, '');
88
+ const text =
89
+ (element.textContent && element.textContent.trim()) ||
90
+ element.getAttribute("aria-label") ||
91
+ "menu";
92
+ return text
93
+ .replace(/^(Open|Close)\s+/i, "")
94
+ .replace(/\s+(menu|navigation)$/i, "");
79
95
  }
80
96
 
81
97
  // Desktop dropdown system
82
98
  function setupDynamicDropdowns() {
83
- const dropdownWrappers = document.querySelectorAll('[data-hs-nav="dropdown"]');
99
+ const dropdownWrappers = document.querySelectorAll(
100
+ '[data-hs-nav="dropdown"]',
101
+ );
84
102
  const allDropdowns = [];
85
-
103
+
86
104
  const closeAllDropdowns = (exceptWrapper = null) => {
87
- allDropdowns.forEach(dropdown => {
105
+ allDropdowns.forEach((dropdown) => {
88
106
  if (dropdown.wrapper !== exceptWrapper && dropdown.isOpen) {
89
107
  dropdown.closeDropdown();
90
108
  }
91
109
  });
92
110
  };
93
-
94
- dropdownWrappers.forEach(wrapper => {
95
- const toggle = wrapper.querySelector('a');
111
+
112
+ dropdownWrappers.forEach((wrapper) => {
113
+ const toggle = wrapper.querySelector("a");
96
114
  if (!toggle) return;
97
-
98
- const allElements = wrapper.querySelectorAll('*');
115
+
116
+ const allElements = wrapper.querySelectorAll("*");
99
117
  let dropdownList = null;
100
-
118
+
101
119
  for (const element of allElements) {
102
- const links = element.querySelectorAll('a');
120
+ const links = element.querySelectorAll("a");
103
121
  if (links.length >= 2 && !element.contains(toggle)) {
104
122
  dropdownList = element;
105
123
  break;
106
124
  }
107
125
  }
108
-
126
+
109
127
  if (!dropdownList) return;
110
-
111
- const toggleText = toggle.textContent?.trim() || 'dropdown';
128
+
129
+ const toggleText =
130
+ (toggle.textContent && toggle.textContent.trim()) || "dropdown";
112
131
  const sanitizedText = sanitizeForID(toggleText);
113
132
  const toggleId = `navbar-dropdown-${sanitizedText}-toggle`;
114
133
  const listId = `navbar-dropdown-${sanitizedText}-list`;
115
-
134
+
116
135
  toggle.id = toggleId;
117
- toggle.setAttribute('aria-haspopup', 'menu');
118
- toggle.setAttribute('aria-expanded', 'false');
119
- toggle.setAttribute('aria-controls', listId);
120
-
136
+ toggle.setAttribute("aria-haspopup", "menu");
137
+ toggle.setAttribute("aria-expanded", "false");
138
+ toggle.setAttribute("aria-controls", listId);
139
+
121
140
  dropdownList.id = listId;
122
- dropdownList.setAttribute('role', 'menu');
123
- dropdownList.setAttribute('aria-hidden', 'true');
124
-
125
- const menuItems = dropdownList.querySelectorAll('a');
126
- menuItems.forEach(item => {
127
- item.setAttribute('role', 'menuitem');
128
- item.setAttribute('tabindex', '-1');
141
+ dropdownList.setAttribute("role", "menu");
142
+ dropdownList.setAttribute("aria-hidden", "true");
143
+
144
+ const menuItems = dropdownList.querySelectorAll("a");
145
+ menuItems.forEach((item) => {
146
+ item.setAttribute("role", "menuitem");
147
+ item.setAttribute("tabindex", "-1");
129
148
  });
130
-
149
+
131
150
  let isOpen = false;
132
151
  let currentMenuItemIndex = -1;
133
-
152
+
134
153
  function openDropdown() {
135
154
  if (isOpen) return;
136
155
  closeAllDropdowns(wrapper);
137
156
  isOpen = true;
138
- toggle.setAttribute('aria-expanded', 'true');
139
- dropdownList.setAttribute('aria-hidden', 'false');
140
- menuItems.forEach(item => {
141
- item.setAttribute('tabindex', '0');
157
+ toggle.setAttribute("aria-expanded", "true");
158
+ dropdownList.setAttribute("aria-hidden", "false");
159
+ menuItems.forEach((item) => {
160
+ item.setAttribute("tabindex", "0");
142
161
  });
143
-
162
+
144
163
  // Announce to screen readers
145
164
  const menuName = getMenuName(toggle);
146
165
  announceToScreenReader(`${menuName} menu opened`);
147
-
148
- const clickEvent = new MouseEvent('click', {
166
+
167
+ const clickEvent = new MouseEvent("click", {
149
168
  bubbles: true,
150
169
  cancelable: true,
151
- view: window
170
+ view: window,
152
171
  });
153
172
  wrapper.dispatchEvent(clickEvent);
154
173
  }
155
-
174
+
156
175
  function closeDropdown() {
157
176
  if (!isOpen) return;
158
177
  const shouldRestoreFocus = dropdownList.contains(document.activeElement);
@@ -161,76 +180,80 @@ function setupDynamicDropdowns() {
161
180
  if (shouldRestoreFocus) {
162
181
  toggle.focus();
163
182
  }
164
- toggle.setAttribute('aria-expanded', 'false');
165
- dropdownList.setAttribute('aria-hidden', 'true');
166
- menuItems.forEach(item => {
167
- item.setAttribute('tabindex', '-1');
183
+ toggle.setAttribute("aria-expanded", "false");
184
+ dropdownList.setAttribute("aria-hidden", "true");
185
+ menuItems.forEach((item) => {
186
+ item.setAttribute("tabindex", "-1");
168
187
  });
169
-
188
+
170
189
  // Announce to screen readers
171
190
  const menuName = getMenuName(toggle);
172
191
  announceToScreenReader(`${menuName} menu closed`);
173
-
174
- const clickEvent = new MouseEvent('click', {
192
+
193
+ const clickEvent = new MouseEvent("click", {
175
194
  bubbles: true,
176
195
  cancelable: true,
177
- view: window
196
+ view: window,
178
197
  });
179
198
  wrapper.dispatchEvent(clickEvent);
180
199
  }
181
-
182
- wrapper.addEventListener('mouseenter', () => {
200
+
201
+ wrapper.addEventListener("mouseenter", () => {
183
202
  if (!isOpen) {
184
- const clickEvent = new MouseEvent('click', {
203
+ const clickEvent = new MouseEvent("click", {
185
204
  bubbles: true,
186
205
  cancelable: true,
187
- view: window
206
+ view: window,
188
207
  });
189
208
  wrapper.dispatchEvent(clickEvent);
190
209
  closeAllDropdowns(wrapper);
191
210
  isOpen = true;
192
- toggle.setAttribute('aria-expanded', 'true');
193
- dropdownList.setAttribute('aria-hidden', 'false');
194
- menuItems.forEach(item => {
195
- item.setAttribute('tabindex', '0');
211
+ toggle.setAttribute("aria-expanded", "true");
212
+ dropdownList.setAttribute("aria-hidden", "false");
213
+ menuItems.forEach((item) => {
214
+ item.setAttribute("tabindex", "0");
196
215
  });
197
216
  }
198
217
  });
199
-
200
- wrapper.addEventListener('mouseleave', () => {
218
+
219
+ wrapper.addEventListener("mouseleave", () => {
201
220
  if (isOpen) {
202
221
  if (dropdownList.contains(document.activeElement)) {
203
222
  toggle.focus();
204
223
  }
205
- const clickEvent = new MouseEvent('click', {
224
+ const clickEvent = new MouseEvent("click", {
206
225
  bubbles: true,
207
226
  cancelable: true,
208
- view: window
227
+ view: window,
209
228
  });
210
229
  wrapper.dispatchEvent(clickEvent);
211
230
  isOpen = false;
212
- toggle.setAttribute('aria-expanded', 'false');
213
- dropdownList.setAttribute('aria-hidden', 'true');
214
- menuItems.forEach(item => {
215
- item.setAttribute('tabindex', '-1');
231
+ toggle.setAttribute("aria-expanded", "false");
232
+ dropdownList.setAttribute("aria-hidden", "true");
233
+ menuItems.forEach((item) => {
234
+ item.setAttribute("tabindex", "-1");
216
235
  });
217
236
  currentMenuItemIndex = -1;
218
237
  }
219
238
  });
220
-
221
- document.addEventListener('keydown', function(e) {
239
+
240
+ document.addEventListener("keydown", function (e) {
222
241
  if (!isOpen) return;
223
242
  if (!wrapper.contains(document.activeElement)) return;
224
-
225
- if (e.key === 'ArrowDown') {
243
+
244
+ if (e.key === "ArrowDown") {
226
245
  e.preventDefault();
227
246
  if (document.activeElement === toggle) {
228
247
  currentMenuItemIndex = 0;
229
248
  menuItems[currentMenuItemIndex].focus();
230
249
  } else {
231
250
  if (currentMenuItemIndex === menuItems.length - 1) {
232
- const nextElement = wrapper.nextElementSibling?.querySelector('a, button') ||
233
- document.querySelector('.navbar_cartsearch_wrap a, .navbar_cartsearch_wrap button');
251
+ const nextElement =
252
+ (wrapper.nextElementSibling &&
253
+ wrapper.nextElementSibling.querySelector("a, button")) ||
254
+ document.querySelector(
255
+ ".navbar_cartsearch_wrap a, .navbar_cartsearch_wrap button",
256
+ );
234
257
  if (nextElement) {
235
258
  closeDropdown();
236
259
  nextElement.focus();
@@ -240,14 +263,16 @@ function setupDynamicDropdowns() {
240
263
  currentMenuItemIndex = (currentMenuItemIndex + 1) % menuItems.length;
241
264
  menuItems[currentMenuItemIndex].focus();
242
265
  }
243
- } else if (e.key === 'ArrowUp') {
266
+ } else if (e.key === "ArrowUp") {
244
267
  e.preventDefault();
245
268
  if (document.activeElement === toggle) {
246
269
  currentMenuItemIndex = menuItems.length - 1;
247
270
  menuItems[currentMenuItemIndex].focus();
248
271
  } else {
249
272
  if (currentMenuItemIndex === 0) {
250
- const prevElement = wrapper.previousElementSibling?.querySelector('a, button');
273
+ const prevElement =
274
+ wrapper.previousElementSibling &&
275
+ wrapper.previousElementSibling.querySelector("a, button");
251
276
  if (prevElement) {
252
277
  closeDropdown();
253
278
  prevElement.focus();
@@ -258,10 +283,13 @@ function setupDynamicDropdowns() {
258
283
  return;
259
284
  }
260
285
  }
261
- currentMenuItemIndex = currentMenuItemIndex <= 0 ? menuItems.length - 1 : currentMenuItemIndex - 1;
286
+ currentMenuItemIndex =
287
+ currentMenuItemIndex <= 0
288
+ ? menuItems.length - 1
289
+ : currentMenuItemIndex - 1;
262
290
  menuItems[currentMenuItemIndex].focus();
263
291
  }
264
- } else if (e.key === 'Tab') {
292
+ } else if (e.key === "Tab") {
265
293
  if (e.shiftKey) {
266
294
  if (document.activeElement === menuItems[0]) {
267
295
  e.preventDefault();
@@ -271,8 +299,12 @@ function setupDynamicDropdowns() {
271
299
  } else {
272
300
  if (document.activeElement === menuItems[menuItems.length - 1]) {
273
301
  e.preventDefault();
274
- const nextElement = wrapper.nextElementSibling?.querySelector('a, button') ||
275
- document.querySelector('.navbar_cartsearch_wrap a, .navbar_cartsearch_wrap button');
302
+ const nextElement =
303
+ (wrapper.nextElementSibling &&
304
+ wrapper.nextElementSibling.querySelector("a, button")) ||
305
+ document.querySelector(
306
+ ".navbar_cartsearch_wrap a, .navbar_cartsearch_wrap button",
307
+ );
276
308
  closeDropdown();
277
309
  if (nextElement) {
278
310
  setTimeout(() => {
@@ -281,32 +313,32 @@ function setupDynamicDropdowns() {
281
313
  }
282
314
  }
283
315
  }
284
- } else if (e.key === 'Escape') {
316
+ } else if (e.key === "Escape") {
285
317
  e.preventDefault();
286
318
  closeDropdown();
287
319
  toggle.focus();
288
- } else if (e.key === 'Home') {
320
+ } else if (e.key === "Home") {
289
321
  e.preventDefault();
290
322
  currentMenuItemIndex = 0;
291
323
  menuItems[0].focus();
292
- } else if (e.key === 'End') {
324
+ } else if (e.key === "End") {
293
325
  e.preventDefault();
294
326
  currentMenuItemIndex = menuItems.length - 1;
295
327
  menuItems[menuItems.length - 1].focus();
296
- } else if (e.key === ' ') {
328
+ } else if (e.key === " ") {
297
329
  e.preventDefault();
298
330
  }
299
331
  });
300
-
301
- toggle.addEventListener('keydown', function(e) {
302
- if (e.key === 'ArrowDown') {
332
+
333
+ toggle.addEventListener("keydown", function (e) {
334
+ if (e.key === "ArrowDown") {
303
335
  e.preventDefault();
304
336
  openDropdown();
305
337
  if (menuItems.length > 0) {
306
338
  currentMenuItemIndex = 0;
307
339
  setTimeout(() => menuItems[0].focus(), 100);
308
340
  }
309
- } else if (e.key === ' ') {
341
+ } else if (e.key === " ") {
310
342
  e.preventDefault();
311
343
  if (isOpen) {
312
344
  closeDropdown();
@@ -317,7 +349,7 @@ function setupDynamicDropdowns() {
317
349
  setTimeout(() => menuItems[0].focus(), 100);
318
350
  }
319
351
  }
320
- } else if (e.key === 'ArrowUp') {
352
+ } else if (e.key === "ArrowUp") {
321
353
  e.preventDefault();
322
354
  if (isOpen) {
323
355
  currentMenuItemIndex = menuItems.length - 1;
@@ -325,29 +357,29 @@ function setupDynamicDropdowns() {
325
357
  } else {
326
358
  closeDropdown();
327
359
  }
328
- } else if (e.key === 'Escape') {
360
+ } else if (e.key === "Escape") {
329
361
  e.preventDefault();
330
362
  closeDropdown();
331
363
  }
332
364
  });
333
-
334
- document.addEventListener('click', function(e) {
365
+
366
+ document.addEventListener("click", function (e) {
335
367
  if (!wrapper.contains(e.target) && isOpen) {
336
368
  closeDropdown();
337
369
  }
338
370
  });
339
-
371
+
340
372
  allDropdowns.push({
341
373
  wrapper,
342
374
  isOpen: () => isOpen,
343
375
  closeDropdown,
344
376
  toggle,
345
- dropdownList
377
+ dropdownList,
346
378
  });
347
379
  });
348
-
349
- document.addEventListener('focusin', function(e) {
350
- allDropdowns.forEach(dropdown => {
380
+
381
+ document.addEventListener("focusin", function (e) {
382
+ allDropdowns.forEach((dropdown) => {
351
383
  if (dropdown.isOpen() && !dropdown.wrapper.contains(e.target)) {
352
384
  dropdown.closeDropdown();
353
385
  }
@@ -359,66 +391,73 @@ function setupDynamicDropdowns() {
359
391
 
360
392
  // Desktop left/right arrow navigation
361
393
  function addDesktopArrowNavigation() {
362
- document.addEventListener('keydown', function(e) {
363
- if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight') return;
364
-
394
+ document.addEventListener("keydown", function (e) {
395
+ if (e.key !== "ArrowLeft" && e.key !== "ArrowRight") return;
396
+
365
397
  const mobileMenu = document.querySelector('[data-hs-nav="menu"]');
366
398
  if (mobileMenu && mobileMenu.contains(document.activeElement)) return;
367
-
368
- const navbar = document.querySelector('[data-hs-nav="wrapper"]') ||
369
- document.querySelector('.navbar_component') ||
370
- document.querySelector('nav[role="navigation"]') ||
371
- document.querySelector('nav');
372
-
399
+
400
+ const navbar =
401
+ document.querySelector('[data-hs-nav="wrapper"]') ||
402
+ document.querySelector(".navbar_component") ||
403
+ document.querySelector('nav[role="navigation"]') ||
404
+ document.querySelector("nav");
405
+
373
406
  if (!navbar || !navbar.contains(document.activeElement)) return;
374
-
375
- const openDropdownList = navbar.querySelector('[aria-hidden="false"][role="menu"]');
376
- if (openDropdownList && openDropdownList.contains(document.activeElement)) return;
377
-
407
+
408
+ const openDropdownList = navbar.querySelector(
409
+ '[aria-hidden="false"][role="menu"]',
410
+ );
411
+ if (openDropdownList && openDropdownList.contains(document.activeElement))
412
+ return;
413
+
378
414
  e.preventDefault();
379
-
380
- const allNavbarElements = navbar.querySelectorAll('a, button');
381
- const focusableElements = Array.from(allNavbarElements).filter(el => {
382
- if (el.getAttribute('tabindex') === '-1') return false;
383
-
415
+
416
+ const allNavbarElements = navbar.querySelectorAll("a, button");
417
+ const focusableElements = Array.from(allNavbarElements).filter((el) => {
418
+ if (el.getAttribute("tabindex") === "-1") return false;
419
+
384
420
  const isInDropdownList = el.closest('[role="menu"]');
385
421
  if (isInDropdownList) return false;
386
-
422
+
387
423
  const isInMobileMenu = el.closest('[data-hs-nav="menu"]');
388
424
  if (isInMobileMenu) return false;
389
-
425
+
390
426
  const computedStyle = window.getComputedStyle(el);
391
- const isHidden = computedStyle.display === 'none' ||
392
- computedStyle.visibility === 'hidden' ||
393
- computedStyle.opacity === '0' ||
394
- el.offsetWidth === 0 ||
395
- el.offsetHeight === 0;
427
+ const isHidden =
428
+ computedStyle.display === "none" ||
429
+ computedStyle.visibility === "hidden" ||
430
+ computedStyle.opacity === "0" ||
431
+ el.offsetWidth === 0 ||
432
+ el.offsetHeight === 0;
396
433
  if (isHidden) return false;
397
-
434
+
398
435
  let parent = el.parentElement;
399
436
  while (parent && parent !== navbar) {
400
437
  const parentStyle = window.getComputedStyle(parent);
401
- const parentHidden = parentStyle.display === 'none' ||
402
- parentStyle.visibility === 'hidden' ||
403
- parent.offsetWidth === 0 ||
404
- parent.offsetHeight === 0;
438
+ const parentHidden =
439
+ parentStyle.display === "none" ||
440
+ parentStyle.visibility === "hidden" ||
441
+ parent.offsetWidth === 0 ||
442
+ parent.offsetHeight === 0;
405
443
  if (parentHidden) return false;
406
444
  parent = parent.parentElement;
407
445
  }
408
-
446
+
409
447
  return true;
410
448
  });
411
-
449
+
412
450
  const currentIndex = focusableElements.indexOf(document.activeElement);
413
451
  if (currentIndex === -1) return;
414
-
452
+
415
453
  let nextIndex;
416
- if (e.key === 'ArrowRight') {
454
+ if (e.key === "ArrowRight") {
417
455
  nextIndex = (currentIndex + 1) % focusableElements.length;
418
456
  } else {
419
- nextIndex = currentIndex === 0 ? focusableElements.length - 1 : currentIndex - 1;
457
+ nextIndex =
458
+ currentIndex === 0 ? focusableElements.length - 1 : currentIndex - 1;
420
459
  }
421
-
460
+
422
461
  focusableElements[nextIndex].focus();
423
462
  });
424
463
  }
@@ -427,241 +466,287 @@ function addDesktopArrowNavigation() {
427
466
  function setupMobileMenuButton() {
428
467
  const menuButton = document.querySelector('[data-hs-nav="menubtn"]');
429
468
  const mobileMenu = document.querySelector('[data-hs-nav="menu"]');
430
-
469
+
431
470
  if (!menuButton || !mobileMenu) return;
432
-
471
+
433
472
  const menuId = `mobile-menu-${Date.now()}`;
434
-
435
- menuButton.setAttribute('aria-expanded', 'false');
436
- menuButton.setAttribute('aria-controls', menuId);
437
- menuButton.setAttribute('aria-label', 'Open navigation menu');
438
-
473
+
474
+ menuButton.setAttribute("aria-expanded", "false");
475
+ menuButton.setAttribute("aria-controls", menuId);
476
+ menuButton.setAttribute("aria-label", "Open navigation menu");
477
+
439
478
  mobileMenu.id = menuId;
440
- mobileMenu.setAttribute('role', 'dialog');
441
- mobileMenu.setAttribute('aria-modal', 'true');
479
+ mobileMenu.setAttribute("role", "dialog");
480
+ mobileMenu.setAttribute("aria-modal", "true");
442
481
  setElementInert(mobileMenu, true);
443
-
482
+
444
483
  let isMenuOpen = false;
445
-
484
+
446
485
  function shouldPreventMobileMenu() {
447
- const menuHideElement = document.querySelector('.menu-hide.is-mobile');
486
+ const menuHideElement = document.querySelector(".menu-hide.is-mobile");
448
487
  if (!menuHideElement) return false;
449
-
488
+
450
489
  const computedStyle = window.getComputedStyle(menuHideElement);
451
- return computedStyle.display === 'none';
490
+ return computedStyle.display === "none";
452
491
  }
453
-
492
+
454
493
  function openMenu() {
455
494
  if (isMenuOpen || shouldPreventMobileMenu()) return;
456
495
  isMenuOpen = true;
457
-
458
- // Add body overflow hidden class
459
- document.body.classList.add('u-overflow-hidden');
460
-
461
- // Add blur effect to modal blur elements
462
- document.querySelectorAll('[data-hs-nav="modal-blur"]').forEach(element => {
463
- element.style.display = 'block';
464
- element.style.opacity = '0.5';
465
- element.style.transition = 'opacity 0.3s ease';
466
- });
467
-
468
- menuButton.setAttribute('aria-expanded', 'true');
469
- menuButton.setAttribute('aria-label', 'Close navigation menu');
470
- setElementInert(mobileMenu, false);
471
-
472
- // Announce to screen readers
473
- const menuName = getMenuName(menuButton);
474
- announceToScreenReader(`${menuName} opened`);
475
-
476
- // Prevent tabbing outside navbar using tabindex management
477
- const navbarWrapper = document.querySelector('[data-hs-nav="wrapper"]') ||
478
- document.querySelector('.navbar_component') ||
479
- document.querySelector('nav[role="navigation"]') ||
480
- document.querySelector('nav');
481
-
482
- const allFocusableElements = document.querySelectorAll('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])');
483
- allFocusableElements.forEach(el => {
484
- if (navbarWrapper && !navbarWrapper.contains(el)) {
485
- el.setAttribute('data-mobile-menu-tabindex', el.getAttribute('tabindex') || '0');
486
- el.setAttribute('tabindex', '-1');
487
- }
488
- });
489
-
490
- const clickEvent = new MouseEvent('click', {
491
- bubbles: true,
492
- cancelable: true,
493
- view: window
494
- });
495
- menuButton.dispatchEvent(clickEvent);
496
+
497
+ try {
498
+ // Add body overflow hidden class
499
+ document.body.classList.add("u-overflow-hidden");
500
+
501
+ // Add blur effect to modal blur elements
502
+ document
503
+ .querySelectorAll('[data-hs-nav="modal-blur"]')
504
+ .forEach((element) => {
505
+ element.style.display = "block";
506
+ element.style.opacity = "0.5";
507
+ element.style.transition = "opacity 0.3s ease";
508
+ });
509
+
510
+ menuButton.setAttribute("aria-expanded", "true");
511
+ menuButton.setAttribute("aria-label", "Close navigation menu");
512
+ setElementInert(mobileMenu, false);
513
+
514
+ // Announce to screen readers
515
+ const menuName = getMenuName(menuButton);
516
+ announceToScreenReader(`${menuName} opened`);
517
+
518
+ // Prevent tabbing outside navbar using tabindex management
519
+ const navbarWrapper =
520
+ document.querySelector('[data-hs-nav="wrapper"]') ||
521
+ document.querySelector(".navbar_component") ||
522
+ document.querySelector('nav[role="navigation"]') ||
523
+ document.querySelector("nav");
524
+
525
+ const allFocusableElements = document.querySelectorAll(
526
+ 'a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])',
527
+ );
528
+ allFocusableElements.forEach((el) => {
529
+ if (navbarWrapper && !navbarWrapper.contains(el)) {
530
+ el.setAttribute(
531
+ "data-mobile-menu-tabindex",
532
+ el.getAttribute("tabindex") || "0",
533
+ );
534
+ el.setAttribute("tabindex", "-1");
535
+ }
536
+ });
537
+
538
+ const clickEvent = new MouseEvent("click", {
539
+ bubbles: true,
540
+ cancelable: true,
541
+ view: window,
542
+ });
543
+ menuButton.dispatchEvent(clickEvent);
544
+ } catch (e) {
545
+ // DOM operation failed, restore state
546
+ isMenuOpen = false;
547
+ menuButton.setAttribute("aria-expanded", "false");
548
+ menuButton.setAttribute("aria-label", "Open navigation menu");
549
+ }
496
550
  }
497
-
551
+
498
552
  function closeMenu() {
499
553
  if (!isMenuOpen) return;
500
554
  isMenuOpen = false;
501
-
502
- // Remove body overflow hidden class
503
- document.body.classList.remove('u-overflow-hidden');
504
-
505
- // Remove blur effect from modal blur elements
506
- document.querySelectorAll('[data-hs-nav="modal-blur"]').forEach(element => {
507
- element.style.opacity = '0';
508
- element.style.transition = 'opacity 0.3s ease';
509
- setTimeout(() => {
510
- element.style.display = 'none';
511
- }, 300);
512
- });
513
-
514
- if (mobileMenu.contains(document.activeElement)) {
515
- menuButton.focus();
516
- }
517
- menuButton.setAttribute('aria-expanded', 'false');
518
- menuButton.setAttribute('aria-label', 'Open navigation menu');
519
- setElementInert(mobileMenu, true);
520
-
521
- // Announce to screen readers
522
- const menuName = getMenuName(menuButton);
523
- announceToScreenReader(`${menuName} closed`);
524
-
525
- // Restore tabbing to entire page using tabindex management
526
- const elementsToRestore = document.querySelectorAll('[data-mobile-menu-tabindex]');
527
- elementsToRestore.forEach(el => {
528
- const originalTabindex = el.getAttribute('data-mobile-menu-tabindex');
529
- if (originalTabindex === '0') {
530
- el.removeAttribute('tabindex');
531
- } else {
532
- el.setAttribute('tabindex', originalTabindex);
555
+
556
+ try {
557
+ // Remove body overflow hidden class
558
+ document.body.classList.remove("u-overflow-hidden");
559
+
560
+ // Remove blur effect from modal blur elements
561
+ document
562
+ .querySelectorAll('[data-hs-nav="modal-blur"]')
563
+ .forEach((element) => {
564
+ element.style.opacity = "0";
565
+ element.style.transition = "opacity 0.3s ease";
566
+ setTimeout(() => {
567
+ element.style.display = "none";
568
+ }, 300);
569
+ });
570
+
571
+ if (mobileMenu.contains(document.activeElement)) {
572
+ menuButton.focus();
533
573
  }
534
- el.removeAttribute('data-mobile-menu-tabindex');
535
- });
536
-
537
- const clickEvent = new MouseEvent('click', {
538
- bubbles: true,
539
- cancelable: true,
540
- view: window
541
- });
542
- menuButton.dispatchEvent(clickEvent);
574
+ menuButton.setAttribute("aria-expanded", "false");
575
+ menuButton.setAttribute("aria-label", "Open navigation menu");
576
+ setElementInert(mobileMenu, true);
577
+
578
+ // Announce to screen readers
579
+ const menuName = getMenuName(menuButton);
580
+ announceToScreenReader(`${menuName} closed`);
581
+
582
+ // Restore tabbing to entire page using tabindex management
583
+ const elementsToRestore = document.querySelectorAll(
584
+ "[data-mobile-menu-tabindex]",
585
+ );
586
+ elementsToRestore.forEach((el) => {
587
+ const originalTabindex = el.getAttribute("data-mobile-menu-tabindex");
588
+ if (originalTabindex === "0") {
589
+ el.removeAttribute("tabindex");
590
+ } else {
591
+ el.setAttribute("tabindex", originalTabindex);
592
+ }
593
+ el.removeAttribute("data-mobile-menu-tabindex");
594
+ });
595
+
596
+ const clickEvent = new MouseEvent("click", {
597
+ bubbles: true,
598
+ cancelable: true,
599
+ view: window,
600
+ });
601
+ menuButton.dispatchEvent(clickEvent);
602
+ } catch (e) {
603
+ // DOM operation failed, ensure consistent state
604
+ isMenuOpen = false;
605
+ menuButton.setAttribute("aria-expanded", "false");
606
+ menuButton.setAttribute("aria-label", "Open navigation menu");
607
+ }
543
608
  }
544
-
609
+
545
610
  function toggleMenu() {
546
611
  if (shouldPreventMobileMenu()) return;
547
-
612
+
548
613
  if (isMenuOpen) {
549
614
  closeMenu();
550
615
  } else {
551
616
  openMenu();
552
617
  }
553
618
  }
554
-
555
- menuButton.addEventListener('keydown', function(e) {
556
- if (e.key === 'Enter' || e.key === ' ') {
619
+
620
+ menuButton.addEventListener("keydown", function (e) {
621
+ if (e.key === "Enter" || e.key === " ") {
557
622
  e.preventDefault();
558
623
  toggleMenu();
559
- } else if (e.key === 'ArrowDown') {
624
+ } else if (e.key === "ArrowDown") {
560
625
  e.preventDefault();
561
626
  if (!isMenuOpen) {
562
627
  openMenu();
563
628
  }
564
- const firstElement = mobileMenu.querySelector('button, a');
629
+ const firstElement = mobileMenu.querySelector("button, a");
565
630
  if (firstElement) {
566
631
  firstElement.focus();
567
632
  }
568
- } else if (e.key === 'ArrowUp') {
633
+ } else if (e.key === "ArrowUp") {
569
634
  e.preventDefault();
570
635
  if (isMenuOpen) {
571
636
  closeMenu();
572
637
  }
573
638
  }
574
639
  });
575
-
576
- menuButton.addEventListener('click', function(e) {
640
+
641
+ menuButton.addEventListener("click", function (e) {
577
642
  if (!e.isTrusted) return;
578
-
643
+
579
644
  if (shouldPreventMobileMenu()) return;
580
-
645
+
581
646
  if (isMenuOpen && mobileMenu.contains(document.activeElement)) {
582
647
  menuButton.focus();
583
648
  }
584
-
649
+
585
650
  const newMenuState = !isMenuOpen;
586
651
  isMenuOpen = newMenuState;
587
-
652
+
588
653
  // Handle body overflow class
589
654
  if (isMenuOpen) {
590
- document.body.classList.add('u-overflow-hidden');
655
+ document.body.classList.add("u-overflow-hidden");
591
656
  } else {
592
- document.body.classList.remove('u-overflow-hidden');
657
+ document.body.classList.remove("u-overflow-hidden");
593
658
  }
594
-
659
+
595
660
  // Handle blur effect
596
- document.querySelectorAll('[data-hs-nav="modal-blur"]').forEach(element => {
597
- if (isMenuOpen) {
598
- element.style.display = 'block';
599
- element.style.opacity = '0.5';
600
- element.style.transition = 'opacity 0.3s ease';
601
- } else {
602
- element.style.opacity = '0';
603
- element.style.transition = 'opacity 0.3s ease';
604
- setTimeout(() => {
605
- element.style.display = 'none';
606
- }, 300);
607
- }
608
- });
609
-
610
- menuButton.setAttribute('aria-expanded', isMenuOpen);
611
- menuButton.setAttribute('aria-label',
612
- isMenuOpen ? 'Close navigation menu' : 'Open navigation menu'
661
+ document
662
+ .querySelectorAll('[data-hs-nav="modal-blur"]')
663
+ .forEach((element) => {
664
+ if (isMenuOpen) {
665
+ element.style.display = "block";
666
+ element.style.opacity = "0.5";
667
+ element.style.transition = "opacity 0.3s ease";
668
+ } else {
669
+ element.style.opacity = "0";
670
+ element.style.transition = "opacity 0.3s ease";
671
+ setTimeout(() => {
672
+ element.style.display = "none";
673
+ }, 300);
674
+ }
675
+ });
676
+
677
+ menuButton.setAttribute("aria-expanded", isMenuOpen);
678
+ menuButton.setAttribute(
679
+ "aria-label",
680
+ isMenuOpen ? "Close navigation menu" : "Open navigation menu",
613
681
  );
614
682
  setElementInert(mobileMenu, !isMenuOpen);
615
-
683
+
616
684
  // Handle tabindex management for external clicks
617
685
  if (isMenuOpen) {
618
- const navbarWrapper = document.querySelector('[data-hs-nav="wrapper"]') ||
619
- document.querySelector('.navbar_component') ||
620
- document.querySelector('nav[role="navigation"]') ||
621
- document.querySelector('nav');
622
-
623
- const allFocusableElements = document.querySelectorAll('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])');
624
- allFocusableElements.forEach(el => {
686
+ const navbarWrapper =
687
+ document.querySelector('[data-hs-nav="wrapper"]') ||
688
+ document.querySelector(".navbar_component") ||
689
+ document.querySelector('nav[role="navigation"]') ||
690
+ document.querySelector("nav");
691
+
692
+ const allFocusableElements = document.querySelectorAll(
693
+ 'a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])',
694
+ );
695
+ allFocusableElements.forEach((el) => {
625
696
  if (navbarWrapper && !navbarWrapper.contains(el)) {
626
- el.setAttribute('data-mobile-menu-tabindex', el.getAttribute('tabindex') || '0');
627
- el.setAttribute('tabindex', '-1');
697
+ el.setAttribute(
698
+ "data-mobile-menu-tabindex",
699
+ el.getAttribute("tabindex") || "0",
700
+ );
701
+ el.setAttribute("tabindex", "-1");
628
702
  }
629
703
  });
630
704
  } else {
631
- const elementsToRestore = document.querySelectorAll('[data-mobile-menu-tabindex]');
632
- elementsToRestore.forEach(el => {
633
- const originalTabindex = el.getAttribute('data-mobile-menu-tabindex');
634
- if (originalTabindex === '0') {
635
- el.removeAttribute('tabindex');
705
+ const elementsToRestore = document.querySelectorAll(
706
+ "[data-mobile-menu-tabindex]",
707
+ );
708
+ elementsToRestore.forEach((el) => {
709
+ const originalTabindex = el.getAttribute("data-mobile-menu-tabindex");
710
+ if (originalTabindex === "0") {
711
+ el.removeAttribute("tabindex");
636
712
  } else {
637
- el.setAttribute('tabindex', originalTabindex);
713
+ el.setAttribute("tabindex", originalTabindex);
638
714
  }
639
- el.removeAttribute('data-mobile-menu-tabindex');
715
+ el.removeAttribute("data-mobile-menu-tabindex");
640
716
  });
641
717
  }
642
718
  });
643
-
719
+
644
720
  // Store the menu state and functions for breakpoint handler
645
721
  window.mobileMenuState = {
646
722
  isMenuOpen: () => isMenuOpen,
647
723
  closeMenu: closeMenu,
648
- openMenu: openMenu
724
+ openMenu: openMenu,
649
725
  };
726
+
727
+ // Cleanup function for window.mobileMenuState
728
+ if (typeof window !== "undefined") {
729
+ window.addEventListener("unload", () => {
730
+ if (window.mobileMenuState) {
731
+ delete window.mobileMenuState;
732
+ }
733
+ });
734
+ }
650
735
  }
651
736
 
652
737
  // Mobile menu breakpoint handler
653
738
  function setupMobileMenuBreakpointHandler() {
654
739
  let preventedMenuState = false;
655
-
740
+
656
741
  function handleBreakpointChange() {
657
- const menuHideElement = document.querySelector('.menu-hide.is-mobile');
742
+ const menuHideElement = document.querySelector(".menu-hide.is-mobile");
658
743
  if (!menuHideElement) return;
659
-
744
+
660
745
  const computedStyle = window.getComputedStyle(menuHideElement);
661
- const shouldPrevent = computedStyle.display === 'none';
662
-
746
+ const shouldPrevent = computedStyle.display === "none";
747
+
663
748
  if (!window.mobileMenuState) return;
664
-
749
+
665
750
  if (shouldPrevent && window.mobileMenuState.isMenuOpen()) {
666
751
  // Store that the menu was open before being prevented
667
752
  preventedMenuState = true;
@@ -672,19 +757,24 @@ function setupMobileMenuBreakpointHandler() {
672
757
  window.mobileMenuState.openMenu();
673
758
  }
674
759
  }
675
-
760
+
676
761
  // Use ResizeObserver for more accurate detection
677
- if (window.ResizeObserver) {
678
- const resizeObserver = new ResizeObserver(handleBreakpointChange);
679
- const menuHideElement = document.querySelector('.menu-hide.is-mobile');
680
- if (menuHideElement) {
681
- resizeObserver.observe(menuHideElement);
762
+ if (typeof ResizeObserver !== "undefined") {
763
+ try {
764
+ const resizeObserver = new ResizeObserver(handleBreakpointChange);
765
+ const menuHideElement = document.querySelector(".menu-hide.is-mobile");
766
+ if (menuHideElement) {
767
+ resizeObserver.observe(menuHideElement);
768
+ }
769
+ } catch (e) {
770
+ // ResizeObserver not supported or error occurred
771
+ // Silently fall back to resize event
682
772
  }
683
773
  }
684
-
774
+
685
775
  // Fallback to resize event
686
- window.addEventListener('resize', handleBreakpointChange);
687
-
776
+ window.addEventListener("resize", handleBreakpointChange);
777
+
688
778
  // Initial check
689
779
  handleBreakpointChange();
690
780
  }
@@ -692,9 +782,9 @@ function setupMobileMenuBreakpointHandler() {
692
782
  function sanitizeForID(text) {
693
783
  return text
694
784
  .toLowerCase()
695
- .replace(/[^a-z0-9\s]/g, '')
696
- .replace(/\s+/g, '-')
697
- .replace(/^-+|-+$/g, '')
785
+ .replace(/[^a-z0-9\s]/g, "")
786
+ .replace(/\s+/g, "-")
787
+ .replace(/^-+|-+$/g, "")
698
788
  .substring(0, 50);
699
789
  }
700
790
 
@@ -703,11 +793,11 @@ function setupMobileMenuARIA() {
703
793
  const menuContainer = document.querySelector('[data-hs-nav="menu"]');
704
794
  if (!menuContainer) return;
705
795
 
706
- const buttons = menuContainer.querySelectorAll('button');
707
- const links = menuContainer.querySelectorAll('a');
796
+ const buttons = menuContainer.querySelectorAll("button");
797
+ const links = menuContainer.querySelectorAll("a");
708
798
 
709
- buttons.forEach(button => {
710
- const buttonText = button.textContent?.trim();
799
+ buttons.forEach((button) => {
800
+ const buttonText = button.textContent && button.textContent.trim();
711
801
  if (!buttonText) return;
712
802
 
713
803
  const sanitizedText = sanitizeForID(buttonText);
@@ -715,66 +805,77 @@ function setupMobileMenuARIA() {
715
805
  const listId = `navbar-mobile-${sanitizedText}-list`;
716
806
 
717
807
  button.id = buttonId;
718
- button.setAttribute('aria-expanded', 'false');
719
- button.setAttribute('aria-controls', listId);
808
+ button.setAttribute("aria-expanded", "false");
809
+ button.setAttribute("aria-controls", listId);
720
810
 
721
811
  // Look for dropdown list in the same container as the button
722
812
  let dropdownList = null;
723
- const buttonContainer = button.closest('.menu-card_dropdown, .menu_contain, [data-hs-nav="menu"]');
724
-
813
+ const buttonContainer = button.closest(
814
+ '.menu-card_dropdown, .menu_contain, [data-hs-nav="menu"]',
815
+ );
816
+
725
817
  if (buttonContainer) {
726
818
  // First try to find a list element within the same container
727
- dropdownList = buttonContainer.querySelector('.menu-card_list, .dropdown-list, [role="menu"]');
728
-
819
+ dropdownList = buttonContainer.querySelector(
820
+ '.menu-card_list, .dropdown-list, [role="menu"]',
821
+ );
822
+
729
823
  // If not found, look for any element with multiple links that's not the button itself
730
- if (!dropdownList || !dropdownList.querySelector('a')) {
731
- const allListElements = buttonContainer.querySelectorAll('div, ul, nav');
732
- dropdownList = Array.from(allListElements).find(el =>
733
- el.querySelectorAll('a').length > 1 &&
734
- !el.contains(button) &&
735
- el !== button
824
+ if (!dropdownList || !dropdownList.querySelector("a")) {
825
+ const allListElements =
826
+ buttonContainer.querySelectorAll("div, ul, nav");
827
+ dropdownList = Array.from(allListElements).find(
828
+ (el) =>
829
+ el.querySelectorAll("a").length > 1 &&
830
+ !el.contains(button) &&
831
+ el !== button,
736
832
  );
737
833
  }
738
834
  }
739
835
 
740
- if (dropdownList && dropdownList.querySelector('a')) {
836
+ if (dropdownList && dropdownList.querySelector("a")) {
741
837
  dropdownList.id = listId;
742
838
  setElementInert(dropdownList, true);
743
839
 
744
- button.addEventListener('click', function() {
745
- const isExpanded = button.getAttribute('aria-expanded') === 'true';
840
+ button.addEventListener("click", function () {
841
+ const isExpanded = button.getAttribute("aria-expanded") === "true";
746
842
  const newState = !isExpanded;
747
- button.setAttribute('aria-expanded', newState);
843
+ button.setAttribute("aria-expanded", newState);
748
844
  setElementInert(dropdownList, !newState);
749
-
845
+
750
846
  // Announce to screen readers
751
847
  const menuName = getMenuName(button);
752
- announceToScreenReader(`${menuName} submenu ${newState ? 'opened' : 'closed'}`);
848
+ announceToScreenReader(
849
+ `${menuName} submenu ${newState ? "opened" : "closed"}`,
850
+ );
753
851
  });
754
852
  }
755
853
  });
756
854
 
757
- links.forEach(link => {
758
- const linkText = link.textContent?.trim();
855
+ links.forEach((link) => {
856
+ const linkText = link.textContent && link.textContent.trim();
759
857
  if (!linkText) return;
760
858
 
761
859
  const sanitizedText = sanitizeForID(linkText);
762
860
  const linkId = `navbar-mobile-${sanitizedText}-link`;
763
861
  link.id = linkId;
764
862
  });
765
-
863
+
766
864
  setupMobileMenuArrowNavigation(menuContainer);
767
865
  }
768
866
 
769
867
  // Mobile menu arrow navigation
770
868
  function setupMobileMenuArrowNavigation(menuContainer) {
771
869
  function getFocusableElements() {
772
- const allElements = menuContainer.querySelectorAll('button, a');
773
- return Array.from(allElements).filter(el => {
870
+ const allElements = menuContainer.querySelectorAll("button, a");
871
+ return Array.from(allElements).filter((el) => {
774
872
  let current = el;
775
873
  while (current && current !== menuContainer) {
776
874
  // Check both native inert and polyfill inert
777
- if (current.inert === true || current.getAttribute('data-inert') === 'true') {
875
+ if (
876
+ current.inert === true ||
877
+ current.getAttribute("data-inert") === "true"
878
+ ) {
778
879
  return false;
779
880
  }
780
881
  current = current.parentElement;
@@ -782,17 +883,17 @@ function setupMobileMenuArrowNavigation(menuContainer) {
782
883
  return true;
783
884
  });
784
885
  }
785
-
886
+
786
887
  let currentFocusIndex = -1;
787
-
788
- menuContainer.addEventListener('keydown', function(e) {
888
+
889
+ menuContainer.addEventListener("keydown", function (e) {
789
890
  const focusableElements = getFocusableElements();
790
891
  if (focusableElements.length === 0) return;
791
-
892
+
792
893
  const activeElement = document.activeElement;
793
894
  currentFocusIndex = focusableElements.indexOf(activeElement);
794
-
795
- if (e.key === 'ArrowDown') {
895
+
896
+ if (e.key === "ArrowDown") {
796
897
  e.preventDefault();
797
898
  if (currentFocusIndex >= focusableElements.length - 1) {
798
899
  currentFocusIndex = 0;
@@ -800,10 +901,12 @@ function setupMobileMenuArrowNavigation(menuContainer) {
800
901
  currentFocusIndex = currentFocusIndex + 1;
801
902
  }
802
903
  focusableElements[currentFocusIndex].focus();
803
- } else if (e.key === 'ArrowUp') {
904
+ } else if (e.key === "ArrowUp") {
804
905
  e.preventDefault();
805
906
  if (currentFocusIndex <= 0) {
806
- const mobileMenuButton = document.querySelector('[data-hs-nav="menubtn"]');
907
+ const mobileMenuButton = document.querySelector(
908
+ '[data-hs-nav="menubtn"]',
909
+ );
807
910
  if (mobileMenuButton) {
808
911
  mobileMenuButton.focus();
809
912
  return;
@@ -811,40 +914,50 @@ function setupMobileMenuArrowNavigation(menuContainer) {
811
914
  }
812
915
  currentFocusIndex = currentFocusIndex - 1;
813
916
  focusableElements[currentFocusIndex].focus();
814
- } else if (e.key === 'ArrowRight') {
917
+ } else if (e.key === "ArrowRight") {
815
918
  e.preventDefault();
816
- if (activeElement.tagName === 'BUTTON' && activeElement.hasAttribute('aria-controls')) {
817
- const isExpanded = activeElement.getAttribute('aria-expanded') === 'true';
919
+ if (
920
+ activeElement.tagName === "BUTTON" &&
921
+ activeElement.hasAttribute("aria-controls")
922
+ ) {
923
+ const isExpanded =
924
+ activeElement.getAttribute("aria-expanded") === "true";
818
925
  if (!isExpanded) {
819
926
  activeElement.click();
820
927
  }
821
928
  return;
822
929
  }
823
- } else if (e.key === 'ArrowLeft') {
930
+ } else if (e.key === "ArrowLeft") {
824
931
  e.preventDefault();
825
- if (activeElement.tagName === 'BUTTON' && activeElement.hasAttribute('aria-controls')) {
826
- const isExpanded = activeElement.getAttribute('aria-expanded') === 'true';
932
+ if (
933
+ activeElement.tagName === "BUTTON" &&
934
+ activeElement.hasAttribute("aria-controls")
935
+ ) {
936
+ const isExpanded =
937
+ activeElement.getAttribute("aria-expanded") === "true";
827
938
  if (isExpanded) {
828
939
  activeElement.click();
829
940
  }
830
941
  return;
831
942
  }
832
- } else if (e.key === 'Home') {
943
+ } else if (e.key === "Home") {
833
944
  e.preventDefault();
834
945
  currentFocusIndex = 0;
835
946
  focusableElements[0].focus();
836
- } else if (e.key === 'End') {
947
+ } else if (e.key === "End") {
837
948
  e.preventDefault();
838
949
  currentFocusIndex = focusableElements.length - 1;
839
950
  focusableElements[focusableElements.length - 1].focus();
840
- } else if (e.key === ' ' && activeElement.tagName === 'A') {
951
+ } else if (e.key === " " && activeElement.tagName === "A") {
841
952
  e.preventDefault();
842
- } else if (e.key === 'Escape') {
843
- const mobileMenuButton = document.querySelector('[data-hs-nav="menubtn"]');
953
+ } else if (e.key === "Escape") {
954
+ const mobileMenuButton = document.querySelector(
955
+ '[data-hs-nav="menubtn"]',
956
+ );
844
957
  if (mobileMenuButton) {
845
958
  mobileMenuButton.click();
846
959
  mobileMenuButton.focus();
847
960
  }
848
961
  }
849
962
  });
850
- }
963
+ }