@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
@@ -0,0 +1,251 @@
1
+ /**
2
+ * @sc4rfurryx/proteusjs/scroll
3
+ * Scroll-driven animations with CSS Scroll-Linked Animations
4
+ *
5
+ * @version 1.1.0
6
+ * @author sc4rfurry
7
+ * @license MIT
8
+ */
9
+
10
+ export interface ScrollAnimateOptions {
11
+ keyframes: Keyframe[];
12
+ range?: [string, string];
13
+ timeline?: {
14
+ axis?: 'block' | 'inline';
15
+ start?: string;
16
+ end?: string;
17
+ };
18
+ fallback?: 'io' | false;
19
+ }
20
+
21
+ /**
22
+ * Zero-boilerplate setup for CSS Scroll-Linked Animations with fallbacks
23
+ */
24
+ export function scrollAnimate(
25
+ target: Element | string,
26
+ opts: ScrollAnimateOptions
27
+ ): void {
28
+ const targetEl = typeof target === 'string' ? document.querySelector(target) : target;
29
+ if (!targetEl) {
30
+ throw new Error('Target element not found');
31
+ }
32
+
33
+ const {
34
+ keyframes,
35
+ range = ['0%', '100%'],
36
+ timeline = {},
37
+ fallback = 'io'
38
+ } = opts;
39
+
40
+ const {
41
+ axis = 'block',
42
+ start = '0%',
43
+ end = '100%'
44
+ } = timeline;
45
+
46
+ // Check for CSS Scroll-Linked Animations support
47
+ const hasScrollTimeline = 'CSS' in window && CSS.supports('animation-timeline', 'scroll()');
48
+
49
+ // Check for reduced motion preference
50
+ const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
51
+
52
+ if (prefersReducedMotion) {
53
+ // Respect user preference - either disable or reduce animation
54
+ if (fallback === false) return;
55
+
56
+ // Apply only the end state for reduced motion
57
+ const endKeyframe = keyframes[keyframes.length - 1];
58
+ Object.assign((targetEl as HTMLElement).style, endKeyframe);
59
+ return;
60
+ }
61
+
62
+ if (hasScrollTimeline) {
63
+ // Use native CSS Scroll-Linked Animations
64
+ const timelineName = `scroll-timeline-${Math.random().toString(36).substr(2, 9)}`;
65
+
66
+ // Create scroll timeline
67
+ const style = document.createElement('style');
68
+ style.textContent = `
69
+ @scroll-timeline ${timelineName} {
70
+ source: nearest;
71
+ orientation: ${axis};
72
+ scroll-offsets: ${start}, ${end};
73
+ }
74
+
75
+ .scroll-animate-${timelineName} {
76
+ animation-timeline: ${timelineName};
77
+ animation-duration: 1ms; /* Required but ignored */
78
+ animation-fill-mode: both;
79
+ }
80
+ `;
81
+ document.head.appendChild(style);
82
+
83
+ // Apply animation class
84
+ targetEl.classList.add(`scroll-animate-${timelineName}`);
85
+
86
+ // Create Web Animations API animation
87
+ const animation = targetEl.animate(keyframes, {
88
+ duration: 1, // Required but ignored with scroll timeline
89
+ fill: 'both'
90
+ });
91
+
92
+ // Set scroll timeline (when supported)
93
+ if ('timeline' in animation) {
94
+ (animation as any).timeline = new (window as any).ScrollTimeline({
95
+ source: document.scrollingElement,
96
+ orientation: axis,
97
+ scrollOffsets: [
98
+ { target: targetEl, edge: 'start', threshold: parseFloat(start) / 100 },
99
+ { target: targetEl, edge: 'end', threshold: parseFloat(end) / 100 }
100
+ ]
101
+ });
102
+ }
103
+
104
+ } else if (fallback === 'io') {
105
+ // Fallback using Intersection Observer
106
+ let animation: Animation | null = null;
107
+
108
+ const observer = new IntersectionObserver(
109
+ (entries) => {
110
+ entries.forEach(entry => {
111
+ const progress = Math.max(0, Math.min(1, entry.intersectionRatio));
112
+
113
+ if (!animation) {
114
+ animation = targetEl.animate(keyframes, {
115
+ duration: 1000,
116
+ fill: 'both'
117
+ });
118
+ animation.pause();
119
+ }
120
+
121
+ // Update animation progress based on intersection
122
+ animation.currentTime = progress * 1000;
123
+ });
124
+ },
125
+ {
126
+ threshold: Array.from({ length: 101 }, (_, i) => i / 100) // 0 to 1 in 0.01 steps
127
+ }
128
+ );
129
+
130
+ observer.observe(targetEl);
131
+
132
+ // Store cleanup function
133
+ (targetEl as any)._scrollAnimateCleanup = () => {
134
+ observer.disconnect();
135
+ if (animation) {
136
+ animation.cancel();
137
+ }
138
+ };
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Create a scroll-triggered animation that plays once when element enters viewport
144
+ */
145
+ export function scrollTrigger(
146
+ target: Element | string,
147
+ keyframes: Keyframe[],
148
+ options: KeyframeAnimationOptions = {}
149
+ ): void {
150
+ const targetEl = typeof target === 'string' ? document.querySelector(target) : target;
151
+ if (!targetEl) {
152
+ throw new Error('Target element not found');
153
+ }
154
+
155
+ // Check for reduced motion preference
156
+ const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
157
+
158
+ if (prefersReducedMotion) {
159
+ // Apply end state immediately
160
+ const endKeyframe = keyframes[keyframes.length - 1];
161
+ Object.assign((targetEl as HTMLElement).style, endKeyframe);
162
+ return;
163
+ }
164
+
165
+ const observer = new IntersectionObserver(
166
+ (entries) => {
167
+ entries.forEach(entry => {
168
+ if (entry.isIntersecting) {
169
+ // Play animation
170
+ targetEl.animate(keyframes, {
171
+ duration: 600,
172
+ easing: 'ease-out',
173
+ fill: 'forwards',
174
+ ...options
175
+ });
176
+
177
+ // Disconnect observer after first trigger
178
+ observer.disconnect();
179
+ }
180
+ });
181
+ },
182
+ {
183
+ threshold: 0.1,
184
+ rootMargin: '0px 0px -10% 0px'
185
+ }
186
+ );
187
+
188
+ observer.observe(targetEl);
189
+ }
190
+
191
+ /**
192
+ * Parallax effect using scroll-driven animations
193
+ */
194
+ export function parallax(
195
+ target: Element | string,
196
+ speed: number = 0.5
197
+ ): void {
198
+ const targetEl = typeof target === 'string' ? document.querySelector(target) : target;
199
+ if (!targetEl) {
200
+ throw new Error('Target element not found');
201
+ }
202
+
203
+ // Check for reduced motion preference
204
+ const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
205
+ if (prefersReducedMotion) return;
206
+
207
+ const keyframes = [
208
+ { transform: `translateY(${-100 * speed}px)` },
209
+ { transform: `translateY(${100 * speed}px)` }
210
+ ];
211
+
212
+ scrollAnimate(targetEl, {
213
+ keyframes,
214
+ range: ['0%', '100%'],
215
+ timeline: { axis: 'block' },
216
+ fallback: 'io'
217
+ });
218
+ }
219
+
220
+ /**
221
+ * Cleanup function to remove scroll animations
222
+ */
223
+ export function cleanup(target: Element | string): void {
224
+ const targetEl = typeof target === 'string' ? document.querySelector(target) : target;
225
+ if (!targetEl) return;
226
+
227
+ // Call stored cleanup function if it exists
228
+ if ((targetEl as any)._scrollAnimateCleanup) {
229
+ (targetEl as any)._scrollAnimateCleanup();
230
+ delete (targetEl as any)._scrollAnimateCleanup;
231
+ }
232
+
233
+ // Remove animation classes
234
+ targetEl.classList.forEach(className => {
235
+ if (className.startsWith('scroll-animate-')) {
236
+ targetEl.classList.remove(className);
237
+ }
238
+ });
239
+
240
+ // Cancel any running animations
241
+ const animations = targetEl.getAnimations();
242
+ animations.forEach(animation => animation.cancel());
243
+ }
244
+
245
+ // Export default object for convenience
246
+ export default {
247
+ scrollAnimate,
248
+ scrollTrigger,
249
+ parallax,
250
+ cleanup
251
+ };
@@ -0,0 +1,145 @@
1
+ /**
2
+ * @sc4rfurryx/proteusjs/transitions
3
+ * View Transitions API wrapper with safe fallbacks
4
+ *
5
+ * @version 1.1.0
6
+ * @author sc4rfurry
7
+ * @license MIT
8
+ */
9
+
10
+ export interface TransitionOptions {
11
+ name?: string;
12
+ duration?: number;
13
+ onBefore?: () => void;
14
+ onAfter?: () => void;
15
+ allowInterrupt?: boolean;
16
+ }
17
+
18
+ export interface NavigateOptions {
19
+ name?: string;
20
+ prerender?: boolean;
21
+ }
22
+
23
+ /**
24
+ * One API for animating DOM state changes and cross-document navigations
25
+ * using the View Transitions API with safe fallbacks.
26
+ */
27
+ export async function transition(
28
+ run: () => Promise<any> | any,
29
+ opts: TransitionOptions = {}
30
+ ): Promise<void> {
31
+ const {
32
+ name,
33
+ duration = 300,
34
+ onBefore,
35
+ onAfter,
36
+ allowInterrupt = true
37
+ } = opts;
38
+
39
+ // Check for View Transitions API support
40
+ const hasViewTransitions = 'startViewTransition' in document;
41
+
42
+ if (onBefore) {
43
+ onBefore();
44
+ }
45
+
46
+ if (!hasViewTransitions) {
47
+ // Fallback: run immediately without transitions
48
+ try {
49
+ await run();
50
+ } finally {
51
+ if (onAfter) {
52
+ onAfter();
53
+ }
54
+ }
55
+ return;
56
+ }
57
+
58
+ // Use native View Transitions API
59
+ try {
60
+ const viewTransition = (document as any).startViewTransition(async () => {
61
+ await run();
62
+ });
63
+
64
+ // Add CSS view-transition-name if name provided
65
+ if (name) {
66
+ const style = document.createElement('style');
67
+ style.textContent = `
68
+ ::view-transition-old(${name}),
69
+ ::view-transition-new(${name}) {
70
+ animation-duration: ${duration}ms;
71
+ }
72
+ `;
73
+ document.head.appendChild(style);
74
+
75
+ // Clean up style after transition
76
+ viewTransition.finished.finally(() => {
77
+ style.remove();
78
+ });
79
+ }
80
+
81
+ await viewTransition.finished;
82
+ } catch (error) {
83
+ console.warn('View transition failed, falling back to immediate execution:', error);
84
+ await run();
85
+ } finally {
86
+ if (onAfter) {
87
+ onAfter();
88
+ }
89
+ }
90
+ }
91
+
92
+ /**
93
+ * MPA-friendly navigation with view transitions when supported
94
+ */
95
+ export async function navigate(url: string, opts: NavigateOptions = {}): Promise<void> {
96
+ const { name, prerender = false } = opts;
97
+
98
+ // Optional prerender hint (basic implementation)
99
+ if (prerender && 'speculation' in HTMLScriptElement.prototype) {
100
+ const script = document.createElement('script');
101
+ script.type = 'speculationrules';
102
+ script.textContent = JSON.stringify({
103
+ prerender: [{ where: { href_matches: url } }]
104
+ });
105
+ document.head.appendChild(script);
106
+ }
107
+
108
+ // Check for View Transitions API support
109
+ const hasViewTransitions = 'startViewTransition' in document;
110
+
111
+ if (!hasViewTransitions) {
112
+ // Fallback: normal navigation
113
+ window.location.href = url;
114
+ return;
115
+ }
116
+
117
+ try {
118
+ // Use view transitions for navigation
119
+ const viewTransition = (document as any).startViewTransition(() => {
120
+ window.location.href = url;
121
+ });
122
+
123
+ if (name) {
124
+ const style = document.createElement('style');
125
+ style.textContent = `
126
+ ::view-transition-old(${name}),
127
+ ::view-transition-new(${name}) {
128
+ animation-duration: 300ms;
129
+ }
130
+ `;
131
+ document.head.appendChild(style);
132
+ }
133
+
134
+ await viewTransition.finished;
135
+ } catch (error) {
136
+ console.warn('View transition navigation failed, falling back to normal navigation:', error);
137
+ window.location.href = url;
138
+ }
139
+ }
140
+
141
+ // Export default object for convenience
142
+ export default {
143
+ transition,
144
+ navigate
145
+ };
@@ -0,0 +1,239 @@
1
+ /**
2
+ * @sc4rfurryx/proteusjs/typography
3
+ * Fluid typography with CSS-first approach
4
+ *
5
+ * @version 1.1.0
6
+ * @author sc4rfurry
7
+ * @license MIT
8
+ */
9
+
10
+ export interface FluidTypeOptions {
11
+ minViewportPx?: number;
12
+ maxViewportPx?: number;
13
+ lineHeight?: number;
14
+ containerUnits?: boolean;
15
+ }
16
+
17
+ export interface FluidTypeResult {
18
+ css: string;
19
+ }
20
+
21
+ /**
22
+ * Generate pure-CSS clamp() rules for fluid typography
23
+ */
24
+ export function fluidType(
25
+ minRem: number,
26
+ maxRem: number,
27
+ options: FluidTypeOptions = {}
28
+ ): FluidTypeResult {
29
+ const {
30
+ minViewportPx = 320,
31
+ maxViewportPx = 1200,
32
+ lineHeight,
33
+ containerUnits = false
34
+ } = options;
35
+
36
+ // Convert rem to px for calculations (assuming 16px base)
37
+ const minPx = minRem * 16;
38
+ const maxPx = maxRem * 16;
39
+
40
+ // Calculate slope and y-intercept for linear interpolation
41
+ const slope = (maxPx - minPx) / (maxViewportPx - minViewportPx);
42
+ const yIntercept = minPx - slope * minViewportPx;
43
+
44
+ // Generate clamp() function
45
+ const viewportUnit = containerUnits ? 'cqw' : 'vw';
46
+ const clampValue = `clamp(${minRem}rem, ${yIntercept / 16}rem + ${slope * 100}${viewportUnit}, ${maxRem}rem)`;
47
+
48
+ let css = `font-size: ${clampValue};`;
49
+
50
+ // Add line-height if specified
51
+ if (lineHeight) {
52
+ css += `\nline-height: ${lineHeight};`;
53
+ }
54
+
55
+ return { css };
56
+ }
57
+
58
+ /**
59
+ * Apply fluid typography to elements
60
+ */
61
+ export function applyFluidType(
62
+ selector: string,
63
+ minRem: number,
64
+ maxRem: number,
65
+ options: FluidTypeOptions = {}
66
+ ): void {
67
+ const { css } = fluidType(minRem, maxRem, options);
68
+
69
+ const styleElement = document.createElement('style');
70
+ styleElement.textContent = `${selector} {\n ${css.replace(/\n/g, '\n ')}\n}`;
71
+ styleElement.setAttribute('data-proteus-typography', selector);
72
+ document.head.appendChild(styleElement);
73
+ }
74
+
75
+ /**
76
+ * Create a complete typographic scale
77
+ */
78
+ export function createTypographicScale(
79
+ baseSize: number = 1,
80
+ ratio: number = 1.25,
81
+ steps: number = 6,
82
+ options: FluidTypeOptions = {}
83
+ ): Record<string, FluidTypeResult> {
84
+ const scale: Record<string, FluidTypeResult> = {};
85
+
86
+ for (let i = -2; i <= steps - 3; i++) {
87
+ const size = baseSize * Math.pow(ratio, i);
88
+ const minSize = size * 0.8; // 20% smaller at min viewport
89
+ const maxSize = size * 1.2; // 20% larger at max viewport
90
+
91
+ const stepName = i <= 0 ? `small${Math.abs(i)}` : `large${i}`;
92
+ scale[stepName] = fluidType(minSize, maxSize, options);
93
+ }
94
+
95
+ return scale;
96
+ }
97
+
98
+ /**
99
+ * Generate CSS custom properties for a typographic scale
100
+ */
101
+ export function generateScaleCSS(
102
+ scale: Record<string, FluidTypeResult>,
103
+ prefix: string = '--font-size'
104
+ ): string {
105
+ const cssVars = Object.entries(scale)
106
+ .map(([name, result]) => ` ${prefix}-${name}: ${result.css.replace('font-size: ', '').replace(';', '')};`)
107
+ .join('\n');
108
+
109
+ return `:root {\n${cssVars}\n}`;
110
+ }
111
+
112
+ /**
113
+ * Optimize line height for readability
114
+ */
115
+ export function optimizeLineHeight(fontSize: number, measure: number = 65): number {
116
+ // Optimal line height based on font size and measure (characters per line)
117
+ // Smaller fonts need more line height, larger fonts need less
118
+ const baseLineHeight = 1.4;
119
+ const sizeAdjustment = Math.max(0.1, Math.min(0.3, (1 - fontSize) * 0.5));
120
+ const measureAdjustment = Math.max(-0.1, Math.min(0.1, (65 - measure) * 0.002));
121
+
122
+ return baseLineHeight + sizeAdjustment + measureAdjustment;
123
+ }
124
+
125
+ /**
126
+ * Calculate optimal font size for container width
127
+ */
128
+ export function calculateOptimalSize(
129
+ containerWidth: number,
130
+ targetCharacters: number = 65,
131
+ baseCharWidth: number = 0.5
132
+ ): number {
133
+ // Calculate font size to achieve target characters per line
134
+ const optimalFontSize = containerWidth / (targetCharacters * baseCharWidth);
135
+
136
+ // Clamp to reasonable bounds (12px to 24px)
137
+ return Math.max(0.75, Math.min(1.5, optimalFontSize));
138
+ }
139
+
140
+ /**
141
+ * Apply responsive typography to an element
142
+ */
143
+ export function makeResponsive(
144
+ target: Element | string,
145
+ options: {
146
+ minSize?: number;
147
+ maxSize?: number;
148
+ targetCharacters?: number;
149
+ autoLineHeight?: boolean;
150
+ } = {}
151
+ ): void {
152
+ const targetEl = typeof target === 'string' ? document.querySelector(target) : target;
153
+ if (!targetEl) {
154
+ throw new Error('Target element not found');
155
+ }
156
+
157
+ const {
158
+ minSize = 0.875,
159
+ maxSize = 1.25,
160
+ targetCharacters = 65,
161
+ autoLineHeight = true
162
+ } = options;
163
+
164
+ // Apply fluid typography
165
+ const { css } = fluidType(minSize, maxSize);
166
+ const element = targetEl as HTMLElement;
167
+
168
+ // Parse and apply CSS
169
+ const styles = css.split(';').filter(Boolean);
170
+ styles.forEach(style => {
171
+ const [property, value] = style.split(':').map(s => s.trim());
172
+ if (property && value) {
173
+ element.style.setProperty(property, value);
174
+ }
175
+ });
176
+
177
+ // Auto line height if enabled
178
+ if (autoLineHeight) {
179
+ const updateLineHeight = () => {
180
+ const computedStyle = getComputedStyle(element);
181
+ const fontSize = parseFloat(computedStyle.fontSize);
182
+ const containerWidth = element.getBoundingClientRect().width;
183
+ const charactersPerLine = containerWidth / (fontSize * 0.5);
184
+
185
+ const optimalLineHeight = optimizeLineHeight(fontSize / 16, charactersPerLine);
186
+ element.style.lineHeight = optimalLineHeight.toString();
187
+ };
188
+
189
+ updateLineHeight();
190
+
191
+ // Update on resize
192
+ if ('ResizeObserver' in window) {
193
+ const resizeObserver = new ResizeObserver(updateLineHeight);
194
+ resizeObserver.observe(element);
195
+
196
+ // Store cleanup function
197
+ (element as any)._proteusTypographyCleanup = () => {
198
+ resizeObserver.disconnect();
199
+ };
200
+ }
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Remove applied typography styles
206
+ */
207
+ export function cleanup(target?: Element | string): void {
208
+ if (target) {
209
+ const targetEl = typeof target === 'string' ? document.querySelector(target) : target;
210
+ if (targetEl && (targetEl as any)._proteusTypographyCleanup) {
211
+ (targetEl as any)._proteusTypographyCleanup();
212
+ delete (targetEl as any)._proteusTypographyCleanup;
213
+ }
214
+ } else {
215
+ // Remove all typography style elements
216
+ const styleElements = document.querySelectorAll('style[data-proteus-typography]');
217
+ styleElements.forEach(element => element.remove());
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Check if container query units are supported
223
+ */
224
+ export function supportsContainerUnits(): boolean {
225
+ return CSS.supports('width', '1cqw');
226
+ }
227
+
228
+ // Export default object for convenience
229
+ export default {
230
+ fluidType,
231
+ applyFluidType,
232
+ createTypographicScale,
233
+ generateScaleCSS,
234
+ optimizeLineHeight,
235
+ calculateOptimalSize,
236
+ makeResponsive,
237
+ cleanup,
238
+ supportsContainerUnits
239
+ };
@@ -2,7 +2,7 @@
2
2
  * Version utilities for ProteusJS
3
3
  */
4
4
 
5
- export const version = '1.0.0';
5
+ export const version = '1.1.0';
6
6
 
7
7
  export function getVersion(): string {
8
8
  return version;