gaugeit.js 0.1.0 → 0.1.1

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.
@@ -0,0 +1,1107 @@
1
+ # Gaugeit.js option reference for AI code generation
2
+
3
+ This document describes the public option model in Gaugeit.js 0.1.1. Use exact key
4
+ names and nesting. Do not invent aliases unless this document explicitly lists one.
5
+
6
+ Runtime option resolution order is:
7
+
8
+ 1. shared base defaults;
9
+ 2. selected type defaults;
10
+ 3. instance options;
11
+ 4. optional type `configure()` hook;
12
+ 5. validation and normalization.
13
+
14
+ Plain objects merge recursively. Arrays are replaced. A selected type may override many
15
+ shared defaults, so the values below are the **base defaults**, not necessarily the
16
+ final resolved values for every built-in type.
17
+
18
+ ## Complete base-default shape
19
+
20
+ ```js
21
+ {
22
+ type: 'arc',
23
+ min: 0,
24
+ max: 100,
25
+ value: 0,
26
+ invert: false,
27
+ overflow: 'clamp',
28
+ precision: 3,
29
+ className: '',
30
+
31
+ layout: {
32
+ mode: 'intrinsic',
33
+ padding: 8,
34
+ includeGeometry: true
35
+ },
36
+
37
+ geometry: {
38
+ mode: 'radial',
39
+ width: 320,
40
+ height: 220,
41
+ centerX: 160,
42
+ centerY: 174,
43
+ radius: 120,
44
+ startAngle: 180,
45
+ endAngle: 360,
46
+ preserveAspectRatio: 'xMidYMid meet',
47
+ aspectRatio: null
48
+ },
49
+
50
+ linear: {
51
+ originY: 140,
52
+ startX: 36,
53
+ startY: 0,
54
+ endX: 284,
55
+ endY: 0,
56
+ tickSide: 'negative',
57
+ labelOffset: 34,
58
+ zoneOffset: -12
59
+ },
60
+
61
+ face: {
62
+ visible: false,
63
+ shape: 'panel',
64
+ fit: 'geometry',
65
+ contentPadding: 8,
66
+ minWidth: null,
67
+ minHeight: null,
68
+ clipContent: false,
69
+ x: 6,
70
+ y: 6,
71
+ width: 308,
72
+ height: 208,
73
+ cornerRadius: 18,
74
+ color: '#ffffff',
75
+ strokeColor: '#d1d5db',
76
+ strokeWidth: 2,
77
+ innerStrokeColor: '#f3f4f6',
78
+ innerStrokeWidth: 1,
79
+ shadow: false,
80
+ glass: false
81
+ },
82
+
83
+ track: {
84
+ visible: true,
85
+ color: '#e5e7eb',
86
+ width: 28,
87
+ radius: 112,
88
+ opacity: 1,
89
+ lineCap: 'butt',
90
+ showUnderZones: false
91
+ },
92
+
93
+ zones: [],
94
+
95
+ scale: {
96
+ endpointMode: 'auto',
97
+ radius: 112,
98
+ position: 'inside',
99
+ major: {
100
+ visible: true,
101
+ interval: null,
102
+ divisions: 5,
103
+ length: 13,
104
+ width: 2,
105
+ color: '#111827',
106
+ opacity: 1
107
+ },
108
+ minor: {
109
+ visible: true,
110
+ subdivisions: 4,
111
+ length: 7,
112
+ width: 1,
113
+ color: '#6b7280',
114
+ opacity: 0.9
115
+ }
116
+ },
117
+
118
+ labels: {
119
+ visible: true,
120
+ interval: null,
121
+ radius: 84,
122
+ color: '#111827',
123
+ fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
124
+ fontSize: 11,
125
+ fontWeight: 500,
126
+ fractionDigits: 0,
127
+ rotation: 'horizontal',
128
+ formatter: null
129
+ },
130
+
131
+ pointer: {
132
+ visible: true,
133
+ type: 'needle',
134
+ length: 94,
135
+ shaftLength: null,
136
+ tailLength: 13,
137
+ width: 8,
138
+ color: '#111827',
139
+ strokeColor: 'none',
140
+ strokeWidth: 0,
141
+ opacity: 1,
142
+ colorByZone: false,
143
+ cap: {
144
+ visible: true,
145
+ stackOrder: 'above',
146
+ radius: 8,
147
+ color: '#111827',
148
+ strokeColor: '#ffffff',
149
+ strokeWidth: 2
150
+ }
151
+ },
152
+
153
+ indicator: {
154
+ visible: false,
155
+ type: 'hairline',
156
+ position: 'cross',
157
+ length: 76,
158
+ centerOffset: null,
159
+ offset: 0,
160
+ width: 2,
161
+ color: '#b91c1c',
162
+ strokeColor: 'none',
163
+ strokeWidth: 0,
164
+ opacity: 1,
165
+ lineCap: 'round',
166
+ colorByZone: false,
167
+ markerSize: 8,
168
+ carriageWidth: 14,
169
+ carriageRadius: 4,
170
+ cap: {
171
+ visible: false,
172
+ stackOrder: 'above',
173
+ radius: 4,
174
+ color: '#b91c1c',
175
+ strokeColor: '#ffffff',
176
+ strokeWidth: 1
177
+ }
178
+ },
179
+
180
+ centerZero: {
181
+ zeroValue: 0,
182
+ symmetric: false,
183
+ marker: {
184
+ visible: false,
185
+ radius: null,
186
+ position: 'cross',
187
+ centerOffset: 0,
188
+ length: 20,
189
+ width: 3,
190
+ color: '#111827',
191
+ opacity: 1,
192
+ lineCap: 'round'
193
+ }
194
+ },
195
+
196
+ tape: {
197
+ orientation: 'vertical',
198
+ x: 64,
199
+ y: 58,
200
+ width: 192,
201
+ height: 210,
202
+ cornerRadius: 12,
203
+ windowColor: '#e5e7eb',
204
+ windowStrokeColor: '#475569',
205
+ windowStrokeWidth: 2,
206
+ windowInnerStrokeColor: '#d1d5db',
207
+ windowInnerStrokeWidth: 1,
208
+ stripColor: '#ffffff',
209
+ textColor: '#111827',
210
+ tickColor: '#334155',
211
+ minorTickColor: '#64748b',
212
+ minorTickOpacity: 0.85,
213
+ majorTickWidth: 2,
214
+ minorTickWidth: 1,
215
+ majorTickLength: 24,
216
+ minorTickLength: 12,
217
+ tickInset: 64,
218
+ labelOffset: 10,
219
+ interval: null,
220
+ minorSubdivisions: 4,
221
+ majorSpacing: 52,
222
+ indicatorColor: '#b91c1c',
223
+ indicatorWidth: 2.5,
224
+ fadeSize: 32,
225
+ fadeColor: '#ffffff',
226
+ colorByZone: false
227
+ },
228
+
229
+ light: {
230
+ visible: false,
231
+ x: 93,
232
+ y: 110,
233
+ radius: 8,
234
+ glowRadius: 20,
235
+ glowSharpness: 0,
236
+ color: 'red',
237
+ customColor: '',
238
+ offColor: '#f3ead6',
239
+ offEdgeColor: '#817768',
240
+ bezelColor: '#4b5563',
241
+ bezelHighlightColor: '#f8fafc',
242
+ opacity: 1,
243
+ glowOpacity: 0.15,
244
+ on: false,
245
+ glow: true,
246
+ pulse: false,
247
+ pulseInterval: 2600,
248
+ blink: false,
249
+ blinkInterval: 1000,
250
+ flicker: true,
251
+ flickerIntensity: 0.1,
252
+ flickerGlowRadius: 0.1,
253
+ flickerMinInterval: 45,
254
+ flickerMaxInterval: 140,
255
+ trigger: {
256
+ mode: 'below',
257
+ source: 'target',
258
+ value: 15,
259
+ min: 0,
260
+ max: 15
261
+ }
262
+ },
263
+
264
+ readout: {
265
+ visible: true,
266
+ title: {
267
+ visible: false,
268
+ text: '',
269
+ x: 'auto',
270
+ y: 'auto',
271
+ position: 'top',
272
+ margin: 12,
273
+ offsetX: 0,
274
+ offsetY: 0,
275
+ color: '#374151',
276
+ fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
277
+ fontSize: 14,
278
+ fontWeight: 700,
279
+ letterSpacing: 0.8
280
+ },
281
+ value: {
282
+ visible: false,
283
+ x: 'auto',
284
+ y: 54,
285
+ position: 'center',
286
+ margin: 12,
287
+ offsetX: 0,
288
+ offsetY: 0,
289
+ color: '#111827',
290
+ fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
291
+ fontSize: 38,
292
+ fontWeight: 750,
293
+ fractionDigits: 0,
294
+ prefix: '',
295
+ suffix: '',
296
+ locale: undefined,
297
+ formatter: null
298
+ },
299
+ unit: {
300
+ visible: false,
301
+ text: '',
302
+ x: 'auto',
303
+ y: 76,
304
+ position: 'bottom',
305
+ margin: 12,
306
+ offsetX: 0,
307
+ offsetY: 0,
308
+ color: '#6b7280',
309
+ fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
310
+ fontSize: 12,
311
+ fontWeight: 600
312
+ }
313
+ },
314
+
315
+ animation: {
316
+ enabled: true,
317
+ duration: 850,
318
+ easing: 'easeOutCubic',
319
+ animateOnInit: true,
320
+ startFrom: 'min',
321
+ respectReducedMotion: true
322
+ },
323
+
324
+ effects: {
325
+ jitter: {
326
+ enabled: false,
327
+ amplitude: 0.45,
328
+ amplitudeUnit: 'value',
329
+ frequency: 4,
330
+ smoothing: 0.86,
331
+ whenIdleOnly: true
332
+ }
333
+ },
334
+
335
+ accessibility: {
336
+ label: 'Gauge',
337
+ description: '',
338
+ valueText: null
339
+ },
340
+
341
+ performance: {
342
+ pauseWhenHidden: true,
343
+ pauseWhenOffscreen: true
344
+ },
345
+
346
+ responsive: true
347
+ }
348
+ ```
349
+
350
+ ## Top-level options
351
+
352
+ | Key | Type | Base default | Allowed or normalized behavior | Layout impact |
353
+ | --- | --- | --- | --- | --- |
354
+ | `type` | string | `arc` | must be a registered type name | selects all type defaults and layers |
355
+ | `min` | number | `0` | non-finite becomes `0` | scale, zones, labels, movement |
356
+ | `max` | number | `100` | forced to greater than `min` | scale, zones, labels, movement |
357
+ | `value` | number | `0` | normalized according to `overflow` | dynamic only unless range expands |
358
+ | `invert` | boolean | `false` | only literal `true` enables | reverses visual mapping |
359
+ | `overflow` | string | `clamp` | `clamp`, `expand`, `allow` | `expand` may rebuild range |
360
+ | `precision` | integer | `3` | clamped to `0 ... 12` | display/event rounding |
361
+ | `className` | string | empty | extra generated-root class | CSS only |
362
+ | `responsive` | boolean | `true` | false disables width-100% sizing | host sizing |
363
+ | `layout` | object | see below | restored when malformed | viewBox and host size |
364
+ | `geometry` | object | see below | restored and normalized | primary composition |
365
+ | `linear` | object | see below | used by linear family | primary composition |
366
+ | `face` | object | see below | shared static layer | bounds and clipping |
367
+ | `track` | object | see below | radial and linear tracks | bounds |
368
+ | `zones` | array | `[]` | array replaced; entries normalized | bounds and colors |
369
+ | `scale` | object | see below | major/minor scale | bounds |
370
+ | `labels` | object | see below | static text labels | bounds |
371
+ | `pointer` | object | see below | radial moving visual | bounds and dynamic update |
372
+ | `indicator` | object | see below | linear moving visual | bounds and dynamic update |
373
+ | `centerZero` | object | see below | fixed zero reference | range and bounds |
374
+ | `tape` | object | see below | rolling tape family | bounds and dynamic update |
375
+ | `light` | object | see below | shared annunciator | bounds, timers, dynamic state |
376
+ | `readout` | object | see below | title/value/unit | bounds and dynamic text |
377
+ | `animation` | object | see below | base-value transition | runtime state |
378
+ | `effects.jitter` | object | see below | visual-only movement | moving artwork only |
379
+ | `accessibility` | object | see below | meter semantics | no visual layout |
380
+ | `performance` | object | see below | lifecycle observers | no visual layout |
381
+
382
+ ### `type`
383
+
384
+ Example:
385
+
386
+ ```js
387
+ { type: 'heritage-round' }
388
+ ```
389
+
390
+ Use one exact built-in name or a previously registered custom type. An unknown type
391
+ causes registry lookup to fail. Do not silently substitute a made-up name.
392
+
393
+ ### `min`, `max`, and `value`
394
+
395
+ Example:
396
+
397
+ ```js
398
+ {
399
+ min: -50,
400
+ max: 50,
401
+ value: 12
402
+ }
403
+ ```
404
+
405
+ If `max <= min`, runtime normalization sets `max = min + 1`.
406
+
407
+ ### `overflow`
408
+
409
+ ```js
410
+ { overflow: 'clamp' }
411
+ ```
412
+
413
+ - `clamp`: constrain incoming values to the range.
414
+ - `expand`: extend `min` or `max` when an incoming value exceeds the range, then
415
+ re-resolve options and render.
416
+ - `allow`: preserve the configured range but allow the dynamic value outside it.
417
+
418
+ Common misuse: using `allow` when the visual should stay within the scale. Prefer
419
+ `clamp` for meters that represent bounded values.
420
+
421
+ ### `precision`
422
+
423
+ This is the internal displayed-value rounding precision, not the same as label or
424
+ readout fraction digits.
425
+
426
+ ## `layout`
427
+
428
+ | Key | Type | Base default | Normalization | Layout impact |
429
+ | --- | --- | --- | --- | --- |
430
+ | `mode` | `'intrinsic' \| 'contain'` | `intrinsic` | invalid becomes `intrinsic` | determines host sizing mode |
431
+ | `padding` | number | `8` | minimum `0` | expands final viewBox |
432
+ | `includeGeometry` | boolean | `true` | false only when explicitly false | keeps logical dimensions as minimum bounds |
433
+
434
+ Example:
435
+
436
+ ```js
437
+ layout: {
438
+ mode: 'contain',
439
+ padding: 12,
440
+ includeGeometry: true
441
+ }
442
+ ```
443
+
444
+ `intrinsic` calculates content-aware bounds and gives the SVG a natural aspect ratio.
445
+ `contain` uses the same viewBox inside a host whose width and height are controlled by
446
+ CSS.
447
+
448
+ Common misuse: selecting `contain` without giving the host a usable CSS height.
449
+
450
+ ## `geometry`
451
+
452
+ | Key | Type | Base default | Normalization | Used by |
453
+ | --- | --- | --- | --- | --- |
454
+ | `mode` | `'radial' \| 'linear' \| 'tape'` | `radial` | invalid becomes `radial` | type family |
455
+ | `width` | number | `320` | minimum `40` | all families and intrinsic sizing |
456
+ | `height` | number | `220` | minimum `40` | all families and intrinsic sizing |
457
+ | `centerX` | number | `160` | fallback `width / 2` | radial |
458
+ | `centerY` | number | `174` | fallback `height * 0.78` | radial |
459
+ | `radius` | number | `120` | minimum `1` | radial and custom layers |
460
+ | `startAngle` | number | `180` | finite; zero sweep repaired | radial |
461
+ | `endAngle` | number | `360` | sweep limited to 360 degrees | radial |
462
+ | `preserveAspectRatio` | string | `xMidYMid meet` | converted to string | SVG |
463
+ | `aspectRatio` | number or `null` | `null` | positive or null | resizable presets |
464
+
465
+ Example semicircle:
466
+
467
+ ```js
468
+ geometry: {
469
+ startAngle: 180,
470
+ endAngle: 360
471
+ }
472
+ ```
473
+
474
+ Example using the public helper:
475
+
476
+ ```js
477
+ const angles = Gaugeit.geometry.centeredArcAngles(220, 15);
478
+
479
+ const options = {
480
+ geometry: angles
481
+ };
482
+ ```
483
+
484
+ `centerAngle: 0` means the normal upright orientation, corresponding to SVG angle 270.
485
+ Positive center angles rotate clockwise.
486
+
487
+ Common misuse: manually setting `geometry.mode` to a family inconsistent with the
488
+ selected built-in type. Prefer selecting the correct type and overriding only necessary
489
+ coordinates.
490
+
491
+ ## `linear`
492
+
493
+ Public linear Y coordinates are Cartesian around `originY`: positive values move
494
+ upward even though internal SVG Y increases downward.
495
+
496
+ | Key | Type | Base default | Meaning |
497
+ | --- | --- | --- | --- |
498
+ | `originY` | number | `140` | SVG baseline representing public Y zero |
499
+ | `startX` | number | `36` | minimum-side X before inversion |
500
+ | `startY` | number | `0` | minimum-side positive-up Y |
501
+ | `endX` | number | `284` | maximum-side X before inversion |
502
+ | `endY` | number | `0` | maximum-side positive-up Y |
503
+ | `tickSide` | `'negative' \| 'positive' \| 'cross'` | `negative` | side of axis normal |
504
+ | `labelOffset` | number | `34` | positive moves visually upward on left-to-right axis |
505
+ | `zoneOffset` | number | `-12` | positive moves visually upward on left-to-right axis |
506
+
507
+ A zero-length axis is repaired to one logical unit.
508
+
509
+ Example vertical axis:
510
+
511
+ ```js
512
+ linear: {
513
+ originY: 270,
514
+ startX: 90,
515
+ startY: 0,
516
+ endX: 90,
517
+ endY: 220,
518
+ tickSide: 'positive'
519
+ }
520
+ ```
521
+
522
+ Common misuse: applying raw SVG downward-positive Y assumptions to these public fields.
523
+
524
+ ## `face`
525
+
526
+ | Key | Type | Base default | Notes |
527
+ | --- | --- | --- | --- |
528
+ | `visible` | boolean | `false` | type presets may enable |
529
+ | `shape` | `'panel' \| 'circle'` | `panel` | invalid becomes panel |
530
+ | `fit` | `'geometry' \| 'content'` | `geometry` | content fit follows rendered content |
531
+ | `contentPadding` | number | `8` | minimum zero |
532
+ | `minWidth` | number or `null` | `null` | minimum one when present |
533
+ | `minHeight` | number or `null` | `null` | minimum one when present |
534
+ | `clipContent` | boolean | `false` | clips later non-face layers to inner bezel |
535
+ | `x`, `y` | number | `6`, `6` | face origin |
536
+ | `width`, `height` | number | `308`, `208` | minimum one |
537
+ | `cornerRadius` | number | `18` | minimum zero |
538
+ | `color` | CSS color | `#ffffff` | face fill |
539
+ | `strokeColor` | CSS color | `#d1d5db` | outer stroke |
540
+ | `strokeWidth` | number | `2` | minimum zero |
541
+ | `innerStrokeColor` | CSS color | `#f3f4f6` | inner bezel |
542
+ | `innerStrokeWidth` | number | `1` | minimum zero |
543
+ | `shadow` | boolean | `false` | reserves extra bounds |
544
+ | `glass` | boolean | `false` | visual highlight |
545
+
546
+ Example:
547
+
548
+ ```js
549
+ face: {
550
+ visible: true,
551
+ fit: 'content',
552
+ contentPadding: 12,
553
+ color: '#fff8e7',
554
+ glass: true
555
+ }
556
+ ```
557
+
558
+ Interaction: when `clipContent` is true and the face layer is first, the renderer
559
+ applies the clip path to later layers. New custom layers should use `renderer.group()`
560
+ to inherit this behavior.
561
+
562
+ ## `track`
563
+
564
+ | Key | Type | Base default | Notes |
565
+ | --- | --- | --- | --- |
566
+ | `visible` | boolean | `true` | base scale track |
567
+ | `color` | CSS color | `#e5e7eb` | track color |
568
+ | `width` | number | `28` | zone default-width fallback |
569
+ | `radius` | number | `112` | radial track radius |
570
+ | `opacity` | number | `1` | visual opacity |
571
+ | `lineCap` | string | `butt` | SVG stroke-linecap |
572
+ | `showUnderZones` | boolean | `false` | controls transparent-zone gaps |
573
+
574
+ When `showUnderZones` is false, a transparent zone is a true gap. When true, the track
575
+ remains visible under every zone.
576
+
577
+ ## `zones`
578
+
579
+ Type: array of zone objects. The entire array is replaced by option updates.
580
+
581
+ ```js
582
+ zones: [
583
+ {
584
+ min: 0,
585
+ max: 25,
586
+ color: '#ef4444',
587
+ width: 20,
588
+ taper: {
589
+ startWidth: 0,
590
+ endWidth: 32,
591
+ segments: 48
592
+ },
593
+ radius: 112,
594
+ opacity: 1,
595
+ lineCap: 'butt',
596
+ className: 'warning-zone',
597
+ offset: 0
598
+ }
599
+ ]
600
+ ```
601
+
602
+ | Key | Type | Default | Applies to |
603
+ | --- | --- | --- | --- |
604
+ | `min` | number | required | all |
605
+ | `max` | number | required | all |
606
+ | `color` | CSS color | transparent when missing | all |
607
+ | `strokeStyle` | CSS color | alias for `color` | compatibility |
608
+ | `width` | number | track width fallback | radial and linear |
609
+ | `taper.startWidth` | number | zone width fallback | radial |
610
+ | `taper.endWidth` | number | zone width fallback | radial |
611
+ | `taper.segments` | integer | `48` | radial, clamped `2 ... 256` |
612
+ | `radius` | number | track/type fallback | radial |
613
+ | `opacity` | number | `1` | all |
614
+ | `lineCap` | string | type/track fallback | uniform radial and linear |
615
+ | `className` | string | empty | generated zone element |
616
+ | `offset` | number | linear zone offset fallback | linear |
617
+
618
+ Zones are clamped to `min ... max`; invalid or empty intervals are removed. Later
619
+ overlapping zones win. `pointer.colorByZone`, `indicator.colorByZone`, and
620
+ `tape.colorByZone` use the last matching zone.
621
+
622
+ A missing color, `transparent`, or `none` produces a gap when the track is not shown
623
+ under zones.
624
+
625
+ ## `scale`
626
+
627
+ Shared fields:
628
+
629
+ | Key | Type | Base default | Notes |
630
+ | --- | --- | --- | --- |
631
+ | `endpointMode` | `'auto' \| 'both' \| 'dedupe'` | `auto` | auto deduplicates closed-scale maximum |
632
+ | `radius` | number | `112` | minimum one |
633
+ | `position` | `'inside' \| 'outside' \| 'cross'` | `inside` | tick placement |
634
+
635
+ ### `scale.major`
636
+
637
+ | Key | Type | Base default | Notes |
638
+ | --- | --- | --- | --- |
639
+ | `visible` | boolean | `true` | type preset may change |
640
+ | `interval` | number or `null` | `null` | positive explicit spacing |
641
+ | `divisions` | integer | `5` | desired divisions if no interval |
642
+ | `length` | number | `13` | minimum zero |
643
+ | `width` | number | `2` | minimum zero |
644
+ | `color` | CSS color | `#111827` | stroke |
645
+ | `opacity` | number | `1` | stroke opacity |
646
+
647
+ ### `scale.minor`
648
+
649
+ | Key | Type | Base default | Notes |
650
+ | --- | --- | --- | --- |
651
+ | `visible` | boolean | `true` | type preset may change |
652
+ | `subdivisions` | integer | `4` | count between adjacent major ticks |
653
+ | `length` | number | `7` | minimum zero |
654
+ | `width` | number | `1` | minimum zero |
655
+ | `color` | CSS color | `#6b7280` | stroke |
656
+ | `opacity` | number | `0.9` | stroke opacity |
657
+
658
+ `major.interval` takes precedence over `major.divisions`.
659
+
660
+ ## `labels`
661
+
662
+ | Key | Type | Base default | Notes |
663
+ | --- | --- | --- | --- |
664
+ | `visible` | boolean | `true` | static scale labels |
665
+ | `interval` | number or `null` | `null` | falls back to major interval |
666
+ | `radius` | number | `84` | radial placement |
667
+ | `color` | CSS color | `#111827` | text color |
668
+ | `fontFamily` | string | system stack | SVG text |
669
+ | `fontSize` | number | `11` | minimum one |
670
+ | `fontWeight` | string or number | `500` | SVG text |
671
+ | `fractionDigits` | integer | `0` | clamped `0 ... 12` |
672
+ | `rotation` | `'horizontal' \| 'radial' \| 'tangent'` | `horizontal` | radial labels |
673
+ | `formatter` | function or `null` | `null` | `(value, context) => string` |
674
+
675
+ Formatter context:
676
+
677
+ ```js
678
+ {
679
+ min,
680
+ max,
681
+ options
682
+ }
683
+ ```
684
+
685
+ Common misuse: returning non-string application objects. Coerce to readable text.
686
+
687
+ ## `pointer`
688
+
689
+ Radial moving visual:
690
+
691
+ | Key | Type | Base default | Notes |
692
+ | --- | --- | --- | --- |
693
+ | `visible` | boolean | `true` | radial layer |
694
+ | `type` | `'needle' \| 'line' \| 'arrow' \| 'spear'` | `needle` | invalid becomes needle |
695
+ | `length` | number | `94` | minimum zero |
696
+ | `shaftLength` | number or `null` | `null` | clamped to pointer length |
697
+ | `tailLength` | number | `13` | minimum zero |
698
+ | `width` | number | `8` | minimum `0.5` |
699
+ | `color` | CSS color | `#111827` | fill/stroke by type |
700
+ | `strokeColor` | CSS color | `none` | outline |
701
+ | `strokeWidth` | number | `0` | minimum zero |
702
+ | `opacity` | number | `1` | visual opacity |
703
+ | `colorByZone` | boolean | `false` | use active zone color |
704
+ | `cap.visible` | boolean | `true` | center cap |
705
+ | `cap.stackOrder` | `'above' \| 'below'` | `above` | SVG paint relation |
706
+ | `cap.radius` | number | `8` | minimum zero |
707
+ | `cap.color` | CSS color | `#111827` | fill |
708
+ | `cap.strokeColor` | CSS color | `#ffffff` | outline |
709
+ | `cap.strokeWidth` | number | `2` | minimum zero |
710
+
711
+ Layer order, not CSS z-index, controls whether the entire pointer paints over readout
712
+ text. `cap.stackOrder` only controls cap-versus-pointer order inside the pointer layer.
713
+
714
+ ## `indicator`
715
+
716
+ Linear moving visual:
717
+
718
+ | Key | Type | Base default | Notes |
719
+ | --- | --- | --- | --- |
720
+ | `visible` | boolean | `false` | linear presets enable |
721
+ | `type` | `'hairline' \| 'marker' \| 'carriage'` | `hairline` | indicator shape |
722
+ | `position` | `'negative' \| 'positive' \| 'cross'` | `cross` | normal span |
723
+ | `length` | number | `76` | minimum zero |
724
+ | `centerOffset` | number | `null` in base | positive-up public offset |
725
+ | `offset` | number | `0` | deprecated compatibility alias |
726
+ | `width` | number | `2` | minimum `0.5` |
727
+ | `color` | CSS color | `#b91c1c` | fill/stroke |
728
+ | `strokeColor` | CSS color | `none` | outline |
729
+ | `strokeWidth` | number | `0` | minimum zero |
730
+ | `opacity` | number | `1` | visual opacity |
731
+ | `lineCap` | string | `round` | SVG linecap |
732
+ | `colorByZone` | boolean | `false` | use active zone color |
733
+ | `markerSize` | number | `8` | marker type |
734
+ | `carriageWidth` | number | `14` | carriage type |
735
+ | `carriageRadius` | number | `4` | carriage corners |
736
+ | `cap.visible` | boolean | `false` | optional cap |
737
+ | `cap.stackOrder` | `'above' \| 'below'` | `above` | cap relation |
738
+ | `cap.radius` | number | `4` | minimum zero |
739
+ | `cap.color` | CSS color | `#b91c1c` | fill |
740
+ | `cap.strokeColor` | CSS color | `#ffffff` | outline |
741
+ | `cap.strokeWidth` | number | `1` | minimum zero |
742
+
743
+ Use `centerOffset` in new code. Positive values move upward for a left-to-right axis.
744
+ The older `offset` retains its original raw axis-normal sign.
745
+
746
+ ## `centerZero`
747
+
748
+ | Key | Type | Base default | Notes |
749
+ | --- | --- | --- | --- |
750
+ | `zeroValue` | number | `0` | numeric center reference |
751
+ | `symmetric` | boolean | `false` | expands range equally around zeroValue |
752
+ | `marker.visible` | boolean | `false` | preset may enable |
753
+ | `marker.radius` | number or `null` | `null` | radial marker radius |
754
+ | `marker.position` | `'negative' \| 'positive' \| 'cross'` | `cross` | linear marker span |
755
+ | `marker.centerOffset` | number | `0` | positive-up on horizontal axis |
756
+ | `marker.length` | number | `20` | minimum zero |
757
+ | `marker.width` | number | `3` | minimum `0.5` |
758
+ | `marker.color` | CSS color | `#111827` | stroke |
759
+ | `marker.opacity` | number | `1` | clamped `0 ... 1` |
760
+ | `marker.lineCap` | string | `round` | SVG linecap |
761
+
762
+ For center-zero types, use a range that contains `zeroValue`. With
763
+ `symmetric: true`, the runtime expands the larger side across the reference:
764
+
765
+ ```js
766
+ {
767
+ min: -30,
768
+ max: 80,
769
+ centerZero: {
770
+ zeroValue: 0,
771
+ symmetric: true
772
+ }
773
+ }
774
+ ```
775
+
776
+ resolves to a symmetric `-80 ... 80` range.
777
+
778
+ ## `tape`
779
+
780
+ Used by `rolling-tape` and custom tape types:
781
+
782
+ | Key | Type | Base default | Notes |
783
+ | --- | --- | --- | --- |
784
+ | `orientation` | `'vertical' \| 'horizontal'` | `vertical` | travel axis |
785
+ | `x`, `y` | number | `64`, `58` | window origin |
786
+ | `width`, `height` | number | `192`, `210` | minimum 20 |
787
+ | `cornerRadius` | number | `12` | minimum zero |
788
+ | `windowColor` | CSS color | `#e5e7eb` | outer window |
789
+ | `windowStrokeColor` | CSS color | `#475569` | border |
790
+ | `windowStrokeWidth` | number | `2` | minimum zero |
791
+ | `windowInnerStrokeColor` | CSS color | `#d1d5db` | inner border |
792
+ | `windowInnerStrokeWidth` | number | `1` | minimum zero |
793
+ | `stripColor` | CSS color | `#ffffff` | moving strip |
794
+ | `textColor` | CSS color | `#111827` | numeric labels |
795
+ | `tickColor` | CSS color | `#334155` | major ticks |
796
+ | `minorTickColor` | CSS color | `#64748b` | minor ticks |
797
+ | `minorTickOpacity` | number | `0.85` | clamped `0 ... 1` |
798
+ | `majorTickWidth` | number | `2` | minimum `0.5` |
799
+ | `minorTickWidth` | number | `1` | minimum `0.5` |
800
+ | `majorTickLength` | number | `24` | minimum zero |
801
+ | `minorTickLength` | number | `12` | minimum zero |
802
+ | `tickInset` | number | `64` | placement from edge |
803
+ | `labelOffset` | number | `10` | text placement |
804
+ | `interval` | number or `null` | `null` | positive explicit major interval |
805
+ | `minorSubdivisions` | integer | `4` | count between majors |
806
+ | `majorSpacing` | number | `52` | minimum two |
807
+ | `indicatorColor` | CSS color | `#b91c1c` | fixed reference line |
808
+ | `indicatorWidth` | number | `2.5` | minimum `0.5` |
809
+ | `fadeSize` | number | `32` | clamped to half travel-axis size |
810
+ | `fadeColor` | CSS color | `#ffffff` | edge fade |
811
+ | `colorByZone` | boolean | `false` | slot/reference color behavior |
812
+
813
+ The scale moves behind a fixed edge-to-edge indicator. Do not animate a separate
814
+ pointer for this type. Slot reuse keeps DOM size independent of the full numeric range.
815
+
816
+ ## `light`
817
+
818
+ The shared light is available to every built-in type. A custom type must include
819
+ `Gaugeit.layers.light`.
820
+
821
+ | Key | Type | Base default | Normalization and interaction |
822
+ | --- | --- | --- | --- |
823
+ | `visible` | boolean | `false` | only true renders |
824
+ | `x`, `y` | number | `93`, `110` | linear Y uses positive-up convention |
825
+ | `radius` | number | `8` | minimum one; physical bulb |
826
+ | `glowRadius` | number or `null` | `20` | minimum zero; null derives `radius * 1.82` |
827
+ | `glowSharpness` | number | `0` | clamped `0 ... 1`; 1 removes blur |
828
+ | `color` | named palette | `red` | ten allowed names |
829
+ | `customColor` | CSS color string | empty | overrides active palette when non-empty |
830
+ | `offColor` | CSS color | `#f3ead6` | unlit lens center |
831
+ | `offEdgeColor` | CSS color | `#817768` | unlit lens edge |
832
+ | `bezelColor` | CSS color | `#4b5563` | hardware |
833
+ | `bezelHighlightColor` | CSS color | `#f8fafc` | hardware highlight |
834
+ | `opacity` | number | `1` | illuminated-lens opacity only |
835
+ | `glowOpacity` | number | `0.15` | halo opacity only |
836
+ | `on` | boolean | `false` | manual trigger state |
837
+ | `glow` | boolean | `true` | false hides halo |
838
+ | `pulse` | boolean | `false` | active steady-light breathing |
839
+ | `pulseInterval` | number | `2600` | minimum 400 ms |
840
+ | `blink` | boolean | `false` | active blink; takes precedence over pulse |
841
+ | `blinkInterval` | number | `1000` | minimum 100 ms |
842
+ | `flicker` | boolean | `true` | randomized overlay while active |
843
+ | `flickerIntensity` | number | `0.1` | clamped `0 ... 1` |
844
+ | `flickerGlowRadius` | number | `0.1` | relative ± variation, clamped `0 ... 1` |
845
+ | `flickerMinInterval` | number | `45` | minimum 16 ms |
846
+ | `flickerMaxInterval` | number | `140` | minimum 16 ms; order repaired |
847
+ | `trigger.mode` | enum | `below` | `manual`, `below`, `above`, `between` |
848
+ | `trigger.source` | enum | `target` | `target`, `displayed`, `pointer` |
849
+ | `trigger.value` | number | `15` | inclusive below/above threshold |
850
+ | `trigger.min` | number | `0` | inclusive between lower bound |
851
+ | `trigger.max` | number | `15` | inclusive between upper bound |
852
+
853
+ Allowed palettes:
854
+
855
+ ```text
856
+ red
857
+ green
858
+ blue
859
+ amber
860
+ yellow
861
+ orange
862
+ white
863
+ halogen
864
+ cyan
865
+ brown
866
+ ```
867
+
868
+ Examples:
869
+
870
+ ```js
871
+ light: {
872
+ visible: true,
873
+ color: 'amber',
874
+ glowRadius: 28,
875
+ glowSharpness: 0.25,
876
+ glowOpacity: 0.22,
877
+ trigger: {
878
+ mode: 'above',
879
+ source: 'target',
880
+ value: 80
881
+ }
882
+ }
883
+ ```
884
+
885
+ ```js
886
+ light: {
887
+ visible: true,
888
+ on: true,
889
+ pulse: true,
890
+ blink: false,
891
+ flicker: false,
892
+ trigger: {
893
+ mode: 'manual'
894
+ }
895
+ }
896
+ ```
897
+
898
+ Important interactions:
899
+
900
+ - blink disables pulse while active;
901
+ - flicker may run with blink or pulse;
902
+ - flicker and pulse run only while the trigger is active;
903
+ - reduced motion suppresses blink, pulse, and flicker motion;
904
+ - `opacity` does not fade the unlit physical lens or bezel;
905
+ - `glowOpacity` is independent from lens `opacity`;
906
+ - `glowRadius` controls halo size independently from bulb `radius`;
907
+ - glow bounds are calculated automatically and must stay synchronized with rendering.
908
+
909
+ Common misuse: enabling `on: true` while leaving trigger mode at the default `below`.
910
+ `on` is read only by `trigger.mode: 'manual'`.
911
+
912
+ ## `readout`
913
+
914
+ `readout.visible` controls the complete group. Each child also needs `visible: true`.
915
+
916
+ Shared text-part fields:
917
+
918
+ | Key | Type | Base title default | Meaning |
919
+ | --- | --- | --- | --- |
920
+ | `visible` | boolean | `false` | only true renders |
921
+ | `text` | string | empty | title/unit content |
922
+ | `x` | number or `'auto'` | `auto` | auto centers |
923
+ | `y` | number or `'auto'` | `auto` for title | auto uses position/margin |
924
+ | `position` | `'top' \| 'center' \| 'bottom'` | `top` | automatic Y anchor |
925
+ | `margin` | number | `12` | minimum zero |
926
+ | `offsetX`, `offsetY` | number | `0` | refine automatic placement only |
927
+ | `color` | CSS color | type-specific | text fill |
928
+ | `fontFamily` | string | system stack | SVG text |
929
+ | `fontSize` | number | `14` title | minimum one |
930
+ | `fontWeight` | string or number | `700` title | SVG text |
931
+ | `letterSpacing` | number | `0.8` title | SVG text |
932
+
933
+ `readout.value` additionally supports:
934
+
935
+ | Key | Type | Base default |
936
+ | --- | --- | --- |
937
+ | `fractionDigits` | integer | `0`, clamped `0 ... 12` |
938
+ | `prefix` | string | empty |
939
+ | `suffix` | string | empty |
940
+ | `locale` | string or string array | undefined |
941
+ | `formatter` | function or null | null |
942
+
943
+ A value formatter takes precedence over locale and fraction settings.
944
+
945
+ Numeric coordinates are final SVG coordinates. Automatic offsets apply only when the
946
+ corresponding coordinate is `'auto'`.
947
+
948
+ Example:
949
+
950
+ ```js
951
+ readout: {
952
+ visible: true,
953
+ title: {
954
+ visible: true,
955
+ text: 'TEMPERATURE'
956
+ },
957
+ value: {
958
+ visible: true,
959
+ fractionDigits: 1
960
+ },
961
+ unit: {
962
+ visible: true,
963
+ text: '°C'
964
+ }
965
+ }
966
+ ```
967
+
968
+ Common misuse: setting only `readout.visible: true` and expecting the numeric value to
969
+ appear.
970
+
971
+ ## `animation`
972
+
973
+ | Key | Type | Base default | Notes |
974
+ | --- | --- | --- | --- |
975
+ | `enabled` | boolean | `true` | global value animation |
976
+ | `duration` | number | `850` | milliseconds, minimum zero |
977
+ | `easing` | string or function | `easeOutCubic` | resolved by easing system |
978
+ | `animateOnInit` | boolean | `true` | startup animation |
979
+ | `startFrom` | `'min' \| 'max' \| 'value' \| number` | `min` | invalid becomes min |
980
+ | `respectReducedMotion` | boolean | `true` | suppresses animation when requested |
981
+
982
+ Per-update overrides:
983
+
984
+ ```js
985
+ gauge.setValue(80, {
986
+ animate: true,
987
+ duration: 1200,
988
+ easing: 'easeInOutCubic'
989
+ });
990
+ ```
991
+
992
+ ## `effects.jitter`
993
+
994
+ | Key | Type | Base default | Notes |
995
+ | --- | --- | --- | --- |
996
+ | `enabled` | boolean | `false` | visual-only |
997
+ | `amplitude` | number | `0.45` | minimum zero |
998
+ | `amplitudeUnit` | `'value' \| 'percent'` | `value` | percent of range |
999
+ | `frequency` | number | `4` | targets per second, minimum 0.1 |
1000
+ | `smoothing` | number | `0.86` | clamped `0 ... 0.999` |
1001
+ | `whenIdleOnly` | boolean | `true` | suppress during base animation |
1002
+
1003
+ Jitter affects only radial pointer, linear indicator, or rolling tape visual position.
1004
+ It does not affect:
1005
+
1006
+ - `getValue()`;
1007
+ - `getDisplayedValue()`;
1008
+ - readout text;
1009
+ - DOM event values;
1010
+ - ARIA value;
1011
+ - default light trigger source.
1012
+
1013
+ ## `accessibility`
1014
+
1015
+ | Key | Type | Base default | Notes |
1016
+ | --- | --- | --- | --- |
1017
+ | `label` | string | `Gauge` | becomes `aria-label` |
1018
+ | `description` | string | empty | rendered in SVG description |
1019
+ | `valueText` | string, function, or null | null | becomes `aria-valuetext` |
1020
+
1021
+ Example:
1022
+
1023
+ ```js
1024
+ accessibility: {
1025
+ label: 'Battery charge',
1026
+ description: 'Remaining charge in the main battery.',
1027
+ valueText: (value) => `${Math.round(value)} percent`
1028
+ }
1029
+ ```
1030
+
1031
+ The generated root receives `role="meter"`, `aria-valuemin`, `aria-valuemax`, and
1032
+ `aria-valuenow`.
1033
+
1034
+ ## `performance`
1035
+
1036
+ | Key | Type | Base default | Notes |
1037
+ | --- | --- | --- | --- |
1038
+ | `pauseWhenHidden` | boolean | `true` | uses page visibility |
1039
+ | `pauseWhenOffscreen` | boolean | `true` | uses IntersectionObserver when available |
1040
+
1041
+ Runtime option replacement reconfigures the observers.
1042
+
1043
+ ## Type-specific defaults and AI strategy
1044
+
1045
+ Built-in types intentionally override base defaults. An AI should:
1046
+
1047
+ 1. select the closest built-in type;
1048
+ 2. provide only application-specific overrides;
1049
+ 3. avoid copying all base defaults into every example;
1050
+ 4. read the selected type file when exact preset coordinates or colors matter;
1051
+ 5. use `replaceOptions()` when switching complete declarative configurations.
1052
+
1053
+ Minimal type-specific usage:
1054
+
1055
+ ```js
1056
+ Gaugeit.createGauge('#gauge', {
1057
+ type: 'center-zero',
1058
+ value: -18
1059
+ });
1060
+ ```
1061
+
1062
+ is usually safer than reconstructing a center-zero instrument from base defaults.
1063
+
1064
+ ## Update semantics reference
1065
+
1066
+ ```js
1067
+ gauge.setValue(value, {
1068
+ animate,
1069
+ duration,
1070
+ easing
1071
+ });
1072
+ ```
1073
+
1074
+ Dynamic value update; no static re-render unless `overflow: 'expand'` changes range.
1075
+
1076
+ ```js
1077
+ gauge.updateOptions(patch, {
1078
+ animateToValue
1079
+ });
1080
+ ```
1081
+
1082
+ Recursive patch merge; arrays replaced.
1083
+
1084
+ ```js
1085
+ gauge.replaceOptions(options, {
1086
+ animateToValue
1087
+ });
1088
+ ```
1089
+
1090
+ Complete declarative replacement; omitted keys return to selected type defaults.
1091
+
1092
+ ## Option-generation checklist
1093
+
1094
+ Before generating a Gaugeit.js option object, verify:
1095
+
1096
+ - the type name exists;
1097
+ - `min < max`;
1098
+ - center-zero ranges contain the zero reference;
1099
+ - radial, linear, and tape options match the selected family;
1100
+ - readout child visibility is explicit;
1101
+ - light manual mode is used when `on` controls state;
1102
+ - blink/pulse precedence is intentional;
1103
+ - CDN or package version is pinned where appropriate;
1104
+ - accessibility label is meaningful;
1105
+ - CSS is included unless standalone is used;
1106
+ - high-frequency value changes use `setValue()`;
1107
+ - arrays are intentionally replaced during option updates.