@24vlh/vds 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.
@@ -1,502 +1 @@
1
- /************************************************************
2
- * VLAH DESIGN SYSTEM (VDS) - Charts / Data Visualization
3
- *
4
- * Responsibilities:
5
- * - Provide a tokenised foundation for data-viz: chart palettes,
6
- * gridlines, axis styling, series families, loading patterns,
7
- * and structural layout containers
8
- * - Define chart containers (header, meta, actions, body, legend)
9
- * that reliably wrap SVG, canvas, or third-party chart libraries
10
- * - Expose semantic series tokens (1–6) and positive/negative/neutral
11
- * mappings aligned with VDS semantic color architecture
12
- * - Supply empty, error, and loading states for dashboard reliability
13
- *
14
- * System Notes:
15
- * - Pure CSS; no assumptions about Chart.js, ECharts, Recharts, D3, or SVG
16
- * - Fully theme-driven: all colors flow from active theme tokens
17
- * - API-agnostic: classes apply to SVG <path>, <line>, <text>, and bars
18
- * regardless of rendering engine
19
- * - Loading shimmer and series-soft areas use `color-mix` for adaptive contrast
20
- ************************************************************/
21
-
22
- /* ---------------------------------------------------------
23
- 1. CHART TOKEN DEFINITIONS (extend root)
24
- --------------------------------------------------------- */
25
-
26
- [data-vds-chart],
27
- .vds-chart {
28
- --chart-min-height-sm: 10rem;
29
- --chart-min-height: 14rem;
30
- --chart-min-height-lg: 18rem;
31
-
32
- --chart-padding-sm: var(--space-3);
33
- --chart-padding: var(--space-4);
34
- --chart-padding-lg: var(--space-6);
35
-
36
- --chart-gap: var(--space-3);
37
- --chart-legend-gap: var(--space-2);
38
-
39
- --chart-axis-color: var(--color-text-soft);
40
- --chart-axis-text-color: var(--color-text-muted);
41
- --chart-gridline-color: color-mix(in srgb, var(--color-border-subtle) 70%, transparent);
42
- --chart-gridline-emph: color-mix(in srgb, var(--color-border-strong) 80%, transparent);
43
-
44
- --chart-surface-bg: var(--color-surface);
45
- --chart-surface-subtle: var(--color-surface-subtle);
46
- --chart-border-color: var(--color-border-subtle);
47
-
48
- --chart-series-1: var(--color-accent);
49
- --chart-series-1-soft: color-mix(in srgb, var(--color-accent) 18%, transparent);
50
- --chart-series-1-line: color-mix(in srgb, var(--color-accent) 80%, black 5%);
51
-
52
- --chart-series-2: var(--color-success);
53
- --chart-series-2-soft: color-mix(in srgb, var(--color-success) 18%, transparent);
54
- --chart-series-2-line: color-mix(in srgb, var(--color-success) 80%, black 5%);
55
-
56
- --chart-series-3: var(--color-info);
57
- --chart-series-3-soft: color-mix(in srgb, var(--color-info) 18%, transparent);
58
- --chart-series-3-line: color-mix(in srgb, var(--color-info) 80%, black 5%);
59
-
60
- --chart-series-4: var(--color-warning);
61
- --chart-series-4-soft: color-mix(in srgb, var(--color-warning) 18%, transparent);
62
- --chart-series-4-line: color-mix(in srgb, var(--color-warning) 80%, black 5%);
63
-
64
- --chart-series-5: var(--color-danger);
65
- --chart-series-5-soft: color-mix(in srgb, var(--color-danger) 18%, transparent);
66
- --chart-series-5-line: color-mix(in srgb, var(--color-danger) 80%, black 5%);
67
-
68
- --chart-series-6: var(--color-text-muted, var(--color-text-soft));
69
- --chart-series-6-soft: color-mix(in srgb, var(--chart-series-6) 18%, transparent);
70
- --chart-series-6-line: color-mix(in srgb, var(--chart-series-6) 80%, black 5%);
71
-
72
- --chart-series-positive: var(--color-success);
73
- --chart-series-negative: var(--color-danger);
74
- --chart-series-neutral: var(--color-muted-border);
75
-
76
- --chart-loading-stripes: linear-gradient(
77
- 90deg,
78
- var(--color-muted-bg),
79
- var(--color-surface),
80
- var(--color-muted-bg)
81
- );
82
- }
83
-
84
- /* ---------------------------------------------------------
85
- 2. CHART CONTAINERS
86
- --------------------------------------------------------- */
87
-
88
- .chart {
89
- display: flex;
90
- flex-direction: column;
91
- gap: var(--chart-gap);
92
- padding: var(--chart-padding);
93
- background-color: var(--chart-surface-bg);
94
- border-radius: var(--radius-md);
95
- border: var(--border-width) solid var(--chart-border-color);
96
- min-height: var(--chart-min-height);
97
- }
98
-
99
- .chart--sm {
100
- padding: var(--chart-padding-sm);
101
- min-height: var(--chart-min-height-sm);
102
- }
103
-
104
- .chart--lg {
105
- padding: var(--chart-padding-lg);
106
- min-height: var(--chart-min-height-lg);
107
- }
108
-
109
- .chart--bare {
110
- background-color: transparent;
111
- border: none;
112
- padding: 0;
113
- border-radius: 0;
114
- }
115
-
116
- .card .chart {
117
- border: none;
118
- padding: 0;
119
- background-color: transparent;
120
- min-height: var(--chart-min-height);
121
- }
122
-
123
- /* ---------------------------------------------------------
124
- 3. HEADER, META & ACTIONS
125
- --------------------------------------------------------- */
126
-
127
- .chart__header {
128
- display: flex;
129
- align-items: center;
130
- justify-content: space-between;
131
- gap: var(--space-3);
132
- }
133
-
134
- .chart__title {
135
- font-size: var(--text-sm);
136
- font-weight: var(--font-weight-semibold);
137
- color: var(--color-text);
138
- }
139
-
140
- .chart__subtitle {
141
- margin-top: var(--space-1);
142
- font-size: var(--text-xs);
143
- color: var(--color-text-muted);
144
- }
145
-
146
- .chart__meta {
147
- display: flex;
148
- align-items: center;
149
- gap: var(--space-2);
150
- font-size: var(--text-xs);
151
- color: var(--color-text-muted);
152
- }
153
-
154
- .chart__meta-item {
155
- display: inline-flex;
156
- align-items: center;
157
- gap: var(--space-1);
158
- white-space: nowrap;
159
- }
160
-
161
- .chart__meta-dot {
162
- width: var(--space-1_5);
163
- height: var(--space-1_5);
164
- border-radius: var(--radius-full);
165
- background-color: var(--color-border-subtle);
166
- }
167
-
168
- .chart__actions {
169
- display: flex;
170
- align-items: center;
171
- gap: var(--space-2);
172
- }
173
-
174
- /* ---------------------------------------------------------
175
- 4. BODY & CANVAS WRAPPER
176
- --------------------------------------------------------- */
177
-
178
- .chart__body {
179
- position: relative;
180
- flex: 1 1 auto;
181
- min-height: var(--chart-min-height-sm);
182
- display: flex;
183
- align-items: stretch;
184
- }
185
-
186
- .chart__canvas {
187
- flex: 1 1 auto;
188
- min-height: inherit;
189
- position: relative;
190
- }
191
-
192
- .chart__canvas canvas,
193
- .chart__canvas svg {
194
- width: 100% !important;
195
- height: 100% !important;
196
- }
197
-
198
- .chart__canvas canvas,
199
- .chart__canvas svg {
200
- display: block;
201
- }
202
-
203
- /* ---------------------------------------------------------
204
- 5. LEGEND SYSTEM
205
- --------------------------------------------------------- */
206
-
207
- .chart__legend {
208
- display: flex;
209
- flex-wrap: wrap;
210
- gap: var(--chart-legend-gap);
211
- align-items: center;
212
- font-size: var(--text-xs);
213
- color: var(--color-text-muted);
214
- }
215
-
216
- .chart__legend--right {
217
- margin-left: auto;
218
- justify-content: flex-end;
219
- }
220
-
221
- .chart-legend__item {
222
- display: inline-flex;
223
- align-items: center;
224
- gap: var(--space-1_5);
225
- }
226
-
227
- .chart-legend__swatch {
228
- width: var(--space-2);
229
- height: var(--space-2);
230
- border-radius: var(--radius-full);
231
- background-color: var(--chart-series-1);
232
- box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-bg-soft) 70%, transparent);
233
- }
234
-
235
- .chart-legend__swatch--1 {
236
- background-color: var(--chart-series-1);
237
- }
238
-
239
- .chart-legend__swatch--2 {
240
- background-color: var(--chart-series-2);
241
- }
242
-
243
- .chart-legend__swatch--3 {
244
- background-color: var(--chart-series-3);
245
- }
246
-
247
- .chart-legend__swatch--4 {
248
- background-color: var(--chart-series-4);
249
- }
250
-
251
- .chart-legend__swatch--5 {
252
- background-color: var(--chart-series-5);
253
- }
254
-
255
- .chart-legend__swatch--6 {
256
- background-color: var(--chart-series-6);
257
- }
258
-
259
- .chart-legend__label {
260
- white-space: nowrap;
261
- }
262
-
263
- /* ---------------------------------------------------------
264
- 6. AXES & GRIDLINE HOOKS (SVG / Canvas wrappers)
265
- --------------------------------------------------------- */
266
-
267
- .chart-axis line,
268
- .chart-axis path {
269
- stroke: var(--chart-axis-color);
270
- stroke-width: 1;
271
- }
272
-
273
- .chart-axis text {
274
- fill: var(--chart-axis-text-color);
275
- font-size: var(--text-xxs);
276
- }
277
-
278
- .chart-axis--x text {
279
- dominant-baseline: hanging;
280
- }
281
-
282
- .chart-axis--y text {
283
- dominant-baseline: middle;
284
- }
285
-
286
- .chart-grid line {
287
- stroke: var(--chart-gridline-color);
288
- stroke-width: 1;
289
- shape-rendering: crispEdges;
290
- }
291
-
292
- .chart-grid line[data-major="true"],
293
- .chart-grid line.major {
294
- stroke: var(--chart-gridline-emph);
295
- }
296
-
297
- /* ---------------------------------------------------------
298
- 7. SERIES HOOKS (lines, areas, bars)
299
- Apply to SVG elements created by chart libs or custom D3.
300
- --------------------------------------------------------- */
301
-
302
- .chart-series--1 {
303
- stroke: var(--chart-series-1-line);
304
- fill: none;
305
- }
306
-
307
- .chart-series--2 {
308
- stroke: var(--chart-series-2-line);
309
- fill: none;
310
- }
311
-
312
- .chart-series--3 {
313
- stroke: var(--chart-series-3-line);
314
- fill: none;
315
- }
316
-
317
- .chart-series--4 {
318
- stroke: var(--chart-series-4-line);
319
- fill: none;
320
- }
321
-
322
- .chart-series--5 {
323
- stroke: var(--chart-series-5-line);
324
- fill: none;
325
- }
326
-
327
- .chart-series--6 {
328
- stroke: var(--chart-series-6-line);
329
- fill: none;
330
- }
331
-
332
- .chart-area--1 {
333
- fill: var(--chart-series-1-soft);
334
- }
335
-
336
- .chart-area--2 {
337
- fill: var(--chart-series-2-soft);
338
- }
339
-
340
- .chart-area--3 {
341
- fill: var(--chart-series-3-soft);
342
- }
343
-
344
- .chart-area--4 {
345
- fill: var(--chart-series-4-soft);
346
- }
347
-
348
- .chart-area--5 {
349
- fill: var(--chart-series-5-soft);
350
- }
351
-
352
- .chart-area--6 {
353
- fill: var(--chart-series-6-soft);
354
- }
355
-
356
- .chart-bar--1 {
357
- background-color: var(--chart-series-1);
358
- fill: var(--chart-series-1);
359
- }
360
-
361
- .chart-bar--2 {
362
- background-color: var(--chart-series-2);
363
- fill: var(--chart-series-2);
364
- }
365
-
366
- .chart-bar--3 {
367
- background-color: var(--chart-series-3);
368
- fill: var(--chart-series-3);
369
- }
370
-
371
- .chart-bar--4 {
372
- background-color: var(--chart-series-4);
373
- fill: var(--chart-series-4);
374
- }
375
-
376
- .chart-bar--5 {
377
- background-color: var(--chart-series-5);
378
- fill: var(--chart-series-5);
379
- }
380
-
381
- .chart-bar--6 {
382
- background-color: var(--chart-series-6);
383
- fill: var(--chart-series-6);
384
- }
385
-
386
- .chart-bar--positive {
387
- background-color: var(--chart-series-positive);
388
- fill: var(--chart-series-positive);
389
- }
390
-
391
- .chart-bar--negative {
392
- background-color: var(--chart-series-negative);
393
- fill: var(--chart-series-negative);
394
- }
395
-
396
- .chart-bar--neutral {
397
- background-color: var(--chart-series-neutral);
398
- fill: var(--chart-series-neutral);
399
- }
400
-
401
- /* ---------------------------------------------------------
402
- 8. EMPTY / ERROR / LOADING STATES
403
- --------------------------------------------------------- */
404
-
405
- .chart--empty .chart__canvas,
406
- .chart--error .chart__canvas,
407
- .chart--loading .chart__canvas {
408
- display: flex;
409
- align-items: center;
410
- justify-content: center;
411
- }
412
-
413
- .chart-state {
414
- display: flex;
415
- flex-direction: column;
416
- align-items: center;
417
- gap: var(--space-2);
418
- text-align: center;
419
- max-width: 26rem;
420
- }
421
-
422
- .chart-state__icon {
423
- width: var(--icon-lg);
424
- height: var(--icon-lg);
425
- display: inline-flex;
426
- align-items: center;
427
- justify-content: center;
428
- color: var(--color-text-soft);
429
- }
430
-
431
- .chart-state__title {
432
- font-size: var(--text-sm);
433
- font-weight: var(--font-weight-medium);
434
- color: var(--color-text);
435
- }
436
-
437
- .chart-state__body {
438
- font-size: var(--text-xs);
439
- color: var(--color-text-muted);
440
- }
441
-
442
- .chart--empty .chart-state__icon {
443
- color: var(--color-text-soft);
444
- }
445
-
446
- .chart--error .chart-state__icon {
447
- color: var(--color-danger);
448
- }
449
-
450
- .chart--loading .chart__canvas::before {
451
- content: "";
452
- position: absolute;
453
- top: 0;
454
- right: 0;
455
- bottom: 0;
456
- left: 0;
457
- border-radius: var(--radius-md);
458
- background-image: var(--chart-loading-stripes);
459
- background-size: 200% 100%;
460
- opacity: 0.55;
461
- animation: chart-loading-shimmer 1.4s infinite;
462
- }
463
-
464
- /* ---------------------------------------------------------
465
- 9. RESPONSIVE TUNING
466
- --------------------------------------------------------- */
467
-
468
- @media (max-width: 768px) {
469
- .chart {
470
- padding: var(--chart-padding-sm);
471
- min-height: var(--chart-min-height-sm);
472
- }
473
-
474
- .chart__header {
475
- flex-direction: column;
476
- align-items: flex-start;
477
- gap: var(--space-2);
478
- }
479
-
480
- .chart__actions {
481
- width: 100%;
482
- justify-content: flex-start;
483
- flex-wrap: wrap;
484
- }
485
-
486
- .chart__legend {
487
- width: 100%;
488
- }
489
- }
490
-
491
- /* ---------------------------------------------------------
492
- 10. ANIMATIONS
493
- --------------------------------------------------------- */
494
-
495
- @keyframes chart-loading-shimmer {
496
- 0% {
497
- background-position: -150% 0;
498
- }
499
- 100% {
500
- background-position: 150% 0;
501
- }
502
- }
1
+ .vds-chart,[data-vds-chart]{--chart-min-height-sm:10rem;--chart-min-height:14rem;--chart-min-height-lg:18rem;--chart-padding-sm:var(--space-3);--chart-padding:var(--space-4);--chart-padding-lg:var(--space-6);--chart-gap:var(--space-3);--chart-legend-gap:var(--space-2);--chart-axis-color:var(--color-text-soft);--chart-axis-text-color:var(--color-text-muted);--chart-gridline-color:color-mix(in srgb,var(--color-border-subtle) 70%,transparent);--chart-gridline-emph:color-mix(in srgb,var(--color-border-strong) 80%,transparent);--chart-surface-bg:var(--color-surface);--chart-surface-subtle:var(--color-surface-subtle);--chart-border-color:var(--color-border-subtle);--chart-series-1:var(--color-accent);--chart-series-1-soft:color-mix(in srgb,var(--color-accent) 18%,transparent);--chart-series-1-line:color-mix(in srgb,var(--color-accent) 80%,#000 5%);--chart-series-2:var(--color-success);--chart-series-2-soft:color-mix(in srgb,var(--color-success) 18%,transparent);--chart-series-2-line:color-mix(in srgb,var(--color-success) 80%,#000 5%);--chart-series-3:var(--color-info);--chart-series-3-soft:color-mix(in srgb,var(--color-info) 18%,transparent);--chart-series-3-line:color-mix(in srgb,var(--color-info) 80%,#000 5%);--chart-series-4:var(--color-warning);--chart-series-4-soft:color-mix(in srgb,var(--color-warning) 18%,transparent);--chart-series-4-line:color-mix(in srgb,var(--color-warning) 80%,#000 5%);--chart-series-5:var(--color-danger);--chart-series-5-soft:color-mix(in srgb,var(--color-danger) 18%,transparent);--chart-series-5-line:color-mix(in srgb,var(--color-danger) 80%,#000 5%);--chart-series-6:var(--color-text-muted,var(--color-text-soft));--chart-series-6-soft:color-mix(in srgb,var(--chart-series-6) 18%,transparent);--chart-series-6-line:color-mix(in srgb,var(--chart-series-6) 80%,#000 5%);--chart-series-positive:var(--color-success);--chart-series-negative:var(--color-danger);--chart-series-neutral:var(--color-muted-border);--chart-loading-stripes:linear-gradient(90deg,var(--color-muted-bg),var(--color-surface),var(--color-muted-bg))}.chart{background-color:var(--chart-surface-bg);border:var(--border-width) solid var(--chart-border-color);border-radius:var(--radius-md);display:flex;flex-direction:column;gap:var(--chart-gap);min-height:var(--chart-min-height);padding:var(--chart-padding)}.chart--sm{min-height:var(--chart-min-height-sm);padding:var(--chart-padding-sm)}.chart--lg{min-height:var(--chart-min-height-lg);padding:var(--chart-padding-lg)}.chart--bare{border:none;border-radius:0}.card .chart,.chart--bare{background-color:transparent;padding:0}.card .chart{border:none;min-height:var(--chart-min-height)}.chart__header{align-items:center;display:flex;gap:var(--space-3);justify-content:space-between}.chart__title{color:var(--color-text);font-size:var(--text-sm);font-weight:var(--font-weight-semibold)}.chart__subtitle{margin-top:var(--space-1)}.chart__meta,.chart__subtitle{color:var(--color-text-muted);font-size:var(--text-xs)}.chart__meta{align-items:center;display:flex;gap:var(--space-2)}.chart__meta-item{align-items:center;display:inline-flex;gap:var(--space-1);white-space:nowrap}.chart__meta-dot{background-color:var(--color-border-subtle);border-radius:var(--radius-full);height:var(--space-1_5);width:var(--space-1_5)}.chart__actions{align-items:center;display:flex;gap:var(--space-2)}.chart__body{align-items:stretch;display:flex;min-height:var(--chart-min-height-sm)}.chart__body,.chart__canvas{flex:1 1 auto;position:relative}.chart__canvas{min-height:inherit}.chart__canvas canvas,.chart__canvas svg{display:block;height:100%!important;width:100%!important}.chart__legend{align-items:center;color:var(--color-text-muted);display:flex;flex-wrap:wrap;font-size:var(--text-xs);gap:var(--chart-legend-gap)}.chart__legend--right{justify-content:flex-end;margin-left:auto}.chart-legend__item{align-items:center;display:inline-flex;gap:var(--space-1_5)}.chart-legend__swatch{border-radius:var(--radius-full);box-shadow:0 0 0 1px color-mix(in srgb,var(--color-bg-soft) 70%,transparent);height:var(--space-2);width:var(--space-2)}.chart-legend__swatch,.chart-legend__swatch--1{background-color:var(--chart-series-1)}.chart-legend__swatch--2{background-color:var(--chart-series-2)}.chart-legend__swatch--3{background-color:var(--chart-series-3)}.chart-legend__swatch--4{background-color:var(--chart-series-4)}.chart-legend__swatch--5{background-color:var(--chart-series-5)}.chart-legend__swatch--6{background-color:var(--chart-series-6)}.chart-legend__label{white-space:nowrap}.chart-axis line,.chart-axis path{stroke:var(--chart-axis-color);stroke-width:1}.chart-axis text{fill:var(--chart-axis-text-color);font-size:var(--text-xxs)}.chart-axis--x text{dominant-baseline:hanging}.chart-axis--y text{dominant-baseline:middle}.chart-grid line{stroke:var(--chart-gridline-color);stroke-width:1;shape-rendering:crispEdges}.chart-grid line.major,.chart-grid line[data-major=true]{stroke:var(--chart-gridline-emph)}.chart-series--1{stroke:var(--chart-series-1-line);fill:none}.chart-series--2{stroke:var(--chart-series-2-line);fill:none}.chart-series--3{stroke:var(--chart-series-3-line);fill:none}.chart-series--4{stroke:var(--chart-series-4-line);fill:none}.chart-series--5{stroke:var(--chart-series-5-line);fill:none}.chart-series--6{stroke:var(--chart-series-6-line);fill:none}.chart-area--1{fill:var(--chart-series-1-soft)}.chart-area--2{fill:var(--chart-series-2-soft)}.chart-area--3{fill:var(--chart-series-3-soft)}.chart-area--4{fill:var(--chart-series-4-soft)}.chart-area--5{fill:var(--chart-series-5-soft)}.chart-area--6{fill:var(--chart-series-6-soft)}.chart-bar--1{fill:var(--chart-series-1);background-color:var(--chart-series-1)}.chart-bar--2{fill:var(--chart-series-2);background-color:var(--chart-series-2)}.chart-bar--3{fill:var(--chart-series-3);background-color:var(--chart-series-3)}.chart-bar--4{fill:var(--chart-series-4);background-color:var(--chart-series-4)}.chart-bar--5{fill:var(--chart-series-5);background-color:var(--chart-series-5)}.chart-bar--6{fill:var(--chart-series-6);background-color:var(--chart-series-6)}.chart-bar--positive{fill:var(--chart-series-positive);background-color:var(--chart-series-positive)}.chart-bar--negative{fill:var(--chart-series-negative);background-color:var(--chart-series-negative)}.chart-bar--neutral{fill:var(--chart-series-neutral);background-color:var(--chart-series-neutral)}.chart--empty .chart__canvas,.chart--error .chart__canvas,.chart--loading .chart__canvas{align-items:center;display:flex;justify-content:center}.chart-state{align-items:center;display:flex;flex-direction:column;gap:var(--space-2);max-width:26rem;text-align:center}.chart-state__icon{align-items:center;color:var(--color-text-soft);display:inline-flex;height:var(--icon-lg);justify-content:center;width:var(--icon-lg)}.chart-state__title{color:var(--color-text);font-size:var(--text-sm);font-weight:var(--font-weight-medium)}.chart-state__body{color:var(--color-text-muted);font-size:var(--text-xs)}.chart--empty .chart-state__icon{color:var(--color-text-soft)}.chart--error .chart-state__icon{color:var(--color-danger)}.chart--loading .chart__canvas:before{animation:chart-loading-shimmer 1.4s infinite;background-image:var(--chart-loading-stripes);background-size:200% 100%;border-radius:var(--radius-md);bottom:0;content:"";left:0;opacity:.55;position:absolute;right:0;top:0}@media (max-width:768px){.chart{min-height:var(--chart-min-height-sm);padding:var(--chart-padding-sm)}.chart__header{align-items:flex-start;flex-direction:column;gap:var(--space-2)}.chart__actions{flex-wrap:wrap;justify-content:flex-start;width:100%}.chart__legend{width:100%}}@keyframes chart-loading-shimmer{0%{background-position:-150% 0}to{background-position:150% 0}}