dowel-ui 0.23.0 → 0.24.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.
package/dist/theme.css CHANGED
@@ -80,6 +80,95 @@
80
80
  --bad-soft: color-mix(in oklab, var(--bad) 14%, transparent);
81
81
  --info-soft: color-mix(in oklab, var(--info) 15%, transparent);
82
82
 
83
+ /* Series colours, for charts.
84
+ *
85
+ * Like the status hues and unlike everything else here, these do NOT follow
86
+ * the product accent - and that is the whole decision. A series is a
87
+ * property of the data, not of the product showing it; a palette derived
88
+ * from the accent would paint the same series differently in kilna and in
89
+ * kasl-server, which is the one thing a series colour must never do.
90
+ *
91
+ * Deriving them from the accent was measured, not guessed: rotating the hue
92
+ * by fixed angles put the best candidate at Delta-E 12.1 from the product's
93
+ * own accent with adjacent pairs at 9.8, and threw 46 of 152 colours outside
94
+ * sRGB. The fixed palette does better on both counts.
95
+ *
96
+ * The cost of fixing them is stated plainly: 13 of the line's 19 accents sit
97
+ * within Delta-E 8 of some slot (lyrid 2.7, atlas 3.1, turnout 3.2). A chart
98
+ * shows one accent, so this is survivable - but it is exactly why identity is
99
+ * carried by the legend and by direct labels, never by hue on its own. The
100
+ * components of this block enforce that; it is not advice.
101
+ *
102
+ * Assigned in order, 1..8, and never cycled: a ninth series folds into
103
+ * "other", or the chart becomes small multiples. The order is the
104
+ * colour-blind-safety mechanism - the worst adjacent pair is Delta-E 8.4
105
+ * under simulated protanopia - so re-ordering these breaks a gate.
106
+ *
107
+ * Slot 8 is the line's own value rather than the reference palette's. The
108
+ * reference red sat Delta-E 1.9 from `--bad`, which would have let a series
109
+ * impersonate a status; this one clears every status by 15 and costs
110
+ * nothing, the worst adjacent pair being unchanged.
111
+ *
112
+ * Some slots sit below 3:1 against the surface. That is allowed only because
113
+ * the relief ships with them: visible values, or the table view.
114
+ */
115
+ --series-1: #3987e5;
116
+ --series-2: #d95926;
117
+ --series-3: #199e70;
118
+ --series-4: #c98500;
119
+ --series-5: #d55181;
120
+ --series-6: #008300;
121
+ --series-7: #9085e9;
122
+ --series-8: #a3215a;
123
+
124
+ /* Magnitude rather than identity: one hue, light to dark. A heatmap cell
125
+ * reads this. The lightest step is allowed to recede toward the surface,
126
+ * because "near zero" should; an ordered-but-discrete scale (tiers, funnel
127
+ * stages) starts at 300 instead, where the contrast still holds. */
128
+ --scale-100: #0d366b;
129
+ --scale-200: #184f95;
130
+ --scale-300: #256abf;
131
+ --scale-400: #2a78d6;
132
+ --scale-500: #3987e5;
133
+ --scale-600: #5598e7;
134
+ --scale-700: #86b6ef;
135
+
136
+ /* The chart's own furniture. A gridline that competes with the data is drawn
137
+ * wrong, so it is quieter than `--line`; the axis is the one that may be
138
+ * seen. */
139
+ --chart-grid: rgb(255 255 255 / 0.06);
140
+ --chart-axis: rgb(255 255 255 / 0.14);
141
+
142
+ /* Heat, for a grid of cells where colour is the only thing carrying the
143
+ * value - a year of activity, a month of a team's hours.
144
+ *
145
+ * Five discrete steps rather than the seven of `--scale-*`, and a separate
146
+ * set rather than a slice of them, because the two answer different
147
+ * questions. `--scale-*` is sequential: a continuous magnitude, where the
148
+ * lightest step means "near zero" and may recede toward the surface.
149
+ * Heat is ordinal: five shades a reader tells apart at a glance and matches
150
+ * against a legend, which needs wider gaps between them (no slice of
151
+ * `--scale-*` clears the 0.06 lightness step - they sit 0.047 apart) and a
152
+ * bottom step that stays distinct from an empty cell.
153
+ *
154
+ * That last one is the whole point. A heatmap cell has more meanings than a
155
+ * number: nothing recorded, a day still running, a day outside the data, and
156
+ * a value. If the faintest value looks like the empty cell, the grid says
157
+ * somebody did nothing on a day nobody reported - and `--heat-1` clears the
158
+ * empty square by 2.1:1 so it cannot.
159
+ *
160
+ * Fixed, like the series and unlike almost everything else here. Derived
161
+ * from the accent - as the first consumer did - the faintest step sat 1.3:1
162
+ * from the empty cell for half the line, and pinning lightness instead ran
163
+ * out of sRGB: the darkest accents cannot reach the top of the ramp at their
164
+ * own chroma.
165
+ */
166
+ --heat-1: #28528e;
167
+ --heat-2: #3a65a3;
168
+ --heat-3: #4d78b7;
169
+ --heat-4: #608ccd;
170
+ --heat-5: #73a0e2;
171
+
83
172
  /* Elevation, three steps. The products had one shadow and used it for
84
173
  * everything that leaves the flow - a toast, a dropdown and a modal all
85
174
  * floated by the same amount, so a modal never felt further away than the
@@ -143,6 +232,38 @@
143
232
  --bad-soft: color-mix(in oklab, var(--bad) 12%, transparent);
144
233
  --info-soft: color-mix(in oklab, var(--info) 11%, transparent);
145
234
 
235
+ /* Series and scale, stepped for the light surface - the same eight hues
236
+ * chosen again against this ground, not the dark values lightened. The dark
237
+ * block says why these do not follow the product accent. */
238
+ --series-1: #2a78d6;
239
+ --series-2: #eb6834;
240
+ --series-3: #1baf7a;
241
+ --series-4: #eda100;
242
+ --series-5: #e87ba4;
243
+ --series-6: #008300;
244
+ --series-7: #4a3aa7;
245
+ --series-8: #85284d;
246
+
247
+ --scale-100: #cde2fb;
248
+ --scale-200: #9ec5f4;
249
+ --scale-300: #6da7ec;
250
+ --scale-400: #3987e5;
251
+ --scale-500: #2a78d6;
252
+ --scale-600: #1c5cab;
253
+ --scale-700: #104281;
254
+
255
+ --chart-grid: oklch(from color-mix(in oklab, var(--neutral-base) 30%, #181420) l c h / 0.08);
256
+ --chart-axis: oklch(from color-mix(in oklab, var(--neutral-base) 30%, #181420) l c h / 0.18);
257
+
258
+ /* The same five steps chosen again for the light ground, running the other
259
+ * way: here "more" is darker. See the dark block for why heat is its own
260
+ * ramp and not a slice of `--scale-*`. */
261
+ --heat-1: #84a8fd;
262
+ --heat-2: #7193e7;
263
+ --heat-3: #5e7fd1;
264
+ --heat-4: #4c6cbb;
265
+ --heat-5: #3b58a6;
266
+
146
267
  --shadow-lift: 0 2px 8px rgb(30 24 38 / 0.08);
147
268
  --shadow-raise: 0 10px 30px rgb(30 24 38 / 0.14);
148
269
  --shadow-float: 0 24px 60px rgb(30 24 38 / 0.18);
@@ -182,6 +303,38 @@
182
303
  --bad-soft: color-mix(in oklab, var(--bad) 12%, transparent);
183
304
  --info-soft: color-mix(in oklab, var(--info) 11%, transparent);
184
305
 
306
+ /* Series and scale, stepped for the light surface - the same eight hues
307
+ * chosen again against this ground, not the dark values lightened. The dark
308
+ * block says why these do not follow the product accent. */
309
+ --series-1: #2a78d6;
310
+ --series-2: #eb6834;
311
+ --series-3: #1baf7a;
312
+ --series-4: #eda100;
313
+ --series-5: #e87ba4;
314
+ --series-6: #008300;
315
+ --series-7: #4a3aa7;
316
+ --series-8: #85284d;
317
+
318
+ --scale-100: #cde2fb;
319
+ --scale-200: #9ec5f4;
320
+ --scale-300: #6da7ec;
321
+ --scale-400: #3987e5;
322
+ --scale-500: #2a78d6;
323
+ --scale-600: #1c5cab;
324
+ --scale-700: #104281;
325
+
326
+ --chart-grid: oklch(from color-mix(in oklab, var(--neutral-base) 30%, #181420) l c h / 0.08);
327
+ --chart-axis: oklch(from color-mix(in oklab, var(--neutral-base) 30%, #181420) l c h / 0.18);
328
+
329
+ /* The same five steps chosen again for the light ground, running the other
330
+ * way: here "more" is darker. See the dark block for why heat is its own
331
+ * ramp and not a slice of `--scale-*`. */
332
+ --heat-1: #84a8fd;
333
+ --heat-2: #7193e7;
334
+ --heat-3: #5e7fd1;
335
+ --heat-4: #4c6cbb;
336
+ --heat-5: #3b58a6;
337
+
185
338
  --shadow-lift: 0 2px 8px rgb(30 24 38 / 0.08);
186
339
  --shadow-raise: 0 10px 30px rgb(30 24 38 / 0.14);
187
340
  --shadow-float: 0 24px 60px rgb(30 24 38 / 0.18);
@@ -275,6 +428,33 @@
275
428
  --color-info: var(--info);
276
429
  --color-info-soft: var(--info-soft);
277
430
 
431
+ /* Charts. `bg-series-3`, `fill-series-3`, `stroke-scale-500` - the same
432
+ * utilities the rest of the vocabulary gets, so a chart is written in
433
+ * tokens like everything else and `dowel/no-raw-color` can hold it to
434
+ * that. */
435
+ --color-series-1: var(--series-1);
436
+ --color-series-2: var(--series-2);
437
+ --color-series-3: var(--series-3);
438
+ --color-series-4: var(--series-4);
439
+ --color-series-5: var(--series-5);
440
+ --color-series-6: var(--series-6);
441
+ --color-series-7: var(--series-7);
442
+ --color-series-8: var(--series-8);
443
+ --color-scale-100: var(--scale-100);
444
+ --color-scale-200: var(--scale-200);
445
+ --color-scale-300: var(--scale-300);
446
+ --color-scale-400: var(--scale-400);
447
+ --color-scale-500: var(--scale-500);
448
+ --color-scale-600: var(--scale-600);
449
+ --color-scale-700: var(--scale-700);
450
+ --color-chart-grid: var(--chart-grid);
451
+ --color-chart-axis: var(--chart-axis);
452
+ --color-heat-1: var(--heat-1);
453
+ --color-heat-2: var(--heat-2);
454
+ --color-heat-3: var(--heat-3);
455
+ --color-heat-4: var(--heat-4);
456
+ --color-heat-5: var(--heat-5);
457
+
278
458
  /* Kept because they are not palette choices: a hairline is `transparent`,
279
459
  * an SVG follows `currentColor`, and pure black and white are what an
280
460
  * overlay scrim and a print sheet are made of. */
package/dist/tokens.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "$description": "The dowel token vocabulary: the scales every product of the lacodda line is drawn on. Colours are omitted - they are derived per product from its own accent.",
2
+ "$description": "The dowel token vocabulary: the scales every product of the lacodda line is drawn on. Most colours are omitted - they are derived per product from its own accent - except the chart series and the magnitude scale, which are fixed for the whole line.",
3
3
  "radius": {
4
4
  "$type": "dimension",
5
5
  "$description": "Corner radius. `md` is the control radius: inputs, buttons, list rows.",
@@ -40,6 +40,112 @@
40
40
  }
41
41
  }
42
42
  },
43
+ "series": {
44
+ "$type": "color",
45
+ "$description": "Chart series, assigned 1..8 in order and never cycled. Alone among the colours here they do not follow the product accent: a series belongs to the data. The order is what keeps adjacent slots apart under colour blindness, so it is not a display order.",
46
+ "dark": {
47
+ "series-1": {
48
+ "$value": "#3987e5"
49
+ },
50
+ "series-2": {
51
+ "$value": "#d95926"
52
+ },
53
+ "series-3": {
54
+ "$value": "#199e70"
55
+ },
56
+ "series-4": {
57
+ "$value": "#c98500"
58
+ },
59
+ "series-5": {
60
+ "$value": "#d55181"
61
+ },
62
+ "series-6": {
63
+ "$value": "#008300"
64
+ },
65
+ "series-7": {
66
+ "$value": "#9085e9"
67
+ },
68
+ "series-8": {
69
+ "$value": "#a3215a"
70
+ }
71
+ },
72
+ "light": {
73
+ "series-1": {
74
+ "$value": "#2a78d6"
75
+ },
76
+ "series-2": {
77
+ "$value": "#eb6834"
78
+ },
79
+ "series-3": {
80
+ "$value": "#1baf7a"
81
+ },
82
+ "series-4": {
83
+ "$value": "#eda100"
84
+ },
85
+ "series-5": {
86
+ "$value": "#e87ba4"
87
+ },
88
+ "series-6": {
89
+ "$value": "#008300"
90
+ },
91
+ "series-7": {
92
+ "$value": "#4a3aa7"
93
+ },
94
+ "series-8": {
95
+ "$value": "#85284d"
96
+ }
97
+ }
98
+ },
99
+ "scale": {
100
+ "$type": "color",
101
+ "$description": "Magnitude: one hue, running away from the ground as the value grows. Heatmap cells read this. For an ordered-but-discrete scale start at 300, where contrast still holds.",
102
+ "dark": {
103
+ "scale-100": {
104
+ "$value": "#0d366b"
105
+ },
106
+ "scale-200": {
107
+ "$value": "#184f95"
108
+ },
109
+ "scale-300": {
110
+ "$value": "#256abf"
111
+ },
112
+ "scale-400": {
113
+ "$value": "#2a78d6"
114
+ },
115
+ "scale-500": {
116
+ "$value": "#3987e5"
117
+ },
118
+ "scale-600": {
119
+ "$value": "#5598e7"
120
+ },
121
+ "scale-700": {
122
+ "$value": "#86b6ef"
123
+ }
124
+ },
125
+ "light": {
126
+ "scale-100": {
127
+ "$value": "#cde2fb"
128
+ },
129
+ "scale-200": {
130
+ "$value": "#9ec5f4"
131
+ },
132
+ "scale-300": {
133
+ "$value": "#6da7ec"
134
+ },
135
+ "scale-400": {
136
+ "$value": "#3987e5"
137
+ },
138
+ "scale-500": {
139
+ "$value": "#2a78d6"
140
+ },
141
+ "scale-600": {
142
+ "$value": "#1c5cab"
143
+ },
144
+ "scale-700": {
145
+ "$value": "#104281"
146
+ }
147
+ }
148
+ },
43
149
  "typography": {
44
150
  "$type": "typography",
45
151
  "$description": "Type steps, each with the line height it is set on.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dowel-ui",
3
- "version": "0.23.0",
3
+ "version": "0.24.0",
4
4
  "description": "The lacodda line design system: theme tokens and React primitives, distributed as a shadcn-compatible registry.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -61,13 +61,13 @@
61
61
  "react-dom": "^19.0.0"
62
62
  },
63
63
  "devDependencies": {
64
- "@types/node": "^26.4.1",
65
- "@types/react": "^19.2.18",
66
- "@types/react-dom": "^19.2.7",
64
+ "@types/node": "^26.5.1",
65
+ "@types/react": "^19.3.0",
66
+ "@types/react-dom": "^19.3.0",
67
67
  "class-variance-authority": "^0.7.1",
68
68
  "eslint": "^10.10.0",
69
- "react": "^19.2.8",
70
- "react-dom": "^19.2.8"
69
+ "react": "^19.3.0",
70
+ "react-dom": "^19.3.0"
71
71
  },
72
72
  "publishConfig": {
73
73
  "access": "public",
@@ -75,7 +75,7 @@
75
75
  },
76
76
  "dependencies": {
77
77
  "clsx": "^2.1.1",
78
- "tailwind-merge": "^3.6.0"
78
+ "tailwind-merge": "^3.7.0"
79
79
  },
80
80
  "peerDependenciesMeta": {
81
81
  "eslint": {