@sc4rfurryx/proteusjs 1.0.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.
- package/API.md +438 -0
- package/FEATURES.md +286 -0
- package/LICENSE +21 -0
- package/README.md +645 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/proteus.cjs.js +16014 -0
- package/dist/proteus.cjs.js.map +1 -0
- package/dist/proteus.d.ts +3018 -0
- package/dist/proteus.esm.js +16005 -0
- package/dist/proteus.esm.js.map +1 -0
- package/dist/proteus.esm.min.js +8 -0
- package/dist/proteus.esm.min.js.map +1 -0
- package/dist/proteus.js +16020 -0
- package/dist/proteus.js.map +1 -0
- package/dist/proteus.min.js +8 -0
- package/dist/proteus.min.js.map +1 -0
- package/package.json +98 -0
- package/src/__tests__/mvp-integration.test.ts +518 -0
- package/src/accessibility/AccessibilityEngine.ts +2106 -0
- package/src/accessibility/ScreenReaderSupport.ts +444 -0
- package/src/accessibility/__tests__/ScreenReaderSupport.test.ts +435 -0
- package/src/animations/FLIPAnimationSystem.ts +491 -0
- package/src/compatibility/BrowserCompatibility.ts +1076 -0
- package/src/containers/BreakpointSystem.ts +347 -0
- package/src/containers/ContainerBreakpoints.ts +726 -0
- package/src/containers/ContainerManager.ts +370 -0
- package/src/containers/ContainerUnits.ts +336 -0
- package/src/containers/ContextIsolation.ts +394 -0
- package/src/containers/ElementQueries.ts +411 -0
- package/src/containers/SmartContainer.ts +536 -0
- package/src/containers/SmartContainers.ts +376 -0
- package/src/containers/__tests__/ContainerBreakpoints.test.ts +411 -0
- package/src/containers/__tests__/SmartContainers.test.ts +281 -0
- package/src/content/ResponsiveImages.ts +570 -0
- package/src/core/EventSystem.ts +147 -0
- package/src/core/MemoryManager.ts +321 -0
- package/src/core/PerformanceMonitor.ts +238 -0
- package/src/core/PluginSystem.ts +275 -0
- package/src/core/ProteusJS.test.ts +164 -0
- package/src/core/ProteusJS.ts +962 -0
- package/src/developer/PerformanceProfiler.ts +567 -0
- package/src/developer/VisualDebuggingTools.ts +656 -0
- package/src/developer/ZeroConfigSystem.ts +593 -0
- package/src/index.ts +35 -0
- package/src/integration.test.ts +227 -0
- package/src/layout/AdaptiveGrid.ts +429 -0
- package/src/layout/ContentReordering.ts +532 -0
- package/src/layout/FlexboxEnhancer.ts +406 -0
- package/src/layout/FlowLayout.ts +545 -0
- package/src/layout/SpacingSystem.ts +512 -0
- package/src/observers/IntersectionObserverPolyfill.ts +289 -0
- package/src/observers/ObserverManager.ts +299 -0
- package/src/observers/ResizeObserverPolyfill.ts +179 -0
- package/src/performance/BatchDOMOperations.ts +519 -0
- package/src/performance/CSSOptimizationEngine.ts +646 -0
- package/src/performance/CacheOptimizationSystem.ts +601 -0
- package/src/performance/EfficientEventHandler.ts +740 -0
- package/src/performance/LazyEvaluationSystem.ts +532 -0
- package/src/performance/MemoryManagementSystem.ts +497 -0
- package/src/performance/PerformanceMonitor.ts +931 -0
- package/src/performance/__tests__/BatchDOMOperations.test.ts +309 -0
- package/src/performance/__tests__/EfficientEventHandler.test.ts +268 -0
- package/src/performance/__tests__/PerformanceMonitor.test.ts +422 -0
- package/src/polyfills/BrowserPolyfills.ts +586 -0
- package/src/polyfills/__tests__/BrowserPolyfills.test.ts +328 -0
- package/src/test/setup.ts +115 -0
- package/src/theming/SmartThemeSystem.ts +591 -0
- package/src/types/index.ts +134 -0
- package/src/typography/ClampScaling.ts +356 -0
- package/src/typography/FluidTypography.ts +759 -0
- package/src/typography/LineHeightOptimization.ts +430 -0
- package/src/typography/LineHeightOptimizer.ts +326 -0
- package/src/typography/TextFitting.ts +355 -0
- package/src/typography/TypographicScale.ts +428 -0
- package/src/typography/VerticalRhythm.ts +369 -0
- package/src/typography/__tests__/FluidTypography.test.ts +432 -0
- package/src/typography/__tests__/LineHeightOptimization.test.ts +436 -0
- package/src/utils/Logger.ts +173 -0
- package/src/utils/debounce.ts +259 -0
- package/src/utils/performance.ts +371 -0
- package/src/utils/support.ts +106 -0
- package/src/utils/version.ts +24 -0
@@ -0,0 +1,369 @@
|
|
1
|
+
/**
|
2
|
+
* Responsive Vertical Rhythm for ProteusJS
|
3
|
+
* Maintains consistent baseline grid and spacing ratios
|
4
|
+
*/
|
5
|
+
|
6
|
+
export interface RhythmConfig {
|
7
|
+
baseFontSize: number;
|
8
|
+
baseLineHeight: number;
|
9
|
+
baselineUnit: number;
|
10
|
+
scale: 'minor-second' | 'major-second' | 'minor-third' | 'major-third' | 'perfect-fourth' | 'golden-ratio' | number;
|
11
|
+
precision: number;
|
12
|
+
responsive: boolean;
|
13
|
+
containerAware: boolean;
|
14
|
+
}
|
15
|
+
|
16
|
+
export interface SpacingScale {
|
17
|
+
xs: number;
|
18
|
+
sm: number;
|
19
|
+
md: number;
|
20
|
+
lg: number;
|
21
|
+
xl: number;
|
22
|
+
xxl: number;
|
23
|
+
}
|
24
|
+
|
25
|
+
export interface RhythmResult {
|
26
|
+
baselineGrid: number;
|
27
|
+
spacingScale: SpacingScale;
|
28
|
+
lineHeights: Record<string, number>;
|
29
|
+
margins: Record<string, number>;
|
30
|
+
paddings: Record<string, number>;
|
31
|
+
}
|
32
|
+
|
33
|
+
export class VerticalRhythm {
|
34
|
+
private static readonly SCALE_RATIOS = {
|
35
|
+
'minor-second': 1.067,
|
36
|
+
'major-second': 1.125,
|
37
|
+
'minor-third': 1.2,
|
38
|
+
'major-third': 1.25,
|
39
|
+
'perfect-fourth': 1.333,
|
40
|
+
'golden-ratio': 1.618
|
41
|
+
};
|
42
|
+
|
43
|
+
private config: Required<RhythmConfig>;
|
44
|
+
private baselineGrid: number;
|
45
|
+
|
46
|
+
constructor(config: RhythmConfig) {
|
47
|
+
const defaults = {
|
48
|
+
baseFontSize: 16,
|
49
|
+
baseLineHeight: 1.5,
|
50
|
+
baselineUnit: 24, // 16px * 1.5
|
51
|
+
scale: 'minor-third' as const,
|
52
|
+
precision: 0.001,
|
53
|
+
responsive: true,
|
54
|
+
containerAware: false
|
55
|
+
};
|
56
|
+
this.config = { ...defaults, ...config };
|
57
|
+
|
58
|
+
this.baselineGrid = this.config.baselineUnit;
|
59
|
+
}
|
60
|
+
|
61
|
+
/**
|
62
|
+
* Generate complete vertical rhythm system
|
63
|
+
*/
|
64
|
+
public generateRhythm(containerSize?: number): RhythmResult {
|
65
|
+
// Adjust baseline for container size if container-aware
|
66
|
+
if (this.config.containerAware && containerSize) {
|
67
|
+
this.adjustBaselineForContainer(containerSize);
|
68
|
+
}
|
69
|
+
|
70
|
+
const spacingScale = this.generateSpacingScale();
|
71
|
+
const lineHeights = this.generateLineHeights();
|
72
|
+
const margins = this.generateMargins();
|
73
|
+
const paddings = this.generatePaddings();
|
74
|
+
|
75
|
+
return {
|
76
|
+
baselineGrid: this.baselineGrid,
|
77
|
+
spacingScale,
|
78
|
+
lineHeights,
|
79
|
+
margins,
|
80
|
+
paddings
|
81
|
+
};
|
82
|
+
}
|
83
|
+
|
84
|
+
/**
|
85
|
+
* Apply rhythm to element
|
86
|
+
*/
|
87
|
+
public applyRhythm(element: Element, rhythm: RhythmResult): void {
|
88
|
+
const htmlElement = element as HTMLElement;
|
89
|
+
|
90
|
+
// Set CSS custom properties for the rhythm system
|
91
|
+
Object.entries(rhythm.spacingScale).forEach(([key, value]) => {
|
92
|
+
htmlElement.style.setProperty(`--rhythm-${key}`, `${value}px`);
|
93
|
+
});
|
94
|
+
|
95
|
+
Object.entries(rhythm.lineHeights).forEach(([key, value]) => {
|
96
|
+
htmlElement.style.setProperty(`--line-height-${key}`, value.toString());
|
97
|
+
});
|
98
|
+
|
99
|
+
Object.entries(rhythm.margins).forEach(([key, value]) => {
|
100
|
+
htmlElement.style.setProperty(`--margin-${key}`, `${value}px`);
|
101
|
+
});
|
102
|
+
|
103
|
+
Object.entries(rhythm.paddings).forEach(([key, value]) => {
|
104
|
+
htmlElement.style.setProperty(`--padding-${key}`, `${value}px`);
|
105
|
+
});
|
106
|
+
|
107
|
+
// Set baseline grid
|
108
|
+
htmlElement.style.setProperty('--baseline-grid', `${rhythm.baselineGrid}px`);
|
109
|
+
}
|
110
|
+
|
111
|
+
/**
|
112
|
+
* Calculate spacing that aligns to baseline grid
|
113
|
+
*/
|
114
|
+
public toBaseline(value: number): number {
|
115
|
+
return Math.round(value / this.baselineGrid) * this.baselineGrid;
|
116
|
+
}
|
117
|
+
|
118
|
+
/**
|
119
|
+
* Calculate line height that maintains baseline alignment
|
120
|
+
*/
|
121
|
+
public calculateBaselineLineHeight(fontSize: number): number {
|
122
|
+
const rawLineHeight = fontSize * this.config.baseLineHeight;
|
123
|
+
const baselineAligned = this.toBaseline(rawLineHeight);
|
124
|
+
return baselineAligned / fontSize;
|
125
|
+
}
|
126
|
+
|
127
|
+
/**
|
128
|
+
* Generate responsive spacing scale
|
129
|
+
*/
|
130
|
+
public generateResponsiveSpacing(
|
131
|
+
containerSizes: number[],
|
132
|
+
scaleFactors: number[]
|
133
|
+
): Record<string, SpacingScale> {
|
134
|
+
const responsiveSpacing: Record<string, SpacingScale> = {};
|
135
|
+
|
136
|
+
containerSizes.forEach((size, index) => {
|
137
|
+
const scaleFactor = scaleFactors[index] || 1;
|
138
|
+
const scaledBaseline = this.baselineGrid * scaleFactor;
|
139
|
+
|
140
|
+
responsiveSpacing[`container-${size}`] = {
|
141
|
+
xs: scaledBaseline * 0.25,
|
142
|
+
sm: scaledBaseline * 0.5,
|
143
|
+
md: scaledBaseline,
|
144
|
+
lg: scaledBaseline * 1.5,
|
145
|
+
xl: scaledBaseline * 2,
|
146
|
+
xxl: scaledBaseline * 3
|
147
|
+
};
|
148
|
+
});
|
149
|
+
|
150
|
+
return responsiveSpacing;
|
151
|
+
}
|
152
|
+
|
153
|
+
/**
|
154
|
+
* Create CSS for vertical rhythm
|
155
|
+
*/
|
156
|
+
public generateCSS(rhythm: RhythmResult): string {
|
157
|
+
let css = ':root {\n';
|
158
|
+
|
159
|
+
// Baseline grid
|
160
|
+
css += ` --baseline-grid: ${rhythm.baselineGrid}px;\n`;
|
161
|
+
|
162
|
+
// Spacing scale
|
163
|
+
Object.entries(rhythm.spacingScale).forEach(([key, value]) => {
|
164
|
+
css += ` --rhythm-${key}: ${value}px;\n`;
|
165
|
+
});
|
166
|
+
|
167
|
+
// Line heights
|
168
|
+
Object.entries(rhythm.lineHeights).forEach(([key, value]) => {
|
169
|
+
css += ` --line-height-${key}: ${value};\n`;
|
170
|
+
});
|
171
|
+
|
172
|
+
// Margins
|
173
|
+
Object.entries(rhythm.margins).forEach(([key, value]) => {
|
174
|
+
css += ` --margin-${key}: ${value}px;\n`;
|
175
|
+
});
|
176
|
+
|
177
|
+
// Paddings
|
178
|
+
Object.entries(rhythm.paddings).forEach(([key, value]) => {
|
179
|
+
css += ` --padding-${key}: ${value}px;\n`;
|
180
|
+
});
|
181
|
+
|
182
|
+
css += '}\n\n';
|
183
|
+
|
184
|
+
// Utility classes
|
185
|
+
css += this.generateUtilityClasses(rhythm);
|
186
|
+
|
187
|
+
return css;
|
188
|
+
}
|
189
|
+
|
190
|
+
/**
|
191
|
+
* Validate rhythm consistency
|
192
|
+
*/
|
193
|
+
public validateRhythm(rhythm: RhythmResult): { valid: boolean; issues: string[] } {
|
194
|
+
const issues: string[] = [];
|
195
|
+
|
196
|
+
// Check if all spacing values align to baseline
|
197
|
+
Object.entries(rhythm.spacingScale).forEach(([key, value]) => {
|
198
|
+
if (value % rhythm.baselineGrid !== 0) {
|
199
|
+
issues.push(`Spacing ${key} (${value}px) doesn't align to baseline grid (${rhythm.baselineGrid}px)`);
|
200
|
+
}
|
201
|
+
});
|
202
|
+
|
203
|
+
// Check if margins align to baseline
|
204
|
+
Object.entries(rhythm.margins).forEach(([key, value]) => {
|
205
|
+
if (value % rhythm.baselineGrid !== 0) {
|
206
|
+
issues.push(`Margin ${key} (${value}px) doesn't align to baseline grid`);
|
207
|
+
}
|
208
|
+
});
|
209
|
+
|
210
|
+
return {
|
211
|
+
valid: issues.length === 0,
|
212
|
+
issues
|
213
|
+
};
|
214
|
+
}
|
215
|
+
|
216
|
+
/**
|
217
|
+
* Generate spacing scale based on ratio
|
218
|
+
*/
|
219
|
+
private generateSpacingScale(): SpacingScale {
|
220
|
+
const ratio = typeof this.config.scale === 'number'
|
221
|
+
? this.config.scale
|
222
|
+
: VerticalRhythm.SCALE_RATIOS[this.config.scale];
|
223
|
+
|
224
|
+
const base = this.baselineGrid;
|
225
|
+
|
226
|
+
return {
|
227
|
+
xs: this.toBaseline(base / (ratio * ratio)), // base / ratio²
|
228
|
+
sm: this.toBaseline(base / ratio), // base / ratio
|
229
|
+
md: base, // base
|
230
|
+
lg: this.toBaseline(base * ratio), // base * ratio
|
231
|
+
xl: this.toBaseline(base * ratio * ratio), // base * ratio²
|
232
|
+
xxl: this.toBaseline(base * ratio * ratio * ratio) // base * ratio³
|
233
|
+
};
|
234
|
+
}
|
235
|
+
|
236
|
+
/**
|
237
|
+
* Generate line heights for different text sizes
|
238
|
+
*/
|
239
|
+
private generateLineHeights(): Record<string, number> {
|
240
|
+
return {
|
241
|
+
'tight': 1.2,
|
242
|
+
'normal': 1.5,
|
243
|
+
'relaxed': 1.75,
|
244
|
+
'loose': 2.0
|
245
|
+
};
|
246
|
+
}
|
247
|
+
|
248
|
+
/**
|
249
|
+
* Generate margin scale
|
250
|
+
*/
|
251
|
+
private generateMargins(): Record<string, number> {
|
252
|
+
const spacingScale = this.generateSpacingScale();
|
253
|
+
|
254
|
+
return {
|
255
|
+
'none': 0,
|
256
|
+
'xs': spacingScale.xs,
|
257
|
+
'sm': spacingScale.sm,
|
258
|
+
'md': spacingScale.md,
|
259
|
+
'lg': spacingScale.lg,
|
260
|
+
'xl': spacingScale.xl,
|
261
|
+
'auto': -1 // Special value for auto margins
|
262
|
+
};
|
263
|
+
}
|
264
|
+
|
265
|
+
/**
|
266
|
+
* Generate padding scale
|
267
|
+
*/
|
268
|
+
private generatePaddings(): Record<string, number> {
|
269
|
+
const spacingScale = this.generateSpacingScale();
|
270
|
+
|
271
|
+
return {
|
272
|
+
'none': 0,
|
273
|
+
'xs': spacingScale.xs,
|
274
|
+
'sm': spacingScale.sm,
|
275
|
+
'md': spacingScale.md,
|
276
|
+
'lg': spacingScale.lg,
|
277
|
+
'xl': spacingScale.xl
|
278
|
+
};
|
279
|
+
}
|
280
|
+
|
281
|
+
/**
|
282
|
+
* Adjust baseline for container size
|
283
|
+
*/
|
284
|
+
private adjustBaselineForContainer(containerSize: number): void {
|
285
|
+
// Scale baseline based on container size
|
286
|
+
// Smaller containers get smaller baseline, larger containers get larger baseline
|
287
|
+
const scaleFactor = Math.max(0.75, Math.min(1.5, containerSize / 800));
|
288
|
+
this.baselineGrid = Math.round(this.config.baselineUnit * scaleFactor);
|
289
|
+
}
|
290
|
+
|
291
|
+
/**
|
292
|
+
* Generate utility CSS classes
|
293
|
+
*/
|
294
|
+
private generateUtilityClasses(rhythm: RhythmResult): string {
|
295
|
+
let css = '';
|
296
|
+
|
297
|
+
// Spacing utilities
|
298
|
+
Object.entries(rhythm.spacingScale).forEach(([key, value]) => {
|
299
|
+
css += `.m-${key} { margin: ${value}px; }\n`;
|
300
|
+
css += `.mt-${key} { margin-top: ${value}px; }\n`;
|
301
|
+
css += `.mr-${key} { margin-right: ${value}px; }\n`;
|
302
|
+
css += `.mb-${key} { margin-bottom: ${value}px; }\n`;
|
303
|
+
css += `.ml-${key} { margin-left: ${value}px; }\n`;
|
304
|
+
css += `.mx-${key} { margin-left: ${value}px; margin-right: ${value}px; }\n`;
|
305
|
+
css += `.my-${key} { margin-top: ${value}px; margin-bottom: ${value}px; }\n`;
|
306
|
+
|
307
|
+
css += `.p-${key} { padding: ${value}px; }\n`;
|
308
|
+
css += `.pt-${key} { padding-top: ${value}px; }\n`;
|
309
|
+
css += `.pr-${key} { padding-right: ${value}px; }\n`;
|
310
|
+
css += `.pb-${key} { padding-bottom: ${value}px; }\n`;
|
311
|
+
css += `.pl-${key} { padding-left: ${value}px; }\n`;
|
312
|
+
css += `.px-${key} { padding-left: ${value}px; padding-right: ${value}px; }\n`;
|
313
|
+
css += `.py-${key} { padding-top: ${value}px; padding-bottom: ${value}px; }\n`;
|
314
|
+
});
|
315
|
+
|
316
|
+
// Line height utilities
|
317
|
+
Object.entries(rhythm.lineHeights).forEach(([key, value]) => {
|
318
|
+
css += `.leading-${key} { line-height: ${value}; }\n`;
|
319
|
+
});
|
320
|
+
|
321
|
+
// Baseline alignment utilities
|
322
|
+
css += '.baseline-align { ';
|
323
|
+
css += `background-image: linear-gradient(to bottom, transparent ${rhythm.baselineGrid - 1}px, rgba(255, 0, 0, 0.1) ${rhythm.baselineGrid - 1}px, rgba(255, 0, 0, 0.1) ${rhythm.baselineGrid}px, transparent ${rhythm.baselineGrid}px); `;
|
324
|
+
css += `background-size: 100% ${rhythm.baselineGrid}px; `;
|
325
|
+
css += '}\n';
|
326
|
+
|
327
|
+
return css;
|
328
|
+
}
|
329
|
+
|
330
|
+
/**
|
331
|
+
* Create responsive vertical rhythm
|
332
|
+
*/
|
333
|
+
public createResponsiveRhythm(
|
334
|
+
element: Element,
|
335
|
+
containerSizes: number[],
|
336
|
+
scaleFactors: number[]
|
337
|
+
): () => void {
|
338
|
+
const updateRhythm = () => {
|
339
|
+
const containerWidth = element.getBoundingClientRect().width;
|
340
|
+
|
341
|
+
// Find appropriate scale factor
|
342
|
+
let scaleFactor = 1;
|
343
|
+
for (let i = 0; i < containerSizes.length; i++) {
|
344
|
+
if (containerWidth >= containerSizes[i]!) {
|
345
|
+
scaleFactor = scaleFactors[i] || 1;
|
346
|
+
}
|
347
|
+
}
|
348
|
+
|
349
|
+
// Generate rhythm for current container size
|
350
|
+
const rhythm = this.generateRhythm(containerWidth);
|
351
|
+
this.applyRhythm(element, rhythm);
|
352
|
+
};
|
353
|
+
|
354
|
+
// Initial application
|
355
|
+
updateRhythm();
|
356
|
+
|
357
|
+
// Set up resize observer
|
358
|
+
const resizeObserver = new ResizeObserver(() => {
|
359
|
+
updateRhythm();
|
360
|
+
});
|
361
|
+
|
362
|
+
resizeObserver.observe(element);
|
363
|
+
|
364
|
+
// Return cleanup function
|
365
|
+
return () => {
|
366
|
+
resizeObserver.disconnect();
|
367
|
+
};
|
368
|
+
}
|
369
|
+
}
|