juxscript 1.0.7 → 1.0.8

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.
@@ -2413,6 +2413,18 @@
2413
2413
  "returns": "this",
2414
2414
  "description": "Set animationDuration"
2415
2415
  },
2416
+ {
2417
+ "name": "borderRadius",
2418
+ "params": "(value)",
2419
+ "returns": "this",
2420
+ "description": "Set borderRadius"
2421
+ },
2422
+ {
2423
+ "name": "showAccentBar",
2424
+ "params": "(value)",
2425
+ "returns": "this",
2426
+ "description": "Set showAccentBar"
2427
+ },
2416
2428
  {
2417
2429
  "name": "class",
2418
2430
  "params": "(value)",
@@ -3431,5 +3443,5 @@
3431
3443
  }
3432
3444
  ],
3433
3445
  "version": "1.0.0",
3434
- "lastUpdated": "2026-01-21T04:25:36.636Z"
3446
+ "lastUpdated": "2026-01-21T05:03:38.497Z"
3435
3447
  }
@@ -21,9 +21,11 @@ export interface KPICardOptions {
21
21
  width?: number;
22
22
  height?: number;
23
23
  theme?: 'google' | 'seriesa' | 'hr' | 'figma' | 'notion' | 'chalk' | 'mint';
24
- styleMode?: 'default' | 'gradient' | 'glass' | 'outline';
24
+ styleMode?: 'default' | 'gradient' | 'glass' | 'outline' | 'glow';
25
25
  animate?: boolean;
26
26
  animationDuration?: number;
27
+ borderRadius?: number;
28
+ showAccentBar?: boolean;
27
29
  class?: string;
28
30
  style?: string;
29
31
  }
@@ -40,9 +42,11 @@ type KPICardState = {
40
42
  width: number;
41
43
  height: number;
42
44
  theme: 'google' | 'seriesa' | 'hr' | 'figma' | 'notion' | 'chalk' | 'mint';
43
- styleMode: 'default' | 'gradient' | 'glass' | 'outline';
45
+ styleMode: 'default' | 'gradient' | 'glass' | 'outline' | 'glow';
44
46
  animate: boolean;
45
47
  animationDuration: number;
48
+ borderRadius: number;
49
+ showAccentBar: boolean;
46
50
  class: string;
47
51
  style: string;
48
52
  };
@@ -56,6 +60,7 @@ type KPICardState = {
56
60
  * .value('78k')
57
61
  * .delta(10)
58
62
  * .theme('mint')
63
+ * .styleMode('gradient')
59
64
  * .render('#app');
60
65
  */
61
66
  export class KPICard {
@@ -75,6 +80,8 @@ export class KPICard {
75
80
  styleMode: 'default',
76
81
  animate: true,
77
82
  animationDuration: 600,
83
+ borderRadius: 16,
84
+ showAccentBar: true,
78
85
  class: '',
79
86
  style: ''
80
87
  };
@@ -100,6 +107,8 @@ export class KPICard {
100
107
  if (options.styleMode !== undefined) this.state.styleMode = options.styleMode;
101
108
  if (options.animate !== undefined) this.state.animate = options.animate;
102
109
  if (options.animationDuration !== undefined) this.state.animationDuration = options.animationDuration;
110
+ if (options.borderRadius !== undefined) this.state.borderRadius = options.borderRadius;
111
+ if (options.showAccentBar !== undefined) this.state.showAccentBar = options.showAccentBar;
103
112
  if (options.class !== undefined) this.state.class = options.class;
104
113
  if (options.style !== undefined) this.state.style = options.style;
105
114
  }
@@ -205,6 +214,16 @@ export class KPICard {
205
214
  return this;
206
215
  }
207
216
 
217
+ borderRadius(value: number): this {
218
+ this.state.borderRadius = value;
219
+ return this;
220
+ }
221
+
222
+ showAccentBar(value: boolean): this {
223
+ this.state.showAccentBar = value;
224
+ return this;
225
+ }
226
+
208
227
  class(value: string): this {
209
228
  this.state.class = value;
210
229
  return this;
@@ -223,6 +242,7 @@ export class KPICard {
223
242
  return this;
224
243
  }
225
244
 
245
+ this._loadThemeFont();
226
246
  this._buildCard(element as HTMLElement);
227
247
  return this;
228
248
  }
@@ -235,9 +255,21 @@ export class KPICard {
235
255
 
236
256
  // Clear and rebuild
237
257
  element.innerHTML = '';
258
+ this._loadThemeFont();
238
259
  this._buildCard(element as HTMLElement);
239
260
  }
240
261
 
262
+ private _loadThemeFont(): void {
263
+ const themeConfig = this._getThemeConfig();
264
+
265
+ if (themeConfig.font && !document.querySelector(`link[href="${themeConfig.font}"]`)) {
266
+ const link = document.createElement('link');
267
+ link.rel = 'stylesheet';
268
+ link.href = themeConfig.font;
269
+ document.head.appendChild(link);
270
+ }
271
+ }
272
+
241
273
  private _buildCard(container: HTMLElement): void {
242
274
  const { width, height, class: className, style, animate, animationDuration } = this.state;
243
275
 
@@ -277,12 +309,18 @@ export class KPICard {
277
309
  }
278
310
 
279
311
  private _createContent(): HTMLElement {
280
- const { title, value, delta, prefix, suffix, animate, animationDuration } = this.state;
312
+ const { title, value, delta, prefix, suffix, animate, animationDuration, styleMode, width, height } = this.state;
313
+ const themeConfig = this._getThemeConfig();
314
+
315
+ // Calculate scale factor based on default dimensions (280x200)
316
+ const baseWidth = 280;
317
+ const baseHeight = 200;
318
+ const scaleFactor = Math.min(width / baseWidth, height / baseHeight);
281
319
 
282
320
  const content = document.createElement('div');
283
321
  content.className = 'jux-kpicard-content';
284
322
  content.style.cssText = `
285
- padding: 24px;
323
+ padding: ${24 * scaleFactor}px;
286
324
  display: flex;
287
325
  flex-direction: column;
288
326
  height: 100%;
@@ -294,11 +332,11 @@ export class KPICard {
294
332
  titleEl.className = 'jux-kpicard-title';
295
333
  titleEl.textContent = title;
296
334
  titleEl.style.cssText = `
297
- font-size: 16px;
335
+ font-size: ${16 * scaleFactor}px;
298
336
  font-weight: 500;
299
- color: #6b7280;
300
- margin-bottom: 16px;
301
- font-family: inherit;
337
+ color: ${styleMode === 'gradient' ? 'rgba(255, 255, 255, 0.95)' : '#6b7280'};
338
+ margin-bottom: ${16 * scaleFactor}px;
339
+ font-family: ${themeConfig.variables['--chart-font-family']};
302
340
  `;
303
341
  content.appendChild(titleEl);
304
342
 
@@ -307,7 +345,7 @@ export class KPICard {
307
345
  valueContainer.style.cssText = `
308
346
  display: flex;
309
347
  align-items: baseline;
310
- margin-bottom: 12px;
348
+ margin-bottom: ${12 * scaleFactor}px;
311
349
  `;
312
350
 
313
351
  // Value
@@ -315,11 +353,12 @@ export class KPICard {
315
353
  valueEl.className = 'jux-kpicard-value';
316
354
  valueEl.textContent = `${prefix}${value}${suffix}`;
317
355
  valueEl.style.cssText = `
318
- font-size: 56px;
356
+ font-size: ${56 * scaleFactor}px;
319
357
  font-weight: 800;
320
- color: #1f2937;
358
+ color: ${styleMode === 'gradient' ? '#ffffff' : '#1f2937'};
321
359
  line-height: 1;
322
- font-family: inherit;
360
+ font-family: ${themeConfig.variables['--chart-font-family']};
361
+ ${styleMode === 'glow' ? `text-shadow: 0 0 ${20 * scaleFactor}px ${themeConfig.colors[0]}40;` : ''}
323
362
  `;
324
363
 
325
364
  if (animate) {
@@ -345,21 +384,25 @@ export class KPICard {
345
384
  deltaContainer.style.cssText = `
346
385
  display: flex;
347
386
  align-items: center;
348
- gap: 8px;
387
+ gap: ${8 * scaleFactor}px;
349
388
  `;
350
389
 
351
390
  // Arrow SVG
352
- const arrow = this._createArrowSVG(delta > 0);
391
+ const arrow = this._createArrowSVG(delta > 0, styleMode === 'gradient', scaleFactor);
353
392
  deltaContainer.appendChild(arrow);
354
393
 
355
394
  // Delta text
356
395
  const deltaText = document.createElement('span');
357
396
  deltaText.textContent = `${delta > 0 ? '+' : ''}${delta}%`;
397
+ const deltaColor = styleMode === 'gradient'
398
+ ? (delta > 0 ? 'rgba(255, 255, 255, 0.95)' : 'rgba(255, 200, 200, 0.95)')
399
+ : (delta > 0 ? '#10b981' : '#ef4444');
400
+
358
401
  deltaText.style.cssText = `
359
- font-size: 18px;
402
+ font-size: ${18 * scaleFactor}px;
360
403
  font-weight: 700;
361
- color: ${delta > 0 ? '#10b981' : '#ef4444'};
362
- font-family: inherit;
404
+ color: ${deltaColor};
405
+ font-family: ${themeConfig.variables['--chart-font-family']};
363
406
  `;
364
407
 
365
408
  if (animate) {
@@ -382,37 +425,87 @@ export class KPICard {
382
425
  return content;
383
426
  }
384
427
 
385
- private _createArrowSVG(isUp: boolean): SVGSVGElement {
428
+ private _createArrowSVG(isUp: boolean, isGradientMode: boolean, scaleFactor: number = 1): SVGSVGElement {
429
+ const baseWidth = 120;
430
+ const baseHeight = 32;
431
+ const width = baseWidth * scaleFactor;
432
+ const height = baseHeight * scaleFactor;
433
+
386
434
  const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
387
- svg.setAttribute('width', '24');
388
- svg.setAttribute('height', '24');
389
- svg.setAttribute('viewBox', '0 0 24 24');
435
+ svg.setAttribute('width', width.toString());
436
+ svg.setAttribute('height', height.toString());
437
+ svg.setAttribute('viewBox', `0 0 ${baseWidth} ${baseHeight}`);
390
438
  svg.setAttribute('fill', 'none');
391
439
  svg.style.flexShrink = '0';
392
440
 
393
- const color = isUp ? '#10b981' : '#ef4444';
441
+ const color = isGradientMode
442
+ ? (isUp ? 'rgba(255, 255, 255, 0.9)' : 'rgba(255, 200, 200, 0.9)')
443
+ : (isUp ? '#10b981' : '#ef4444');
394
444
 
395
- // Create squiggly arrow path
396
- const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
397
-
398
445
  if (isUp) {
399
- // Up arrow with squiggly line
400
- path.setAttribute('d', 'M12 4L12 20M12 4L8 8M12 4L16 8M10 10Q11 11 12 10T14 10M10 14Q11 15 12 14T14 14');
446
+ // Upward trending arrow - wider
447
+ const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
448
+ path.setAttribute('d', 'M4 26 L22 18 L40 22 L58 14 L76 18 L94 10');
449
+ path.setAttribute('stroke', color);
450
+ path.setAttribute('stroke-width', '3');
451
+ path.setAttribute('stroke-linecap', 'round');
452
+ path.setAttribute('stroke-linejoin', 'round');
453
+ path.setAttribute('fill', 'none');
454
+ svg.appendChild(path);
455
+
456
+ // Points at each vertex - bigger circles
457
+ const points = [
458
+ { x: 4, y: 26 },
459
+ { x: 22, y: 18 },
460
+ { x: 40, y: 22 },
461
+ { x: 58, y: 14 },
462
+ { x: 76, y: 18 },
463
+ { x: 94, y: 10 }
464
+ ];
465
+
466
+ points.forEach(point => {
467
+ const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
468
+ circle.setAttribute('cx', point.x.toString());
469
+ circle.setAttribute('cy', point.y.toString());
470
+ circle.setAttribute('r', '4.5');
471
+ circle.setAttribute('fill', color);
472
+ svg.appendChild(circle);
473
+ });
401
474
  } else {
402
- // Down arrow with squiggly line
403
- path.setAttribute('d', 'M12 20L12 4M12 20L8 16M12 20L16 16M10 10Q11 9 12 10T14 10M10 14Q11 13 12 14T14 14');
475
+ // Downward trending arrow - wider
476
+ const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
477
+ path.setAttribute('d', 'M4 6 L22 14 L40 10 L58 18 L76 14 L94 22');
478
+ path.setAttribute('stroke', color);
479
+ path.setAttribute('stroke-width', '3');
480
+ path.setAttribute('stroke-linecap', 'round');
481
+ path.setAttribute('stroke-linejoin', 'round');
482
+ path.setAttribute('fill', 'none');
483
+ svg.appendChild(path);
484
+
485
+ // Points at each vertex - bigger circles
486
+ const points = [
487
+ { x: 4, y: 6 },
488
+ { x: 22, y: 14 },
489
+ { x: 40, y: 10 },
490
+ { x: 58, y: 18 },
491
+ { x: 76, y: 14 },
492
+ { x: 94, y: 22 }
493
+ ];
494
+
495
+ points.forEach(point => {
496
+ const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
497
+ circle.setAttribute('cx', point.x.toString());
498
+ circle.setAttribute('cy', point.y.toString());
499
+ circle.setAttribute('r', '4.5');
500
+ circle.setAttribute('fill', color);
501
+ svg.appendChild(circle);
502
+ });
404
503
  }
405
-
406
- path.setAttribute('stroke', color);
407
- path.setAttribute('stroke-width', '2');
408
- path.setAttribute('stroke-linecap', 'round');
409
- path.setAttribute('stroke-linejoin', 'round');
410
504
 
411
- svg.appendChild(path);
412
505
  return svg;
413
506
  }
414
507
 
415
- private _getThemeColors(): string[] {
508
+ private _getThemeConfig() {
416
509
  const { theme } = this.state;
417
510
 
418
511
  switch (theme) {
@@ -436,63 +529,74 @@ export class KPICard {
436
529
  }
437
530
 
438
531
  private _applyThemeAndStyle(wrapper: HTMLElement): void {
439
- const { styleMode } = this.state;
440
- const colors = this._getThemeColors();
532
+ const { styleMode, borderRadius, showAccentBar, width, height } = this.state;
533
+ const themeConfig = this._getThemeConfig();
534
+ const colors = themeConfig.colors;
535
+
536
+ // Calculate scale factor for accent bar
537
+ const baseWidth = 280;
538
+ const scaleFactor = width / baseWidth;
539
+ const accentBarHeight = 4 * scaleFactor;
441
540
 
442
541
  const baseStyles = `
443
- border-radius: 16px;
542
+ border-radius: ${borderRadius}px;
444
543
  overflow: hidden;
445
544
  position: relative;
446
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
545
+ font-family: ${themeConfig.variables['--chart-font-family']};
447
546
  `;
448
547
 
449
548
  if (styleMode === 'gradient') {
450
549
  wrapper.style.cssText += `
451
550
  ${baseStyles}
452
- background: linear-gradient(135deg, ${colors[0]} 0%, ${colors[1]} 100%);
453
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
551
+ background: linear-gradient(135deg, ${colors[0]} 0%, ${colors[1]} 50%, ${colors[2]} 100%);
552
+ box-shadow: 0 ${8 * scaleFactor}px ${32 * scaleFactor}px ${colors[0]}40;
454
553
  `;
455
-
456
- // Adjust text colors for gradient
457
- const titleElements = wrapper.querySelectorAll('.jux-kpicard-title');
458
- const valueElements = wrapper.querySelectorAll('.jux-kpicard-value');
459
- titleElements.forEach(el => (el as HTMLElement).style.color = 'rgba(255, 255, 255, 0.9)');
460
- valueElements.forEach(el => (el as HTMLElement).style.color = '#ffffff');
461
554
  } else if (styleMode === 'glass') {
462
555
  wrapper.style.cssText += `
463
556
  ${baseStyles}
464
- background: rgba(255, 255, 255, 0.7);
465
- backdrop-filter: blur(10px);
466
- border: 1px solid rgba(255, 255, 255, 0.8);
467
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
557
+ background: linear-gradient(135deg, ${colors[0]}20 0%, ${colors[1]}20 100%);
558
+ backdrop-filter: blur(${12 * scaleFactor}px) saturate(180%);
559
+ border: ${1 * scaleFactor}px solid ${colors[0]}30;
560
+ box-shadow: 0 ${8 * scaleFactor}px ${32 * scaleFactor}px rgba(0, 0, 0, 0.1);
468
561
  `;
469
562
  } else if (styleMode === 'outline') {
470
563
  wrapper.style.cssText += `
471
564
  ${baseStyles}
472
- background: transparent;
473
- border: 3px solid ${colors[0]};
565
+ background: #ffffff;
566
+ border: ${3 * scaleFactor}px solid ${colors[0]};
567
+ box-shadow: 0 ${4 * scaleFactor}px ${12 * scaleFactor}px ${colors[0]}20;
568
+ `;
569
+ } else if (styleMode === 'glow') {
570
+ wrapper.style.cssText += `
571
+ ${baseStyles}
572
+ background: #ffffff;
573
+ border: ${2 * scaleFactor}px solid ${colors[0]}40;
574
+ box-shadow: 0 0 ${30 * scaleFactor}px ${colors[0]}30, 0 ${4 * scaleFactor}px ${12 * scaleFactor}px rgba(0, 0, 0, 0.1);
474
575
  `;
475
576
  } else {
476
577
  // default
477
578
  wrapper.style.cssText += `
478
579
  ${baseStyles}
479
580
  background: #ffffff;
480
- border: 1px solid #e5e7eb;
481
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
581
+ border: ${1 * scaleFactor}px solid #e5e7eb;
582
+ box-shadow: 0 ${1 * scaleFactor}px ${3 * scaleFactor}px rgba(0, 0, 0, 0.1);
482
583
  `;
483
584
  }
484
585
 
485
- // Apply theme color accent
486
- const accentBar = document.createElement('div');
487
- accentBar.style.cssText = `
488
- position: absolute;
489
- top: 0;
490
- left: 0;
491
- right: 0;
492
- height: 4px;
493
- background: linear-gradient(90deg, ${colors[0]}, ${colors[1]});
494
- `;
495
- wrapper.appendChild(accentBar);
586
+ // Apply theme color accent bar (optional)
587
+ if (showAccentBar) {
588
+ const accentBar = document.createElement('div');
589
+ accentBar.style.cssText = `
590
+ position: absolute;
591
+ top: 0;
592
+ left: 0;
593
+ right: 0;
594
+ height: ${accentBarHeight}px;
595
+ background: linear-gradient(90deg, ${colors[0]}, ${colors[1]}, ${colors[2]});
596
+ border-radius: ${borderRadius}px ${borderRadius}px 0 0;
597
+ `;
598
+ wrapper.appendChild(accentBar);
599
+ }
496
600
  }
497
601
  }
498
602
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "lib/jux.js",