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