@sc4rfurryx/proteusjs 1.0.0 → 1.1.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 (65) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +331 -77
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/adapters/react.d.ts +139 -0
  5. package/dist/adapters/react.esm.js +848 -0
  6. package/dist/adapters/react.esm.js.map +1 -0
  7. package/dist/adapters/svelte.d.ts +181 -0
  8. package/dist/adapters/svelte.esm.js +908 -0
  9. package/dist/adapters/svelte.esm.js.map +1 -0
  10. package/dist/adapters/vue.d.ts +205 -0
  11. package/dist/adapters/vue.esm.js +872 -0
  12. package/dist/adapters/vue.esm.js.map +1 -0
  13. package/dist/modules/a11y-audit.d.ts +39 -0
  14. package/dist/modules/a11y-audit.esm.js +509 -0
  15. package/dist/modules/a11y-audit.esm.js.map +1 -0
  16. package/dist/modules/a11y-primitives.d.ts +69 -0
  17. package/dist/modules/a11y-primitives.esm.js +445 -0
  18. package/dist/modules/a11y-primitives.esm.js.map +1 -0
  19. package/dist/modules/anchor.d.ts +29 -0
  20. package/dist/modules/anchor.esm.js +218 -0
  21. package/dist/modules/anchor.esm.js.map +1 -0
  22. package/dist/modules/container.d.ts +60 -0
  23. package/dist/modules/container.esm.js +194 -0
  24. package/dist/modules/container.esm.js.map +1 -0
  25. package/dist/modules/perf.d.ts +82 -0
  26. package/dist/modules/perf.esm.js +257 -0
  27. package/dist/modules/perf.esm.js.map +1 -0
  28. package/dist/modules/popover.d.ts +33 -0
  29. package/dist/modules/popover.esm.js +191 -0
  30. package/dist/modules/popover.esm.js.map +1 -0
  31. package/dist/modules/scroll.d.ts +43 -0
  32. package/dist/modules/scroll.esm.js +195 -0
  33. package/dist/modules/scroll.esm.js.map +1 -0
  34. package/dist/modules/transitions.d.ts +35 -0
  35. package/dist/modules/transitions.esm.js +120 -0
  36. package/dist/modules/transitions.esm.js.map +1 -0
  37. package/dist/modules/typography.d.ts +72 -0
  38. package/dist/modules/typography.esm.js +168 -0
  39. package/dist/modules/typography.esm.js.map +1 -0
  40. package/dist/proteus.cjs.js +2332 -12
  41. package/dist/proteus.cjs.js.map +1 -1
  42. package/dist/proteus.d.ts +561 -12
  43. package/dist/proteus.esm.js +2323 -12
  44. package/dist/proteus.esm.js.map +1 -1
  45. package/dist/proteus.esm.min.js +3 -3
  46. package/dist/proteus.esm.min.js.map +1 -1
  47. package/dist/proteus.js +2332 -12
  48. package/dist/proteus.js.map +1 -1
  49. package/dist/proteus.min.js +3 -3
  50. package/dist/proteus.min.js.map +1 -1
  51. package/package.json +61 -4
  52. package/src/adapters/react.ts +264 -0
  53. package/src/adapters/svelte.ts +321 -0
  54. package/src/adapters/vue.ts +268 -0
  55. package/src/index.ts +33 -6
  56. package/src/modules/a11y-audit/index.ts +608 -0
  57. package/src/modules/a11y-primitives/index.ts +554 -0
  58. package/src/modules/anchor/index.ts +257 -0
  59. package/src/modules/container/index.ts +230 -0
  60. package/src/modules/perf/index.ts +291 -0
  61. package/src/modules/popover/index.ts +238 -0
  62. package/src/modules/scroll/index.ts +251 -0
  63. package/src/modules/transitions/index.ts +145 -0
  64. package/src/modules/typography/index.ts +239 -0
  65. package/src/utils/version.ts +1 -1
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * ProteusJS v1.0.0
2
+ * ProteusJS v1.1.0
3
3
  * Shape-shifting responsive design that adapts like the sea god himself
4
- * (c) 2024 sc4rfurry
4
+ * (c) 2025 sc4rfurry
5
5
  * Released under the MIT License
6
6
  */
7
7
  /**
@@ -15158,7 +15158,7 @@ class BrowserPolyfills {
15158
15158
  /**
15159
15159
  * Check if the current environment supports ProteusJS features
15160
15160
  */
15161
- function isSupported() {
15161
+ function isSupported$1() {
15162
15162
  const support = getSupportInfo();
15163
15163
  return support.resizeObserver && support.intersectionObserver;
15164
15164
  }
@@ -15225,7 +15225,7 @@ function checkPassiveEventListenerSupport() {
15225
15225
  /**
15226
15226
  * Version utilities for ProteusJS
15227
15227
  */
15228
- const version = '1.0.0';
15228
+ const version = '1.1.0';
15229
15229
 
15230
15230
  /**
15231
15231
  * ProteusJS - Main library class
@@ -15357,7 +15357,7 @@ class ProteusJS {
15357
15357
  return this;
15358
15358
  }
15359
15359
  // Check browser support
15360
- if (!isSupported()) {
15360
+ if (!isSupported$1()) {
15361
15361
  logger.error('Browser not supported. Missing required APIs.');
15362
15362
  return this;
15363
15363
  }
@@ -15728,7 +15728,7 @@ class ProteusJS {
15728
15728
  * Check if browser is supported
15729
15729
  */
15730
15730
  static isSupported() {
15731
- return isSupported();
15731
+ return isSupported$1();
15732
15732
  }
15733
15733
  /**
15734
15734
  * Get or create global instance
@@ -15989,17 +15989,2328 @@ class ProteusJS {
15989
15989
  ProteusJS.instance = null;
15990
15990
 
15991
15991
  /**
15992
- * ProteusJS - Dynamic Responsive Design Library
15992
+ * @sc4rfurryx/proteusjs/transitions
15993
+ * View Transitions API wrapper with safe fallbacks
15994
+ *
15995
+ * @version 1.1.0
15996
+ * @author sc4rfurry
15997
+ * @license MIT
15998
+ */
15999
+ /**
16000
+ * One API for animating DOM state changes and cross-document navigations
16001
+ * using the View Transitions API with safe fallbacks.
16002
+ */
16003
+ async function transition(run, opts = {}) {
16004
+ const { name, duration = 300, onBefore, onAfter, allowInterrupt = true } = opts;
16005
+ // Check for View Transitions API support
16006
+ const hasViewTransitions = 'startViewTransition' in document;
16007
+ if (onBefore) {
16008
+ onBefore();
16009
+ }
16010
+ if (!hasViewTransitions) {
16011
+ // Fallback: run immediately without transitions
16012
+ try {
16013
+ await run();
16014
+ }
16015
+ finally {
16016
+ if (onAfter) {
16017
+ onAfter();
16018
+ }
16019
+ }
16020
+ return;
16021
+ }
16022
+ // Use native View Transitions API
16023
+ try {
16024
+ const viewTransition = document.startViewTransition(async () => {
16025
+ await run();
16026
+ });
16027
+ // Add CSS view-transition-name if name provided
16028
+ if (name) {
16029
+ const style = document.createElement('style');
16030
+ style.textContent = `
16031
+ ::view-transition-old(${name}),
16032
+ ::view-transition-new(${name}) {
16033
+ animation-duration: ${duration}ms;
16034
+ }
16035
+ `;
16036
+ document.head.appendChild(style);
16037
+ // Clean up style after transition
16038
+ viewTransition.finished.finally(() => {
16039
+ style.remove();
16040
+ });
16041
+ }
16042
+ await viewTransition.finished;
16043
+ }
16044
+ catch (error) {
16045
+ console.warn('View transition failed, falling back to immediate execution:', error);
16046
+ await run();
16047
+ }
16048
+ finally {
16049
+ if (onAfter) {
16050
+ onAfter();
16051
+ }
16052
+ }
16053
+ }
16054
+ /**
16055
+ * MPA-friendly navigation with view transitions when supported
16056
+ */
16057
+ async function navigate(url, opts = {}) {
16058
+ const { name, prerender = false } = opts;
16059
+ // Optional prerender hint (basic implementation)
16060
+ if (prerender && 'speculation' in HTMLScriptElement.prototype) {
16061
+ const script = document.createElement('script');
16062
+ script.type = 'speculationrules';
16063
+ script.textContent = JSON.stringify({
16064
+ prerender: [{ where: { href_matches: url } }]
16065
+ });
16066
+ document.head.appendChild(script);
16067
+ }
16068
+ // Check for View Transitions API support
16069
+ const hasViewTransitions = 'startViewTransition' in document;
16070
+ if (!hasViewTransitions) {
16071
+ // Fallback: normal navigation
16072
+ window.location.href = url;
16073
+ return;
16074
+ }
16075
+ try {
16076
+ // Use view transitions for navigation
16077
+ const viewTransition = document.startViewTransition(() => {
16078
+ window.location.href = url;
16079
+ });
16080
+ if (name) {
16081
+ const style = document.createElement('style');
16082
+ style.textContent = `
16083
+ ::view-transition-old(${name}),
16084
+ ::view-transition-new(${name}) {
16085
+ animation-duration: 300ms;
16086
+ }
16087
+ `;
16088
+ document.head.appendChild(style);
16089
+ }
16090
+ await viewTransition.finished;
16091
+ }
16092
+ catch (error) {
16093
+ console.warn('View transition navigation failed, falling back to normal navigation:', error);
16094
+ window.location.href = url;
16095
+ }
16096
+ }
16097
+ // Export default object for convenience
16098
+ var index$g = {
16099
+ transition,
16100
+ navigate
16101
+ };
16102
+
16103
+ var index$h = /*#__PURE__*/Object.freeze({
16104
+ __proto__: null,
16105
+ default: index$g,
16106
+ navigate: navigate,
16107
+ transition: transition
16108
+ });
16109
+
16110
+ /**
16111
+ * @sc4rfurryx/proteusjs/scroll
16112
+ * Scroll-driven animations with CSS Scroll-Linked Animations
16113
+ *
16114
+ * @version 1.1.0
16115
+ * @author sc4rfurry
16116
+ * @license MIT
16117
+ */
16118
+ /**
16119
+ * Zero-boilerplate setup for CSS Scroll-Linked Animations with fallbacks
16120
+ */
16121
+ function scrollAnimate(target, opts) {
16122
+ const targetEl = typeof target === 'string' ? document.querySelector(target) : target;
16123
+ if (!targetEl) {
16124
+ throw new Error('Target element not found');
16125
+ }
16126
+ const { keyframes, range = ['0%', '100%'], timeline = {}, fallback = 'io' } = opts;
16127
+ const { axis = 'block', start = '0%', end = '100%' } = timeline;
16128
+ // Check for CSS Scroll-Linked Animations support
16129
+ const hasScrollTimeline = 'CSS' in window && CSS.supports('animation-timeline', 'scroll()');
16130
+ // Check for reduced motion preference
16131
+ const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
16132
+ if (prefersReducedMotion) {
16133
+ // Respect user preference - either disable or reduce animation
16134
+ if (fallback === false)
16135
+ return;
16136
+ // Apply only the end state for reduced motion
16137
+ const endKeyframe = keyframes[keyframes.length - 1];
16138
+ Object.assign(targetEl.style, endKeyframe);
16139
+ return;
16140
+ }
16141
+ if (hasScrollTimeline) {
16142
+ // Use native CSS Scroll-Linked Animations
16143
+ const timelineName = `scroll-timeline-${Math.random().toString(36).substr(2, 9)}`;
16144
+ // Create scroll timeline
16145
+ const style = document.createElement('style');
16146
+ style.textContent = `
16147
+ @scroll-timeline ${timelineName} {
16148
+ source: nearest;
16149
+ orientation: ${axis};
16150
+ scroll-offsets: ${start}, ${end};
16151
+ }
16152
+
16153
+ .scroll-animate-${timelineName} {
16154
+ animation-timeline: ${timelineName};
16155
+ animation-duration: 1ms; /* Required but ignored */
16156
+ animation-fill-mode: both;
16157
+ }
16158
+ `;
16159
+ document.head.appendChild(style);
16160
+ // Apply animation class
16161
+ targetEl.classList.add(`scroll-animate-${timelineName}`);
16162
+ // Create Web Animations API animation
16163
+ const animation = targetEl.animate(keyframes, {
16164
+ duration: 1, // Required but ignored with scroll timeline
16165
+ fill: 'both'
16166
+ });
16167
+ // Set scroll timeline (when supported)
16168
+ if ('timeline' in animation) {
16169
+ animation.timeline = new window.ScrollTimeline({
16170
+ source: document.scrollingElement,
16171
+ orientation: axis,
16172
+ scrollOffsets: [
16173
+ { target: targetEl, edge: 'start', threshold: parseFloat(start) / 100 },
16174
+ { target: targetEl, edge: 'end', threshold: parseFloat(end) / 100 }
16175
+ ]
16176
+ });
16177
+ }
16178
+ }
16179
+ else if (fallback === 'io') {
16180
+ // Fallback using Intersection Observer
16181
+ let animation = null;
16182
+ const observer = new IntersectionObserver((entries) => {
16183
+ entries.forEach(entry => {
16184
+ const progress = Math.max(0, Math.min(1, entry.intersectionRatio));
16185
+ if (!animation) {
16186
+ animation = targetEl.animate(keyframes, {
16187
+ duration: 1000,
16188
+ fill: 'both'
16189
+ });
16190
+ animation.pause();
16191
+ }
16192
+ // Update animation progress based on intersection
16193
+ animation.currentTime = progress * 1000;
16194
+ });
16195
+ }, {
16196
+ threshold: Array.from({ length: 101 }, (_, i) => i / 100) // 0 to 1 in 0.01 steps
16197
+ });
16198
+ observer.observe(targetEl);
16199
+ // Store cleanup function
16200
+ targetEl._scrollAnimateCleanup = () => {
16201
+ observer.disconnect();
16202
+ if (animation) {
16203
+ animation.cancel();
16204
+ }
16205
+ };
16206
+ }
16207
+ }
16208
+ /**
16209
+ * Create a scroll-triggered animation that plays once when element enters viewport
16210
+ */
16211
+ function scrollTrigger(target, keyframes, options = {}) {
16212
+ const targetEl = typeof target === 'string' ? document.querySelector(target) : target;
16213
+ if (!targetEl) {
16214
+ throw new Error('Target element not found');
16215
+ }
16216
+ // Check for reduced motion preference
16217
+ const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
16218
+ if (prefersReducedMotion) {
16219
+ // Apply end state immediately
16220
+ const endKeyframe = keyframes[keyframes.length - 1];
16221
+ Object.assign(targetEl.style, endKeyframe);
16222
+ return;
16223
+ }
16224
+ const observer = new IntersectionObserver((entries) => {
16225
+ entries.forEach(entry => {
16226
+ if (entry.isIntersecting) {
16227
+ // Play animation
16228
+ targetEl.animate(keyframes, {
16229
+ duration: 600,
16230
+ easing: 'ease-out',
16231
+ fill: 'forwards',
16232
+ ...options
16233
+ });
16234
+ // Disconnect observer after first trigger
16235
+ observer.disconnect();
16236
+ }
16237
+ });
16238
+ }, {
16239
+ threshold: 0.1,
16240
+ rootMargin: '0px 0px -10% 0px'
16241
+ });
16242
+ observer.observe(targetEl);
16243
+ }
16244
+ /**
16245
+ * Parallax effect using scroll-driven animations
16246
+ */
16247
+ function parallax(target, speed = 0.5) {
16248
+ const targetEl = typeof target === 'string' ? document.querySelector(target) : target;
16249
+ if (!targetEl) {
16250
+ throw new Error('Target element not found');
16251
+ }
16252
+ // Check for reduced motion preference
16253
+ const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
16254
+ if (prefersReducedMotion)
16255
+ return;
16256
+ const keyframes = [
16257
+ { transform: `translateY(${ -100 * speed}px)` },
16258
+ { transform: `translateY(${100 * speed}px)` }
16259
+ ];
16260
+ scrollAnimate(targetEl, {
16261
+ keyframes,
16262
+ range: ['0%', '100%'],
16263
+ timeline: { axis: 'block' },
16264
+ fallback: 'io'
16265
+ });
16266
+ }
16267
+ /**
16268
+ * Cleanup function to remove scroll animations
16269
+ */
16270
+ function cleanup$2(target) {
16271
+ const targetEl = typeof target === 'string' ? document.querySelector(target) : target;
16272
+ if (!targetEl)
16273
+ return;
16274
+ // Call stored cleanup function if it exists
16275
+ if (targetEl._scrollAnimateCleanup) {
16276
+ targetEl._scrollAnimateCleanup();
16277
+ delete targetEl._scrollAnimateCleanup;
16278
+ }
16279
+ // Remove animation classes
16280
+ targetEl.classList.forEach(className => {
16281
+ if (className.startsWith('scroll-animate-')) {
16282
+ targetEl.classList.remove(className);
16283
+ }
16284
+ });
16285
+ // Cancel any running animations
16286
+ const animations = targetEl.getAnimations();
16287
+ animations.forEach(animation => animation.cancel());
16288
+ }
16289
+ // Export default object for convenience
16290
+ var index$e = {
16291
+ scrollAnimate,
16292
+ scrollTrigger,
16293
+ parallax,
16294
+ cleanup: cleanup$2
16295
+ };
16296
+
16297
+ var index$f = /*#__PURE__*/Object.freeze({
16298
+ __proto__: null,
16299
+ cleanup: cleanup$2,
16300
+ default: index$e,
16301
+ parallax: parallax,
16302
+ scrollAnimate: scrollAnimate,
16303
+ scrollTrigger: scrollTrigger
16304
+ });
16305
+
16306
+ /**
16307
+ * @sc4rfurryx/proteusjs/anchor
16308
+ * CSS Anchor Positioning utilities with robust JS fallback
16309
+ *
16310
+ * @version 1.1.0
16311
+ * @author sc4rfurry
16312
+ * @license MIT
16313
+ */
16314
+ /**
16315
+ * Declarative tethers (tooltips, callouts) via CSS Anchor Positioning when available;
16316
+ * robust JS fallback with flip/collision detection
16317
+ */
16318
+ function tether(floating, opts) {
16319
+ const floatingEl = typeof floating === 'string' ? document.querySelector(floating) : floating;
16320
+ const anchorEl = typeof opts.anchor === 'string' ? document.querySelector(opts.anchor) : opts.anchor;
16321
+ if (!floatingEl || !anchorEl) {
16322
+ throw new Error('Both floating and anchor elements must exist');
16323
+ }
16324
+ const { placement = 'bottom', align = 'center', offset = 8, strategy = 'absolute' } = opts;
16325
+ // Check for CSS Anchor Positioning support
16326
+ const hasAnchorPositioning = CSS.supports('anchor-name', 'test');
16327
+ let isDestroyed = false;
16328
+ let resizeObserver = null;
16329
+ const setupCSSAnchorPositioning = () => {
16330
+ if (!hasAnchorPositioning)
16331
+ return false;
16332
+ // Generate unique anchor name
16333
+ const anchorName = `anchor-${Math.random().toString(36).substring(2, 11)}`;
16334
+ // Set anchor name on anchor element
16335
+ anchorEl.style.setProperty('anchor-name', anchorName);
16336
+ // Position floating element using CSS anchor positioning
16337
+ const floatingStyle = floatingEl;
16338
+ floatingStyle.style.position = strategy;
16339
+ floatingStyle.style.setProperty('position-anchor', anchorName);
16340
+ // Set position based on placement
16341
+ switch (placement) {
16342
+ case 'top':
16343
+ floatingStyle.style.bottom = `anchor(bottom, ${offset}px)`;
16344
+ break;
16345
+ case 'bottom':
16346
+ floatingStyle.style.top = `anchor(bottom, ${offset}px)`;
16347
+ break;
16348
+ case 'left':
16349
+ floatingStyle.style.right = `anchor(left, ${offset}px)`;
16350
+ break;
16351
+ case 'right':
16352
+ floatingStyle.style.left = `anchor(right, ${offset}px)`;
16353
+ break;
16354
+ }
16355
+ // Set alignment
16356
+ if (placement === 'top' || placement === 'bottom') {
16357
+ switch (align) {
16358
+ case 'start':
16359
+ floatingStyle.style.left = 'anchor(left)';
16360
+ break;
16361
+ case 'center':
16362
+ floatingStyle.style.left = 'anchor(center)';
16363
+ floatingStyle.style.transform = 'translateX(-50%)';
16364
+ break;
16365
+ case 'end':
16366
+ floatingStyle.style.right = 'anchor(right)';
16367
+ break;
16368
+ }
16369
+ }
16370
+ else {
16371
+ switch (align) {
16372
+ case 'start':
16373
+ floatingStyle.style.top = 'anchor(top)';
16374
+ break;
16375
+ case 'center':
16376
+ floatingStyle.style.top = 'anchor(center)';
16377
+ floatingStyle.style.transform = 'translateY(-50%)';
16378
+ break;
16379
+ case 'end':
16380
+ floatingStyle.style.bottom = 'anchor(bottom)';
16381
+ break;
16382
+ }
16383
+ }
16384
+ return true;
16385
+ };
16386
+ const calculatePosition = () => {
16387
+ const anchorRect = anchorEl.getBoundingClientRect();
16388
+ const floatingRect = floatingEl.getBoundingClientRect();
16389
+ const viewport = {
16390
+ width: window.innerWidth,
16391
+ height: window.innerHeight
16392
+ };
16393
+ let finalPlacement = placement;
16394
+ let x = 0;
16395
+ let y = 0;
16396
+ // Calculate base position
16397
+ switch (finalPlacement) {
16398
+ case 'top':
16399
+ x = anchorRect.left;
16400
+ y = anchorRect.top - floatingRect.height - offset;
16401
+ break;
16402
+ case 'bottom':
16403
+ x = anchorRect.left;
16404
+ y = anchorRect.bottom + offset;
16405
+ break;
16406
+ case 'left':
16407
+ x = anchorRect.left - floatingRect.width - offset;
16408
+ y = anchorRect.top;
16409
+ break;
16410
+ case 'right':
16411
+ x = anchorRect.right + offset;
16412
+ y = anchorRect.top;
16413
+ break;
16414
+ case 'auto': {
16415
+ // Choose best placement based on available space
16416
+ const spaces = {
16417
+ top: anchorRect.top,
16418
+ bottom: viewport.height - anchorRect.bottom,
16419
+ left: anchorRect.left,
16420
+ right: viewport.width - anchorRect.right
16421
+ };
16422
+ const bestPlacement = Object.entries(spaces).reduce((a, b) => spaces[a[0]] > spaces[b[0]] ? a : b)[0];
16423
+ finalPlacement = bestPlacement;
16424
+ return calculatePosition(); // Recursive call with determined placement
16425
+ }
16426
+ }
16427
+ // Apply alignment
16428
+ if (finalPlacement === 'top' || finalPlacement === 'bottom') {
16429
+ switch (align) {
16430
+ case 'start':
16431
+ // x already set correctly
16432
+ break;
16433
+ case 'center':
16434
+ x = anchorRect.left + (anchorRect.width - floatingRect.width) / 2;
16435
+ break;
16436
+ case 'end':
16437
+ x = anchorRect.right - floatingRect.width;
16438
+ break;
16439
+ }
16440
+ }
16441
+ else {
16442
+ switch (align) {
16443
+ case 'start':
16444
+ // y already set correctly
16445
+ break;
16446
+ case 'center':
16447
+ y = anchorRect.top + (anchorRect.height - floatingRect.height) / 2;
16448
+ break;
16449
+ case 'end':
16450
+ y = anchorRect.bottom - floatingRect.height;
16451
+ break;
16452
+ }
16453
+ }
16454
+ // Collision detection and adjustment
16455
+ if (x < 0)
16456
+ x = 8;
16457
+ if (y < 0)
16458
+ y = 8;
16459
+ if (x + floatingRect.width > viewport.width) {
16460
+ x = viewport.width - floatingRect.width - 8;
16461
+ }
16462
+ if (y + floatingRect.height > viewport.height) {
16463
+ y = viewport.height - floatingRect.height - 8;
16464
+ }
16465
+ return { x, y };
16466
+ };
16467
+ const updatePosition = () => {
16468
+ if (isDestroyed)
16469
+ return;
16470
+ if (!hasAnchorPositioning) {
16471
+ const { x, y } = calculatePosition();
16472
+ const floatingStyle = floatingEl;
16473
+ floatingStyle.style.position = strategy;
16474
+ floatingStyle.style.left = `${x}px`;
16475
+ floatingStyle.style.top = `${y}px`;
16476
+ }
16477
+ };
16478
+ const setupJSFallback = () => {
16479
+ updatePosition();
16480
+ // Set up observers for position updates
16481
+ resizeObserver = new ResizeObserver(updatePosition);
16482
+ resizeObserver.observe(anchorEl);
16483
+ resizeObserver.observe(floatingEl);
16484
+ window.addEventListener('scroll', updatePosition, { passive: true });
16485
+ window.addEventListener('resize', updatePosition, { passive: true });
16486
+ };
16487
+ const destroy = () => {
16488
+ isDestroyed = true;
16489
+ if (resizeObserver) {
16490
+ resizeObserver.disconnect();
16491
+ resizeObserver = null;
16492
+ }
16493
+ window.removeEventListener('scroll', updatePosition);
16494
+ window.removeEventListener('resize', updatePosition);
16495
+ // Clean up CSS anchor positioning
16496
+ if (hasAnchorPositioning) {
16497
+ anchorEl.style.removeProperty('anchor-name');
16498
+ const floatingStyle = floatingEl;
16499
+ floatingStyle.style.removeProperty('position-anchor');
16500
+ floatingStyle.style.position = '';
16501
+ }
16502
+ };
16503
+ // Initialize
16504
+ if (!setupCSSAnchorPositioning()) {
16505
+ setupJSFallback();
16506
+ }
16507
+ return {
16508
+ destroy
16509
+ };
16510
+ }
16511
+ // Export default object for convenience
16512
+ var index$c = {
16513
+ tether
16514
+ };
16515
+
16516
+ var index$d = /*#__PURE__*/Object.freeze({
16517
+ __proto__: null,
16518
+ default: index$c,
16519
+ tether: tether
16520
+ });
16521
+
16522
+ /**
16523
+ * @sc4rfurryx/proteusjs/popover
16524
+ * HTML Popover API wrapper with robust focus/inert handling
16525
+ *
16526
+ * @version 1.1.0
16527
+ * @author sc4rfurry
16528
+ * @license MIT
16529
+ */
16530
+ /**
16531
+ * Unified API for menus, tooltips, and dialogs using the native Popover API
16532
+ * with robust focus/inert handling
16533
+ */
16534
+ function attach(trigger, panel, opts = {}) {
16535
+ const triggerEl = typeof trigger === 'string' ? document.querySelector(trigger) : trigger;
16536
+ const panelEl = typeof panel === 'string' ? document.querySelector(panel) : panel;
16537
+ if (!triggerEl || !panelEl) {
16538
+ throw new Error('Both trigger and panel elements must exist');
16539
+ }
16540
+ const { type = 'menu', trapFocus = type === 'dialog', restoreFocus = true, closeOnEscape = true, onOpen, onClose } = opts;
16541
+ let isOpen = false;
16542
+ let previousFocus = null;
16543
+ let focusTrap = null;
16544
+ // Check for native Popover API support
16545
+ const hasPopoverAPI = 'popover' in HTMLElement.prototype;
16546
+ // Set up ARIA attributes
16547
+ const setupAria = () => {
16548
+ const panelId = panelEl.id || `popover-${Math.random().toString(36).substr(2, 9)}`;
16549
+ panelEl.id = panelId;
16550
+ triggerEl.setAttribute('aria-expanded', 'false');
16551
+ triggerEl.setAttribute('aria-controls', panelId);
16552
+ if (type === 'menu') {
16553
+ triggerEl.setAttribute('aria-haspopup', 'menu');
16554
+ panelEl.setAttribute('role', 'menu');
16555
+ }
16556
+ else if (type === 'dialog') {
16557
+ triggerEl.setAttribute('aria-haspopup', 'dialog');
16558
+ panelEl.setAttribute('role', 'dialog');
16559
+ panelEl.setAttribute('aria-modal', 'true');
16560
+ }
16561
+ else if (type === 'tooltip') {
16562
+ triggerEl.setAttribute('aria-describedby', panelId);
16563
+ panelEl.setAttribute('role', 'tooltip');
16564
+ }
16565
+ };
16566
+ // Set up native popover if supported
16567
+ const setupNativePopover = () => {
16568
+ if (hasPopoverAPI) {
16569
+ panelEl.popover = type === 'dialog' ? 'manual' : 'auto';
16570
+ triggerEl.setAttribute('popovertarget', panelEl.id);
16571
+ }
16572
+ };
16573
+ // Focus trap implementation
16574
+ class FocusTrap {
16575
+ constructor(container) {
16576
+ this.container = container;
16577
+ this.focusableElements = [];
16578
+ this.handleKeyDown = (e) => {
16579
+ if (e.key !== 'Tab')
16580
+ return;
16581
+ const firstElement = this.focusableElements[0];
16582
+ const lastElement = this.focusableElements[this.focusableElements.length - 1];
16583
+ if (e.shiftKey) {
16584
+ if (document.activeElement === firstElement) {
16585
+ e.preventDefault();
16586
+ lastElement.focus();
16587
+ }
16588
+ }
16589
+ else {
16590
+ if (document.activeElement === lastElement) {
16591
+ e.preventDefault();
16592
+ firstElement.focus();
16593
+ }
16594
+ }
16595
+ };
16596
+ this.updateFocusableElements();
16597
+ }
16598
+ updateFocusableElements() {
16599
+ const selector = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
16600
+ this.focusableElements = Array.from(this.container.querySelectorAll(selector));
16601
+ }
16602
+ activate() {
16603
+ this.updateFocusableElements();
16604
+ if (this.focusableElements.length > 0) {
16605
+ this.focusableElements[0].focus();
16606
+ }
16607
+ document.addEventListener('keydown', this.handleKeyDown);
16608
+ }
16609
+ deactivate() {
16610
+ document.removeEventListener('keydown', this.handleKeyDown);
16611
+ }
16612
+ }
16613
+ const open = () => {
16614
+ if (isOpen)
16615
+ return;
16616
+ if (restoreFocus) {
16617
+ previousFocus = document.activeElement;
16618
+ }
16619
+ if (hasPopoverAPI) {
16620
+ panelEl.showPopover();
16621
+ }
16622
+ else {
16623
+ panelEl.style.display = 'block';
16624
+ panelEl.setAttribute('data-popover-open', 'true');
16625
+ }
16626
+ triggerEl.setAttribute('aria-expanded', 'true');
16627
+ isOpen = true;
16628
+ if (trapFocus) {
16629
+ focusTrap = new FocusTrap(panelEl);
16630
+ focusTrap.activate();
16631
+ }
16632
+ if (onOpen) {
16633
+ onOpen();
16634
+ }
16635
+ };
16636
+ const close = () => {
16637
+ if (!isOpen)
16638
+ return;
16639
+ if (hasPopoverAPI) {
16640
+ panelEl.hidePopover();
16641
+ }
16642
+ else {
16643
+ panelEl.style.display = 'none';
16644
+ panelEl.removeAttribute('data-popover-open');
16645
+ }
16646
+ triggerEl.setAttribute('aria-expanded', 'false');
16647
+ isOpen = false;
16648
+ if (focusTrap) {
16649
+ focusTrap.deactivate();
16650
+ focusTrap = null;
16651
+ }
16652
+ if (restoreFocus && previousFocus) {
16653
+ previousFocus.focus();
16654
+ previousFocus = null;
16655
+ }
16656
+ if (onClose) {
16657
+ onClose();
16658
+ }
16659
+ };
16660
+ const toggle = () => {
16661
+ if (isOpen) {
16662
+ close();
16663
+ }
16664
+ else {
16665
+ open();
16666
+ }
16667
+ };
16668
+ const handleKeyDown = (e) => {
16669
+ if (closeOnEscape && e.key === 'Escape' && isOpen) {
16670
+ e.preventDefault();
16671
+ close();
16672
+ }
16673
+ };
16674
+ const handleClick = (e) => {
16675
+ e.preventDefault();
16676
+ toggle();
16677
+ };
16678
+ const destroy = () => {
16679
+ triggerEl.removeEventListener('click', handleClick);
16680
+ document.removeEventListener('keydown', handleKeyDown);
16681
+ if (focusTrap) {
16682
+ focusTrap.deactivate();
16683
+ }
16684
+ if (isOpen) {
16685
+ close();
16686
+ }
16687
+ };
16688
+ // Initialize
16689
+ setupAria();
16690
+ setupNativePopover();
16691
+ triggerEl.addEventListener('click', handleClick);
16692
+ document.addEventListener('keydown', handleKeyDown);
16693
+ return {
16694
+ open,
16695
+ close,
16696
+ toggle,
16697
+ destroy
16698
+ };
16699
+ }
16700
+ // Export default object for convenience
16701
+ var index$a = {
16702
+ attach
16703
+ };
16704
+
16705
+ var index$b = /*#__PURE__*/Object.freeze({
16706
+ __proto__: null,
16707
+ attach: attach,
16708
+ default: index$a
16709
+ });
16710
+
16711
+ /**
16712
+ * @sc4rfurryx/proteusjs/container
16713
+ * Container/Style Query helpers with visualization devtools
16714
+ *
16715
+ * @version 1.1.0
16716
+ * @author sc4rfurry
16717
+ * @license MIT
16718
+ */
16719
+ /**
16720
+ * Sugar on native container queries with dev visualization
16721
+ */
16722
+ function defineContainer(target, name, opts = {}) {
16723
+ const targetEl = typeof target === 'string' ? document.querySelector(target) : target;
16724
+ if (!targetEl) {
16725
+ throw new Error('Target element not found');
16726
+ }
16727
+ const { type = 'size', inlineSize: _inlineSize = true } = opts;
16728
+ const containerName = name || `container-${Math.random().toString(36).substring(2, 11)}`;
16729
+ // Apply container properties
16730
+ const element = targetEl;
16731
+ element.style.containerName = containerName;
16732
+ element.style.containerType = type;
16733
+ // Warn if containment settings are missing
16734
+ const computedStyle = getComputedStyle(element);
16735
+ if (!computedStyle.contain || computedStyle.contain === 'none') {
16736
+ console.warn(`Container "${containerName}" may need explicit containment settings for optimal performance`);
16737
+ }
16738
+ // Dev overlay (only in development)
16739
+ if (process.env['NODE_ENV'] === 'development' || window.__PROTEUS_DEV__) {
16740
+ createDevOverlay(element, containerName);
16741
+ }
16742
+ }
16743
+ /**
16744
+ * Create development overlay showing container bounds and breakpoints
16745
+ */
16746
+ function createDevOverlay(element, name) {
16747
+ const overlay = document.createElement('div');
16748
+ overlay.className = 'proteus-container-overlay';
16749
+ overlay.style.cssText = `
16750
+ position: absolute;
16751
+ top: 0;
16752
+ left: 0;
16753
+ right: 0;
16754
+ bottom: 0;
16755
+ pointer-events: none;
16756
+ border: 2px dashed rgba(255, 0, 255, 0.5);
16757
+ background: rgba(255, 0, 255, 0.05);
16758
+ z-index: 9999;
16759
+ font-family: monospace;
16760
+ font-size: 12px;
16761
+ color: #ff00ff;
16762
+ `;
16763
+ const label = document.createElement('div');
16764
+ label.style.cssText = `
16765
+ position: absolute;
16766
+ top: -20px;
16767
+ left: 0;
16768
+ background: rgba(255, 0, 255, 0.9);
16769
+ color: white;
16770
+ padding: 2px 6px;
16771
+ border-radius: 3px;
16772
+ font-size: 10px;
16773
+ white-space: nowrap;
16774
+ `;
16775
+ label.textContent = `Container: ${name}`;
16776
+ const sizeInfo = document.createElement('div');
16777
+ sizeInfo.style.cssText = `
16778
+ position: absolute;
16779
+ bottom: 2px;
16780
+ right: 2px;
16781
+ background: rgba(0, 0, 0, 0.7);
16782
+ color: white;
16783
+ padding: 2px 4px;
16784
+ border-radius: 2px;
16785
+ font-size: 10px;
16786
+ `;
16787
+ overlay.appendChild(label);
16788
+ overlay.appendChild(sizeInfo);
16789
+ // Position overlay relative to container
16790
+ if (getComputedStyle(element).position === 'static') {
16791
+ element.style.position = 'relative';
16792
+ }
16793
+ element.appendChild(overlay);
16794
+ // Update size info
16795
+ const updateSizeInfo = () => {
16796
+ const rect = element.getBoundingClientRect();
16797
+ sizeInfo.textContent = `${Math.round(rect.width)}×${Math.round(rect.height)}`;
16798
+ };
16799
+ updateSizeInfo();
16800
+ // Update on resize
16801
+ if ('ResizeObserver' in window) {
16802
+ const resizeObserver = new ResizeObserver(updateSizeInfo);
16803
+ resizeObserver.observe(element);
16804
+ }
16805
+ // Store cleanup function
16806
+ element._proteusContainerCleanup = () => {
16807
+ overlay.remove();
16808
+ };
16809
+ }
16810
+ /**
16811
+ * Helper to create container query CSS rules
16812
+ */
16813
+ function createContainerQuery(containerName, condition, styles) {
16814
+ const cssRules = Object.entries(styles)
16815
+ .map(([property, value]) => ` ${property}: ${value};`)
16816
+ .join('\n');
16817
+ return `@container ${containerName} (${condition}) {\n${cssRules}\n}`;
16818
+ }
16819
+ /**
16820
+ * Apply container query styles dynamically
16821
+ */
16822
+ function applyContainerQuery(containerName, condition, styles) {
16823
+ const css = createContainerQuery(containerName, condition, styles);
16824
+ const styleElement = document.createElement('style');
16825
+ styleElement.textContent = css;
16826
+ styleElement.setAttribute('data-proteus-container', containerName);
16827
+ document.head.appendChild(styleElement);
16828
+ }
16829
+ /**
16830
+ * Remove container query styles
16831
+ */
16832
+ function removeContainerQuery(containerName) {
16833
+ const styleElements = document.querySelectorAll(`style[data-proteus-container="${containerName}"]`);
16834
+ styleElements.forEach(element => element.remove());
16835
+ }
16836
+ /**
16837
+ * Get container size information
16838
+ */
16839
+ function getContainerSize(target) {
16840
+ const targetEl = typeof target === 'string' ? document.querySelector(target) : target;
16841
+ if (!targetEl) {
16842
+ throw new Error('Target element not found');
16843
+ }
16844
+ const rect = targetEl.getBoundingClientRect();
16845
+ return {
16846
+ width: rect.width,
16847
+ height: rect.height
16848
+ };
16849
+ }
16850
+ /**
16851
+ * Check if container queries are supported
16852
+ */
16853
+ function isSupported() {
16854
+ return CSS.supports('container-type', 'size');
16855
+ }
16856
+ /**
16857
+ * Cleanup container overlays and observers
16858
+ */
16859
+ function cleanup$1(target) {
16860
+ const targetEl = typeof target === 'string' ? document.querySelector(target) : target;
16861
+ if (!targetEl)
16862
+ return;
16863
+ // Call stored cleanup function if it exists
16864
+ const elementWithCleanup = targetEl;
16865
+ if (elementWithCleanup._proteusContainerCleanup) {
16866
+ elementWithCleanup._proteusContainerCleanup();
16867
+ delete elementWithCleanup._proteusContainerCleanup;
16868
+ }
16869
+ }
16870
+ /**
16871
+ * Toggle dev overlay visibility
16872
+ */
16873
+ function toggleDevOverlay(visible) {
16874
+ const overlays = document.querySelectorAll('.proteus-container-overlay');
16875
+ overlays.forEach(overlay => {
16876
+ const element = overlay;
16877
+ if (visible !== undefined) {
16878
+ element.style.display = visible ? 'block' : 'none';
16879
+ }
16880
+ else {
16881
+ element.style.display = element.style.display === 'none' ? 'block' : 'none';
16882
+ }
16883
+ });
16884
+ }
16885
+ // Export default object for convenience
16886
+ var index$8 = {
16887
+ defineContainer,
16888
+ createContainerQuery,
16889
+ applyContainerQuery,
16890
+ removeContainerQuery,
16891
+ getContainerSize,
16892
+ isSupported,
16893
+ cleanup: cleanup$1,
16894
+ toggleDevOverlay
16895
+ };
16896
+
16897
+ var index$9 = /*#__PURE__*/Object.freeze({
16898
+ __proto__: null,
16899
+ applyContainerQuery: applyContainerQuery,
16900
+ cleanup: cleanup$1,
16901
+ createContainerQuery: createContainerQuery,
16902
+ default: index$8,
16903
+ defineContainer: defineContainer,
16904
+ getContainerSize: getContainerSize,
16905
+ isSupported: isSupported,
16906
+ removeContainerQuery: removeContainerQuery,
16907
+ toggleDevOverlay: toggleDevOverlay
16908
+ });
16909
+
16910
+ /**
16911
+ * @sc4rfurryx/proteusjs/typography
16912
+ * Fluid typography with CSS-first approach
16913
+ *
16914
+ * @version 1.1.0
16915
+ * @author sc4rfurry
16916
+ * @license MIT
16917
+ */
16918
+ /**
16919
+ * Generate pure-CSS clamp() rules for fluid typography
16920
+ */
16921
+ function fluidType(minRem, maxRem, options = {}) {
16922
+ const { minViewportPx = 320, maxViewportPx = 1200, lineHeight, containerUnits = false } = options;
16923
+ // Convert rem to px for calculations (assuming 16px base)
16924
+ const minPx = minRem * 16;
16925
+ const maxPx = maxRem * 16;
16926
+ // Calculate slope and y-intercept for linear interpolation
16927
+ const slope = (maxPx - minPx) / (maxViewportPx - minViewportPx);
16928
+ const yIntercept = minPx - slope * minViewportPx;
16929
+ // Generate clamp() function
16930
+ const viewportUnit = containerUnits ? 'cqw' : 'vw';
16931
+ const clampValue = `clamp(${minRem}rem, ${yIntercept / 16}rem + ${slope * 100}${viewportUnit}, ${maxRem}rem)`;
16932
+ let css = `font-size: ${clampValue};`;
16933
+ // Add line-height if specified
16934
+ if (lineHeight) {
16935
+ css += `\nline-height: ${lineHeight};`;
16936
+ }
16937
+ return { css };
16938
+ }
16939
+ /**
16940
+ * Apply fluid typography to elements
16941
+ */
16942
+ function applyFluidType(selector, minRem, maxRem, options = {}) {
16943
+ const { css } = fluidType(minRem, maxRem, options);
16944
+ const styleElement = document.createElement('style');
16945
+ styleElement.textContent = `${selector} {\n ${css.replace(/\n/g, '\n ')}\n}`;
16946
+ styleElement.setAttribute('data-proteus-typography', selector);
16947
+ document.head.appendChild(styleElement);
16948
+ }
16949
+ /**
16950
+ * Create a complete typographic scale
16951
+ */
16952
+ function createTypographicScale(baseSize = 1, ratio = 1.25, steps = 6, options = {}) {
16953
+ const scale = {};
16954
+ for (let i = -2; i <= steps - 3; i++) {
16955
+ const size = baseSize * Math.pow(ratio, i);
16956
+ const minSize = size * 0.8; // 20% smaller at min viewport
16957
+ const maxSize = size * 1.2; // 20% larger at max viewport
16958
+ const stepName = i <= 0 ? `small${Math.abs(i)}` : `large${i}`;
16959
+ scale[stepName] = fluidType(minSize, maxSize, options);
16960
+ }
16961
+ return scale;
16962
+ }
16963
+ /**
16964
+ * Generate CSS custom properties for a typographic scale
16965
+ */
16966
+ function generateScaleCSS(scale, prefix = '--font-size') {
16967
+ const cssVars = Object.entries(scale)
16968
+ .map(([name, result]) => ` ${prefix}-${name}: ${result.css.replace('font-size: ', '').replace(';', '')};`)
16969
+ .join('\n');
16970
+ return `:root {\n${cssVars}\n}`;
16971
+ }
16972
+ /**
16973
+ * Optimize line height for readability
16974
+ */
16975
+ function optimizeLineHeight(fontSize, measure = 65) {
16976
+ // Optimal line height based on font size and measure (characters per line)
16977
+ // Smaller fonts need more line height, larger fonts need less
16978
+ const baseLineHeight = 1.4;
16979
+ const sizeAdjustment = Math.max(0.1, Math.min(0.3, (1 - fontSize) * 0.5));
16980
+ const measureAdjustment = Math.max(-0.1, Math.min(0.1, (65 - measure) * 0.002));
16981
+ return baseLineHeight + sizeAdjustment + measureAdjustment;
16982
+ }
16983
+ /**
16984
+ * Calculate optimal font size for container width
16985
+ */
16986
+ function calculateOptimalSize(containerWidth, targetCharacters = 65, baseCharWidth = 0.5) {
16987
+ // Calculate font size to achieve target characters per line
16988
+ const optimalFontSize = containerWidth / (targetCharacters * baseCharWidth);
16989
+ // Clamp to reasonable bounds (12px to 24px)
16990
+ return Math.max(0.75, Math.min(1.5, optimalFontSize));
16991
+ }
16992
+ /**
16993
+ * Apply responsive typography to an element
16994
+ */
16995
+ function makeResponsive(target, options = {}) {
16996
+ const targetEl = typeof target === 'string' ? document.querySelector(target) : target;
16997
+ if (!targetEl) {
16998
+ throw new Error('Target element not found');
16999
+ }
17000
+ const { minSize = 0.875, maxSize = 1.25, targetCharacters = 65, autoLineHeight = true } = options;
17001
+ // Apply fluid typography
17002
+ const { css } = fluidType(minSize, maxSize);
17003
+ const element = targetEl;
17004
+ // Parse and apply CSS
17005
+ const styles = css.split(';').filter(Boolean);
17006
+ styles.forEach(style => {
17007
+ const [property, value] = style.split(':').map(s => s.trim());
17008
+ if (property && value) {
17009
+ element.style.setProperty(property, value);
17010
+ }
17011
+ });
17012
+ // Auto line height if enabled
17013
+ if (autoLineHeight) {
17014
+ const updateLineHeight = () => {
17015
+ const computedStyle = getComputedStyle(element);
17016
+ const fontSize = parseFloat(computedStyle.fontSize);
17017
+ const containerWidth = element.getBoundingClientRect().width;
17018
+ const charactersPerLine = containerWidth / (fontSize * 0.5);
17019
+ const optimalLineHeight = optimizeLineHeight(fontSize / 16, charactersPerLine);
17020
+ element.style.lineHeight = optimalLineHeight.toString();
17021
+ };
17022
+ updateLineHeight();
17023
+ // Update on resize
17024
+ if ('ResizeObserver' in window) {
17025
+ const resizeObserver = new ResizeObserver(updateLineHeight);
17026
+ resizeObserver.observe(element);
17027
+ // Store cleanup function
17028
+ element._proteusTypographyCleanup = () => {
17029
+ resizeObserver.disconnect();
17030
+ };
17031
+ }
17032
+ }
17033
+ }
17034
+ /**
17035
+ * Remove applied typography styles
17036
+ */
17037
+ function cleanup(target) {
17038
+ if (target) {
17039
+ const targetEl = typeof target === 'string' ? document.querySelector(target) : target;
17040
+ if (targetEl && targetEl._proteusTypographyCleanup) {
17041
+ targetEl._proteusTypographyCleanup();
17042
+ delete targetEl._proteusTypographyCleanup;
17043
+ }
17044
+ }
17045
+ else {
17046
+ // Remove all typography style elements
17047
+ const styleElements = document.querySelectorAll('style[data-proteus-typography]');
17048
+ styleElements.forEach(element => element.remove());
17049
+ }
17050
+ }
17051
+ /**
17052
+ * Check if container query units are supported
17053
+ */
17054
+ function supportsContainerUnits() {
17055
+ return CSS.supports('width', '1cqw');
17056
+ }
17057
+ // Export default object for convenience
17058
+ var index$6 = {
17059
+ fluidType,
17060
+ applyFluidType,
17061
+ createTypographicScale,
17062
+ generateScaleCSS,
17063
+ optimizeLineHeight,
17064
+ calculateOptimalSize,
17065
+ makeResponsive,
17066
+ cleanup,
17067
+ supportsContainerUnits
17068
+ };
17069
+
17070
+ var index$7 = /*#__PURE__*/Object.freeze({
17071
+ __proto__: null,
17072
+ applyFluidType: applyFluidType,
17073
+ calculateOptimalSize: calculateOptimalSize,
17074
+ cleanup: cleanup,
17075
+ createTypographicScale: createTypographicScale,
17076
+ default: index$6,
17077
+ fluidType: fluidType,
17078
+ generateScaleCSS: generateScaleCSS,
17079
+ makeResponsive: makeResponsive,
17080
+ optimizeLineHeight: optimizeLineHeight,
17081
+ supportsContainerUnits: supportsContainerUnits
17082
+ });
17083
+
17084
+ /**
17085
+ * @sc4rfurryx/proteusjs/a11y-audit
17086
+ * Accessibility audits for development (dev-only)
17087
+ *
17088
+ * @version 1.1.0
17089
+ * @author sc4rfurry
17090
+ * @license MIT
17091
+ */
17092
+ /**
17093
+ * Run accessibility audits with actionable output
17094
+ * DEV-ONLY: This module should be tree-shaken in production
17095
+ */
17096
+ async function audit(target = document, options = {}) {
17097
+ // Ensure this only runs in development
17098
+ if (process.env['NODE_ENV'] === 'production') {
17099
+ console.warn('a11y-audit should not be used in production');
17100
+ return {
17101
+ violations: [],
17102
+ passes: 0,
17103
+ incomplete: 0,
17104
+ timestamp: Date.now(),
17105
+ url: window.location.href
17106
+ };
17107
+ }
17108
+ const { rules = ['color-contrast', 'heading-order', 'image-alt', 'label', 'link-name', 'button-name'], format = 'console', openInBrowser = false } = options;
17109
+ const violations = [];
17110
+ let passes = 0;
17111
+ let incomplete = 0;
17112
+ // Basic accessibility checks
17113
+ const checks = {
17114
+ 'color-contrast': checkColorContrast,
17115
+ 'heading-order': checkHeadingOrder,
17116
+ 'image-alt': checkImageAlt,
17117
+ 'label': checkFormLabels,
17118
+ 'link-name': checkLinkNames,
17119
+ 'button-name': checkButtonNames,
17120
+ 'focus-visible': checkFocusVisible,
17121
+ 'aria-labels': checkAriaLabels,
17122
+ 'landmark-roles': checkLandmarkRoles,
17123
+ 'skip-links': checkSkipLinks
17124
+ };
17125
+ // Run selected checks
17126
+ for (const ruleId of rules) {
17127
+ if (checks[ruleId]) {
17128
+ try {
17129
+ const result = await checks[ruleId](target);
17130
+ if (result.violations.length > 0) {
17131
+ violations.push(...result.violations);
17132
+ }
17133
+ else {
17134
+ passes++;
17135
+ }
17136
+ }
17137
+ catch (error) {
17138
+ incomplete++;
17139
+ console.warn(`Failed to run accessibility check: ${ruleId}`, error);
17140
+ }
17141
+ }
17142
+ }
17143
+ const report = {
17144
+ violations,
17145
+ passes,
17146
+ incomplete,
17147
+ timestamp: Date.now(),
17148
+ url: window.location.href
17149
+ };
17150
+ // Output results
17151
+ if (format === 'console') {
17152
+ outputToConsole(report);
17153
+ }
17154
+ if (openInBrowser) {
17155
+ openReportInBrowser(report);
17156
+ }
17157
+ return report;
17158
+ }
17159
+ // Individual check functions
17160
+ async function checkColorContrast(target) {
17161
+ const violations = [];
17162
+ const elements = target.querySelectorAll('*');
17163
+ elements.forEach(element => {
17164
+ const style = getComputedStyle(element);
17165
+ const color = style.color;
17166
+ const backgroundColor = style.backgroundColor;
17167
+ // Simple contrast check (would need more sophisticated implementation)
17168
+ if (color && backgroundColor && color !== 'rgba(0, 0, 0, 0)' && backgroundColor !== 'rgba(0, 0, 0, 0)') {
17169
+ const contrast = calculateContrast(color, backgroundColor);
17170
+ if (contrast < 4.5) {
17171
+ violations.push({
17172
+ id: 'color-contrast',
17173
+ impact: 'serious',
17174
+ nodes: 1,
17175
+ help: 'Elements must have sufficient color contrast',
17176
+ fix: `Increase contrast ratio to at least 4.5:1. Current: ${contrast.toFixed(2)}:1`,
17177
+ elements: [element]
17178
+ });
17179
+ }
17180
+ }
17181
+ });
17182
+ return { violations };
17183
+ }
17184
+ async function checkHeadingOrder(target) {
17185
+ const violations = [];
17186
+ const headings = target.querySelectorAll('h1, h2, h3, h4, h5, h6');
17187
+ let lastLevel = 0;
17188
+ headings.forEach(heading => {
17189
+ const level = parseInt(heading.tagName.charAt(1));
17190
+ if (level > lastLevel + 1) {
17191
+ violations.push({
17192
+ id: 'heading-order',
17193
+ impact: 'moderate',
17194
+ nodes: 1,
17195
+ help: 'Heading levels should only increase by one',
17196
+ fix: `Change ${heading.tagName} to H${lastLevel + 1} or add intermediate headings`,
17197
+ elements: [heading]
17198
+ });
17199
+ }
17200
+ lastLevel = level;
17201
+ });
17202
+ return { violations };
17203
+ }
17204
+ async function checkImageAlt(target) {
17205
+ const violations = [];
17206
+ const images = target.querySelectorAll('img');
17207
+ images.forEach(img => {
17208
+ if (!img.hasAttribute('alt')) {
17209
+ violations.push({
17210
+ id: 'image-alt',
17211
+ impact: 'critical',
17212
+ nodes: 1,
17213
+ help: 'Images must have alternative text',
17214
+ fix: 'Add alt attribute with descriptive text or alt="" for decorative images',
17215
+ elements: [img]
17216
+ });
17217
+ }
17218
+ });
17219
+ return { violations };
17220
+ }
17221
+ async function checkFormLabels(target) {
17222
+ const violations = [];
17223
+ const inputs = target.querySelectorAll('input, select, textarea');
17224
+ inputs.forEach(input => {
17225
+ const hasLabel = input.hasAttribute('aria-label') ||
17226
+ input.hasAttribute('aria-labelledby') ||
17227
+ target.querySelector(`label[for="${input.id}"]`) ||
17228
+ input.closest('label');
17229
+ if (!hasLabel) {
17230
+ violations.push({
17231
+ id: 'label',
17232
+ impact: 'critical',
17233
+ nodes: 1,
17234
+ help: 'Form elements must have labels',
17235
+ fix: 'Add a label element, aria-label, or aria-labelledby attribute',
17236
+ elements: [input]
17237
+ });
17238
+ }
17239
+ });
17240
+ return { violations };
17241
+ }
17242
+ async function checkLinkNames(target) {
17243
+ const violations = [];
17244
+ const links = target.querySelectorAll('a[href]');
17245
+ links.forEach(link => {
17246
+ const text = link.textContent?.trim();
17247
+ const ariaLabel = link.getAttribute('aria-label');
17248
+ if (!text && !ariaLabel) {
17249
+ violations.push({
17250
+ id: 'link-name',
17251
+ impact: 'serious',
17252
+ nodes: 1,
17253
+ help: 'Links must have discernible text',
17254
+ fix: 'Add descriptive text content or aria-label attribute',
17255
+ elements: [link]
17256
+ });
17257
+ }
17258
+ });
17259
+ return { violations };
17260
+ }
17261
+ async function checkButtonNames(target) {
17262
+ const violations = [];
17263
+ const buttons = target.querySelectorAll('button, input[type="button"], input[type="submit"]');
17264
+ buttons.forEach(button => {
17265
+ const text = button.textContent?.trim();
17266
+ const ariaLabel = button.getAttribute('aria-label');
17267
+ const value = button.getAttribute('value');
17268
+ if (!text && !ariaLabel && !value) {
17269
+ violations.push({
17270
+ id: 'button-name',
17271
+ impact: 'serious',
17272
+ nodes: 1,
17273
+ help: 'Buttons must have discernible text',
17274
+ fix: 'Add text content, aria-label, or value attribute',
17275
+ elements: [button]
17276
+ });
17277
+ }
17278
+ });
17279
+ return { violations };
17280
+ }
17281
+ async function checkFocusVisible(target) {
17282
+ const violations = [];
17283
+ const focusableElements = target.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
17284
+ focusableElements.forEach(element => {
17285
+ const styles = getComputedStyle(element);
17286
+ const hasVisibleFocus = styles.outline !== 'none' && styles.outline !== '0px' &&
17287
+ styles.outline !== '0' && styles.outlineWidth !== '0px';
17288
+ // Check if element has custom focus styles
17289
+ const hasCustomFocus = styles.boxShadow.includes('inset') ||
17290
+ styles.border !== styles.borderColor ||
17291
+ element.hasAttribute('data-focus-visible');
17292
+ if (!hasVisibleFocus && !hasCustomFocus) {
17293
+ violations.push({
17294
+ id: 'focus-visible',
17295
+ impact: 'serious',
17296
+ nodes: 1,
17297
+ help: 'Interactive elements must have visible focus indicators',
17298
+ fix: 'Add outline, box-shadow, or other visible focus styles',
17299
+ elements: [element]
17300
+ });
17301
+ }
17302
+ });
17303
+ return { violations };
17304
+ }
17305
+ async function checkAriaLabels(target) {
17306
+ const violations = [];
17307
+ // Check for aria-labelledby pointing to non-existent elements
17308
+ const elementsWithLabelledBy = target.querySelectorAll('[aria-labelledby]');
17309
+ elementsWithLabelledBy.forEach(element => {
17310
+ const labelledBy = element.getAttribute('aria-labelledby');
17311
+ if (labelledBy) {
17312
+ const labelIds = labelledBy.split(' ');
17313
+ const missingIds = labelIds.filter(id => !target.querySelector(`#${id}`));
17314
+ if (missingIds.length > 0) {
17315
+ violations.push({
17316
+ id: 'aria-labelledby-invalid',
17317
+ impact: 'serious',
17318
+ nodes: 1,
17319
+ help: 'aria-labelledby must reference existing elements',
17320
+ fix: `Fix or remove references to missing IDs: ${missingIds.join(', ')}`,
17321
+ elements: [element]
17322
+ });
17323
+ }
17324
+ }
17325
+ });
17326
+ // Check for aria-describedby pointing to non-existent elements
17327
+ const elementsWithDescribedBy = target.querySelectorAll('[aria-describedby]');
17328
+ elementsWithDescribedBy.forEach(element => {
17329
+ const describedBy = element.getAttribute('aria-describedby');
17330
+ if (describedBy) {
17331
+ const descriptionIds = describedBy.split(' ');
17332
+ const missingIds = descriptionIds.filter(id => !target.querySelector(`#${id}`));
17333
+ if (missingIds.length > 0) {
17334
+ violations.push({
17335
+ id: 'aria-describedby-invalid',
17336
+ impact: 'moderate',
17337
+ nodes: 1,
17338
+ help: 'aria-describedby must reference existing elements',
17339
+ fix: `Fix or remove references to missing IDs: ${missingIds.join(', ')}`,
17340
+ elements: [element]
17341
+ });
17342
+ }
17343
+ }
17344
+ });
17345
+ return { violations };
17346
+ }
17347
+ async function checkLandmarkRoles(target) {
17348
+ const violations = [];
17349
+ // Check for missing main landmark
17350
+ const mainElements = target.querySelectorAll('main, [role="main"]');
17351
+ if (mainElements.length === 0) {
17352
+ violations.push({
17353
+ id: 'landmark-main-missing',
17354
+ impact: 'moderate',
17355
+ nodes: 0,
17356
+ help: 'Page should have a main landmark',
17357
+ fix: 'Add a <main> element or role="main" to identify the main content area'
17358
+ });
17359
+ }
17360
+ else if (mainElements.length > 1) {
17361
+ violations.push({
17362
+ id: 'landmark-main-multiple',
17363
+ impact: 'moderate',
17364
+ nodes: mainElements.length,
17365
+ help: 'Page should have only one main landmark',
17366
+ fix: 'Ensure only one main element or role="main" exists per page',
17367
+ elements: Array.from(mainElements)
17368
+ });
17369
+ }
17370
+ // Check for navigation landmarks without labels when multiple exist
17371
+ const navElements = target.querySelectorAll('nav, [role="navigation"]');
17372
+ if (navElements.length > 1) {
17373
+ navElements.forEach(nav => {
17374
+ const hasLabel = nav.hasAttribute('aria-label') ||
17375
+ nav.hasAttribute('aria-labelledby') ||
17376
+ nav.querySelector('h1, h2, h3, h4, h5, h6');
17377
+ if (!hasLabel) {
17378
+ violations.push({
17379
+ id: 'landmark-nav-unlabeled',
17380
+ impact: 'moderate',
17381
+ nodes: 1,
17382
+ help: 'Multiple navigation landmarks should be labeled',
17383
+ fix: 'Add aria-label or aria-labelledby to distinguish navigation areas',
17384
+ elements: [nav]
17385
+ });
17386
+ }
17387
+ });
17388
+ }
17389
+ return { violations };
17390
+ }
17391
+ async function checkSkipLinks(target) {
17392
+ const violations = [];
17393
+ // Check for skip links in documents with navigation
17394
+ const navElements = target.querySelectorAll('nav, [role="navigation"]');
17395
+ const mainElement = target.querySelector('main, [role="main"]');
17396
+ if (navElements.length > 0 && mainElement) {
17397
+ const skipLinks = target.querySelectorAll('a[href^="#"]');
17398
+ const hasSkipToMain = Array.from(skipLinks).some(link => {
17399
+ const href = link.getAttribute('href');
17400
+ return href && (href === '#main' ||
17401
+ href === `#${mainElement.id}` ||
17402
+ link.textContent?.toLowerCase().includes('skip to main') ||
17403
+ link.textContent?.toLowerCase().includes('skip to content'));
17404
+ });
17405
+ if (!hasSkipToMain) {
17406
+ violations.push({
17407
+ id: 'skip-link-missing',
17408
+ impact: 'moderate',
17409
+ nodes: 0,
17410
+ help: 'Page with navigation should have skip links',
17411
+ fix: 'Add a skip link to the main content area for keyboard users'
17412
+ });
17413
+ }
17414
+ }
17415
+ // Check that skip links are properly positioned (should be first focusable element)
17416
+ const firstFocusable = target.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
17417
+ if (firstFocusable && firstFocusable.tagName === 'A') {
17418
+ const href = firstFocusable.getAttribute('href');
17419
+ if (!href?.startsWith('#')) {
17420
+ violations.push({
17421
+ id: 'skip-link-not-first',
17422
+ impact: 'minor',
17423
+ nodes: 1,
17424
+ help: 'Skip links should be the first focusable elements',
17425
+ fix: 'Move skip links to the beginning of the document',
17426
+ elements: [firstFocusable]
17427
+ });
17428
+ }
17429
+ }
17430
+ return { violations };
17431
+ }
17432
+ // Utility functions
17433
+ function calculateContrast(color1, color2) {
17434
+ // Convert colors to RGB values
17435
+ const rgb1 = parseColor(color1);
17436
+ const rgb2 = parseColor(color2);
17437
+ if (!rgb1 || !rgb2)
17438
+ return 4.5; // Fallback if parsing fails
17439
+ // Calculate relative luminance
17440
+ const l1 = getRelativeLuminance(rgb1);
17441
+ const l2 = getRelativeLuminance(rgb2);
17442
+ // Calculate contrast ratio
17443
+ const lighter = Math.max(l1, l2);
17444
+ const darker = Math.min(l1, l2);
17445
+ return (lighter + 0.05) / (darker + 0.05);
17446
+ }
17447
+ function parseColor(color) {
17448
+ // Handle rgb() format
17449
+ const rgbMatch = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
17450
+ if (rgbMatch && rgbMatch[1] && rgbMatch[2] && rgbMatch[3]) {
17451
+ return {
17452
+ r: parseInt(rgbMatch[1], 10),
17453
+ g: parseInt(rgbMatch[2], 10),
17454
+ b: parseInt(rgbMatch[3], 10)
17455
+ };
17456
+ }
17457
+ // Handle hex format
17458
+ const hexMatch = color.match(/^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i);
17459
+ if (hexMatch && hexMatch[1] && hexMatch[2] && hexMatch[3]) {
17460
+ return {
17461
+ r: parseInt(hexMatch[1], 16),
17462
+ g: parseInt(hexMatch[2], 16),
17463
+ b: parseInt(hexMatch[3], 16)
17464
+ };
17465
+ }
17466
+ // Handle named colors (basic set)
17467
+ const namedColors = {
17468
+ 'black': { r: 0, g: 0, b: 0 },
17469
+ 'white': { r: 255, g: 255, b: 255 },
17470
+ 'red': { r: 255, g: 0, b: 0 },
17471
+ 'green': { r: 0, g: 128, b: 0 },
17472
+ 'blue': { r: 0, g: 0, b: 255 }
17473
+ };
17474
+ return namedColors[color.toLowerCase()] || null;
17475
+ }
17476
+ function getRelativeLuminance(rgb) {
17477
+ const { r, g, b } = rgb;
17478
+ // Convert to sRGB
17479
+ const rsRGB = r / 255;
17480
+ const gsRGB = g / 255;
17481
+ const bsRGB = b / 255;
17482
+ // Apply gamma correction
17483
+ const rLinear = rsRGB <= 0.03928 ? rsRGB / 12.92 : Math.pow((rsRGB + 0.055) / 1.055, 2.4);
17484
+ const gLinear = gsRGB <= 0.03928 ? gsRGB / 12.92 : Math.pow((gsRGB + 0.055) / 1.055, 2.4);
17485
+ const bLinear = bsRGB <= 0.03928 ? bsRGB / 12.92 : Math.pow((bsRGB + 0.055) / 1.055, 2.4);
17486
+ // Calculate relative luminance
17487
+ return 0.2126 * rLinear + 0.7152 * gLinear + 0.0722 * bLinear;
17488
+ }
17489
+ function outputToConsole(report) {
17490
+ console.group('🔍 Accessibility Audit Results');
17491
+ if (report.violations.length === 0) {
17492
+ console.log('✅ No accessibility violations found!');
17493
+ }
17494
+ else {
17495
+ console.log(`❌ Found ${report.violations.length} accessibility violations:`);
17496
+ report.violations.forEach(violation => {
17497
+ const emoji = violation.impact === 'critical' ? '🚨' :
17498
+ violation.impact === 'serious' ? '⚠️' :
17499
+ violation.impact === 'moderate' ? '⚡' : 'ℹ️';
17500
+ console.group(`${emoji} ${violation.help}`);
17501
+ console.log(`Impact: ${violation.impact}`);
17502
+ console.log(`Fix: ${violation.fix}`);
17503
+ if (violation.elements) {
17504
+ console.log('Elements:', violation.elements);
17505
+ }
17506
+ console.groupEnd();
17507
+ });
17508
+ }
17509
+ console.log(`✅ ${report.passes} checks passed`);
17510
+ if (report.incomplete > 0) {
17511
+ console.log(`⚠️ ${report.incomplete} checks incomplete`);
17512
+ }
17513
+ console.groupEnd();
17514
+ }
17515
+ function openReportInBrowser(report) {
17516
+ const html = generateHTMLReport(report);
17517
+ const blob = new Blob([html], { type: 'text/html' });
17518
+ const url = URL.createObjectURL(blob);
17519
+ const newWindow = window.open(url, '_blank');
17520
+ if (!newWindow) {
17521
+ console.warn('Could not open report in new window. Please check popup blocker settings.');
17522
+ // Fallback: download the report
17523
+ const link = document.createElement('a');
17524
+ link.href = url;
17525
+ link.download = `proteus-a11y-report-${Date.now()}.html`;
17526
+ link.click();
17527
+ }
17528
+ // Clean up the blob URL after a delay
17529
+ setTimeout(() => URL.revokeObjectURL(url), 1000);
17530
+ }
17531
+ function generateHTMLReport(report) {
17532
+ const violationsList = report.violations.map(violation => `
17533
+ <div class="violation violation--${violation.impact}">
17534
+ <h3>${violation.help}</h3>
17535
+ <p><strong>Impact:</strong> ${violation.impact}</p>
17536
+ <p><strong>Fix:</strong> ${violation.fix}</p>
17537
+ <p><strong>Affected elements:</strong> ${violation.nodes}</p>
17538
+ </div>
17539
+ `).join('');
17540
+ return `
17541
+ <!DOCTYPE html>
17542
+ <html lang="en">
17543
+ <head>
17544
+ <meta charset="UTF-8">
17545
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
17546
+ <title>ProteusJS Accessibility Report</title>
17547
+ <style>
17548
+ body { font-family: system-ui, sans-serif; margin: 2rem; line-height: 1.6; }
17549
+ .header { border-bottom: 2px solid #e5e7eb; padding-bottom: 1rem; margin-bottom: 2rem; }
17550
+ .summary { background: #f3f4f6; padding: 1rem; border-radius: 8px; margin-bottom: 2rem; }
17551
+ .violation { border-left: 4px solid #ef4444; padding: 1rem; margin-bottom: 1rem; background: #fef2f2; }
17552
+ .violation--critical { border-color: #dc2626; background: #fef2f2; }
17553
+ .violation--serious { border-color: #ea580c; background: #fff7ed; }
17554
+ .violation--moderate { border-color: #d97706; background: #fffbeb; }
17555
+ .violation--minor { border-color: #65a30d; background: #f7fee7; }
17556
+ .violation h3 { margin-top: 0; color: #374151; }
17557
+ .no-violations { text-align: center; color: #059669; font-size: 1.25rem; padding: 2rem; }
17558
+ </style>
17559
+ </head>
17560
+ <body>
17561
+ <div class="header">
17562
+ <h1>🌊 ProteusJS Accessibility Report</h1>
17563
+ <p>Generated on ${new Date(report.timestamp).toLocaleString()}</p>
17564
+ <p>URL: ${report.url}</p>
17565
+ </div>
17566
+
17567
+ <div class="summary">
17568
+ <h2>Summary</h2>
17569
+ <p><strong>Violations:</strong> ${report.violations.length}</p>
17570
+ <p><strong>Checks passed:</strong> ${report.passes}</p>
17571
+ <p><strong>Incomplete checks:</strong> ${report.incomplete}</p>
17572
+ </div>
17573
+
17574
+ ${report.violations.length === 0 ?
17575
+ '<div class="no-violations">✅ No accessibility violations found!</div>' :
17576
+ `<h2>Violations</h2>${violationsList}`}
17577
+ </body>
17578
+ </html>`;
17579
+ }
17580
+ // Export default object for convenience
17581
+ var index$4 = {
17582
+ audit
17583
+ };
17584
+
17585
+ var index$5 = /*#__PURE__*/Object.freeze({
17586
+ __proto__: null,
17587
+ audit: audit,
17588
+ default: index$4
17589
+ });
17590
+
17591
+ /**
17592
+ * @sc4rfurryx/proteusjs/a11y-primitives
17593
+ * Headless accessibility patterns (no styles)
17594
+ *
17595
+ * @version 1.1.0
17596
+ * @author sc4rfurry
17597
+ * @license MIT
17598
+ */
17599
+ /**
17600
+ * Dialog primitive with proper ARIA and focus management
17601
+ */
17602
+ function dialog(root, opts = {}) {
17603
+ const rootEl = typeof root === 'string' ? document.querySelector(root) : root;
17604
+ if (!rootEl)
17605
+ throw new Error('Dialog root element not found');
17606
+ const { modal = true, restoreFocus = true } = opts;
17607
+ let isOpen = false;
17608
+ const setup = () => {
17609
+ rootEl.setAttribute('role', 'dialog');
17610
+ if (modal) {
17611
+ rootEl.setAttribute('aria-modal', 'true');
17612
+ }
17613
+ // Ensure dialog is initially hidden
17614
+ if (!rootEl.hasAttribute('hidden')) {
17615
+ rootEl.setAttribute('hidden', '');
17616
+ }
17617
+ };
17618
+ const handleKeyDown = (e) => {
17619
+ if (e.key === 'Escape' && isOpen) ;
17620
+ };
17621
+ setup();
17622
+ document.addEventListener('keydown', handleKeyDown);
17623
+ return {
17624
+ destroy: () => {
17625
+ document.removeEventListener('keydown', handleKeyDown);
17626
+ }
17627
+ };
17628
+ }
17629
+ /**
17630
+ * Tooltip primitive with delay and proper ARIA
17631
+ */
17632
+ function tooltip(trigger, panel, opts = {}) {
17633
+ const triggerEl = typeof trigger === 'string' ? document.querySelector(trigger) : trigger;
17634
+ const panelEl = typeof panel === 'string' ? document.querySelector(panel) : panel;
17635
+ if (!triggerEl || !panelEl) {
17636
+ throw new Error('Both trigger and panel elements must exist');
17637
+ }
17638
+ const { delay = 500 } = opts;
17639
+ let timeoutId = null;
17640
+ let isVisible = false;
17641
+ const setup = () => {
17642
+ const tooltipId = panelEl.id || `tooltip-${Math.random().toString(36).substring(2, 11)}`;
17643
+ panelEl.id = tooltipId;
17644
+ panelEl.setAttribute('role', 'tooltip');
17645
+ triggerEl.setAttribute('aria-describedby', tooltipId);
17646
+ // Initially hidden
17647
+ panelEl.style.display = 'none';
17648
+ };
17649
+ const show = () => {
17650
+ if (isVisible)
17651
+ return;
17652
+ panelEl.style.display = 'block';
17653
+ isVisible = true;
17654
+ };
17655
+ const hide = () => {
17656
+ if (!isVisible)
17657
+ return;
17658
+ panelEl.style.display = 'none';
17659
+ isVisible = false;
17660
+ };
17661
+ const handleMouseEnter = () => {
17662
+ if (timeoutId)
17663
+ clearTimeout(timeoutId);
17664
+ timeoutId = window.setTimeout(show, delay);
17665
+ };
17666
+ const handleMouseLeave = () => {
17667
+ if (timeoutId) {
17668
+ clearTimeout(timeoutId);
17669
+ timeoutId = null;
17670
+ }
17671
+ hide();
17672
+ };
17673
+ const handleFocus = () => {
17674
+ show();
17675
+ };
17676
+ const handleBlur = () => {
17677
+ hide();
17678
+ };
17679
+ setup();
17680
+ triggerEl.addEventListener('mouseenter', handleMouseEnter);
17681
+ triggerEl.addEventListener('mouseleave', handleMouseLeave);
17682
+ triggerEl.addEventListener('focus', handleFocus);
17683
+ triggerEl.addEventListener('blur', handleBlur);
17684
+ return {
17685
+ destroy: () => {
17686
+ if (timeoutId)
17687
+ clearTimeout(timeoutId);
17688
+ triggerEl.removeEventListener('mouseenter', handleMouseEnter);
17689
+ triggerEl.removeEventListener('mouseleave', handleMouseLeave);
17690
+ triggerEl.removeEventListener('focus', handleFocus);
17691
+ triggerEl.removeEventListener('blur', handleBlur);
17692
+ hide();
17693
+ }
17694
+ };
17695
+ }
17696
+ /**
17697
+ * Listbox primitive with keyboard navigation
17698
+ */
17699
+ function listbox(root, opts = {}) {
17700
+ const rootEl = typeof root === 'string' ? document.querySelector(root) : root;
17701
+ if (!rootEl)
17702
+ throw new Error('Listbox root element not found');
17703
+ const { multiselect = false } = opts;
17704
+ let currentIndex = -1;
17705
+ const setup = () => {
17706
+ rootEl.setAttribute('role', 'listbox');
17707
+ if (multiselect) {
17708
+ rootEl.setAttribute('aria-multiselectable', 'true');
17709
+ }
17710
+ // Set up options
17711
+ const options = rootEl.querySelectorAll('[role="option"]');
17712
+ options.forEach((option, _index) => {
17713
+ option.setAttribute('aria-selected', 'false');
17714
+ option.setAttribute('tabindex', '-1');
17715
+ });
17716
+ if (options.length > 0) {
17717
+ options[0]?.setAttribute('tabindex', '0');
17718
+ currentIndex = 0;
17719
+ }
17720
+ };
17721
+ const getOptions = () => rootEl.querySelectorAll('[role="option"]');
17722
+ const setCurrentIndex = (index) => {
17723
+ const options = getOptions();
17724
+ if (index < 0 || index >= options.length)
17725
+ return;
17726
+ // Remove tabindex from all options
17727
+ options.forEach(option => option.setAttribute('tabindex', '-1'));
17728
+ // Set current option
17729
+ currentIndex = index;
17730
+ options[currentIndex]?.setAttribute('tabindex', '0');
17731
+ options[currentIndex]?.focus();
17732
+ };
17733
+ const selectOption = (index) => {
17734
+ const options = getOptions();
17735
+ if (index < 0 || index >= options.length)
17736
+ return;
17737
+ if (multiselect) {
17738
+ const isSelected = options[index]?.getAttribute('aria-selected') === 'true';
17739
+ options[index]?.setAttribute('aria-selected', (!isSelected).toString());
17740
+ }
17741
+ else {
17742
+ // Single select - clear all others
17743
+ options.forEach(option => option.setAttribute('aria-selected', 'false'));
17744
+ options[index]?.setAttribute('aria-selected', 'true');
17745
+ }
17746
+ };
17747
+ const handleKeyDown = (e) => {
17748
+ const keyEvent = e;
17749
+ const options = getOptions();
17750
+ switch (keyEvent.key) {
17751
+ case 'ArrowDown':
17752
+ keyEvent.preventDefault();
17753
+ setCurrentIndex(Math.min(currentIndex + 1, options.length - 1));
17754
+ break;
17755
+ case 'ArrowUp':
17756
+ keyEvent.preventDefault();
17757
+ setCurrentIndex(Math.max(currentIndex - 1, 0));
17758
+ break;
17759
+ case 'Home':
17760
+ keyEvent.preventDefault();
17761
+ setCurrentIndex(0);
17762
+ break;
17763
+ case 'End':
17764
+ keyEvent.preventDefault();
17765
+ setCurrentIndex(options.length - 1);
17766
+ break;
17767
+ case 'Enter':
17768
+ case ' ':
17769
+ keyEvent.preventDefault();
17770
+ selectOption(currentIndex);
17771
+ break;
17772
+ }
17773
+ };
17774
+ const handleClick = (e) => {
17775
+ const target = e.target;
17776
+ const option = target.closest('[role="option"]');
17777
+ if (!option)
17778
+ return;
17779
+ const options = Array.from(getOptions());
17780
+ const index = options.indexOf(option);
17781
+ if (index >= 0) {
17782
+ setCurrentIndex(index);
17783
+ selectOption(index);
17784
+ }
17785
+ };
17786
+ setup();
17787
+ rootEl.addEventListener('keydown', handleKeyDown);
17788
+ rootEl.addEventListener('click', handleClick);
17789
+ return {
17790
+ destroy: () => {
17791
+ rootEl.removeEventListener('keydown', handleKeyDown);
17792
+ rootEl.removeEventListener('click', handleClick);
17793
+ }
17794
+ };
17795
+ }
17796
+ /**
17797
+ * Combobox primitive with filtering and multiselect
17798
+ */
17799
+ function combobox(root, opts = {}) {
17800
+ const rootEl = typeof root === 'string' ? document.querySelector(root) : root;
17801
+ if (!rootEl)
17802
+ throw new Error('Combobox root element not found');
17803
+ const { multiselect = false, filtering: _filtering } = opts;
17804
+ let isOpen = false;
17805
+ const setup = () => {
17806
+ rootEl.setAttribute('role', 'combobox');
17807
+ rootEl.setAttribute('aria-expanded', 'false');
17808
+ if (multiselect) {
17809
+ rootEl.setAttribute('aria-multiselectable', 'true');
17810
+ }
17811
+ };
17812
+ const handleKeyDown = (e) => {
17813
+ const keyEvent = e;
17814
+ switch (keyEvent.key) {
17815
+ case 'ArrowDown':
17816
+ keyEvent.preventDefault();
17817
+ if (!isOpen) {
17818
+ isOpen = true;
17819
+ rootEl.setAttribute('aria-expanded', 'true');
17820
+ }
17821
+ // Navigate options logic would go here
17822
+ break;
17823
+ case 'Escape':
17824
+ keyEvent.preventDefault();
17825
+ isOpen = false;
17826
+ rootEl.setAttribute('aria-expanded', 'false');
17827
+ break;
17828
+ }
17829
+ };
17830
+ setup();
17831
+ rootEl.addEventListener('keydown', handleKeyDown);
17832
+ return {
17833
+ destroy: () => {
17834
+ rootEl.removeEventListener('keydown', handleKeyDown);
17835
+ }
17836
+ };
17837
+ }
17838
+ /**
17839
+ * Tabs primitive with keyboard navigation
17840
+ */
17841
+ function tabs(root) {
17842
+ const rootEl = typeof root === 'string' ? document.querySelector(root) : root;
17843
+ if (!rootEl)
17844
+ throw new Error('Tabs root element not found');
17845
+ let currentIndex = 0;
17846
+ const setup = () => {
17847
+ const tabList = rootEl.querySelector('[role="tablist"]');
17848
+ const tabs = rootEl.querySelectorAll('[role="tab"]');
17849
+ const panels = rootEl.querySelectorAll('[role="tabpanel"]');
17850
+ if (!tabList) {
17851
+ rootEl.setAttribute('role', 'tablist');
17852
+ }
17853
+ tabs.forEach((tab, index) => {
17854
+ tab.setAttribute('tabindex', index === 0 ? '0' : '-1');
17855
+ tab.setAttribute('aria-selected', index === 0 ? 'true' : 'false');
17856
+ });
17857
+ panels.forEach((panel, index) => {
17858
+ panel.setAttribute('hidden', index === 0 ? '' : 'true');
17859
+ });
17860
+ };
17861
+ const handleKeyDown = (e) => {
17862
+ const keyEvent = e;
17863
+ const tabs = Array.from(rootEl.querySelectorAll('[role="tab"]'));
17864
+ switch (keyEvent.key) {
17865
+ case 'ArrowRight':
17866
+ keyEvent.preventDefault();
17867
+ currentIndex = (currentIndex + 1) % tabs.length;
17868
+ activateTab(currentIndex);
17869
+ break;
17870
+ case 'ArrowLeft':
17871
+ keyEvent.preventDefault();
17872
+ currentIndex = currentIndex === 0 ? tabs.length - 1 : currentIndex - 1;
17873
+ activateTab(currentIndex);
17874
+ break;
17875
+ }
17876
+ };
17877
+ const activateTab = (index) => {
17878
+ const tabs = rootEl.querySelectorAll('[role="tab"]');
17879
+ const panels = rootEl.querySelectorAll('[role="tabpanel"]');
17880
+ tabs.forEach((tab, i) => {
17881
+ tab.setAttribute('tabindex', i === index ? '0' : '-1');
17882
+ tab.setAttribute('aria-selected', i === index ? 'true' : 'false');
17883
+ if (i === index) {
17884
+ tab.focus();
17885
+ }
17886
+ });
17887
+ panels.forEach((panel, i) => {
17888
+ if (i === index) {
17889
+ panel.removeAttribute('hidden');
17890
+ }
17891
+ else {
17892
+ panel.setAttribute('hidden', 'true');
17893
+ }
17894
+ });
17895
+ };
17896
+ setup();
17897
+ rootEl.addEventListener('keydown', handleKeyDown);
17898
+ return {
17899
+ destroy: () => {
17900
+ rootEl.removeEventListener('keydown', handleKeyDown);
17901
+ }
17902
+ };
17903
+ }
17904
+ /**
17905
+ * Menu primitive with keyboard navigation
17906
+ */
17907
+ function menu(root) {
17908
+ const rootEl = typeof root === 'string' ? document.querySelector(root) : root;
17909
+ if (!rootEl)
17910
+ throw new Error('Menu root element not found');
17911
+ let currentIndex = -1;
17912
+ const setup = () => {
17913
+ rootEl.setAttribute('role', 'menu');
17914
+ const items = rootEl.querySelectorAll('[role="menuitem"]');
17915
+ items.forEach((item, index) => {
17916
+ item.setAttribute('tabindex', index === 0 ? '0' : '-1');
17917
+ });
17918
+ if (items.length > 0) {
17919
+ currentIndex = 0;
17920
+ }
17921
+ };
17922
+ const handleKeyDown = (e) => {
17923
+ const keyEvent = e;
17924
+ const items = Array.from(rootEl.querySelectorAll('[role="menuitem"]'));
17925
+ switch (keyEvent.key) {
17926
+ case 'ArrowDown':
17927
+ keyEvent.preventDefault();
17928
+ currentIndex = (currentIndex + 1) % items.length;
17929
+ setCurrentItem(currentIndex);
17930
+ break;
17931
+ case 'ArrowUp':
17932
+ keyEvent.preventDefault();
17933
+ currentIndex = currentIndex === 0 ? items.length - 1 : currentIndex - 1;
17934
+ setCurrentItem(currentIndex);
17935
+ break;
17936
+ case 'Enter':
17937
+ case ' ':
17938
+ keyEvent.preventDefault();
17939
+ if (items[currentIndex]) {
17940
+ items[currentIndex].click();
17941
+ }
17942
+ break;
17943
+ }
17944
+ };
17945
+ const setCurrentItem = (index) => {
17946
+ const items = rootEl.querySelectorAll('[role="menuitem"]');
17947
+ items.forEach((item, i) => {
17948
+ item.setAttribute('tabindex', i === index ? '0' : '-1');
17949
+ if (i === index) {
17950
+ item.focus();
17951
+ }
17952
+ });
17953
+ };
17954
+ setup();
17955
+ rootEl.addEventListener('keydown', handleKeyDown);
17956
+ return {
17957
+ destroy: () => {
17958
+ rootEl.removeEventListener('keydown', handleKeyDown);
17959
+ }
17960
+ };
17961
+ }
17962
+ /**
17963
+ * Focus trap utility
17964
+ */
17965
+ function focusTrap(root) {
17966
+ const rootEl = typeof root === 'string' ? document.querySelector(root) : root;
17967
+ if (!rootEl)
17968
+ throw new Error('Focus trap root element not found');
17969
+ let isActive = false;
17970
+ let focusableElements = [];
17971
+ const updateFocusableElements = () => {
17972
+ const selector = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
17973
+ focusableElements = Array.from(rootEl.querySelectorAll(selector));
17974
+ };
17975
+ const handleKeyDown = (e) => {
17976
+ if (!isActive || e.key !== 'Tab')
17977
+ return;
17978
+ updateFocusableElements();
17979
+ if (focusableElements.length === 0)
17980
+ return;
17981
+ const firstElement = focusableElements[0];
17982
+ const lastElement = focusableElements[focusableElements.length - 1];
17983
+ if (e.shiftKey) {
17984
+ if (document.activeElement === firstElement) {
17985
+ e.preventDefault();
17986
+ lastElement.focus();
17987
+ }
17988
+ }
17989
+ else {
17990
+ if (document.activeElement === lastElement) {
17991
+ e.preventDefault();
17992
+ firstElement.focus();
17993
+ }
17994
+ }
17995
+ };
17996
+ const activate = () => {
17997
+ if (isActive)
17998
+ return;
17999
+ isActive = true;
18000
+ updateFocusableElements();
18001
+ if (focusableElements.length > 0) {
18002
+ focusableElements[0].focus();
18003
+ }
18004
+ document.addEventListener('keydown', handleKeyDown);
18005
+ };
18006
+ const deactivate = () => {
18007
+ if (!isActive)
18008
+ return;
18009
+ isActive = false;
18010
+ document.removeEventListener('keydown', handleKeyDown);
18011
+ };
18012
+ return {
18013
+ activate,
18014
+ deactivate
18015
+ };
18016
+ }
18017
+ // Export all functions
18018
+ var index$2 = {
18019
+ dialog,
18020
+ tooltip,
18021
+ combobox,
18022
+ listbox,
18023
+ tabs,
18024
+ menu,
18025
+ focusTrap
18026
+ };
18027
+
18028
+ var index$3 = /*#__PURE__*/Object.freeze({
18029
+ __proto__: null,
18030
+ combobox: combobox,
18031
+ default: index$2,
18032
+ dialog: dialog,
18033
+ focusTrap: focusTrap,
18034
+ listbox: listbox,
18035
+ menu: menu,
18036
+ tabs: tabs,
18037
+ tooltip: tooltip
18038
+ });
18039
+
18040
+ /**
18041
+ * @sc4rfurryx/proteusjs/perf
18042
+ * Performance guardrails and CWV-friendly patterns
18043
+ *
18044
+ * @version 1.1.0
18045
+ * @author sc4rfurry
18046
+ * @license MIT
18047
+ */
18048
+ /**
18049
+ * Apply content-visibility for performance optimization
18050
+ */
18051
+ function contentVisibility(selector, mode = 'auto', opts = {}) {
18052
+ const elements = typeof selector === 'string'
18053
+ ? document.querySelectorAll(selector)
18054
+ : [selector];
18055
+ const { containIntrinsicSize = '1000px 400px' } = opts;
18056
+ elements.forEach(element => {
18057
+ const el = element;
18058
+ el.style.contentVisibility = mode;
18059
+ if (mode === 'auto') {
18060
+ el.style.containIntrinsicSize = containIntrinsicSize;
18061
+ }
18062
+ });
18063
+ }
18064
+ /**
18065
+ * Set fetch priority for resources
18066
+ */
18067
+ function fetchPriority(selector, priority) {
18068
+ const elements = typeof selector === 'string'
18069
+ ? document.querySelectorAll(selector)
18070
+ : [selector];
18071
+ elements.forEach(element => {
18072
+ if (element instanceof HTMLImageElement ||
18073
+ element instanceof HTMLLinkElement ||
18074
+ element instanceof HTMLScriptElement) {
18075
+ element.fetchPriority = priority;
18076
+ }
18077
+ });
18078
+ }
18079
+ /**
18080
+ * Set up speculation rules for prerendering and prefetching
18081
+ */
18082
+ function speculate(opts) {
18083
+ const { prerender = [], prefetch = [], sameOriginOnly = true } = opts;
18084
+ // Check for Speculation Rules API support
18085
+ if (!('supports' in HTMLScriptElement && HTMLScriptElement.supports('speculationrules'))) {
18086
+ console.warn('Speculation Rules API not supported');
18087
+ return;
18088
+ }
18089
+ const rules = {};
18090
+ if (prerender.length > 0) {
18091
+ rules.prerender = prerender.map(url => {
18092
+ const rule = { where: { href_matches: url } };
18093
+ if (sameOriginOnly) {
18094
+ rule.where.href_matches = new URL(url, window.location.origin).href;
18095
+ }
18096
+ return rule;
18097
+ });
18098
+ }
18099
+ if (prefetch.length > 0) {
18100
+ rules.prefetch = prefetch.map(url => {
18101
+ const rule = { where: { href_matches: url } };
18102
+ if (sameOriginOnly) {
18103
+ rule.where.href_matches = new URL(url, window.location.origin).href;
18104
+ }
18105
+ return rule;
18106
+ });
18107
+ }
18108
+ if (Object.keys(rules).length === 0)
18109
+ return;
18110
+ // Create and inject speculation rules script
18111
+ const script = document.createElement('script');
18112
+ script.type = 'speculationrules';
18113
+ script.textContent = JSON.stringify(rules);
18114
+ document.head.appendChild(script);
18115
+ }
18116
+ /**
18117
+ * Yield to browser using scheduler.yield or postTask when available
18118
+ */
18119
+ async function yieldToBrowser() {
18120
+ // Use scheduler.yield if available (Chrome 115+)
18121
+ if ('scheduler' in window && 'yield' in window.scheduler) {
18122
+ return window.scheduler.yield();
18123
+ }
18124
+ // Use scheduler.postTask if available
18125
+ if ('scheduler' in window && 'postTask' in window.scheduler) {
18126
+ return new Promise(resolve => {
18127
+ window.scheduler.postTask(resolve, { priority: 'user-blocking' });
18128
+ });
18129
+ }
18130
+ // Fallback to setTimeout
18131
+ return new Promise(resolve => {
18132
+ setTimeout(resolve, 0);
18133
+ });
18134
+ }
18135
+ /**
18136
+ * Optimize images with loading and decoding hints
18137
+ */
18138
+ function optimizeImages(selector = 'img') {
18139
+ const images = typeof selector === 'string'
18140
+ ? document.querySelectorAll(selector)
18141
+ : [selector];
18142
+ images.forEach(img => {
18143
+ if (!(img instanceof HTMLImageElement))
18144
+ return;
18145
+ // Set loading attribute if not already set
18146
+ if (!img.hasAttribute('loading')) {
18147
+ const rect = img.getBoundingClientRect();
18148
+ const isAboveFold = rect.top < window.innerHeight;
18149
+ img.loading = isAboveFold ? 'eager' : 'lazy';
18150
+ }
18151
+ // Set decoding hint
18152
+ if (!img.hasAttribute('decoding')) {
18153
+ img.decoding = 'async';
18154
+ }
18155
+ // Set fetch priority for above-fold images
18156
+ if (!img.hasAttribute('fetchpriority')) {
18157
+ const rect = img.getBoundingClientRect();
18158
+ const isAboveFold = rect.top < window.innerHeight;
18159
+ if (isAboveFold) {
18160
+ img.fetchPriority = 'high';
18161
+ }
18162
+ }
18163
+ });
18164
+ }
18165
+ /**
18166
+ * Preload critical resources
18167
+ */
18168
+ function preloadCritical(resources) {
18169
+ resources.forEach(({ href, as, type }) => {
18170
+ // Check if already preloaded
18171
+ const existing = document.querySelector(`link[rel="preload"][href="${href}"]`);
18172
+ if (existing)
18173
+ return;
18174
+ const link = document.createElement('link');
18175
+ link.rel = 'preload';
18176
+ link.href = href;
18177
+ link.as = as;
18178
+ if (type) {
18179
+ link.type = type;
18180
+ }
18181
+ document.head.appendChild(link);
18182
+ });
18183
+ }
18184
+ /**
18185
+ * Measure and report Core Web Vitals
18186
+ */
18187
+ function measureCWV() {
18188
+ return new Promise(resolve => {
18189
+ const metrics = {};
18190
+ let metricsCount = 0;
18191
+ const totalMetrics = 3;
18192
+ const checkComplete = () => {
18193
+ metricsCount++;
18194
+ if (metricsCount >= totalMetrics) {
18195
+ resolve(metrics);
18196
+ }
18197
+ };
18198
+ // LCP (Largest Contentful Paint)
18199
+ if ('PerformanceObserver' in window) {
18200
+ try {
18201
+ const lcpObserver = new PerformanceObserver(list => {
18202
+ const entries = list.getEntries();
18203
+ const lastEntry = entries[entries.length - 1];
18204
+ metrics.lcp = lastEntry.startTime;
18205
+ });
18206
+ lcpObserver.observe({ entryTypes: ['largest-contentful-paint'] });
18207
+ // Stop observing after 10 seconds
18208
+ setTimeout(() => {
18209
+ lcpObserver.disconnect();
18210
+ checkComplete();
18211
+ }, 10000);
18212
+ }
18213
+ catch {
18214
+ checkComplete();
18215
+ }
18216
+ // FID (First Input Delay)
18217
+ try {
18218
+ const fidObserver = new PerformanceObserver(list => {
18219
+ const entries = list.getEntries();
18220
+ entries.forEach((entry) => {
18221
+ metrics.fid = entry.processingStart - entry.startTime;
18222
+ });
18223
+ fidObserver.disconnect();
18224
+ checkComplete();
18225
+ });
18226
+ fidObserver.observe({ entryTypes: ['first-input'] });
18227
+ // If no input after 10 seconds, consider FID as 0
18228
+ setTimeout(() => {
18229
+ if (metrics.fid === undefined) {
18230
+ metrics.fid = 0;
18231
+ fidObserver.disconnect();
18232
+ checkComplete();
18233
+ }
18234
+ }, 10000);
18235
+ }
18236
+ catch {
18237
+ checkComplete();
18238
+ }
18239
+ // CLS (Cumulative Layout Shift)
18240
+ try {
18241
+ let clsValue = 0;
18242
+ const clsObserver = new PerformanceObserver(list => {
18243
+ list.getEntries().forEach((entry) => {
18244
+ if (!entry.hadRecentInput) {
18245
+ clsValue += entry.value;
18246
+ }
18247
+ });
18248
+ metrics.cls = clsValue;
18249
+ });
18250
+ clsObserver.observe({ entryTypes: ['layout-shift'] });
18251
+ // Stop observing after 10 seconds
18252
+ setTimeout(() => {
18253
+ clsObserver.disconnect();
18254
+ checkComplete();
18255
+ }, 10000);
18256
+ }
18257
+ catch {
18258
+ checkComplete();
18259
+ }
18260
+ }
18261
+ else {
18262
+ // Fallback if PerformanceObserver is not supported
18263
+ setTimeout(() => resolve(metrics), 100);
18264
+ }
18265
+ });
18266
+ }
18267
+ // Export boost object to match usage examples in upgrade spec
18268
+ const boost = {
18269
+ contentVisibility,
18270
+ fetchPriority,
18271
+ speculate,
18272
+ yieldToBrowser,
18273
+ optimizeImages,
18274
+ preloadCritical,
18275
+ measureCWV
18276
+ };
18277
+ // Export all functions as named exports and default object
18278
+ var index = {
18279
+ contentVisibility,
18280
+ fetchPriority,
18281
+ speculate,
18282
+ yieldToBrowser,
18283
+ optimizeImages,
18284
+ preloadCritical,
18285
+ measureCWV,
18286
+ boost
18287
+ };
18288
+
18289
+ var index$1 = /*#__PURE__*/Object.freeze({
18290
+ __proto__: null,
18291
+ boost: boost,
18292
+ contentVisibility: contentVisibility,
18293
+ default: index,
18294
+ fetchPriority: fetchPriority,
18295
+ measureCWV: measureCWV,
18296
+ optimizeImages: optimizeImages,
18297
+ preloadCritical: preloadCritical,
18298
+ speculate: speculate,
18299
+ yieldToBrowser: yieldToBrowser
18300
+ });
18301
+
18302
+ /**
18303
+ * ProteusJS - Native-first Web Development Primitives
15993
18304
  * Shape-shifting responsive design that adapts like the sea god himself
15994
18305
  *
15995
- * @version 1.0.0
15996
- * @author ProteusJS Team
18306
+ * @version 1.1.0
18307
+ * @author sc4rfurry
15997
18308
  * @license MIT
15998
18309
  */
15999
- // Core exports
18310
+ // Core exports (legacy compatibility)
16000
18311
  // Constants
16001
- const VERSION = '1.0.0';
18312
+ const VERSION = '1.1.0';
16002
18313
  const LIBRARY_NAME = 'ProteusJS';
16003
18314
 
16004
- export { LIBRARY_NAME, ProteusJS, VERSION, ProteusJS as default, isSupported, version };
18315
+ export { LIBRARY_NAME, ProteusJS, VERSION, index$5 as a11yAudit, index$3 as a11yPrimitives, index$d as anchor, index$9 as container, ProteusJS as default, isSupported$1 as isSupported, index$1 as perf, index$b as popover, index$f as scroll, index$h as transitions, index$7 as typography, version };
16005
18316
  //# sourceMappingURL=proteus.esm.js.map