@aspect-ops/exon-ui 0.2.2 → 0.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.
Files changed (67) hide show
  1. package/README.md +0 -45
  2. package/dist/components/BottomNav/BottomNavItem.svelte +4 -3
  3. package/dist/components/BottomNav/BottomNavItem.svelte.d.ts +2 -1
  4. package/dist/components/CTASection/CTASection.svelte +298 -0
  5. package/dist/components/CTASection/CTASection.svelte.d.ts +15 -0
  6. package/dist/components/CTASection/index.d.ts +2 -0
  7. package/dist/components/CTASection/index.js +1 -0
  8. package/dist/components/DoughnutChart/DoughnutChart.svelte +4 -4
  9. package/dist/components/FlexibleGrid/FlexibleGrid.svelte +118 -0
  10. package/dist/components/FlexibleGrid/FlexibleGrid.svelte.d.ts +7 -0
  11. package/dist/components/FlexibleGrid/README.md +212 -0
  12. package/dist/components/FlexibleGrid/index.d.ts +2 -0
  13. package/dist/components/FlexibleGrid/index.js +1 -0
  14. package/dist/components/GlobalHeader/GlobalHeader.svelte +806 -0
  15. package/dist/components/GlobalHeader/GlobalHeader.svelte.d.ts +3 -0
  16. package/dist/components/GlobalHeader/index.d.ts +2 -0
  17. package/dist/components/GlobalHeader/index.js +1 -0
  18. package/dist/components/Hero/Hero.svelte +306 -0
  19. package/dist/components/Hero/Hero.svelte.d.ts +18 -0
  20. package/dist/components/Hero/index.d.ts +2 -0
  21. package/dist/components/Hero/index.js +1 -0
  22. package/dist/components/HeroLeftAligned/HeroLeftAligned.svelte +182 -0
  23. package/dist/components/HeroLeftAligned/HeroLeftAligned.svelte.d.ts +8 -0
  24. package/dist/components/HeroLeftAligned/README.md +168 -0
  25. package/dist/components/HeroLeftAligned/index.d.ts +2 -0
  26. package/dist/components/HeroLeftAligned/index.js +1 -0
  27. package/dist/components/IconFeatureCard/IconFeatureCard.svelte +173 -0
  28. package/dist/components/IconFeatureCard/IconFeatureCard.svelte.d.ts +4 -0
  29. package/dist/components/IconFeatureCard/README.md +234 -0
  30. package/dist/components/IconFeatureCard/index.d.ts +2 -0
  31. package/dist/components/IconFeatureCard/index.js +1 -0
  32. package/dist/components/ImageTextCard/ImageTextCard.svelte +149 -0
  33. package/dist/components/ImageTextCard/ImageTextCard.svelte.d.ts +22 -0
  34. package/dist/components/ImageTextCard/README.md +177 -0
  35. package/dist/components/ImageTextCard/index.d.ts +2 -0
  36. package/dist/components/ImageTextCard/index.js +1 -0
  37. package/dist/components/LogoCloud/LogoCloud.svelte +333 -0
  38. package/dist/components/LogoCloud/LogoCloud.svelte.d.ts +20 -0
  39. package/dist/components/LogoCloud/index.d.ts +2 -0
  40. package/dist/components/LogoCloud/index.js +1 -0
  41. package/dist/components/ServiceCard/ServiceCard.svelte +359 -0
  42. package/dist/components/ServiceCard/ServiceCard.svelte.d.ts +16 -0
  43. package/dist/components/ServiceCard/index.d.ts +1 -0
  44. package/dist/components/ServiceCard/index.js +1 -0
  45. package/dist/components/Sidebar/SidebarGroup.svelte +1 -4
  46. package/dist/components/SplitSection/SplitSection.svelte +194 -0
  47. package/dist/components/SplitSection/SplitSection.svelte.d.ts +15 -0
  48. package/dist/components/SplitSection/index.d.ts +1 -0
  49. package/dist/components/SplitSection/index.js +1 -0
  50. package/dist/components/TestimonialCard/TestimonialCard.svelte +290 -0
  51. package/dist/components/TestimonialCard/TestimonialCard.svelte.d.ts +14 -0
  52. package/dist/components/TestimonialCard/index.d.ts +1 -0
  53. package/dist/components/TestimonialCard/index.js +1 -0
  54. package/dist/components/Timeline/Timeline.svelte +444 -0
  55. package/dist/components/Timeline/Timeline.svelte.d.ts +19 -0
  56. package/dist/components/Timeline/index.d.ts +2 -0
  57. package/dist/components/Timeline/index.js +1 -0
  58. package/dist/index.d.ts +23 -1
  59. package/dist/index.js +13 -1
  60. package/dist/types/index.d.ts +49 -1
  61. package/dist/types/layout.d.ts +20 -0
  62. package/package.json +9 -2
  63. package/dist/components/Mermaid/Mermaid.svelte +0 -320
  64. package/dist/components/Mermaid/Mermaid.svelte.d.ts +0 -38
  65. package/dist/components/Mermaid/index.d.ts +0 -1
  66. package/dist/components/Mermaid/index.js +0 -1
  67. package/dist/components/Mermaid/mermaid.d.ts +0 -21
@@ -0,0 +1,806 @@
1
+ <script lang="ts">
2
+ import { onMount, onDestroy } from 'svelte';
3
+
4
+ export interface NavItem {
5
+ label: string;
6
+ href?: string;
7
+ children?: NavItem[];
8
+ icon?: string;
9
+ }
10
+
11
+ export interface GlobalHeaderProps {
12
+ logo?: string | import('svelte').Snippet;
13
+ logoHref?: string;
14
+ navItems: NavItem[];
15
+ variant?: 'default' | 'transparent' | 'floating';
16
+ sticky?: boolean;
17
+ showOnScroll?: boolean;
18
+ mobileBreakpoint?: number;
19
+ class?: string;
20
+ actions?: import('svelte').Snippet;
21
+ mobileMenu?: import('svelte').Snippet;
22
+ }
23
+
24
+ interface Props extends GlobalHeaderProps {
25
+ logo?: string | import('svelte').Snippet;
26
+ actions?: import('svelte').Snippet;
27
+ mobileMenu?: import('svelte').Snippet;
28
+ }
29
+
30
+ let {
31
+ logo,
32
+ logoHref = '/',
33
+ navItems,
34
+ variant = 'default',
35
+ sticky = true,
36
+ showOnScroll = false,
37
+ mobileBreakpoint = 768,
38
+ class: className = '',
39
+ actions,
40
+ mobileMenu
41
+ }: Props = $props();
42
+
43
+ let mobileMenuOpen = $state(false);
44
+ let openDropdown = $state<number | null>(null);
45
+ let openMobileSubmenu = $state<number | null>(null);
46
+ let isScrollingUp = $state(true);
47
+ let lastScrollY = $state(0);
48
+ let headerElement: HTMLElement | null = $state(null);
49
+
50
+ function toggleMobileMenu() {
51
+ mobileMenuOpen = !mobileMenuOpen;
52
+ if (typeof document !== 'undefined') {
53
+ if (mobileMenuOpen) {
54
+ document.body.style.overflow = 'hidden';
55
+ } else {
56
+ document.body.style.overflow = '';
57
+ }
58
+ }
59
+ }
60
+
61
+ function closeMobileMenu() {
62
+ mobileMenuOpen = false;
63
+ if (typeof document !== 'undefined') {
64
+ document.body.style.overflow = '';
65
+ }
66
+ }
67
+
68
+ function toggleDropdown(index: number) {
69
+ openDropdown = openDropdown === index ? null : index;
70
+ }
71
+
72
+ function closeDropdown() {
73
+ openDropdown = null;
74
+ }
75
+
76
+ function toggleMobileSubmenu(index: number) {
77
+ openMobileSubmenu = openMobileSubmenu === index ? null : index;
78
+ }
79
+
80
+ function handleKeydown(event: KeyboardEvent) {
81
+ if (event.key === 'Escape') {
82
+ if (mobileMenuOpen) {
83
+ closeMobileMenu();
84
+ }
85
+ if (openDropdown !== null) {
86
+ closeDropdown();
87
+ }
88
+ }
89
+ }
90
+
91
+ function handleScroll() {
92
+ if (!showOnScroll) return;
93
+
94
+ const currentScrollY = window.scrollY;
95
+
96
+ if (currentScrollY > lastScrollY && currentScrollY > 100) {
97
+ // Scrolling down
98
+ isScrollingUp = false;
99
+ } else if (currentScrollY < lastScrollY) {
100
+ // Scrolling up
101
+ isScrollingUp = true;
102
+ }
103
+
104
+ lastScrollY = currentScrollY;
105
+ }
106
+
107
+ onMount(() => {
108
+ if (showOnScroll) {
109
+ window.addEventListener('scroll', handleScroll, { passive: true });
110
+ }
111
+ });
112
+
113
+ onDestroy(() => {
114
+ if (typeof window !== 'undefined' && showOnScroll) {
115
+ window.removeEventListener('scroll', handleScroll);
116
+ }
117
+ if (typeof document !== 'undefined') {
118
+ document.body.style.overflow = '';
119
+ }
120
+ });
121
+
122
+ const variantClass = $derived(
123
+ variant === 'transparent'
124
+ ? 'global-header--transparent'
125
+ : variant === 'floating'
126
+ ? 'global-header--floating'
127
+ : ''
128
+ );
129
+
130
+ const stickyClass = $derived(sticky ? 'global-header--sticky' : '');
131
+ const scrollClass = $derived(showOnScroll && !isScrollingUp ? 'global-header--hidden' : '');
132
+ </script>
133
+
134
+ <svelte:window onkeydown={handleKeydown} />
135
+
136
+ <header
137
+ bind:this={headerElement}
138
+ class="global-header {variantClass} {stickyClass} {scrollClass} {className}"
139
+ >
140
+ <div class="global-header__container">
141
+ {#if logo}
142
+ <a href={logoHref} class="global-header__logo">
143
+ {#if typeof logo === 'string'}
144
+ <img src={logo} alt="Logo" class="global-header__logo-img" />
145
+ {:else}
146
+ {@render logo()}
147
+ {/if}
148
+ </a>
149
+ {/if}
150
+
151
+ <nav class="global-header__nav" aria-label="Main navigation">
152
+ <ul class="global-header__nav-list">
153
+ {#each navItems as item, index}
154
+ <li
155
+ class="global-header__nav-item"
156
+ onmouseenter={() => item.children && item.children.length > 0 && (openDropdown = index)}
157
+ onmouseleave={() => item.children && item.children.length > 0 && (openDropdown = null)}
158
+ >
159
+ {#if item.children && item.children.length > 0}
160
+ {#if item.href}
161
+ <div class="global-header__nav-item-split">
162
+ <a
163
+ href={item.href}
164
+ class="global-header__nav-link global-header__nav-link--with-dropdown"
165
+ >
166
+ {#if item.icon}
167
+ <span class="global-header__nav-icon">{item.icon}</span>
168
+ {/if}
169
+ {item.label}
170
+ </a>
171
+ <button
172
+ type="button"
173
+ class="global-header__dropdown-toggle"
174
+ onclick={() => toggleDropdown(index)}
175
+ aria-expanded={openDropdown === index}
176
+ aria-haspopup="true"
177
+ aria-label="Toggle {item.label} menu"
178
+ >
179
+ <span class="global-header__dropdown-arrow" aria-hidden="true">▼</span>
180
+ </button>
181
+ </div>
182
+ {:else}
183
+ <button
184
+ type="button"
185
+ class="global-header__nav-link global-header__nav-link--dropdown"
186
+ onclick={() => toggleDropdown(index)}
187
+ aria-expanded={openDropdown === index}
188
+ aria-haspopup="true"
189
+ >
190
+ {#if item.icon}
191
+ <span class="global-header__nav-icon">{item.icon}</span>
192
+ {/if}
193
+ {item.label}
194
+ <span class="global-header__dropdown-arrow" aria-hidden="true">▼</span>
195
+ </button>
196
+ {/if}
197
+
198
+ {#if openDropdown === index}
199
+ <ul class="global-header__dropdown" role="menu">
200
+ {#each item.children as child}
201
+ <li role="none">
202
+ <a
203
+ href={child.href || '#'}
204
+ class="global-header__dropdown-item"
205
+ role="menuitem"
206
+ onclick={closeDropdown}
207
+ >
208
+ {#if child.icon}
209
+ <span class="global-header__nav-icon">{child.icon}</span>
210
+ {/if}
211
+ {child.label}
212
+ </a>
213
+ </li>
214
+ {/each}
215
+ </ul>
216
+ {/if}
217
+ {:else}
218
+ <a href={item.href || '#'} class="global-header__nav-link">
219
+ {#if item.icon}
220
+ <span class="global-header__nav-icon">{item.icon}</span>
221
+ {/if}
222
+ {item.label}
223
+ </a>
224
+ {/if}
225
+ </li>
226
+ {/each}
227
+ </ul>
228
+ </nav>
229
+
230
+ {#if actions}
231
+ <div class="global-header__actions">
232
+ {@render actions()}
233
+ </div>
234
+ {/if}
235
+
236
+ <button
237
+ type="button"
238
+ class="global-header__hamburger"
239
+ onclick={toggleMobileMenu}
240
+ aria-expanded={mobileMenuOpen}
241
+ aria-controls="global-header-mobile-menu"
242
+ aria-label={mobileMenuOpen ? 'Close menu' : 'Open menu'}
243
+ >
244
+ <span
245
+ class="global-header__hamburger-icon"
246
+ class:global-header__hamburger-icon--open={mobileMenuOpen}
247
+ >
248
+ <span></span>
249
+ <span></span>
250
+ <span></span>
251
+ </span>
252
+ </button>
253
+ </div>
254
+
255
+ {#if mobileMenuOpen}
256
+ <div
257
+ id="global-header-mobile-menu"
258
+ class="global-header__mobile-menu"
259
+ class:global-header__mobile-menu--open={mobileMenuOpen}
260
+ >
261
+ {#if mobileMenu}
262
+ {@render mobileMenu()}
263
+ {:else}
264
+ <nav class="global-header__mobile-nav" aria-label="Mobile navigation">
265
+ <ul class="global-header__mobile-nav-list">
266
+ {#each navItems as item, index}
267
+ <li class="global-header__mobile-nav-item">
268
+ {#if item.children && item.children.length > 0}
269
+ <div class="global-header__mobile-nav-group">
270
+ <div class="global-header__mobile-nav-header">
271
+ <a
272
+ href={item.href || '#'}
273
+ class="global-header__mobile-nav-link global-header__mobile-nav-link--parent"
274
+ onclick={closeMobileMenu}
275
+ >
276
+ {#if item.icon}
277
+ <span class="global-header__nav-icon">{item.icon}</span>
278
+ {/if}
279
+ {item.label}
280
+ </a>
281
+ <button
282
+ type="button"
283
+ class="global-header__mobile-toggle"
284
+ onclick={() => toggleMobileSubmenu(index)}
285
+ aria-expanded={openMobileSubmenu === index}
286
+ aria-label="Toggle {item.label} submenu"
287
+ >
288
+ <span class="global-header__mobile-toggle-icon">
289
+ {openMobileSubmenu === index ? '−' : '+'}
290
+ </span>
291
+ </button>
292
+ </div>
293
+ {#if openMobileSubmenu === index}
294
+ <ul class="global-header__mobile-nav-sublist">
295
+ {#each item.children as child}
296
+ <li>
297
+ <a
298
+ href={child.href || '#'}
299
+ class="global-header__mobile-nav-link global-header__mobile-nav-link--child"
300
+ onclick={closeMobileMenu}
301
+ >
302
+ {#if child.icon}
303
+ <span class="global-header__nav-icon">{child.icon}</span>
304
+ {/if}
305
+ {child.label}
306
+ </a>
307
+ </li>
308
+ {/each}
309
+ </ul>
310
+ {/if}
311
+ </div>
312
+ {:else}
313
+ <a
314
+ href={item.href || '#'}
315
+ class="global-header__mobile-nav-link"
316
+ onclick={closeMobileMenu}
317
+ >
318
+ {#if item.icon}
319
+ <span class="global-header__nav-icon">{item.icon}</span>
320
+ {/if}
321
+ {item.label}
322
+ </a>
323
+ {/if}
324
+ </li>
325
+ {/each}
326
+ </ul>
327
+
328
+ {#if actions}
329
+ <div class="global-header__mobile-actions">
330
+ {@render actions()}
331
+ </div>
332
+ {/if}
333
+ </nav>
334
+ {/if}
335
+ </div>
336
+ {/if}
337
+ </header>
338
+
339
+ <style>
340
+ .global-header {
341
+ --header-bg: var(--color-bg, #ffffff);
342
+ --header-height: 4rem;
343
+ --header-text-color: var(--color-text, #1f2937);
344
+ --header-border-color: var(--color-border, #e5e7eb);
345
+ --header-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
346
+ --header-z-index: 100;
347
+
348
+ position: relative;
349
+ background: var(--header-bg);
350
+ border-bottom: 1px solid var(--header-border-color);
351
+ font-family: inherit;
352
+ z-index: var(--header-z-index);
353
+ transition: transform var(--transition-normal, 250ms ease);
354
+ }
355
+
356
+ .global-header--sticky {
357
+ position: sticky;
358
+ top: 0;
359
+ }
360
+
361
+ .global-header--transparent {
362
+ background: transparent;
363
+ border-bottom: 1px solid transparent;
364
+ --header-shadow: none;
365
+ }
366
+
367
+ .global-header--floating {
368
+ margin: var(--space-md, 1rem);
369
+ border-radius: var(--radius-lg, 0.5rem);
370
+ box-shadow: var(--header-shadow);
371
+ border: 1px solid var(--header-border-color);
372
+ }
373
+
374
+ .global-header--hidden {
375
+ transform: translateY(-100%);
376
+ }
377
+
378
+ .global-header__container {
379
+ display: flex;
380
+ align-items: center;
381
+ justify-content: space-between;
382
+ gap: var(--space-md, 1rem);
383
+ max-width: var(--max-width, 80rem);
384
+ margin: 0 auto;
385
+ padding: 0 var(--space-md, 1rem);
386
+ height: var(--header-height);
387
+ }
388
+
389
+ .global-header__logo {
390
+ display: flex;
391
+ align-items: center;
392
+ flex-shrink: 0;
393
+ color: var(--header-text-color);
394
+ text-decoration: none;
395
+ }
396
+
397
+ .global-header__logo:focus-visible {
398
+ outline: 2px solid var(--color-primary, #3b82f6);
399
+ outline-offset: 2px;
400
+ border-radius: var(--radius-sm, 0.25rem);
401
+ }
402
+
403
+ .global-header__logo-img {
404
+ max-height: 2.5rem;
405
+ height: auto;
406
+ width: auto;
407
+ }
408
+
409
+ .global-header__nav {
410
+ display: none;
411
+ flex: 1;
412
+ justify-content: center;
413
+ }
414
+
415
+ .global-header__nav-list {
416
+ display: flex;
417
+ align-items: center;
418
+ gap: var(--space-xs, 0.25rem);
419
+ list-style: none;
420
+ margin: 0;
421
+ padding: 0;
422
+ }
423
+
424
+ .global-header__nav-item {
425
+ position: relative;
426
+ }
427
+
428
+ .global-header__nav-link {
429
+ display: flex;
430
+ align-items: center;
431
+ gap: var(--space-xs, 0.25rem);
432
+ padding: var(--space-sm, 0.5rem) var(--space-md, 1rem);
433
+ color: var(--header-text-color);
434
+ text-decoration: none;
435
+ font-size: var(--text-sm, 0.875rem);
436
+ font-weight: 500;
437
+ border-radius: var(--radius-md, 0.375rem);
438
+ transition: all var(--transition-fast, 150ms ease);
439
+ white-space: nowrap;
440
+ background: transparent;
441
+ border: none;
442
+ cursor: pointer;
443
+ font-family: inherit;
444
+ /* Touch target minimum */
445
+ min-height: var(--touch-target-min, 44px);
446
+ }
447
+
448
+ .global-header__nav-link:hover {
449
+ background: var(--color-bg-subtle, rgba(0, 0, 0, 0.05));
450
+ color: var(--color-primary, #3b82f6);
451
+ }
452
+
453
+ .global-header__nav-link:focus-visible {
454
+ outline: 2px solid var(--color-primary, #3b82f6);
455
+ outline-offset: 2px;
456
+ }
457
+
458
+ .global-header__nav-link--dropdown {
459
+ width: 100%;
460
+ text-align: left;
461
+ }
462
+
463
+ .global-header__nav-item-split {
464
+ display: flex;
465
+ align-items: center;
466
+ gap: 0;
467
+ }
468
+
469
+ .global-header__nav-link--with-dropdown {
470
+ flex: 1;
471
+ padding-right: var(--space-xs, 0.25rem);
472
+ }
473
+
474
+ .global-header__dropdown-toggle {
475
+ display: flex;
476
+ align-items: center;
477
+ justify-content: center;
478
+ padding: var(--space-sm, 0.5rem);
479
+ background: transparent;
480
+ border: none;
481
+ cursor: pointer;
482
+ font-family: inherit;
483
+ color: var(--header-text-color);
484
+ border-radius: var(--radius-md, 0.375rem);
485
+ transition: all var(--transition-fast, 150ms ease);
486
+ /* Touch target minimum */
487
+ min-height: var(--touch-target-min, 44px);
488
+ min-width: 2rem;
489
+ }
490
+
491
+ .global-header__dropdown-toggle:hover {
492
+ background: var(--color-bg-subtle, rgba(0, 0, 0, 0.05));
493
+ color: var(--color-primary, #3b82f6);
494
+ }
495
+
496
+ .global-header__dropdown-toggle:focus-visible {
497
+ outline: 2px solid var(--color-primary, #3b82f6);
498
+ outline-offset: 2px;
499
+ }
500
+
501
+ .global-header__nav-icon {
502
+ display: inline-flex;
503
+ align-items: center;
504
+ }
505
+
506
+ .global-header__dropdown-arrow {
507
+ margin-left: var(--space-xs, 0.25rem);
508
+ font-size: 0.625rem;
509
+ transition: transform var(--transition-fast, 150ms ease);
510
+ }
511
+
512
+ .global-header__nav-link[aria-expanded='true'] .global-header__dropdown-arrow,
513
+ .global-header__dropdown-toggle[aria-expanded='true'] .global-header__dropdown-arrow {
514
+ transform: rotate(180deg);
515
+ }
516
+
517
+ .global-header__dropdown {
518
+ position: absolute;
519
+ top: calc(100% + 0.25rem);
520
+ left: 0;
521
+ min-width: 12rem;
522
+ background: var(--color-bg, #ffffff);
523
+ border: 1px solid var(--header-border-color);
524
+ border-radius: var(--radius-md, 0.375rem);
525
+ box-shadow:
526
+ 0 4px 6px -1px rgba(0, 0, 0, 0.1),
527
+ 0 2px 4px -1px rgba(0, 0, 0, 0.06);
528
+ list-style: none;
529
+ margin: 0;
530
+ padding: var(--space-xs, 0.25rem);
531
+ z-index: calc(var(--header-z-index) + 1);
532
+ animation: dropdown-fade-in 150ms ease-out;
533
+ }
534
+
535
+ .global-header__dropdown-item {
536
+ display: flex;
537
+ align-items: center;
538
+ gap: var(--space-xs, 0.25rem);
539
+ padding: var(--space-sm, 0.5rem) var(--space-md, 1rem);
540
+ color: var(--header-text-color);
541
+ text-decoration: none;
542
+ font-size: var(--text-sm, 0.875rem);
543
+ border-radius: var(--radius-sm, 0.25rem);
544
+ transition: background var(--transition-fast, 150ms ease);
545
+ white-space: nowrap;
546
+ /* Touch target minimum */
547
+ min-height: var(--touch-target-min, 44px);
548
+ }
549
+
550
+ .global-header__dropdown-item:hover {
551
+ background: var(--color-bg-subtle, rgba(0, 0, 0, 0.05));
552
+ color: var(--color-primary, #3b82f6);
553
+ }
554
+
555
+ .global-header__dropdown-item:focus-visible {
556
+ outline: 2px solid var(--color-primary, #3b82f6);
557
+ outline-offset: -2px;
558
+ }
559
+
560
+ .global-header__actions {
561
+ display: none;
562
+ align-items: center;
563
+ gap: var(--space-sm, 0.5rem);
564
+ }
565
+
566
+ .global-header__hamburger {
567
+ display: flex;
568
+ align-items: center;
569
+ justify-content: center;
570
+ /* Touch target minimum */
571
+ min-width: var(--touch-target-min, 44px);
572
+ min-height: var(--touch-target-min, 44px);
573
+ padding: var(--space-sm, 0.5rem);
574
+ background: transparent;
575
+ border: none;
576
+ cursor: pointer;
577
+ -webkit-tap-highlight-color: transparent;
578
+ }
579
+
580
+ .global-header__hamburger:focus-visible {
581
+ outline: 2px solid var(--color-primary, #3b82f6);
582
+ outline-offset: 2px;
583
+ border-radius: var(--radius-sm, 0.25rem);
584
+ }
585
+
586
+ .global-header__hamburger-icon {
587
+ position: relative;
588
+ width: 1.5rem;
589
+ height: 1.25rem;
590
+ }
591
+
592
+ .global-header__hamburger-icon span {
593
+ position: absolute;
594
+ left: 0;
595
+ width: 100%;
596
+ height: 2px;
597
+ background: var(--header-text-color);
598
+ border-radius: var(--radius-full, 9999px);
599
+ transition: all var(--transition-fast, 150ms ease);
600
+ }
601
+
602
+ .global-header__hamburger-icon span:nth-child(1) {
603
+ top: 0;
604
+ }
605
+
606
+ .global-header__hamburger-icon span:nth-child(2) {
607
+ top: 50%;
608
+ transform: translateY(-50%);
609
+ }
610
+
611
+ .global-header__hamburger-icon span:nth-child(3) {
612
+ bottom: 0;
613
+ }
614
+
615
+ .global-header__hamburger-icon--open span:nth-child(1) {
616
+ top: 50%;
617
+ transform: translateY(-50%) rotate(45deg);
618
+ }
619
+
620
+ .global-header__hamburger-icon--open span:nth-child(2) {
621
+ opacity: 0;
622
+ }
623
+
624
+ .global-header__hamburger-icon--open span:nth-child(3) {
625
+ bottom: 50%;
626
+ transform: translateY(50%) rotate(-45deg);
627
+ }
628
+
629
+ .global-header__mobile-menu {
630
+ position: absolute;
631
+ top: 100%;
632
+ left: 0;
633
+ right: 0;
634
+ width: 100%;
635
+ background: var(--color-bg, #ffffff);
636
+ border-top: 1px solid var(--header-border-color);
637
+ border-bottom: 1px solid var(--header-border-color);
638
+ padding: var(--space-lg, 1.5rem);
639
+ overflow-y: auto;
640
+ max-height: calc(100vh - var(--header-height));
641
+ z-index: calc(var(--header-z-index) - 1);
642
+ animation: slide-down 200ms ease-out;
643
+ font-family: inherit;
644
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
645
+ }
646
+
647
+ .global-header__mobile-nav-list {
648
+ list-style: none;
649
+ margin: 0;
650
+ padding: 0;
651
+ display: flex;
652
+ flex-direction: column;
653
+ gap: var(--space-xs, 0.25rem);
654
+ }
655
+
656
+ .global-header__mobile-nav-item {
657
+ display: flex;
658
+ flex-direction: column;
659
+ }
660
+
661
+ .global-header__mobile-nav-link {
662
+ display: flex;
663
+ align-items: center;
664
+ gap: var(--space-sm, 0.5rem);
665
+ padding: var(--space-md, 1rem);
666
+ color: var(--header-text-color);
667
+ text-decoration: none;
668
+ font-size: var(--text-base, 1rem);
669
+ border-radius: var(--radius-md, 0.375rem);
670
+ transition: background var(--transition-fast, 150ms ease);
671
+ /* Touch target minimum */
672
+ min-height: var(--touch-target-min, 44px);
673
+ }
674
+
675
+ .global-header__mobile-nav-link:hover {
676
+ background: var(--color-bg-subtle, rgba(0, 0, 0, 0.05));
677
+ }
678
+
679
+ .global-header__mobile-nav-link:focus-visible {
680
+ outline: 2px solid var(--color-primary, #3b82f6);
681
+ outline-offset: -2px;
682
+ }
683
+
684
+ .global-header__mobile-nav-group {
685
+ display: flex;
686
+ flex-direction: column;
687
+ gap: var(--space-xs, 0.25rem);
688
+ }
689
+
690
+ .global-header__mobile-nav-header {
691
+ display: flex;
692
+ align-items: center;
693
+ gap: var(--space-xs, 0.25rem);
694
+ }
695
+
696
+ .global-header__mobile-nav-link--parent {
697
+ flex: 1;
698
+ padding-right: var(--space-xs, 0.25rem);
699
+ }
700
+
701
+ .global-header__mobile-toggle {
702
+ display: flex;
703
+ align-items: center;
704
+ justify-content: center;
705
+ min-width: var(--touch-target-min, 44px);
706
+ min-height: var(--touch-target-min, 44px);
707
+ padding: var(--space-sm, 0.5rem);
708
+ background: transparent;
709
+ border: 1px solid var(--color-border, #e5e7eb);
710
+ border-radius: var(--radius-md, 0.375rem);
711
+ cursor: pointer;
712
+ transition: all var(--transition-fast, 150ms ease);
713
+ color: var(--header-text-color);
714
+ }
715
+
716
+ .global-header__mobile-toggle:hover {
717
+ background: var(--color-bg-subtle, rgba(0, 0, 0, 0.05));
718
+ border-color: var(--color-primary, #3b82f6);
719
+ }
720
+
721
+ .global-header__mobile-toggle:focus-visible {
722
+ outline: 2px solid var(--color-primary, #3b82f6);
723
+ outline-offset: 2px;
724
+ }
725
+
726
+ .global-header__mobile-toggle-icon {
727
+ font-size: 1.25rem;
728
+ font-weight: 600;
729
+ line-height: 1;
730
+ }
731
+
732
+ .global-header__mobile-nav-sublist {
733
+ list-style: none;
734
+ margin: 0;
735
+ padding-left: var(--space-lg, 1.5rem);
736
+ display: flex;
737
+ flex-direction: column;
738
+ gap: var(--space-xs, 0.25rem);
739
+ margin-top: var(--space-xs, 0.25rem);
740
+ animation: slide-down 200ms ease-out;
741
+ }
742
+
743
+ .global-header__mobile-nav-link--child {
744
+ font-size: var(--text-sm, 0.875rem);
745
+ padding: var(--space-sm, 0.5rem) var(--space-md, 1rem);
746
+ }
747
+
748
+ .global-header__mobile-actions {
749
+ margin-top: var(--space-lg, 1.5rem);
750
+ padding-top: var(--space-lg, 1.5rem);
751
+ border-top: 1px solid var(--header-border-color);
752
+ display: flex;
753
+ flex-direction: column;
754
+ gap: var(--space-sm, 0.5rem);
755
+ }
756
+
757
+ @keyframes slide-down {
758
+ from {
759
+ opacity: 0;
760
+ transform: translateY(-1rem);
761
+ }
762
+ to {
763
+ opacity: 1;
764
+ transform: translateY(0);
765
+ }
766
+ }
767
+
768
+ @keyframes dropdown-fade-in {
769
+ from {
770
+ opacity: 0;
771
+ transform: translateY(-0.5rem);
772
+ }
773
+ to {
774
+ opacity: 1;
775
+ transform: translateY(0);
776
+ }
777
+ }
778
+
779
+ /* Desktop breakpoint */
780
+ @media (min-width: 768px) {
781
+ .global-header__nav {
782
+ display: flex;
783
+ }
784
+
785
+ .global-header__actions {
786
+ display: flex;
787
+ }
788
+
789
+ .global-header__hamburger {
790
+ display: none;
791
+ }
792
+
793
+ .global-header__mobile-menu {
794
+ display: none;
795
+ }
796
+ }
797
+
798
+ /* Dark mode support */
799
+ @media (prefers-color-scheme: dark) {
800
+ .global-header {
801
+ --header-bg: var(--color-bg-dark, #1f2937);
802
+ --header-text-color: var(--color-text-dark, #f9fafb);
803
+ --header-border-color: var(--color-border-dark, #374151);
804
+ }
805
+ }
806
+ </style>