gaugeit.js 0.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 (43) hide show
  1. package/CONTRIBUTING.md +36 -0
  2. package/LICENSE +21 -0
  3. package/NOTICE.md +5 -0
  4. package/README.md +894 -0
  5. package/SECURITY.md +24 -0
  6. package/adapters/react/Gaugeit.d.ts +11 -0
  7. package/adapters/react/Gaugeit.js +66 -0
  8. package/adapters/react/Gaugeit.jsx +1 -0
  9. package/dist/gaugeit.cjs +4686 -0
  10. package/dist/gaugeit.cjs.map +7 -0
  11. package/dist/gaugeit.css +300 -0
  12. package/dist/gaugeit.d.ts +442 -0
  13. package/dist/gaugeit.esm.js +4683 -0
  14. package/dist/gaugeit.esm.js.map +7 -0
  15. package/dist/gaugeit.esm.min.js +2 -0
  16. package/dist/gaugeit.esm.min.js.map +7 -0
  17. package/dist/gaugeit.min.cjs +2 -0
  18. package/dist/gaugeit.min.cjs.map +7 -0
  19. package/dist/gaugeit.min.css +1 -0
  20. package/dist/gaugeit.standalone.js +4720 -0
  21. package/dist/gaugeit.standalone.js.map +7 -0
  22. package/dist/gaugeit.standalone.min.js +302 -0
  23. package/dist/gaugeit.standalone.min.js.map +7 -0
  24. package/dist/gaugeit.umd.js +4713 -0
  25. package/dist/gaugeit.umd.js.map +7 -0
  26. package/dist/gaugeit.umd.min.js +4 -0
  27. package/dist/gaugeit.umd.min.js.map +7 -0
  28. package/dist/manifest.json +24 -0
  29. package/docs/api.md +456 -0
  30. package/docs/architecture.md +224 -0
  31. package/docs/assets/arc_zones.png +0 -0
  32. package/docs/assets/center_zero.png +0 -0
  33. package/docs/assets/classic_instrument.png +0 -0
  34. package/docs/assets/classic_linear_center_zero.png +0 -0
  35. package/docs/assets/classic_linear_instrument.png +0 -0
  36. package/docs/assets/heritage_rolling_tape.png +0 -0
  37. package/docs/assets/heritage_round.png +0 -0
  38. package/docs/assets/line_scale.png +0 -0
  39. package/docs/assets/rose_balance.png +0 -0
  40. package/docs/creating-gauge-types.md +286 -0
  41. package/docs/development.md +56 -0
  42. package/docs/framework-integration.md +84 -0
  43. package/package.json +114 -0
package/README.md ADDED
@@ -0,0 +1,894 @@
1
+ # Gaugeit.js
2
+
3
+ Gaugeit.js is a lightweight, dependency-free, extensible analog gauge library built with native SVG, JavaScript, and a small optional CSS file.
4
+
5
+ The first release includes eight gauge types:
6
+
7
+ - `arc`: a clean semicircular gauge with configurable colored or transparent sectors;
8
+ - `classic`: a framed, white, old-style instrument meter;
9
+ - `classic-linear`: a cream-faced vintage instrument with a travelling linear indicator;
10
+ - `center-zero`: a cream heritage bidirectional dial whose resting reference is centered at zero;
11
+ - `classic-linear-zero`: the framed classic linear instrument with a fixed zero reference;
12
+ - `heritage-round`: a circular vintage instrument built from the shared classic layers;
13
+ - `line-scale`: a tick-focused gauge with independently configurable major and minor scale lines;
14
+ - `rolling-tape`: a heritage aircraft-style moving number tape behind a fixed edge-to-edge reference line.
15
+
16
+ The runtime has no image, jQuery, canvas, or framework dependency. It can be used from a classic `<script>` tag, an ES module, a custom element, React, Twig, Blade, or another server-rendered application.
17
+
18
+ > **Current release:** `0.1.0`
19
+ >
20
+ > Source code, prebuilt distributions, documentation, examples, and package
21
+ > metadata are maintained in this repository.
22
+
23
+ ## Why SVG
24
+
25
+ SVG is used as the default renderer because it is resolution-independent, responsive without device-pixel-ratio bookkeeping, easy to theme with CSS, accessible through normal DOM attributes, and efficient for the relatively small number of vector elements used by analog gauges. Static parts are rendered once. During animation, Gaugeit.js normally updates only the active pointer, linear indicator, or rolling tape slots together with readout text and the ARIA value.
26
+
27
+ ## Browser installation with separate files
28
+
29
+ This is the recommended browser installation. It keeps JavaScript and CSS separately cacheable.
30
+
31
+ ```html
32
+ <link rel="stylesheet" href="gaugeit.min.css">
33
+ <div id="score-gauge"></div>
34
+ <script src="gaugeit.umd.min.js"></script>
35
+ <script>
36
+ const gauge = Gaugeit.createGauge('#score-gauge', {
37
+ type: 'arc',
38
+ min: 0,
39
+ max: 3000,
40
+ value: 625,
41
+ zones: [
42
+ { min: 0, max: 500, color: '#ef4444' },
43
+ { min: 500, max: 1000, color: '#facc15' },
44
+ { min: 1000, max: 2100, color: '#22c55e' },
45
+ { min: 2100, max: 2500, color: '#facc15' },
46
+ { min: 2500, max: 3000, color: '#ef4444' }
47
+ ]
48
+ });
49
+
50
+ gauge.setValue(1420);
51
+ </script>
52
+ ```
53
+
54
+ Open `example.html` directly in a browser to see all eight built-in types, live controls,
55
+ and a four-instrument customization gallery that replaces preset colors entirely through options.
56
+
57
+ ## One-file standalone browser installation
58
+
59
+ The standalone bundle exposes the same `window.Gaugeit` API and injects the complete
60
+ responsive stylesheet automatically. The stable `#gaugeit-styles` marker ensures that
61
+ the stylesheet is injected only once.
62
+
63
+ ```html
64
+ <div id="gauge"></div>
65
+ <script src="gaugeit.standalone.min.js"></script>
66
+ <script>
67
+ Gaugeit.createGauge('#gauge', { type: 'classic', value: 72 });
68
+ </script>
69
+ ```
70
+
71
+ The standalone and separate-file builds are generated from the same canonical
72
+ `src/gaugeit.css` source. Neither contains a separately maintained CSS copy.
73
+
74
+ ## Native browser ES module
75
+
76
+ ```html
77
+ <link rel="stylesheet" href="./dist/gaugeit.css">
78
+ <div id="temperature"></div>
79
+ <script type="module">
80
+ import Gaugeit, { createGauge } from './dist/gaugeit.esm.js';
81
+
82
+ const gauge = createGauge(document.querySelector('#temperature'), {
83
+ type: 'classic',
84
+ min: -20,
85
+ max: 120,
86
+ value: 68,
87
+ readout: {
88
+ title: { visible: true, text: 'COOLANT' },
89
+ unit: { visible: true, text: '°C' }
90
+ }
91
+ });
92
+
93
+ console.log(Gaugeit.VERSION);
94
+ </script>
95
+ ```
96
+
97
+ ## npm installation
98
+
99
+ ```bash
100
+ npm install gaugeit.js@0.1.0
101
+ ```
102
+
103
+ ```js
104
+ import { createGauge } from 'gaugeit.js';
105
+ import 'gaugeit.js/css';
106
+
107
+ const gauge = createGauge('#gauge', { type: 'arc', value: 64 });
108
+ ```
109
+
110
+ npm consumers receive prebuilt JavaScript, CSS, declarations, and source maps.
111
+ Applications normally do not modify anything in `dist/`.
112
+
113
+ ### CommonJS
114
+
115
+ ```js
116
+ const { createGauge } = require('gaugeit.js');
117
+ const gauge = createGauge(document.querySelector('#gauge'), { value: 64 });
118
+ ```
119
+
120
+ ## CDN installation
121
+
122
+ Use exact versions in production. CDN consumers need no installation or compilation.
123
+
124
+ ### jsDelivr, recommended two-file installation
125
+
126
+ ```html
127
+ <link
128
+ rel="stylesheet"
129
+ href="https://cdn.jsdelivr.net/npm/gaugeit.js@0.1.0/dist/gaugeit.min.css"
130
+ >
131
+ <script
132
+ src="https://cdn.jsdelivr.net/npm/gaugeit.js@0.1.0/dist/gaugeit.umd.min.js"
133
+ ></script>
134
+ ```
135
+
136
+ ### jsDelivr, one-file installation
137
+
138
+ ```html
139
+ <script
140
+ src="https://cdn.jsdelivr.net/npm/gaugeit.js@0.1.0/dist/gaugeit.standalone.min.js"
141
+ ></script>
142
+ ```
143
+
144
+ ### UNPKG
145
+
146
+ ```html
147
+ <link
148
+ rel="stylesheet"
149
+ href="https://unpkg.com/gaugeit.js@0.1.0/dist/gaugeit.min.css"
150
+ >
151
+ <script
152
+ src="https://unpkg.com/gaugeit.js@0.1.0/dist/gaugeit.umd.min.js"
153
+ ></script>
154
+ ```
155
+
156
+ The UNPKG one-file equivalent is
157
+ `https://unpkg.com/gaugeit.js@0.1.0/dist/gaugeit.standalone.min.js`.
158
+
159
+ ## Built-in gauge gallery
160
+
161
+ **Arc zones.** A clean semicircular gauge with configurable colored sectors, tapered zones, and true transparent gaps.
162
+
163
+ ![Arc zones gauge with configurable colored sectors](docs/assets/arc_zones.png)
164
+
165
+ **Classic instrument.** A framed white instrument with a glass highlight, fine scale markings, and a traditional spear pointer.
166
+
167
+ ![Classic framed analog instrument](docs/assets/classic_instrument.png)
168
+
169
+ **Heritage round.** A circular vintage instrument built from the shared classic face, bezel, scale, and pointer layers.
170
+
171
+ ![Heritage round analog instrument](docs/assets/heritage_round.png)
172
+
173
+ **Line scale.** A tick-focused gauge with independently configurable major divisions, minor ticks, labels, and pointer styling.
174
+
175
+ ![Line scale gauge](docs/assets/line_scale.png)
176
+
177
+ **Classic linear instrument.** A cream-faced vintage scale with configurable zones, fine ticks, and a travelling hairline indicator.
178
+
179
+ ![Classic linear analog instrument](docs/assets/classic_linear_instrument.png)
180
+
181
+ **Center zero.** A signed bidirectional dial with a fixed center reference and pointer movement on both sides of zero.
182
+
183
+ ![Center-zero bidirectional analog gauge](docs/assets/center_zero.png)
184
+
185
+ **Classic linear center zero.** The classic linear instrument with a permanent zero marker and signed indicator travel in both directions.
186
+
187
+ ![Classic linear center-zero instrument](docs/assets/classic_linear_center_zero.png)
188
+
189
+ **Heritage rolling tape.** An aircraft-style numeric tape that moves behind a fixed edge-to-edge reference line.
190
+
191
+ ![Heritage aircraft-style rolling tape instrument](docs/assets/heritage_rolling_tape.png)
192
+
193
+ ## Customization example
194
+
195
+ **Rose balance.** The heritage center-zero instrument recolored entirely through ordinary Gaugeit.js configuration options.
196
+
197
+ ![Rose-themed customization of the center-zero gauge](docs/assets/rose_balance.png)
198
+
199
+ ## Minimal configuration
200
+
201
+ Only the target and value are required in ordinary use. Every gauge type supplies its own visual defaults.
202
+
203
+ ```js
204
+ const gauge = Gaugeit.createGauge('#gauge', {
205
+ type: 'line-scale',
206
+ value: 72
207
+ });
208
+ ```
209
+
210
+ ## True transparent zones
211
+
212
+ A zone with `color: 'transparent'`, `color: 'none'`, or no color creates a real gap. The base track is not drawn under that interval unless `track.showUnderZones` is explicitly enabled.
213
+
214
+ ```js
215
+ zones: [
216
+ { min: 0, max: 25, color: '#ef4444' },
217
+ { min: 25, max: 40, color: 'transparent' },
218
+ { min: 40, max: 80, color: '#22c55e' },
219
+ { min: 80, max: 100, color: '#facc15' }
220
+ ]
221
+ ```
222
+
223
+ For compatibility with Gauge.js-style configuration, `strokeStyle` is accepted as an alias for a zone's `color`.
224
+
225
+ Radial zones may also taper smoothly. `width` remains the uniform fallback, while
226
+ `taper.startWidth` and `taper.endWidth` define the thickness at the zone's numeric
227
+ `min` and `max` endpoints. The direction therefore follows the value mapping even when
228
+ `invert` is enabled:
229
+
230
+ ```js
231
+ zones: [
232
+ {
233
+ min: 0,
234
+ max: 60,
235
+ color: '#06b6d4',
236
+ width: 18,
237
+ taper: {
238
+ startWidth: 0,
239
+ endWidth: 36,
240
+ segments: 56
241
+ }
242
+ }
243
+ ]
244
+ ```
245
+
246
+ `segments` controls curve smoothness and is clamped between 2 and 256. Tapered zones
247
+ are rendered as filled SVG annular segments because a native SVG stroke cannot change
248
+ width along one path. Uniform zones continue to use the lighter stroke representation.
249
+
250
+ ## Inverted scales
251
+
252
+ Set the top-level `invert` option to `true` to mirror the value direction while keeping
253
+ the configured geometry unchanged. The minimum and maximum swap visual endpoints, and
254
+ ticks, labels, zones, and the pointer all follow the same value-based mapping:
255
+
256
+ ```js
257
+ createGauge('#fuel', {
258
+ type: 'heritage-round',
259
+ min: 0,
260
+ max: 100,
261
+ invert: true
262
+ });
263
+ ```
264
+
265
+ `invert` defaults to `false`. Zones remain defined with their normal numeric `min` and
266
+ `max` values; low-value warning zones therefore move automatically with the inverted
267
+ scale and do not need to be rewritten.
268
+
269
+ ## Major and minor ticks
270
+
271
+ ```js
272
+ scale: {
273
+ radius: 124,
274
+ position: 'inside', // inside, outside, or cross
275
+ major: {
276
+ interval: 20, // explicit value interval
277
+ divisions: 5, // used only when interval is null
278
+ length: 24,
279
+ width: 2.5,
280
+ color: '#111827'
281
+ },
282
+ minor: {
283
+ subdivisions: 4, // four minor lines between major ticks
284
+ length: 11,
285
+ width: 1,
286
+ color: '#64748b'
287
+ }
288
+ }
289
+ ```
290
+
291
+ ## Pointer types
292
+
293
+ Built-in pointer shapes are:
294
+
295
+ - `needle`: traditional tapered needle;
296
+ - `line`: rounded line pointer;
297
+ - `arrow`: arrow-shaped pointer;
298
+ - `spear`: vintage instrument pointer.
299
+
300
+ ```js
301
+ pointer: {
302
+ type: 'spear',
303
+ length: 98,
304
+ shaftLength: 67, // optional; null preserves the type-specific shape
305
+ tailLength: 14,
306
+ width: 9,
307
+ color: '#111827',
308
+ colorByZone: false,
309
+ cap: {
310
+ visible: true,
311
+ stackOrder: 'above', // above or below the pointer shape
312
+ radius: 9,
313
+ color: '#20242a',
314
+ strokeColor: '#ffffff',
315
+ strokeWidth: 2
316
+ }
317
+ }
318
+ ```
319
+
320
+ Built-in gauge types paint their pointer or linear indicator after the readout, so it
321
+ always crosses above title, value, and unit text. Caps default to
322
+ `stackOrder: 'above'`. A custom type can use `'below'` when its pointer should cross
323
+ over the center cap. This uses deterministic SVG paint order rather than CSS
324
+ `z-index`.
325
+
326
+ ## Classic linear instrument
327
+
328
+ `classic-linear` reuses the classic cream panel, bezel, glass, readouts, scale
329
+ configuration, zones, animation, jitter, and accessibility lifecycle. Its value moves a
330
+ straight indicator instead of rotating a pointer:
331
+
332
+ ```js
333
+ createGauge('#signal', {
334
+ type: 'classic-linear',
335
+ min: 0,
336
+ max: 100,
337
+ value: 62,
338
+ linear: {
339
+ originY: 142,
340
+ startX: 38,
341
+ startY: 0,
342
+ endX: 282,
343
+ endY: 0,
344
+ tickSide: 'negative',
345
+ labelOffset: 38,
346
+ zoneOffset: -16
347
+ },
348
+ indicator: {
349
+ type: 'hairline', // hairline | marker | carriage
350
+ position: 'cross',
351
+ length: 84,
352
+ centerOffset: 0, // positive moves upward on a horizontal axis
353
+ width: 2.4,
354
+ cap: { visible: false }
355
+ },
356
+ zones: [
357
+ { min: 0, max: 20, color: '#b91c1c' },
358
+ { min: 20, max: 70, color: 'transparent' },
359
+ { min: 70, max: 100, color: '#15803d' }
360
+ ]
361
+ });
362
+ ```
363
+
364
+ The axis is defined by two points, so custom types may use horizontal, vertical, or
365
+ diagonal linear scales. Linear Y coordinates use a Cartesian convention around
366
+ `linear.originY`: `0` is the preset baseline, positive values move upward, and negative
367
+ values move downward. X coordinates continue to increase to the right. The built-in
368
+ classic-linear fallback therefore uses `startY: 0`, `endY: 0`, and title `y: 60`.
369
+ `labelOffset`, `zoneOffset`, and `indicator.centerOffset` use the same positive-up
370
+ convention for a left-to-right horizontal scale. `invert: true` reverses value direction
371
+ without changing axis or zone definitions. Major and minor tick lengths remain
372
+ independently configurable through `scale.major.length` and `scale.minor.length`.
373
+
374
+
375
+ ## Center-zero and rolling-tape gauges
376
+
377
+ The specialized built-ins use the same lifecycle, animation, jitter, zones,
378
+ accessibility, responsive layout, and runtime update API as the other instruments.
379
+
380
+ The cream-faced radial `center-zero` preset keeps an automatically symmetric range
381
+ around a custom reference value. Its title is an ordinary readout part and defaults
382
+ between the pointer hub and the lower numeric value; `readout.title.x` and
383
+ `readout.title.y` can move it anywhere in the dial:
384
+
385
+ ```js
386
+ createGauge('#trim', {
387
+ type: 'center-zero',
388
+ min: -50,
389
+ max: 50,
390
+ value: 12,
391
+ centerZero: {
392
+ zeroValue: 0,
393
+ symmetric: true,
394
+ marker: { color: '#5b4636', length: 26, width: 3 }
395
+ }
396
+ });
397
+ ```
398
+
399
+ For a straight scale, `classic-linear-zero` reuses the complete classic linear face,
400
+ axis, ticks, labels, zones, indicator, clipping, and sizing system. It adds only a fixed
401
+ zero-reference layer:
402
+
403
+ ```js
404
+ createGauge('#deviation', {
405
+ type: 'classic-linear-zero',
406
+ min: -100,
407
+ max: 100,
408
+ value: -28,
409
+ geometry: { width: 520, height: 220 },
410
+ centerZero: {
411
+ zeroValue: 0,
412
+ symmetric: true,
413
+ marker: { visible: true, length: 34, centerOffset: 0 }
414
+ }
415
+ });
416
+ ```
417
+
418
+ `rolling-tape` keeps an edge-to-edge reference line fixed while the numerical scale
419
+ moves smoothly underneath it. Its cream face is only a preset: every face, window,
420
+ strip, tick, label, fade, reference-line, and zone color remains configurable.
421
+
422
+ ```js
423
+ createGauge('#altitude', {
424
+ type: 'rolling-tape',
425
+ geometry: { width: 300, height: 480 },
426
+ min: 0,
427
+ max: 10000,
428
+ value: 4250,
429
+ tape: {
430
+ interval: 2000,
431
+ minorSubdivisions: 3,
432
+ majorSpacing: 58,
433
+ stripColor: '#fff9ea',
434
+ textColor: '#211f1a',
435
+ indicatorColor: '#991b1b',
436
+ windowInnerStrokeColor: '#d8cfbd'
437
+ },
438
+ readout: {
439
+ title: { visible: true, text: 'ALTITUDE' },
440
+ value: { visible: true, x: 150, y: 390 },
441
+ unit: { visible: true, text: 'FT' }
442
+ }
443
+ });
444
+ ```
445
+
446
+ The optional separate tape value uses the same independently configurable `x` and `y`
447
+ coordinates as every other readout part. It may be hidden when the moving tape itself
448
+ is the only numeric display required.
449
+
450
+ All three presets accept normal `updateOptions()` calls, `invert`, custom formatters,
451
+ custom CSS classes, and per-zone colors. Omitting an option always falls back to the
452
+ type preset.
453
+
454
+ ## Warning lights and annunciators
455
+
456
+ Every built-in gauge type can host the shared optional `light` layer. The lamp is SVG,
457
+ so its coordinates and radius scale with the gauge viewBox, it respects framed face
458
+ clipping, and it remains correctly positioned at every rendered size. The component is
459
+ disabled by default.
460
+
461
+ ```js
462
+ createGauge('#fuel', {
463
+ type: 'heritage-round',
464
+ min: 0,
465
+ max: 100,
466
+ value: 12,
467
+ light: {
468
+ visible: true,
469
+ x: 108,
470
+ y: 110,
471
+ radius: 10,
472
+ color: 'amber',
473
+ offColor: '#f3ead6',
474
+ pulse: true,
475
+ blink: true,
476
+ blinkInterval: 1000,
477
+ trigger: { mode: 'below', source: 'target', value: 15 }
478
+ }
479
+ });
480
+ ```
481
+
482
+ Built-in palettes are `red`, `green`, `blue`, `amber`, `yellow`, `orange`,
483
+ `white`, `halogen`, `cyan`, and `brown`. Use `customColor` for an application-specific
484
+ active color. The unlit lamp deliberately uses a softly diffused cream lens rather than
485
+ a black bulb; `offColor`, `offEdgeColor`, `bezelColor`, and `bezelHighlightColor` can
486
+ customize that hardware appearance. `pulse` adds a deliberately subtle steady-light
487
+ breathing effect. `blink` alternates the complete illuminated and unlit lens states, so
488
+ the dark half-cycle looks exactly like the normal switched-off lamp rather than a faded
489
+ transparent bulb. Trigger modes are `manual`, `below`, `above`, and `between`. The trigger source
490
+ defaults to `target`, so a low-value lamp does not flash merely because a startup
491
+ animation begins at the minimum. Use `displayed` to follow the animated readout value
492
+ or `pointer` to include visual jitter. For linear gauges, `light.y` follows the same
493
+ positive-up coordinate convention as the linear axis.
494
+
495
+ Custom types can opt in with `Gaugeit.layers.light`; all built-in types already include
496
+ the layer and provide a sensible type-specific fallback position while keeping
497
+ `light.visible` false.
498
+
499
+ ## Intrinsic dimensions and host sizing
500
+
501
+ `classic-linear`, `classic-linear-zero`, and `rolling-tape` can change their internal
502
+ instrument proportions through `geometry.width` and `geometry.height`. The type's
503
+ configuration hook scales only fallback geometry; any explicitly supplied coordinate
504
+ remains authoritative. `geometry.aspectRatio` may derive the missing dimension:
505
+
506
+ ```js
507
+ createGauge('#wide-signal', {
508
+ type: 'classic-linear',
509
+ geometry: { width: 560, aspectRatio: 2.8 }
510
+ });
511
+ ```
512
+
513
+ These logical dimensions control composition and the SVG viewBox. Page layout can still
514
+ size any gauge host independently with normal CSS. Gaugeit provides optional custom
515
+ properties for common integrations:
516
+
517
+ ```css
518
+ #wide-signal {
519
+ --gaugeit-width: 100%;
520
+ --gaugeit-height: 220px;
521
+ --gaugeit-aspect-ratio: 14 / 5;
522
+ }
523
+ ```
524
+
525
+ Use `layout.mode: 'contain'` when both host width and height are constrained and the
526
+ whole instrument must scale inside that box. Leave the default `intrinsic` mode when
527
+ the SVG's natural aspect ratio should determine document height.
528
+
529
+ ## Optional readout text
530
+
531
+ The title, numeric value, and unit are independent SVG text elements. The numeric
532
+ value and unit are hidden by default so a gauge starts as a pure analog instrument.
533
+ `x: 'auto'` centers a part in its instrument reference box. `y: 'auto'` uses
534
+ `position: 'top'`, `center`, or `bottom`, with optional `margin`, `offsetX`, and
535
+ `offsetY`. Offsets refine automatic placement only. Numeric coordinates are final
536
+ SVG positions for radial gauges. In linear gauges, numeric Y coordinates are relative
537
+ to `linear.originY` and increase upward, while numeric X coordinates increase rightward:
538
+
539
+ ```js
540
+ readout: {
541
+ visible: true,
542
+ title: { visible: true, text: 'PRESSURE', x: 160, y: 24 },
543
+ value: { visible: true, x: 160, y: 50, fractionDigits: 0 },
544
+ unit: { visible: true, text: 'kPa', x: 160, y: 72 }
545
+ }
546
+ ```
547
+
548
+ For the built-in `arc` type, the title defaults to `x: 'auto', y: 35`. An enabled
549
+ numeric value defaults to `x: 160, y: 130`, inside the arc between the upper scale
550
+ labels and the pointer pivot.
551
+
552
+ ## Custom arc sweep and responsive layout
553
+
554
+ `geometry.startAngle` and `geometry.endAngle` define the pointer and scale sweep.
555
+ For example, a centered 240-degree arc uses `startAngle: 150` and `endAngle: 390`.
556
+ Interactive controls can use `Gaugeit.geometry.centeredArcAngles(sweep, centerAngle)`: a
557
+ center angle of `0` means the normal upright orientation (internally SVG angle 270),
558
+ while positive values rotate clockwise. The difference may be configured up to 360
559
+ degrees. Full circles are rendered as two
560
+ SVG half-arcs, and the maximum tick/label endpoint is deduplicated by default so it
561
+ does not overlap the minimum endpoint.
562
+
563
+ The default `layout.mode: 'intrinsic'` computes a content-aware viewBox. Increasing a
564
+ sweep to 360 degrees therefore increases the gauge's natural height and pushes the
565
+ following document content down instead of overflowing it. Use `layout.mode: 'contain'`
566
+ when the host has an explicit CSS width and height and the complete instrument should
567
+ scale down to fit that box:
568
+
569
+ ```js
570
+ layout: {
571
+ mode: 'contain', // intrinsic | contain
572
+ padding: 10
573
+ }
574
+ ```
575
+
576
+ Classic panel faces use `face.fit: 'content'`, so the face expands when a larger sweep
577
+ or moved readout needs more drawing space. `face.minWidth` and `face.minHeight` define
578
+ optional lower limits for a content-fitted frame. The built-in framed instruments also
579
+ use `face.clipContent: true`, which clips pointers, indicators, zones, ticks, labels,
580
+ and readouts to the inner bezel instead of allowing them to overflow the frame.
581
+ Circular faces remain available through `shape: 'circle'` and the ready-made
582
+ `heritage-round` type.
583
+
584
+ ## Startup animation
585
+
586
+ ```js
587
+ animation: {
588
+ enabled: true,
589
+ duration: 900,
590
+ easing: 'easeOutCubic',
591
+ animateOnInit: true,
592
+ startFrom: 'min', // min, max, value, or a number
593
+ respectReducedMotion: true
594
+ }
595
+ ```
596
+
597
+ Set `startFrom: 'value'` or `animateOnInit: false` when the pointer must already be at its final position on first paint.
598
+
599
+ ## Realistic pointer jitter
600
+
601
+ Jitter is a pointer-like visual effect. It randomizes the radial pointer angle or linear indicator position without changing the readout, `getValue()`, `getDisplayedValue()`, accessibility value, or application data.
602
+
603
+ ```js
604
+ effects: {
605
+ jitter: {
606
+ enabled: true,
607
+ amplitude: 0.8,
608
+ amplitudeUnit: 'value', // value or percent
609
+ frequency: 5,
610
+ smoothing: 0.88,
611
+ whenIdleOnly: true
612
+ }
613
+ }
614
+ ```
615
+
616
+ Animation and jitter automatically pause when the page is hidden. They also pause when the gauge is offscreen when `IntersectionObserver` is available.
617
+
618
+ ## Updating an instance
619
+
620
+ ```js
621
+ gauge.setValue(76);
622
+ gauge.setValue(18, { duration: 1400, easing: 'easeInOutCubic' });
623
+ gauge.setValue(50, { animate: false });
624
+
625
+ gauge.updateOptions({
626
+ pointer: { type: 'line', color: '#dc2626' },
627
+ labels: { fractionDigits: 1 }
628
+ });
629
+
630
+ // Replace the complete instance configuration and discard earlier overrides.
631
+ gauge.replaceOptions({ type: 'classic', min: 0, max: 100 });
632
+
633
+ gauge.pause();
634
+ gauge.resume();
635
+ gauge.refresh();
636
+ gauge.destroy();
637
+ ```
638
+
639
+ `set(value)` and `setOptions(options)` are convenience aliases. `updateOptions()`
640
+ merges a partial patch, while `replaceOptions()` replaces the complete user-option
641
+ object and is useful for declarative framework adapters.
642
+
643
+ ## Data-attribute initialization
644
+
645
+ ```html
646
+ <div
647
+ data-gaugeit
648
+ data-gaugeit-type="arc"
649
+ data-gaugeit-min="0"
650
+ data-gaugeit-max="100"
651
+ data-gaugeit-value="63"
652
+ data-gaugeit-options='{"animation":{"startFrom":"value"}}'
653
+ ></div>
654
+
655
+ <script src="dist/gaugeit.umd.min.js"></script>
656
+ <script>
657
+ Gaugeit.autoMount();
658
+ </script>
659
+ ```
660
+
661
+ Auto mounting is duplicate-safe.
662
+
663
+ ## Custom element
664
+
665
+ ```html
666
+ <script src="dist/gaugeit.umd.min.js"></script>
667
+ <script>Gaugeit.defineGaugeitElement();</script>
668
+
669
+ <gauge-it
670
+ type="classic"
671
+ min="0"
672
+ max="240"
673
+ value="88"
674
+ options='{"readout":{"title":{"visible":true,"text":"VOLTAGE"}}}'
675
+ ></gauge-it>
676
+ ```
677
+
678
+ The `value` property or attribute can be changed later.
679
+
680
+ With npm, the same API is available through the explicit Web Component subpath:
681
+
682
+ ```js
683
+ import { defineGaugeitElement } from 'gaugeit.js/web-component';
684
+ import 'gaugeit.js/css';
685
+
686
+ defineGaugeitElement();
687
+ ```
688
+
689
+ ## React
690
+
691
+ ```jsx
692
+ import { useState } from 'react';
693
+ import Gaugeit from 'gaugeit.js/react';
694
+ import 'gaugeit.js/css';
695
+
696
+ export default function DashboardGauge() {
697
+ const [value, setValue] = useState(62);
698
+
699
+ return (
700
+ <Gaugeit
701
+ value={value}
702
+ options={{
703
+ type: 'arc',
704
+ min: 0,
705
+ max: 100,
706
+ zones: [
707
+ { min: 0, max: 40, color: '#ef4444' },
708
+ { min: 40, max: 75, color: '#facc15' },
709
+ { min: 75, max: 100, color: '#22c55e' }
710
+ ]
711
+ }}
712
+ />
713
+ );
714
+ }
715
+ ```
716
+
717
+ The React adapter is intentionally thin. React owns the host node and Gaugeit.js owns
718
+ only the generated content inside it. The published `gaugeit.js/react` entry is normal
719
+ browser-ready ESM JavaScript and does not require a package consumer to transpile JSX
720
+ inside `node_modules`. It replaces the complete `options` prop when that prop changes,
721
+ keeps an explicit `value` prop authoritative, destroys the instance on unmount, and
722
+ forwards a ref to the live `Gauge` instance.
723
+
724
+ ## Laravel, Twig, or plain PHP
725
+
726
+ Copy the prebuilt files into a public vendor directory, include the CSS and UMD bundle, and initialize from the page module. No PHP integration is required.
727
+
728
+ A Blade example is included in `examples/laravel-blade.blade.php`.
729
+
730
+ ### Composer distribution
731
+
732
+ The package includes Composer metadata and a cross-platform vendor binary.
733
+
734
+ ```bash
735
+ composer require kasperi/gaugeit-js:^0.1
736
+ vendor/bin/gaugeit-install-assets public/assets/vendor/gaugeit
737
+ ```
738
+
739
+ The default command copies the recommended `gaugeit.umd.min.js` and
740
+ `gaugeit.min.css`. Use `--standalone` for the one-file build, `--all` for all
741
+ browser assets, or `--clean` to remove only known old Gaugeit.js assets before
742
+ copying. Unrelated destination files are never removed.
743
+
744
+ Composer consumers receive prebuilt assets and do not need Node.js, npm, esbuild,
745
+ or a frontend compilation step. A consuming project can automate the explicit
746
+ asset command in its own `composer.json`:
747
+
748
+ ```json
749
+ {
750
+ "scripts": {
751
+ "assets:gaugeit": "vendor/bin/gaugeit-install-assets public/assets/vendor/gaugeit",
752
+ "post-install-cmd": [
753
+ "@assets:gaugeit"
754
+ ],
755
+ "post-update-cmd": [
756
+ "@assets:gaugeit"
757
+ ]
758
+ }
759
+ }
760
+ ```
761
+
762
+ The dependency itself deliberately does not write into an application's public
763
+ directory during Composer installation.
764
+
765
+ For server-rendered applications, install the assets into a versioned public vendor
766
+ path, load them through the application's asset helper, and initialize gauges from a
767
+ reusable module or duplicate-safe data-attribute initializer.
768
+
769
+ ## Extending gauge types
770
+
771
+ A type is a name, a defaults object, and an ordered array of layers. A layer renders static SVG and may return a small controller whose `update(value, state)` method is called for dynamic changes.
772
+
773
+ ```js
774
+ Gaugeit.registerGaugeType('football-score', {
775
+ description: 'A compact score-performance meter.',
776
+ defaults: {
777
+ geometry: { startAngle: 200, endAngle: 340 },
778
+ pointer: { type: 'arrow', color: '#0f172a' },
779
+ readout: { title: { visible: true, text: 'FORM' } }
780
+ },
781
+ layers: [
782
+ Gaugeit.layers.face,
783
+ Gaugeit.layers.zones,
784
+ Gaugeit.layers.ticks,
785
+ Gaugeit.layers.labels,
786
+ Gaugeit.layers.pointer,
787
+ Gaugeit.layers.readout
788
+ ]
789
+ });
790
+ ```
791
+
792
+ Custom layers can add SVG decorations, markers, secondary pointers, trend arrows, warning lights, or a completely different visual language without changing the animation core.
793
+
794
+ See `docs/creating-gauge-types.md` for a complete custom layer example.
795
+
796
+ ## Accessibility
797
+
798
+ The generated root uses `role="meter"` and receives `aria-valuemin`, `aria-valuemax`, and `aria-valuenow`. Provide a meaningful label and optional value text:
799
+
800
+ ```js
801
+ accessibility: {
802
+ label: 'Prediction accuracy',
803
+ description: 'Accuracy across the last 50 predictions.',
804
+ valueText: (value) => `${Math.round(value)} percent accurate`
805
+ }
806
+ ```
807
+
808
+ Color should not be the only way to communicate meaning. Keep labels, text, or surrounding explanations available for important status information.
809
+
810
+ ## Browser support
811
+
812
+ Gaugeit.js targets current evergreen browsers with native SVG, ES2018+, `requestAnimationFrame`, and modern DOM APIs. The UMD build works as a classic script. The ESM build works in modern module-aware browsers and bundlers.
813
+
814
+ Optional optimizations degrade safely:
815
+
816
+ - no `IntersectionObserver`: the gauge continues running while offscreen;
817
+ - no `matchMedia`: reduced-motion detection is skipped;
818
+ - no `customElements`: only the optional web-component adapter is unavailable.
819
+
820
+ ## Build and tests
821
+
822
+ Maintainers edit files in `src/`, adapters, tests, documentation, and build scripts.
823
+ Install the locked development toolchain and regenerate every distribution format:
824
+
825
+ ```bash
826
+ npm ci
827
+ npm run clean
828
+ npm run build
829
+ npm run dev
830
+ npm run typecheck
831
+ npm test
832
+ npm run test:browser
833
+ npm run check
834
+ npm pack --dry-run
835
+ npm publish --dry-run
836
+ ```
837
+
838
+ `npm run build` uses esbuild to recreate the complete `dist/` directory, including
839
+ normal and minified ESM, UMD, CommonJS, standalone bundles, source maps, CSS,
840
+ declarations, and the manifest. `npm run dev` watches source files. `npm run check`
841
+ starts from a clean directory and runs the build, declaration validation, automated
842
+ tests, DOM smoke test, npm package-content dry run, and Composer validation when
843
+ Composer is available. `prepublishOnly` runs the same full check.
844
+
845
+ Minified files, source maps, embedded standalone CSS, declarations in `dist/`, and
846
+ other generated files must never be edited manually. Change their canonical sources
847
+ and run `npm run build`.
848
+
849
+ The optional `npm run test:chromium` command starts a temporary local HTTP server and
850
+ uses a globally available Chromium executable. It is intentionally separate from the
851
+ portable default checks.
852
+
853
+ ## Distribution policy
854
+
855
+ `dist/` is intentionally committed. GitHub source downloads, Composer
856
+ installations, and CDN/npm consumers receive ready-to-use browser files
857
+ without requiring Node.js. CI runs a clean build and can detect differences in the
858
+ committed output. Package users receive prebuilt assets; only maintainers run the
859
+ distribution build.
860
+
861
+ ## Package layout
862
+
863
+ ```text
864
+ Gaugeit.js/
865
+ ├── src/ Canonical modular source and CSS
866
+ ├── dist/ Reproducible, committed distribution files
867
+ ├── adapters/react/ Optional React adapter and declarations
868
+ ├── docs/ Architecture, API, and development documentation
869
+ ├── examples/ Framework and custom-type examples
870
+ ├── tests/ Unit, DOM, packaging, and installer tests
871
+ ├── scripts/ Build and local validation scripts
872
+ ├── bin/ Composer asset installer
873
+ └── example.html Interactive standalone demonstration
874
+ ```
875
+
876
+ ## Release metadata
877
+
878
+ Version `0.1.0` uses the npm package name `gaugeit.js`, Composer package name
879
+ `kasperi/gaugeit-js`, and source repository
880
+ `https://github.com/kasperikoski/gaugeit.js`. Author metadata identifies Kasperi Koski.
881
+
882
+ The build and CI workflows validate release artifacts but never publish, tag, or create
883
+ remote releases automatically. Publishing to GitHub, npm, and Packagist remains an
884
+ explicit maintainer action.
885
+
886
+ ## Contributing and security
887
+
888
+ Contribution setup and pull-request requirements are documented in
889
+ `CONTRIBUTING.md`. Report suspected vulnerabilities privately as described in
890
+ `SECURITY.md`; do not disclose them in public issues.
891
+
892
+ ## License
893
+
894
+ MIT License. See `LICENSE`.