eleventy-plugin-uncharted 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.
@@ -0,0 +1,800 @@
1
+ /* Uncharted - CSS Charts for Eleventy
2
+ * Default stylesheet with CSS custom properties for easy customization
3
+ */
4
+
5
+ /* ==========================================================================
6
+ Color Palette
7
+ ========================================================================== */
8
+
9
+ :root {
10
+ --chart-color-1: #2196f3; /* Blue */
11
+ --chart-color-2: #4caf50; /* Green */
12
+ --chart-color-3: #ff7043; /* Orange */
13
+ --chart-color-4: #ffc107; /* Amber */
14
+ --chart-color-5: #009688; /* Teal */
15
+ --chart-color-6: #9c27b0; /* Purple */
16
+ --chart-color-7: #e91e63; /* Pink */
17
+ --chart-color-8: #78909c; /* Gray */
18
+
19
+ /* Backgrounds - neutral with opacity for light/dark adaptability */
20
+ --chart-bg: rgba(128, 128, 128, 0.15);
21
+
22
+ /* Spacing and sizing */
23
+ --chart-gap: 0.5rem;
24
+ --chart-bar-height: 1.5rem;
25
+ --chart-column-width: 2.5rem;
26
+ --chart-donut-size: 12rem;
27
+ --chart-donut-thickness: 2.5rem;
28
+ --chart-dot-size: 0.75rem;
29
+ }
30
+
31
+ /* ==========================================================================
32
+ Color Classes
33
+ ========================================================================== */
34
+
35
+ .chart-color-1 { --color: var(--chart-color-1); background-color: var(--chart-color-1); }
36
+ .chart-color-2 { --color: var(--chart-color-2); background-color: var(--chart-color-2); }
37
+ .chart-color-3 { --color: var(--chart-color-3); background-color: var(--chart-color-3); }
38
+ .chart-color-4 { --color: var(--chart-color-4); background-color: var(--chart-color-4); }
39
+ .chart-color-5 { --color: var(--chart-color-5); background-color: var(--chart-color-5); }
40
+ .chart-color-6 { --color: var(--chart-color-6); background-color: var(--chart-color-6); }
41
+ .chart-color-7 { --color: var(--chart-color-7); background-color: var(--chart-color-7); }
42
+ .chart-color-8 { --color: var(--chart-color-8); background-color: var(--chart-color-8); }
43
+
44
+ /* ==========================================================================
45
+ Base Chart Styles
46
+ ========================================================================== */
47
+
48
+ .chart {
49
+ margin: 1.5rem 0;
50
+ font-family: inherit;
51
+ }
52
+
53
+ .chart-title {
54
+ display: block;
55
+ font-weight: 600;
56
+ margin-bottom: 0.75rem;
57
+ }
58
+
59
+ .chart-subtitle {
60
+ display: block;
61
+ font-weight: normal;
62
+ font-size: 0.875em;
63
+ opacity: 0.7;
64
+ margin-top: 0.25rem;
65
+ }
66
+
67
+ /* ==========================================================================
68
+ Legend
69
+ ========================================================================== */
70
+
71
+ .chart-legend {
72
+ display: flex;
73
+ flex-wrap: wrap;
74
+ gap: 1rem;
75
+ list-style: none;
76
+ padding: 0;
77
+ margin: 0 0 1rem 0;
78
+ font-size: 0.875rem;
79
+ }
80
+
81
+ .chart-legend-item {
82
+ display: flex;
83
+ align-items: center;
84
+ gap: 0.5rem;
85
+ background: transparent;
86
+ }
87
+
88
+ .chart-legend-item::before {
89
+ content: '';
90
+ width: 0.75rem;
91
+ height: 0.75rem;
92
+ border-radius: 2px;
93
+ background-color: var(--color);
94
+ flex-shrink: 0;
95
+ }
96
+
97
+ /* Dot/scatter charts use circular legend markers */
98
+ .chart-dot .chart-legend-item::before,
99
+ .chart-scatter .chart-legend-item::before {
100
+ border-radius: 50%;
101
+ }
102
+
103
+ .chart-legend-item .legend-value {
104
+ opacity: 0.7;
105
+ margin-left: 0.25rem;
106
+ }
107
+
108
+ /* ==========================================================================
109
+ Axes
110
+ ========================================================================== */
111
+
112
+ .chart-body {
113
+ display: flex;
114
+ gap: 0.5rem;
115
+ }
116
+
117
+ .chart-y-axis {
118
+ display: flex;
119
+ flex-direction: column;
120
+ justify-content: space-between;
121
+ align-items: flex-end;
122
+ min-width: 2rem;
123
+ box-sizing: border-box;
124
+ height: 12rem;
125
+ padding-top: 0.5rem;
126
+ padding-bottom: 0;
127
+ }
128
+
129
+ .chart-y-axis .axis-label {
130
+ font-size: 0.7rem;
131
+ opacity: 0.6;
132
+ font-variant-numeric: tabular-nums;
133
+ line-height: 1;
134
+ }
135
+
136
+ .chart-y-axis .axis-label:first-child {
137
+ transform: translateY(-50%);
138
+ }
139
+
140
+ .chart-y-axis .axis-label:last-child {
141
+ transform: translateY(50%);
142
+ }
143
+
144
+ .chart-x-axis {
145
+ display: flex;
146
+ justify-content: space-between;
147
+ padding: 0.25rem 0;
148
+ margin-top: 0.25rem;
149
+ padding-right: 0.5rem;
150
+ }
151
+
152
+ .chart-x-axis .axis-label {
153
+ font-size: 0.7rem;
154
+ opacity: 0.6;
155
+ font-variant-numeric: tabular-nums;
156
+ }
157
+
158
+ .chart-x-axis .axis-label:first-child {
159
+ transform: translateX(-50%);
160
+ }
161
+
162
+ .chart-x-axis .axis-label:last-child {
163
+ transform: translateX(50%);
164
+ }
165
+
166
+ /* ==========================================================================
167
+ Stacked Bar Chart (Horizontal)
168
+ ========================================================================== */
169
+
170
+ .chart-stacked-bar .chart-bars {
171
+ display: flex;
172
+ flex-direction: column;
173
+ gap: var(--chart-gap);
174
+ }
175
+
176
+ .chart-stacked-bar .bar-row {
177
+ display: grid;
178
+ grid-template-columns: minmax(6rem, auto) 1fr auto;
179
+ align-items: center;
180
+ gap: 0.75rem;
181
+ }
182
+
183
+ .chart-stacked-bar .bar-label {
184
+ font-size: 0.875rem;
185
+ text-align: right;
186
+ }
187
+
188
+ .chart-stacked-bar .bar-track {
189
+ height: var(--chart-bar-height);
190
+ border-radius: 3px;
191
+ overflow: hidden;
192
+ background: var(--chart-bg);
193
+ }
194
+
195
+ .chart-stacked-bar .bar-fills {
196
+ display: flex;
197
+ height: 100%;
198
+ }
199
+
200
+ .chart-stacked-bar .bar-fill {
201
+ height: 100%;
202
+ width: var(--value);
203
+ transition: width 0.3s ease;
204
+ }
205
+
206
+ .chart-stacked-bar .bar-value {
207
+ font-size: 0.875rem;
208
+ font-variant-numeric: tabular-nums;
209
+ min-width: 2rem;
210
+ }
211
+
212
+ /* ==========================================================================
213
+ Stacked Column Chart (Vertical)
214
+ ========================================================================== */
215
+
216
+ .chart-stacked-column .chart-body {
217
+ align-items: stretch;
218
+ }
219
+
220
+ .chart-stacked-column .chart-columns {
221
+ display: flex;
222
+ gap: var(--chart-gap);
223
+ align-items: stretch;
224
+ height: 12rem;
225
+ flex: 1;
226
+ background: var(--chart-bg);
227
+ border-radius: 3px;
228
+ padding: 0.5rem 0.5rem 0 0.5rem;
229
+ box-sizing: border-box;
230
+ }
231
+
232
+ .chart-stacked-column .column-track {
233
+ display: flex;
234
+ flex-direction: column-reverse;
235
+ flex: 1;
236
+ min-width: var(--chart-column-width);
237
+ max-width: calc(var(--chart-column-width) * 2);
238
+ border-radius: 3px 3px 0 0;
239
+ overflow: hidden;
240
+ }
241
+
242
+ .chart-stacked-column .column-segment {
243
+ width: 100%;
244
+ height: var(--value);
245
+ transition: height 0.3s ease;
246
+ }
247
+
248
+ .chart-stacked-column .column-labels {
249
+ display: flex;
250
+ gap: var(--chart-gap);
251
+ padding: 0.25rem 0.5rem 0;
252
+ margin-left: 2.5rem;
253
+ }
254
+
255
+ .chart-stacked-column .column-label {
256
+ flex: 1;
257
+ min-width: var(--chart-column-width);
258
+ max-width: calc(var(--chart-column-width) * 2);
259
+ font-size: 0.75rem;
260
+ text-align: center;
261
+ white-space: nowrap;
262
+ overflow: hidden;
263
+ text-overflow: ellipsis;
264
+ }
265
+
266
+ /* ==========================================================================
267
+ Donut Chart
268
+ ========================================================================== */
269
+
270
+ .chart-donut {
271
+ display: flex;
272
+ flex-direction: column;
273
+ align-items: flex-start;
274
+ }
275
+
276
+ .chart-donut .donut-container {
277
+ position: relative;
278
+ display: flex;
279
+ justify-content: center;
280
+ margin-bottom: 1rem;
281
+ }
282
+
283
+ .chart-donut .donut-ring {
284
+ width: var(--chart-donut-size);
285
+ height: var(--chart-donut-size);
286
+ border-radius: 50%;
287
+ display: flex;
288
+ align-items: center;
289
+ justify-content: center;
290
+ /* Radial mask punches out the center hole */
291
+ --_hole-size: calc((var(--chart-donut-size) - var(--chart-donut-thickness) * 2) / 2);
292
+ mask-image: radial-gradient(
293
+ circle at center,
294
+ transparent var(--_hole-size),
295
+ black var(--_hole-size)
296
+ );
297
+ -webkit-mask-image: radial-gradient(
298
+ circle at center,
299
+ transparent var(--_hole-size),
300
+ black var(--_hole-size)
301
+ );
302
+ }
303
+
304
+ .chart-donut .donut-center {
305
+ position: absolute;
306
+ inset: 0;
307
+ display: flex;
308
+ flex-direction: column;
309
+ align-items: center;
310
+ justify-content: center;
311
+ text-align: center;
312
+ pointer-events: none;
313
+ }
314
+
315
+ .chart-donut .donut-value {
316
+ font-size: 2rem;
317
+ font-weight: 600;
318
+ line-height: 1;
319
+ }
320
+
321
+ .chart-donut .donut-label {
322
+ font-size: 0.75rem;
323
+ opacity: 0.7;
324
+ margin-top: 0.25rem;
325
+ }
326
+
327
+ .chart-donut .chart-legend {
328
+ flex-direction: column;
329
+ gap: 0.5rem;
330
+ }
331
+
332
+ .chart-donut .chart-legend-item {
333
+ min-width: 10rem;
334
+ }
335
+
336
+ .chart-donut .chart-legend-item .legend-label {
337
+ flex: 1;
338
+ }
339
+
340
+ /* ==========================================================================
341
+ Dot Chart (Categorical - columns with dots at Y positions)
342
+ ========================================================================== */
343
+
344
+ .chart-dot .chart-body {
345
+ align-items: stretch;
346
+ }
347
+
348
+ .chart-dot .dot-chart {
349
+ height: 12rem;
350
+ background: var(--chart-bg);
351
+ border-radius: 3px;
352
+ flex: 1;
353
+ box-sizing: border-box;
354
+ position: relative;
355
+ }
356
+
357
+ /* Inner field sized to content area - dots position relative to this */
358
+ .chart-dot .dot-field {
359
+ position: absolute;
360
+ top: 0.5rem;
361
+ right: 0.5rem;
362
+ bottom: 0;
363
+ left: 0.5rem;
364
+ display: flex;
365
+ align-items: stretch;
366
+ gap: 6px;
367
+ }
368
+
369
+ .chart-dot .dot-col {
370
+ flex: 1;
371
+ position: relative;
372
+ min-width: 1.5rem;
373
+ }
374
+
375
+ .chart-dot .dot {
376
+ width: var(--chart-dot-size);
377
+ height: var(--chart-dot-size);
378
+ border-radius: 50%;
379
+ position: absolute;
380
+ left: 50%;
381
+ bottom: var(--value);
382
+ transform: translate(-50%, 50%);
383
+ cursor: default;
384
+ }
385
+
386
+ .chart-dot .dot[title]:hover {
387
+ transform: translate(-50%, 50%) scale(1.3);
388
+ z-index: 1;
389
+ }
390
+
391
+ .chart-dot .dot-labels {
392
+ display: flex;
393
+ gap: 6px;
394
+ padding: 0 0.5rem;
395
+ margin-top: 0.5rem;
396
+ margin-left: calc(2rem + 0.5rem); /* y-axis width + gap */
397
+ }
398
+
399
+ .chart-dot .dot-label {
400
+ flex: 1;
401
+ font-size: 0.75rem;
402
+ text-align: center;
403
+ min-width: 1.5rem;
404
+ }
405
+
406
+ /* ==========================================================================
407
+ Scatter Chart (Continuous X and Y axes)
408
+ ========================================================================== */
409
+
410
+ .chart-scatter .chart-body {
411
+ align-items: stretch;
412
+ }
413
+
414
+ .chart-scatter .scatter-container {
415
+ display: flex;
416
+ flex-direction: column;
417
+ flex: 1;
418
+ }
419
+
420
+ .chart-scatter .dot-area {
421
+ position: relative;
422
+ height: 12rem;
423
+ background: var(--chart-bg);
424
+ border-radius: 3px;
425
+ box-sizing: border-box;
426
+ }
427
+
428
+ /* Inner field sized to content area - dots position relative to this */
429
+ .chart-scatter .dot-field {
430
+ position: absolute;
431
+ top: 0.5rem;
432
+ right: 0.5rem;
433
+ bottom: 0;
434
+ left: 0;
435
+ }
436
+
437
+ .chart-scatter .dot {
438
+ position: absolute;
439
+ width: var(--chart-dot-size);
440
+ height: var(--chart-dot-size);
441
+ border-radius: 50%;
442
+ left: var(--x);
443
+ bottom: var(--value);
444
+ transform: translate(-50%, 50%);
445
+ cursor: default;
446
+ }
447
+
448
+ .chart-scatter .dot[title]:hover {
449
+ transform: translate(-50%, 50%) scale(1.3);
450
+ z-index: 1;
451
+ }
452
+
453
+ /* ==========================================================================
454
+ Animations (Optional)
455
+ Add 'animate: true' to chart config to enable
456
+ Clip-path applied to track containers for smooth whole-bar reveals
457
+ ========================================================================== */
458
+
459
+ /* Row/column index for staggered delays */
460
+ .bar-row:nth-child(1) { --row-index: 0; }
461
+ .bar-row:nth-child(2) { --row-index: 1; }
462
+ .bar-row:nth-child(3) { --row-index: 2; }
463
+ .bar-row:nth-child(4) { --row-index: 3; }
464
+ .bar-row:nth-child(5) { --row-index: 4; }
465
+ .bar-row:nth-child(6) { --row-index: 5; }
466
+ .bar-row:nth-child(7) { --row-index: 6; }
467
+ .bar-row:nth-child(8) { --row-index: 7; }
468
+
469
+ .column-track:nth-child(1) { --col-index: 0; }
470
+ .column-track:nth-child(2) { --col-index: 1; }
471
+ .column-track:nth-child(3) { --col-index: 2; }
472
+ .column-track:nth-child(4) { --col-index: 3; }
473
+ .column-track:nth-child(5) { --col-index: 4; }
474
+ .column-track:nth-child(6) { --col-index: 5; }
475
+ .column-track:nth-child(7) { --col-index: 6; }
476
+ .column-track:nth-child(8) { --col-index: 7; }
477
+ .column-track:nth-child(9) { --col-index: 8; }
478
+ .column-track:nth-child(10) { --col-index: 9; }
479
+ .column-track:nth-child(11) { --col-index: 10; }
480
+ .column-track:nth-child(12) { --col-index: 11; }
481
+
482
+ .dot-col:nth-child(1) { --col-index: 0; }
483
+ .dot-col:nth-child(2) { --col-index: 1; }
484
+ .dot-col:nth-child(3) { --col-index: 2; }
485
+ .dot-col:nth-child(4) { --col-index: 3; }
486
+ .dot-col:nth-child(5) { --col-index: 4; }
487
+ .dot-col:nth-child(6) { --col-index: 5; }
488
+ .dot-col:nth-child(7) { --col-index: 6; }
489
+ .dot-col:nth-child(8) { --col-index: 7; }
490
+
491
+ /* Bar chart: clip-path on fills wrapper, reveals left-to-right */
492
+ .chart-animate .bar-fills {
493
+ animation: bar-reveal 1s cubic-bezier(0.25, 1, 0.5, 1) forwards;
494
+ animation-delay: calc(var(--row-index, 0) * 0.08s);
495
+ }
496
+
497
+ @keyframes bar-reveal {
498
+ from { clip-path: inset(0 100% 0 0); }
499
+ to { clip-path: inset(0 0 0 0); }
500
+ }
501
+
502
+ /* Column chart: clip-path on track, reveals bottom-to-top */
503
+ .chart-animate .column-track {
504
+ animation: column-reveal 0.6s cubic-bezier(0.25, 1, 0.5, 1) forwards;
505
+ animation-delay: calc(var(--col-index, 0) * 0.05s);
506
+ }
507
+
508
+ @keyframes column-reveal {
509
+ from { clip-path: inset(100% 0 0 0); }
510
+ to { clip-path: inset(0 0 0 0); }
511
+ }
512
+
513
+ /* Column chart with negatives: clip-path expands from zero axis */
514
+ .chart-animate.has-negative-y .column-track {
515
+ --zero-from-top: calc(100% - var(--zero-position, 0%));
516
+ animation: column-expand-from-zero 0.6s cubic-bezier(0.25, 1, 0.5, 1) forwards;
517
+ animation-delay: calc(var(--col-index, 0) * 0.05s);
518
+ clip-path: polygon(
519
+ 0% var(--zero-from-top),
520
+ 100% var(--zero-from-top),
521
+ 100% var(--zero-from-top),
522
+ 0% var(--zero-from-top)
523
+ );
524
+ }
525
+
526
+ @keyframes column-expand-from-zero {
527
+ from {
528
+ clip-path: polygon(
529
+ 0% var(--zero-from-top),
530
+ 100% var(--zero-from-top),
531
+ 100% var(--zero-from-top),
532
+ 0% var(--zero-from-top)
533
+ );
534
+ }
535
+ to {
536
+ clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
537
+ }
538
+ }
539
+
540
+ /* Dot chart: dots rise from bottom with staggered delays */
541
+ .chart-animate.chart-dot .dot {
542
+ animation: dot-rise 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
543
+ animation-delay: calc(var(--col-index, 0) * 0.08s);
544
+ opacity: 0;
545
+ }
546
+
547
+ @keyframes dot-rise {
548
+ from {
549
+ bottom: 0;
550
+ opacity: 0;
551
+ transform: translate(-50%, 50%) scale(0.5);
552
+ }
553
+ to {
554
+ /* Let CSS handle final bottom position */
555
+ opacity: 1;
556
+ transform: translate(-50%, 50%) scale(1);
557
+ }
558
+ }
559
+
560
+ /* Dot chart with negatives: dots move from zero axis */
561
+ .chart-animate.chart-dot.has-negative-y .dot {
562
+ animation: dot-from-zero 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
563
+ animation-delay: calc(var(--col-index, 0) * 0.08s);
564
+ opacity: 0;
565
+ }
566
+
567
+ @keyframes dot-from-zero {
568
+ from {
569
+ bottom: var(--zero-position, 0%);
570
+ opacity: 0;
571
+ transform: translate(-50%, 50%) scale(0.5);
572
+ }
573
+ to {
574
+ opacity: 1;
575
+ transform: translate(-50%, 50%) scale(1);
576
+ }
577
+ }
578
+
579
+ /* Donut chart: clockwise reveal using animated mask */
580
+ @property --donut-reveal {
581
+ syntax: '<angle>';
582
+ initial-value: 0deg;
583
+ inherits: false;
584
+ }
585
+
586
+ .chart-animate .donut-ring {
587
+ --_hole-size: calc((var(--chart-donut-size) - var(--chart-donut-thickness) * 2) / 2);
588
+ mask-image:
589
+ radial-gradient(circle at center, transparent var(--_hole-size), black var(--_hole-size)),
590
+ conic-gradient(from 0deg, black var(--donut-reveal), transparent var(--donut-reveal));
591
+ mask-composite: intersect;
592
+ -webkit-mask-image:
593
+ radial-gradient(circle at center, transparent var(--_hole-size), black var(--_hole-size)),
594
+ conic-gradient(from 0deg, black var(--donut-reveal), transparent var(--donut-reveal));
595
+ -webkit-mask-composite: source-in;
596
+ animation: donut-clockwise 0.8s ease-out 0.1s forwards;
597
+ }
598
+
599
+ @keyframes donut-clockwise {
600
+ from { --donut-reveal: 0deg; }
601
+ to { --donut-reveal: 360deg; }
602
+ }
603
+
604
+ /* Scatter chart: dots grow and fade in at position with stagger */
605
+ .chart-animate.chart-scatter .dot {
606
+ animation: scatter-pop 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.5) forwards;
607
+ animation-delay: calc(var(--dot-index, 0) * 0.06s);
608
+ opacity: 0;
609
+ transform: translate(-50%, 50%) scale(0);
610
+ }
611
+
612
+ @keyframes scatter-pop {
613
+ from {
614
+ opacity: 0;
615
+ transform: translate(-50%, 50%) scale(0);
616
+ }
617
+ to {
618
+ opacity: 1;
619
+ transform: translate(-50%, 50%) scale(1);
620
+ }
621
+ }
622
+
623
+ /* Reduced motion preference */
624
+ @media (prefers-reduced-motion: reduce) {
625
+ .chart-animate .bar-fills,
626
+ .chart-animate .column-track,
627
+ .chart-animate.chart-dot .dot,
628
+ .chart-animate.chart-scatter .dot,
629
+ .chart-animate .donut-ring::before {
630
+ animation: none;
631
+ }
632
+
633
+ .chart-animate.chart-dot .dot,
634
+ .chart-animate.chart-scatter .dot {
635
+ opacity: 1;
636
+ transform: none;
637
+ }
638
+
639
+ .chart-animate.has-negative-y .column-track {
640
+ clip-path: none;
641
+ }
642
+ }
643
+
644
+ /* ==========================================================================
645
+ Negative Value Support
646
+ ========================================================================== */
647
+
648
+ /* Column chart negative value support */
649
+ .chart-stacked-column.has-negative-y .chart-columns {
650
+ position: relative;
651
+ padding-bottom: 0.5rem;
652
+ }
653
+
654
+ .chart-stacked-column.has-negative-y .column-track {
655
+ position: relative;
656
+ flex-direction: column; /* Reset from column-reverse */
657
+ overflow: visible; /* Allow segments to show */
658
+ }
659
+
660
+ .chart-stacked-column.has-negative-y .column-segment {
661
+ position: absolute;
662
+ left: 0;
663
+ right: 0;
664
+ bottom: var(--value-bottom, 0);
665
+ height: var(--value-height, 0);
666
+ }
667
+
668
+ /* Rounded corners on outermost segments */
669
+ .chart-stacked-column.has-negative-y .column-segment.is-stack-end:not(.is-negative) {
670
+ border-radius: 3px 3px 0 0;
671
+ }
672
+
673
+ .chart-stacked-column.has-negative-y .column-segment.is-stack-end.is-negative {
674
+ border-radius: 0 0 3px 3px;
675
+ }
676
+
677
+ /* Rounded corners on topmost segment for positive-only charts */
678
+ .chart-stacked-column:not(.has-negative-y) .column-segment.is-stack-end {
679
+ border-radius: 3px 3px 0 0;
680
+ }
681
+
682
+ /* Zero axis line for column charts */
683
+ .chart-stacked-column.has-negative-y .chart-columns::after {
684
+ content: '';
685
+ position: absolute;
686
+ left: 0.5rem;
687
+ right: 0.5rem;
688
+ bottom: calc(0.5rem + var(--zero-position, 0%) * 11 / 12);
689
+ height: 1px;
690
+ background-color: currentColor;
691
+ opacity: 0.4;
692
+ pointer-events: none;
693
+ }
694
+
695
+ /* Column chart Y-axis for negatives */
696
+ .chart-stacked-column.has-negative-y .chart-y-axis {
697
+ position: relative;
698
+ padding-bottom: 0.5rem;
699
+ }
700
+
701
+ .chart-stacked-column.has-negative-y .chart-y-axis .axis-label:nth-child(2) {
702
+ position: absolute;
703
+ right: 0;
704
+ transform: translateY(50%);
705
+ bottom: calc(0.5rem + var(--zero-position, 50%) * 11 / 12);
706
+ }
707
+
708
+ /* Expand dot-field insets for negative values */
709
+ .chart-dot.has-negative-y .dot-field,
710
+ .chart-scatter.has-negative-y .dot-field {
711
+ bottom: 0.5rem;
712
+ }
713
+
714
+ .chart-scatter.has-negative-x .dot-field {
715
+ left: 0.5rem;
716
+ }
717
+
718
+ /* Y-axis padding adjustment for negative values */
719
+ .chart-dot.has-negative-y .chart-y-axis,
720
+ .chart-scatter.has-negative-y .chart-y-axis {
721
+ padding-bottom: 0.5rem;
722
+ }
723
+
724
+ /* Zero axis lines - use dot-field for proper alignment */
725
+ .chart-dot.has-negative-y .dot-field::after {
726
+ content: '';
727
+ position: absolute;
728
+ left: 0;
729
+ right: 0;
730
+ bottom: var(--zero-position, 0);
731
+ height: 1px;
732
+ background-color: currentColor;
733
+ opacity: 0.4;
734
+ pointer-events: none;
735
+ z-index: -1;
736
+ }
737
+
738
+ .chart-scatter.has-negative-y .dot-field::after {
739
+ content: '';
740
+ position: absolute;
741
+ left: 0;
742
+ right: 0;
743
+ bottom: var(--zero-position-y, 0);
744
+ height: 1px;
745
+ background-color: currentColor;
746
+ opacity: 0.4;
747
+ pointer-events: none;
748
+ z-index: -1;
749
+ }
750
+
751
+ .chart-scatter.has-negative-x .dot-field::before {
752
+ content: '';
753
+ position: absolute;
754
+ top: 0;
755
+ bottom: 0;
756
+ left: var(--zero-position-x, 0);
757
+ width: 1px;
758
+ background-color: currentColor;
759
+ opacity: 0.4;
760
+ pointer-events: none;
761
+ z-index: -1;
762
+ }
763
+
764
+ /* ==========================================================================
765
+ Zero-Positioned Axis Labels
766
+ When data spans negative to positive, position the middle label at zero
767
+ ========================================================================== */
768
+
769
+ /* Y-axis needs relative positioning for absolute label */
770
+ .chart-dot.has-negative-y .chart-y-axis,
771
+ .chart-scatter.has-negative-y .chart-y-axis {
772
+ position: relative;
773
+ }
774
+
775
+ /* Position middle Y-axis label at zero */
776
+ .chart-dot.has-negative-y .chart-y-axis .axis-label:nth-child(2),
777
+ .chart-scatter.has-negative-y .chart-y-axis .axis-label:nth-child(2) {
778
+ position: absolute;
779
+ right: 0;
780
+ transform: translateY(50%);
781
+ }
782
+
783
+ .chart-dot.has-negative-y .chart-y-axis .axis-label:nth-child(2) {
784
+ bottom: calc(0.5rem + var(--zero-position, 50%) * 11 / 12);
785
+ }
786
+
787
+ .chart-scatter.has-negative-y .chart-y-axis .axis-label:nth-child(2) {
788
+ bottom: calc(0.5rem + var(--zero-position-y, 50%) * 11 / 12);
789
+ }
790
+
791
+ /* X-axis positioning for scatter */
792
+ .chart-scatter.has-negative-x .chart-x-axis {
793
+ position: relative;
794
+ }
795
+
796
+ .chart-scatter.has-negative-x .chart-x-axis .axis-label:nth-child(2) {
797
+ position: absolute;
798
+ left: calc(0.5rem + var(--zero-position-x, 50%) * 11 / 12);
799
+ transform: translateX(-50%);
800
+ }