neba 1.3.0 → 1.5.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 (135) hide show
  1. package/README.md +7 -7
  2. package/dist/components/alert/Alert.d.ts +9 -1
  3. package/dist/components/alert/Alert.js +1 -1
  4. package/dist/components/area-chart/AreaChart.d.ts +54 -0
  5. package/dist/components/area-chart/AreaChart.js +1 -0
  6. package/dist/components/area-chart/index.d.ts +2 -0
  7. package/dist/components/area-chart/index.js +1 -0
  8. package/dist/components/bar-chart/BarChart.d.ts +63 -0
  9. package/dist/components/bar-chart/BarChart.js +1 -0
  10. package/dist/components/bar-chart/index.d.ts +2 -0
  11. package/dist/components/bar-chart/index.js +1 -0
  12. package/dist/components/bottom-navigation/BottomNavigation.d.ts +111 -0
  13. package/dist/components/bottom-navigation/BottomNavigation.js +1 -0
  14. package/dist/components/bottom-navigation/index.d.ts +1 -0
  15. package/dist/components/bottom-navigation/index.js +1 -0
  16. package/dist/components/breadcrumb/Breadcrumb.d.ts +35 -2
  17. package/dist/components/breadcrumb/Breadcrumb.js +1 -1
  18. package/dist/components/button/Button.d.ts +16 -0
  19. package/dist/components/button/Button.js +1 -1
  20. package/dist/components/carousel/Carousel.d.ts +10 -4
  21. package/dist/components/carousel/Carousel.js +1 -1
  22. package/dist/components/chip/Chip.d.ts +9 -1
  23. package/dist/components/chip/Chip.js +1 -1
  24. package/dist/components/collapsible/Collapsible.d.ts +88 -0
  25. package/dist/components/collapsible/Collapsible.js +1 -0
  26. package/dist/components/collapsible/index.d.ts +1 -0
  27. package/dist/components/collapsible/index.js +1 -0
  28. package/dist/components/combobox/Combobox.d.ts +19 -7
  29. package/dist/components/combobox/Combobox.js +1 -1
  30. package/dist/components/data-table/DataTable.d.ts +323 -0
  31. package/dist/components/data-table/DataTable.js +1 -0
  32. package/dist/components/data-table/index.d.ts +2 -0
  33. package/dist/components/data-table/index.js +1 -0
  34. package/dist/components/dialog/Dialog.d.ts +11 -5
  35. package/dist/components/dialog/Dialog.js +1 -1
  36. package/dist/components/drawer/Drawer.d.ts +11 -5
  37. package/dist/components/drawer/Drawer.js +1 -1
  38. package/dist/components/empty/Empty.d.ts +69 -0
  39. package/dist/components/empty/Empty.js +1 -0
  40. package/dist/components/empty/index.d.ts +2 -0
  41. package/dist/components/empty/index.js +1 -0
  42. package/dist/components/file-picker/FilePicker.d.ts +1 -3
  43. package/dist/components/file-picker/FilePicker.js +1 -1
  44. package/dist/components/floating-action-button/FloatingActionButton.d.ts +146 -0
  45. package/dist/components/floating-action-button/FloatingActionButton.js +1 -0
  46. package/dist/components/floating-action-button/index.d.ts +1 -0
  47. package/dist/components/floating-action-button/index.js +1 -0
  48. package/dist/components/heatmap-chart/HeatmapChart.d.ts +69 -0
  49. package/dist/components/heatmap-chart/HeatmapChart.js +1 -0
  50. package/dist/components/heatmap-chart/index.d.ts +2 -0
  51. package/dist/components/heatmap-chart/index.js +1 -0
  52. package/dist/components/highlight/Highlight.js +1 -1
  53. package/dist/components/line-chart/LineChart.d.ts +74 -0
  54. package/dist/components/line-chart/LineChart.js +1 -0
  55. package/dist/components/line-chart/index.d.ts +2 -0
  56. package/dist/components/line-chart/index.js +1 -0
  57. package/dist/components/mockup/Mockup.d.ts +151 -0
  58. package/dist/components/mockup/Mockup.js +1 -0
  59. package/dist/components/mockup/index.d.ts +2 -0
  60. package/dist/components/mockup/index.js +1 -0
  61. package/dist/components/number-field/NumberField.d.ts +10 -6
  62. package/dist/components/number-field/NumberField.js +1 -1
  63. package/dist/components/overlay/Overlay.d.ts +11 -6
  64. package/dist/components/overlay/Overlay.js +1 -1
  65. package/dist/components/pagination/Pagination.d.ts +28 -6
  66. package/dist/components/pagination/Pagination.js +1 -1
  67. package/dist/components/panes/Panes.js +1 -1
  68. package/dist/components/pie-chart/PieChart.d.ts +58 -0
  69. package/dist/components/pie-chart/PieChart.js +1 -0
  70. package/dist/components/pie-chart/index.d.ts +2 -0
  71. package/dist/components/pie-chart/index.js +1 -0
  72. package/dist/components/popover/Popover.d.ts +11 -5
  73. package/dist/components/popover/Popover.js +1 -1
  74. package/dist/components/progress-box/ProgressBox.js +1 -1
  75. package/dist/components/progress-circular/ProgressCircular.js +1 -1
  76. package/dist/components/progress-linear/ProgressLinear.js +1 -1
  77. package/dist/components/rating/Rating.d.ts +100 -0
  78. package/dist/components/rating/Rating.js +1 -0
  79. package/dist/components/rating/index.d.ts +1 -0
  80. package/dist/components/rating/index.js +1 -0
  81. package/dist/components/scatter-chart/ScatterChart.d.ts +45 -0
  82. package/dist/components/scatter-chart/ScatterChart.js +1 -0
  83. package/dist/components/scatter-chart/index.d.ts +2 -0
  84. package/dist/components/scatter-chart/index.js +1 -0
  85. package/dist/components/select/Select.d.ts +1 -3
  86. package/dist/components/select/Select.js +1 -1
  87. package/dist/components/sparkline/Sparkline.d.ts +67 -0
  88. package/dist/components/sparkline/Sparkline.js +1 -0
  89. package/dist/components/sparkline/index.d.ts +2 -0
  90. package/dist/components/sparkline/index.js +1 -0
  91. package/dist/components/statistic/Statistic.d.ts +7 -0
  92. package/dist/components/statistic/Statistic.js +1 -1
  93. package/dist/components/table/Table.d.ts +13 -2
  94. package/dist/components/table/Table.js +1 -1
  95. package/dist/components/text-link/TextLink.js +1 -1
  96. package/dist/components/time-picker/TimePicker.js +1 -1
  97. package/dist/components/timeline-chart/TimelineChart.d.ts +44 -0
  98. package/dist/components/timeline-chart/TimelineChart.js +1 -0
  99. package/dist/components/timeline-chart/index.d.ts +2 -0
  100. package/dist/components/timeline-chart/index.js +1 -0
  101. package/dist/components/toast/Toast.d.ts +10 -2
  102. package/dist/components/toast/Toast.js +1 -1
  103. package/dist/components/tooltip/Tooltip.d.ts +2 -4
  104. package/dist/components/tooltip/Tooltip.js +1 -1
  105. package/dist/components/tree-view/TreeView.js +1 -1
  106. package/dist/index.d.ts +15 -0
  107. package/dist/index.js +1 -1
  108. package/dist/internal/animate.d.ts +0 -9
  109. package/dist/internal/animate.js +1 -1
  110. package/dist/internal/calendar.js +1 -1
  111. package/dist/internal/chart-frame.d.ts +409 -0
  112. package/dist/internal/chart-frame.js +1 -0
  113. package/dist/internal/chart-line.d.ts +44 -0
  114. package/dist/internal/chart-line.js +1 -0
  115. package/dist/internal/chart.d.ts +456 -0
  116. package/dist/internal/chart.js +1 -0
  117. package/dist/internal/data-table.d.ts +170 -0
  118. package/dist/internal/data-table.js +1 -0
  119. package/dist/internal/date.d.ts +0 -2
  120. package/dist/internal/date.js +1 -1
  121. package/dist/internal/format.d.ts +29 -0
  122. package/dist/internal/format.js +1 -0
  123. package/dist/internal/i18n.d.ts +166 -0
  124. package/dist/internal/i18n.js +1 -1
  125. package/dist/internal/icons.d.ts +2 -0
  126. package/dist/internal/icons.js +1 -1
  127. package/dist/internal/mockup.d.ts +193 -0
  128. package/dist/internal/mockup.js +1 -0
  129. package/dist/internal/picker.d.ts +2 -4
  130. package/dist/internal/picker.js +1 -1
  131. package/dist/internal/progress.d.ts +1 -3
  132. package/dist/styles.css +1 -1
  133. package/dist/tailwind.css +231 -11
  134. package/dist/types.d.ts +214 -0
  135. package/package.json +19 -15
package/dist/tailwind.css CHANGED
@@ -47,9 +47,18 @@
47
47
 
48
48
  /* ---------------------------------------------------------------------------
49
49
  * Light theme (default)
50
+ *
51
+ * `.light` and `[data-theme='light']` carry the same block rather than only
52
+ * `:root`, because forcing light has to work on a nested element too — a
53
+ * preview box inside a dark page has nowhere else to get these values from.
54
+ * Everything here that is not a colour (the radii, the motion, the grain) is
55
+ * identical in both themes, so re-declaring it costs a duplicate that resolves
56
+ * to what was inherited anyway.
50
57
  * ------------------------------------------------------------------------- */
51
58
 
52
- :root {
59
+ :root,
60
+ .light,
61
+ [data-theme='light'] {
53
62
  /* Neutral surfaces and content */
54
63
  --neba-surface: oklch(100% 0 0);
55
64
  --neba-fg: oklch(24% 0.015 262);
@@ -86,8 +95,6 @@
86
95
  bottom bevel — that is what made the surface read as moulded plastic. */
87
96
  --neba-plate-edge: rgb(255 255 255 / 0.18);
88
97
  --neba-plate-top: rgb(255 255 255 / 0.26);
89
- --neba-plate-solid: inset 0 1px 0 var(--neba-plate-top), inset 0 0 0 1px var(--neba-plate-edge);
90
- --neba-plate-glass: inset 0 1px 0 var(--neba-plate-top);
91
98
  /* Off-vertical on purpose: light falling across a sheet at an angle, rather
92
99
  than the symmetric top-down gradient every framework ships. */
93
100
  --neba-sheen: linear-gradient(148deg, rgb(255 255 255 / 0.13), rgb(255 255 255 / 0) 52%);
@@ -138,14 +145,6 @@
138
145
  surface in space, not to advertise it. Level 4 is only reachable by
139
146
  hovering a level-3 surface. */
140
147
  --neba-shadow-ambient: rgb(16 24 40 / 0.13);
141
- --neba-shadow-0: 0 0 #0000;
142
- --neba-shadow-1: 0 1px 2px -1px var(--neba-shadow-ambient);
143
- --neba-shadow-2:
144
- 0 2px 4px -2px var(--neba-shadow-ambient), 0 5px 12px -7px var(--neba-shadow-ambient);
145
- --neba-shadow-3:
146
- 0 4px 8px -3px var(--neba-shadow-ambient), 0 12px 24px -11px var(--neba-shadow-ambient);
147
- --neba-shadow-4:
148
- 0 6px 12px -4px var(--neba-shadow-ambient), 0 20px 40px -15px var(--neba-shadow-ambient);
149
148
 
150
149
  /* Corner radius: ~45% of the control height at every step. Held just short of
151
150
  the 50% that would make it a pill — the flat run along the top and bottom
@@ -219,6 +218,111 @@
219
218
  --neba-info-solid-active: oklch(36.5% 0.068 223);
220
219
  --neba-info-on-solid: oklch(99% 0.004 223);
221
220
  --neba-info-accent: oklch(53.5% 0.096 223);
221
+
222
+ /* The chart series ramp ------------------------------------------------- */
223
+
224
+ /* Eight hues in a fixed order, and the one place in the library where a color
225
+ is not a semantic role. A series is an *entity* — a region, a plan, a
226
+ competitor — and nothing about it says success or danger; what the color has
227
+ to do is stay the same color when the chart is filtered, and be tellable
228
+ apart from its neighbours by every reader.
229
+
230
+ So they are not the six families reused. Those were picked to mean
231
+ something on a control, they are only six, and two of them (secondary,
232
+ info) sit close enough in hue that adjacent series would collapse. These
233
+ eight were solved for instead, and the constraints they satisfy are:
234
+
235
+ - every step inside OKLCH L 0.43–0.77, so no slot is louder than another;
236
+ - chroma at or above 0.10, below which a hue reads as grey and stops
237
+ carrying identity at all;
238
+ - each neighbouring pair at least ΔE 8 apart in OKLab under simulated
239
+ protanopia and deuteranopia — measured at 13.6 here, against a floor of 8;
240
+ - and at least 3:1 against the surface, so a 2px line is a line. Every slot
241
+ clears 4:1 on white and 5.3:1 on the dark sheet.
242
+
243
+ The order is the mechanism, not a preference: slots are handed out in
244
+ sequence and never cycled, which is what keeps the *adjacent* pairs — the
245
+ only ones that touch in a stack or a legend — the ones that were checked.
246
+ A ninth series is not a ninth colour; it is an "Other" row, or a second
247
+ chart. Series one is the primary hue because a one-series chart should look
248
+ like it belongs to the rest of the page. */
249
+ --neba-chart-1: oklch(60.5% 0.214 262);
250
+ --neba-chart-2: oklch(61% 0.166 48);
251
+ --neba-chart-3: oklch(62.5% 0.22 352);
252
+ --neba-chart-4: oklch(59.5% 0.122 92);
253
+ --neba-chart-5: oklch(59% 0.12 232);
254
+ --neba-chart-6: oklch(57.5% 0.18 145);
255
+ --neba-chart-7: oklch(62% 0.22 305);
256
+ --neba-chart-8: oklch(58.5% 0.104 182);
257
+
258
+ /* The sequential ramp — one hue, light to dark, for a mark whose colour is a
259
+ *magnitude* rather than an identity.
260
+
261
+ A different job from the eight above and so a different set, not a subset
262
+ of them. The eight are solved to be as far apart as possible, which is
263
+ exactly what a magnitude must not be: a heatmap coloured from a categorical
264
+ ramp says its cells are five unrelated things. Here the order is the whole
265
+ meaning, so what is solved for is the opposite —
266
+
267
+ - one hue throughout (spread 0°), because two hues in a ramp is a reader
268
+ inventing a boundary where the data has none;
269
+ - lightness strictly falling, in steps of at least ΔL 0.06 so no two are
270
+ the same shade at a glance;
271
+ - and the palest step still at 2:1 on the sheet, or the low end of the
272
+ scale is a cell that reads as empty rather than as small. It is 2.07:1
273
+ here, which is why step one is not paler than it is.
274
+
275
+ Chroma peaks in the middle and eases at both ends: at the pale end because
276
+ sRGB has none to give, and at the dark end because a saturated near-black
277
+ reads as a different hue.
278
+
279
+ Five steps and not a continuous gradient, because a reader cannot rank a
280
+ continuum. The scale legend under a heatmap has five swatches and the cells
281
+ have five values, and that is a picture with an answer in it. */
282
+ --neba-chart-seq-1: oklch(77% 0.09 262);
283
+ --neba-chart-seq-2: oklch(69% 0.13 262);
284
+ --neba-chart-seq-3: oklch(61% 0.17 262);
285
+ --neba-chart-seq-4: oklch(53% 0.19 262);
286
+ --neba-chart-seq-5: oklch(43% 0.17 262);
287
+
288
+ /* The diverging ramp — two poles and a neutral, for a magnitude with a
289
+ *middle* that means something: over and under target, gained and lost.
290
+
291
+ Reached for only when there is a real zero to diverge about. Used on a
292
+ plain magnitude it invents one, and the reader spends the chart looking for
293
+ what changed at the grey.
294
+
295
+ The neutral is grey and not a third hue, which is the rule that makes the
296
+ two arms read as two directions rather than as three categories. Each arm
297
+ is a one-hue ramp in its own right and passes the same three checks the
298
+ sequential ramp does; the two poles additionally clear the categorical
299
+ colour-vision gate at ΔE 17.4 under deuteranopia, because a diverging
300
+ scale whose ends cannot be told apart has no ends. */
301
+ --neba-chart-div-1: oklch(45% 0.09 232);
302
+ --neba-chart-div-2: oklch(63% 0.12 232);
303
+ --neba-chart-div-3: oklch(77% 0.008 262);
304
+ --neba-chart-div-4: oklch(63% 0.15 32);
305
+ --neba-chart-div-5: oklch(45% 0.16 32);
306
+
307
+ /* Which ink a label inside a filled tile wears.
308
+ Inside a mark is the one place text does not wear a text token outright: on
309
+ the pale end of a ramp the ink is right and on the dark end it is invisible,
310
+ and where the flip happens is different in each theme. So it is decided per
311
+ step, here, where the step's own lightness is known — computed rather than
312
+ eyeballed, and every one of the ten clears 4.5:1 against the fill it sits
313
+ on (the tightest is 4.58:1, in the middle of the ramp, which is where a
314
+ sequential scale is always hardest to write on). */
315
+ --neba-chart-seq-on-1: var(--neba-fg);
316
+ --neba-chart-seq-on-2: var(--neba-fg);
317
+ --neba-chart-seq-on-3: var(--neba-fg);
318
+ --neba-chart-seq-on-4: var(--neba-surface);
319
+ --neba-chart-seq-on-5: var(--neba-surface);
320
+
321
+ --neba-chart-div-on-1: var(--neba-surface);
322
+ --neba-chart-div-on-2: var(--neba-fg);
323
+ --neba-chart-div-on-3: var(--neba-fg);
324
+ --neba-chart-div-on-4: var(--neba-fg);
325
+ --neba-chart-div-on-5: var(--neba-surface);
222
326
  }
223
327
 
224
328
  /* ---------------------------------------------------------------------------
@@ -294,6 +398,50 @@
294
398
  --neba-info-solid-active: oklch(46% 0.082 223);
295
399
  --neba-info-on-solid: oklch(99% 0.004 223);
296
400
  --neba-info-accent: oklch(79% 0.11 223);
401
+
402
+ /* The chart series ramp — the same eight hues, re-solved against the dark
403
+ sheet rather than lightened from the values above. The band is narrower
404
+ here (L 0.48–0.67), so every slot sits at one lightness and the identity
405
+ is carried entirely by hue: on a dark surface a step that also varied in
406
+ lightness would read as "this series matters more". Re-measured, not
407
+ flipped: worst adjacent pair ΔE 14.5 under deuteranopia, every slot above
408
+ 5.3:1 on the sheet. */
409
+ --neba-chart-1: oklch(66% 0.18 262);
410
+ --neba-chart-2: oklch(66% 0.18 48);
411
+ --neba-chart-3: oklch(66% 0.22 352);
412
+ --neba-chart-4: oklch(66% 0.134 92);
413
+ --neba-chart-5: oklch(66% 0.134 232);
414
+ --neba-chart-6: oklch(66% 0.206 145);
415
+ --neba-chart-7: oklch(66% 0.22 305);
416
+ --neba-chart-8: oklch(66% 0.118 182);
417
+
418
+ /* The two ramps, re-solved against the dark sheet rather than flipped. The
419
+ check that moves is the one at the *dark* end: there it is the deepest
420
+ step that has to stay clear of the surface, at 2.23:1 here, so the ramp
421
+ runs from a step lighter than the light theme's would. */
422
+ --neba-chart-seq-1: oklch(43% 0.12 262);
423
+ --neba-chart-seq-2: oklch(52% 0.16 262);
424
+ --neba-chart-seq-3: oklch(61% 0.19 262);
425
+ --neba-chart-seq-4: oklch(70% 0.15 262);
426
+ --neba-chart-seq-5: oklch(80% 0.09 262);
427
+
428
+ --neba-chart-div-1: oklch(46% 0.092 232);
429
+ --neba-chart-div-2: oklch(62% 0.125 232);
430
+ --neba-chart-div-3: oklch(76% 0.008 262);
431
+ --neba-chart-div-4: oklch(62% 0.15 32);
432
+ --neba-chart-div-5: oklch(46% 0.15 32);
433
+
434
+ --neba-chart-seq-on-1: var(--neba-fg);
435
+ --neba-chart-seq-on-2: var(--neba-fg);
436
+ --neba-chart-seq-on-3: var(--neba-surface);
437
+ --neba-chart-seq-on-4: var(--neba-surface);
438
+ --neba-chart-seq-on-5: var(--neba-surface);
439
+
440
+ --neba-chart-div-on-1: var(--neba-fg);
441
+ --neba-chart-div-on-2: var(--neba-surface);
442
+ --neba-chart-div-on-3: var(--neba-surface);
443
+ --neba-chart-div-on-4: var(--neba-surface);
444
+ --neba-chart-div-on-5: var(--neba-fg);
297
445
  }
298
446
  }
299
447
 
@@ -363,6 +511,41 @@
363
511
  --neba-info-solid-active: oklch(46% 0.082 223);
364
512
  --neba-info-on-solid: oklch(99% 0.004 223);
365
513
  --neba-info-accent: oklch(79% 0.11 223);
514
+
515
+ /* The chart series ramp — see the note in the `prefers-color-scheme` block. */
516
+ --neba-chart-1: oklch(66% 0.18 262);
517
+ --neba-chart-2: oklch(66% 0.18 48);
518
+ --neba-chart-3: oklch(66% 0.22 352);
519
+ --neba-chart-4: oklch(66% 0.134 92);
520
+ --neba-chart-5: oklch(66% 0.134 232);
521
+ --neba-chart-6: oklch(66% 0.206 145);
522
+ --neba-chart-7: oklch(66% 0.22 305);
523
+ --neba-chart-8: oklch(66% 0.118 182);
524
+
525
+ /* See the note in the `prefers-color-scheme` block. */
526
+ --neba-chart-seq-1: oklch(43% 0.12 262);
527
+ --neba-chart-seq-2: oklch(52% 0.16 262);
528
+ --neba-chart-seq-3: oklch(61% 0.19 262);
529
+ --neba-chart-seq-4: oklch(70% 0.15 262);
530
+ --neba-chart-seq-5: oklch(80% 0.09 262);
531
+
532
+ --neba-chart-div-1: oklch(46% 0.092 232);
533
+ --neba-chart-div-2: oklch(62% 0.125 232);
534
+ --neba-chart-div-3: oklch(76% 0.008 262);
535
+ --neba-chart-div-4: oklch(62% 0.15 32);
536
+ --neba-chart-div-5: oklch(46% 0.15 32);
537
+
538
+ --neba-chart-seq-on-1: var(--neba-fg);
539
+ --neba-chart-seq-on-2: var(--neba-fg);
540
+ --neba-chart-seq-on-3: var(--neba-surface);
541
+ --neba-chart-seq-on-4: var(--neba-surface);
542
+ --neba-chart-seq-on-5: var(--neba-surface);
543
+
544
+ --neba-chart-div-on-1: var(--neba-fg);
545
+ --neba-chart-div-on-2: var(--neba-surface);
546
+ --neba-chart-div-on-3: var(--neba-surface);
547
+ --neba-chart-div-on-4: var(--neba-surface);
548
+ --neba-chart-div-on-5: var(--neba-fg);
366
549
  }
367
550
 
368
551
  /* ---------------------------------------------------------------------------
@@ -380,6 +563,22 @@
380
563
  .light,
381
564
  [data-theme='dark'],
382
565
  [data-theme='light'] {
566
+ /* The plate and the elevation ladder are two base tokens spread into shadow
567
+ lists, so they belong here rather than next to the values they read: left
568
+ in the light block they would keep the light hairline and the light ambient
569
+ inside a `.dark` that is not the document root. */
570
+ --neba-plate-solid: inset 0 1px 0 var(--neba-plate-top), inset 0 0 0 1px var(--neba-plate-edge);
571
+ --neba-plate-glass: inset 0 1px 0 var(--neba-plate-top);
572
+
573
+ --neba-shadow-0: 0 0 #0000;
574
+ --neba-shadow-1: 0 1px 2px -1px var(--neba-shadow-ambient);
575
+ --neba-shadow-2:
576
+ 0 2px 4px -2px var(--neba-shadow-ambient), 0 5px 12px -7px var(--neba-shadow-ambient);
577
+ --neba-shadow-3:
578
+ 0 4px 8px -3px var(--neba-shadow-ambient), 0 12px 24px -11px var(--neba-shadow-ambient);
579
+ --neba-shadow-4:
580
+ 0 6px 12px -4px var(--neba-shadow-ambient), 0 20px 40px -15px var(--neba-shadow-ambient);
581
+
383
582
  --neba-primary-fill: color-mix(
384
583
  in srgb,
385
584
  var(--neba-primary-solid) var(--neba-fill-alpha),
@@ -565,6 +764,27 @@
565
764
  --neba-info-line: color-mix(in srgb, var(--neba-info-accent) 22%, transparent);
566
765
  --neba-info-line-hover: color-mix(in srgb, var(--neba-info-accent) 40%, transparent);
567
766
  --neba-info-ring: color-mix(in srgb, var(--neba-info-accent) 55%, transparent);
767
+
768
+ /* A chart's chrome: the grid, the axis rules, the baseline.
769
+
770
+ One step off the surface and nothing more. Gridlines are the only ink on a
771
+ chart that is not data, and the moment they are as dark as a series the eye
772
+ starts reading them as one. They are derived from `--neba-border` rather
773
+ than given values of their own so a chart's rules and a Card's dividers are
774
+ the same family of line, and they are solid — a dashed grid says
775
+ "projection" or "threshold" when all it is is a grid.
776
+
777
+ The baseline is the exception that is allowed to be darker: it is where a
778
+ bar starts from and where zero is, which is a fact about the data. */
779
+ --neba-chart-grid: color-mix(in srgb, var(--neba-border) 70%, transparent);
780
+ --neba-chart-axis: var(--neba-border);
781
+ --neba-chart-baseline: color-mix(in srgb, var(--neba-muted-fg) 35%, transparent);
782
+
783
+ /* What a mark is separated from its neighbour by, and what a marker is ringed
784
+ with. The surface colour rather than white, so the 2px gap between two
785
+ stacked segments is the sheet showing through and not a stroke drawn on
786
+ top — a border around a mark is ink that is not data. */
787
+ --neba-chart-gap: var(--neba-surface);
568
788
  }
569
789
 
570
790
  /* ---------------------------------------------------------------------------
package/dist/types.d.ts CHANGED
@@ -6,6 +6,7 @@
6
6
  * TextField or a Dialog. Components pick the subset they need from here and
7
7
  * never invent a parallel spelling of the same idea.
8
8
  */
9
+ import type * as React from 'react';
9
10
  /** Scale of a component. `md` is the desktop default. */
10
11
  export type NebaSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
11
12
  /** Semantic color role. Maps to a token family in `styles.css`. */
@@ -241,6 +242,219 @@ export interface NebaTransitionOptions {
241
242
  * language rules out, and a `transition` on a Button would be exactly that.
242
243
  */
243
244
  export type NebaTransition = NebaAnimation | NebaTransitionOptions;
245
+ /**
246
+ * Where a point sits along the category axis.
247
+ *
248
+ * A `Date` is accepted because a time series is the common case and converting
249
+ * one to a string at the call site is what makes two charts of the same data
250
+ * label their axes differently.
251
+ */
252
+ export type NebaChartCategory = string | number | Date;
253
+ /**
254
+ * One value, with everything the chart might want to know about it.
255
+ *
256
+ * `y` of `null` is a **gap** and not a zero — a sensor that was offline, a month
257
+ * that has not closed yet. A line breaks across it, an area breaks with it, and
258
+ * a bar is not drawn. This distinction is the whole reason a datum may be
259
+ * `null` at all: a chart that renders missing data as zero is a chart that
260
+ * reports an outage as a collapse.
261
+ */
262
+ export interface NebaChartPoint {
263
+ /**
264
+ * Its place on the category axis. Optional — without it the point is placed
265
+ * by its index, against `categories` if the chart was given any.
266
+ */
267
+ x?: NebaChartCategory;
268
+ /** The value. `null` is a gap. */
269
+ y: number | null;
270
+ /**
271
+ * A second magnitude, for the marks that have one: the radius of a bubble,
272
+ * the weight of a tile. Ignored by the charts that do not.
273
+ */
274
+ z?: number;
275
+ /**
276
+ * Overrides the series' colour for this one point — the slice worth pointing
277
+ * at, the bar that is over budget. Any CSS colour, or a `NebaColor` family.
278
+ */
279
+ color?: string;
280
+ /** What the tooltip, the legend and any value label say instead of `y`. */
281
+ label?: React.ReactNode;
282
+ }
283
+ /** A number, a gap, or a point that says more about itself. */
284
+ export type NebaChartDatum = number | null | NebaChartPoint;
285
+ /**
286
+ * One line, one band of bars, one ring of slices — and the unit identity is
287
+ * attached to.
288
+ *
289
+ * Colour follows the series, never its position in the drawing: a chart whose
290
+ * legend is filtered keeps every survivor on the colour it had. That is why the
291
+ * slot a series takes is decided by where it sits in this array and not by how
292
+ * many of its neighbours are currently visible.
293
+ */
294
+ export interface NebaChartSeries {
295
+ /**
296
+ * Its name in the legend, the tooltip and the data table. A chart with two or
297
+ * more series always shows a legend, so a series without a name is a series
298
+ * the reader cannot identify.
299
+ */
300
+ name?: string;
301
+ /** The values, in category order. */
302
+ data: readonly NebaChartDatum[];
303
+ /**
304
+ * Overrides the palette slot this series would otherwise take. A `NebaColor`
305
+ * family name, or any CSS colour.
306
+ *
307
+ * This is the one place in the library where a colour is not a semantic role,
308
+ * and it is deliberate: a series is an *entity* — a region, a plan, a
309
+ * competitor — and nothing about it means success or danger. Reach for it to
310
+ * match a brand or to hold a colour steady across two charts, not to say how
311
+ * a number should be felt.
312
+ */
313
+ color?: NebaColor | (string & {});
314
+ /**
315
+ * Starts the series hidden. Only meaningful with an interactive legend, which
316
+ * is what turns it back on.
317
+ * @default false
318
+ */
319
+ hidden?: boolean;
320
+ }
321
+ /**
322
+ * One span on a [TimelineChart] — a stretch of time with two ends.
323
+ *
324
+ * Its own type rather than a `z2` or an `x2` bolted onto `NebaChartPoint`,
325
+ * because a second position on the axis is a field the other six charts would
326
+ * carry and never read. The trade the whole `types.ts` makes is that a name
327
+ * means one thing everywhere; a `NebaChartPoint` that sometimes has an end and
328
+ * usually does not is the opposite of that.
329
+ *
330
+ * `start` and `end` rather than `x` and `end`: a span has two places on the
331
+ * axis, and naming one of them `x` only reads correctly to someone who already
332
+ * knows which one it is.
333
+ */
334
+ export interface NebaTimelinePoint {
335
+ /** When it begins. A `Date`, or a number of milliseconds. */
336
+ start: NebaChartCategory;
337
+ /** And when it is done. A span that ends before it starts is drawn either way round. */
338
+ end: NebaChartCategory;
339
+ /** What the span is called, in the tooltip and the table. */
340
+ label?: React.ReactNode;
341
+ /** Overrides its row's colour for this one span. */
342
+ color?: NebaColor | (string & {});
343
+ }
344
+ /**
345
+ * One row of a TimelineChart, and everything on it.
346
+ *
347
+ * A row is a series — one entity, one name, one colour — but its data are
348
+ * spans rather than values, so it cannot be a `NebaChartSeries`. There is no
349
+ * `hidden` here and no legend to pair it with: a Gantt's rows *are* its
350
+ * category axis, already named down the side, and a twenty-entry legend
351
+ * restating them is not a filter anyone wants.
352
+ */
353
+ export interface NebaTimelineSeries {
354
+ /** Its name on the axis, in the tooltip and in the table. */
355
+ name?: string;
356
+ /** The spans on this row. Overlapping ones are drawn over each other. */
357
+ data: readonly NebaTimelinePoint[];
358
+ /** Overrides the palette slot this row would otherwise take. */
359
+ color?: NebaColor | (string & {});
360
+ }
361
+ /** How a line gets from one point to the next. */
362
+ export type NebaChartCurve = 'linear' | 'smooth' | 'step';
363
+ /**
364
+ * Which values are written onto the marks themselves.
365
+ *
366
+ * The default is `none` everywhere, and that is not timidity — a number beside
367
+ * every point is the most reliable way to make a chart unreadable. Label the
368
+ * end, or the extremes, and let the axis and the tooltip carry the rest;
369
+ * `'all'` is there for the eight-bar chart where it genuinely is the answer.
370
+ */
371
+ export type NebaChartValueLabels = 'none' | 'last' | 'extremes' | 'all';
372
+ /**
373
+ * What the pointer uncovers.
374
+ *
375
+ * - `index` — every series at the category under the pointer, with a crosshair.
376
+ * The default on anything with a shared x axis, because the question a line
377
+ * chart is asked is "what happened in March", not "what is this pixel".
378
+ * - `item` — the one mark being pointed at.
379
+ * - `none` — no tooltip. The values still have to be readable some other way.
380
+ */
381
+ export type NebaChartTooltipMode = 'index' | 'item' | 'none';
382
+ /** One series' answer at the category the pointer is on. */
383
+ export interface NebaChartTooltipItem {
384
+ /** Its place in the `series` array — the same index its colour came from. */
385
+ seriesIndex: number;
386
+ name?: string;
387
+ color: string;
388
+ value: number | null;
389
+ /** `value` run through the chart's `format`. */
390
+ formatted: string;
391
+ /** What the point called itself, if it said. */
392
+ label?: React.ReactNode;
393
+ }
394
+ /** What a custom tooltip is handed. */
395
+ export interface NebaChartTooltipContext {
396
+ index: number;
397
+ category: NebaChartCategory;
398
+ /** Only the series that are visible and have a value here. */
399
+ items: readonly NebaChartTooltipItem[];
400
+ }
401
+ /** The tooltip, when a bare `true` or `false` is not enough. */
402
+ export interface NebaChartTooltip {
403
+ /** @default 'index' */
404
+ mode?: NebaChartTooltipMode;
405
+ /**
406
+ * The line dropped through the plot at the active category. On in `index`
407
+ * mode, where it is what says which column the numbers belong to.
408
+ */
409
+ crosshair?: boolean;
410
+ /** Draws the panel. Without it the chart draws its own. */
411
+ render?: (context: NebaChartTooltipContext) => React.ReactNode;
412
+ }
413
+ /** One axis of a cartesian chart. */
414
+ export interface NebaChartAxis {
415
+ /** Leaves the axis undrawn — its rule, its ticks and its labels. */
416
+ hidden?: boolean;
417
+ /** A name for what the axis measures, set beside it. */
418
+ label?: React.ReactNode;
419
+ /**
420
+ * The gridlines this axis casts across the plot. On by default for the value
421
+ * axis and off for the category axis, which is the only arrangement where the
422
+ * grid helps read a value without turning the plot into graph paper.
423
+ */
424
+ grid?: boolean;
425
+ /**
426
+ * Where the scale starts and ends. Left out, both are taken from the data —
427
+ * the value axis from zero, so a bar's length stays proportional to its
428
+ * value. Set `min` only when zero is genuinely not the baseline.
429
+ */
430
+ min?: number;
431
+ max?: number;
432
+ /** Roughly how many ticks. The scale still rounds to clean numbers. */
433
+ tickCount?: number;
434
+ /** How a tick is written, overriding the chart's own `format`. */
435
+ tickFormat?: (value: NebaChartCategory, index: number) => React.ReactNode;
436
+ /**
437
+ * How much room the axis keeps for its ticks and its label, in pixels.
438
+ * Measured from the ticks themselves otherwise; set it when a long category
439
+ * name needs more, or when two charts stacked on a dashboard have to line
440
+ * their plots up.
441
+ */
442
+ thickness?: number;
443
+ }
444
+ /** Where the legend sits, and whether it does anything when clicked. */
445
+ export interface NebaChartLegend {
446
+ /** Which edge of the plot. @default 'bottom' */
447
+ side?: NebaSide;
448
+ /** Where along that edge. @default 'center' */
449
+ align?: NebaAlign;
450
+ /**
451
+ * Clicking an entry hides and shows its series; hovering one dims the rest.
452
+ * @default true
453
+ */
454
+ interactive?: boolean;
455
+ /** Draws each series' current value beside its name. @default false */
456
+ showValue?: boolean;
457
+ }
244
458
  /** Style props shared by most components; spread into their own prop types. */
245
459
  export interface NebaStyleProps {
246
460
  /** @default 'solid' */
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "neba",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "A comprehensive React UI component library — buttons, fields, menus, dialogs, tables and much more — accessible and themeable, sharing one prop vocabulary and a translucent acrylic design language. ESM only, types included, dark mode built in.",
5
5
  "type": "module",
6
6
  "types": "dist/index.d.ts",
7
+ "sideEffects": [
8
+ "**/*.css"
9
+ ],
7
10
  "author": "CDGet <jooy2.contact@gmail.com>",
8
11
  "license": "MIT",
9
12
  "homepage": "https://neba.cdget.com",
@@ -16,16 +19,16 @@
16
19
  },
17
20
  "scripts": {
18
21
  "build": "npm run format:fix && tsc --project tsconfig.prod.json && npm run minify && node scripts/build-styles.mjs",
19
- "lint": "eslint . --ext .js,.ts .",
20
- "lint:fix": "eslint . --ext .js,.ts --fix .",
22
+ "lint": "eslint . --report-unused-disable-directives",
23
+ "lint:fix": "eslint . --report-unused-disable-directives --fix",
21
24
  "minify": "terser-glob 'dist/**/*.js' --config-file terser.config.json",
22
25
  "prepare": "npm run build",
23
26
  "format": "prettier .",
24
27
  "format:fix": "prettier . --write",
25
28
  "docs:changelog": "node scripts/copy-changelog.mjs",
26
- "docs:dev": "npm run build && npm run docs:changelog && vitepress dev docs",
27
- "docs:build": "npm run build && npm run docs:changelog && vitepress build docs",
28
- "docs:serve": "npm run build && npm run docs:build && vitepress serve docs",
29
+ "docs:dev": "npm run docs:changelog && vitepress dev docs",
30
+ "docs:build": "npm run docs:changelog && vitepress build docs",
31
+ "docs:serve": "npm run docs:build && vitepress serve docs",
29
32
  "test": "vitest run",
30
33
  "test:watch": "vitest",
31
34
  "typecheck": "tsc --project tsconfig.prod.json --noEmit && tsc --project test/tsconfig.json && tsc --project docs/tsconfig.json"
@@ -100,33 +103,34 @@
100
103
  "devDependencies": {
101
104
  "@eslint/js": "^9.39.1",
102
105
  "@tailwindcss/postcss": "^4.3.3",
103
- "@types/node": "^26.1.2",
106
+ "@types/node": "^26.2.0",
104
107
  "@types/react": "^19.2.18",
105
108
  "@types/react-dom": "^19.2.4",
106
- "@typescript-eslint/parser": "^8.66.0",
109
+ "@typescript-eslint/parser": "^8.67.0",
107
110
  "@vitejs/plugin-react": "^6.0.5",
108
111
  "@vitest/browser": "^4.1.10",
109
112
  "@vitest/browser-playwright": "^4.1.10",
110
113
  "eslint": "^9.39.1",
111
114
  "eslint-config-prettier": "^10.1.8",
112
- "eslint-plugin-n": "^18.2.2",
113
- "globals": "^17.9.0",
115
+ "eslint-plugin-n": "^18.3.0",
116
+ "eslint-plugin-react-hooks": "^7.1.1",
117
+ "globals": "^17.10.0",
114
118
  "jiti": "^2.7.0",
115
119
  "playwright": "^1.62.1",
116
- "postcss": "^8.5.25",
120
+ "postcss": "^8.5.26",
117
121
  "prettier": "^3.9.6",
118
122
  "prettier-plugin-tailwindcss": "^0.8.1",
119
123
  "react": "^19.2.8",
120
124
  "react-dom": "^19.2.8",
121
125
  "tailwindcss": "^4.3.3",
122
126
  "terser-glob": "^1.2.1",
123
- "tsx": "^4.23.6",
127
+ "tsx": "^4.23.12",
124
128
  "typescript": "^6.0.3",
125
- "typescript-eslint": "^8.66.0",
126
- "vite": "^8.2.0",
129
+ "typescript-eslint": "^8.67.0",
130
+ "vite": "^8.2.1",
127
131
  "vitepress": "^1.6.4",
128
132
  "vitepress-i18n": "^1.3.5",
129
- "vitepress-sidebar": "^1.36.1",
133
+ "vitepress-sidebar": "^1.38.0",
130
134
  "vitest": "^4.1.10",
131
135
  "vitest-browser-react": "^2.2.0"
132
136
  },