dashtaste 0.0.0-stage → 0.0.2

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 (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +129 -2
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +124 -0
  5. package/dist/color.d.ts +4 -0
  6. package/dist/color.js +43 -0
  7. package/dist/engine.d.ts +11 -0
  8. package/dist/engine.js +39 -0
  9. package/dist/extract-in-page.d.ts +3 -0
  10. package/dist/extract-in-page.js +149 -0
  11. package/dist/extract.d.ts +3 -0
  12. package/dist/extract.js +39 -0
  13. package/dist/format.d.ts +18 -0
  14. package/dist/format.js +65 -0
  15. package/dist/index.d.ts +5 -0
  16. package/dist/index.js +5 -0
  17. package/dist/packs/cn.d.ts +2 -0
  18. package/dist/packs/cn.js +56 -0
  19. package/dist/packs/global.d.ts +2 -0
  20. package/dist/packs/global.js +45 -0
  21. package/dist/packs/index.d.ts +53 -0
  22. package/dist/packs/index.js +13 -0
  23. package/dist/rules/dt001-default-palette.d.ts +3 -0
  24. package/dist/rules/dt001-default-palette.js +11 -0
  25. package/dist/rules/dt002-rainbow-palette.d.ts +3 -0
  26. package/dist/rules/dt002-rainbow-palette.js +11 -0
  27. package/dist/rules/dt003-smooth-line.d.ts +3 -0
  28. package/dist/rules/dt003-smooth-line.js +9 -0
  29. package/dist/rules/dt004-symbols-everywhere.d.ts +3 -0
  30. package/dist/rules/dt004-symbols-everywhere.js +21 -0
  31. package/dist/rules/dt005-pie-slices.d.ts +3 -0
  32. package/dist/rules/dt005-pie-slices.js +13 -0
  33. package/dist/rules/dt006-pie-overuse.d.ts +3 -0
  34. package/dist/rules/dt006-pie-overuse.js +14 -0
  35. package/dist/rules/dt007-three-d.d.ts +3 -0
  36. package/dist/rules/dt007-three-d.js +16 -0
  37. package/dist/rules/dt008-dual-axis.d.ts +3 -0
  38. package/dist/rules/dt008-dual-axis.js +10 -0
  39. package/dist/rules/dt009-missing-unit.d.ts +3 -0
  40. package/dist/rules/dt009-missing-unit.js +8 -0
  41. package/dist/rules/dt010-bar-baseline.d.ts +3 -0
  42. package/dist/rules/dt010-bar-baseline.js +11 -0
  43. package/dist/rules/dt011-raw-large-numbers.d.ts +3 -0
  44. package/dist/rules/dt011-raw-large-numbers.js +21 -0
  45. package/dist/rules/dt012-unit-system.d.ts +3 -0
  46. package/dist/rules/dt012-unit-system.js +12 -0
  47. package/dist/rules/dt013-delta-convention.d.ts +3 -0
  48. package/dist/rules/dt013-delta-convention.js +21 -0
  49. package/dist/rules/dt014-delta-colour-only.d.ts +3 -0
  50. package/dist/rules/dt014-delta-colour-only.js +16 -0
  51. package/dist/rules/dt015-low-contrast-text.d.ts +3 -0
  52. package/dist/rules/dt015-low-contrast-text.js +28 -0
  53. package/dist/rules/dt016-thin-line-contrast.d.ts +3 -0
  54. package/dist/rules/dt016-thin-line-contrast.js +12 -0
  55. package/dist/rules/dt017-gradient-decoration.d.ts +3 -0
  56. package/dist/rules/dt017-gradient-decoration.js +15 -0
  57. package/dist/rules/dt018-glow.d.ts +3 -0
  58. package/dist/rules/dt018-glow.js +27 -0
  59. package/dist/rules/dt019-kpi-overload.d.ts +3 -0
  60. package/dist/rules/dt019-kpi-overload.js +7 -0
  61. package/dist/rules/dt020-kpi-rainbow.d.ts +3 -0
  62. package/dist/rules/dt020-kpi-rainbow.js +27 -0
  63. package/dist/rules/dt021-emoji-icons.d.ts +3 -0
  64. package/dist/rules/dt021-emoji-icons.js +10 -0
  65. package/dist/rules/dt022-cjk-min-size.d.ts +3 -0
  66. package/dist/rules/dt022-cjk-min-size.js +9 -0
  67. package/dist/rules/dt023-legend-single-series.d.ts +3 -0
  68. package/dist/rules/dt023-legend-single-series.js +8 -0
  69. package/dist/rules/dt024-bar-width.d.ts +1 -0
  70. package/dist/rules/dt024-bar-width.js +14 -0
  71. package/dist/rules/dt025-legend-not-direct-label.d.ts +1 -0
  72. package/dist/rules/dt025-legend-not-direct-label.js +11 -0
  73. package/dist/rules/dt026-line-weight.d.ts +1 -0
  74. package/dist/rules/dt026-line-weight.js +8 -0
  75. package/dist/rules/dt027-tabular-numerals.d.ts +1 -0
  76. package/dist/rules/dt027-tabular-numerals.js +16 -0
  77. package/dist/rules/dt028-hero-metric.d.ts +1 -0
  78. package/dist/rules/dt028-hero-metric.js +14 -0
  79. package/dist/rules/helpers.d.ts +32 -0
  80. package/dist/rules/helpers.js +161 -0
  81. package/dist/rules/index.d.ts +2 -0
  82. package/dist/rules/index.js +58 -0
  83. package/dist/types.d.ts +122 -0
  84. package/dist/types.js +1 -0
  85. package/docs/rules.md +735 -0
  86. package/docs/usage.md +126 -0
  87. package/package.json +74 -4
  88. package/skills/dashtaste-cn/SKILL.md +51 -0
  89. package/skills/dashtaste-cn/references/avoid.md +135 -0
  90. package/skills/dashtaste-cn/references/charts.md +122 -0
  91. package/skills/dashtaste-cn/references/layout.md +78 -0
  92. package/skills/dashtaste-cn/references/numbers.md +93 -0
  93. package/skills/dashtaste-cn/references/tokens.md +72 -0
  94. package/skills/dashtaste-global/SKILL.md +43 -0
  95. package/skills/dashtaste-global/references/avoid.md +135 -0
  96. package/skills/dashtaste-global/references/charts.md +122 -0
  97. package/skills/dashtaste-global/references/layout.md +78 -0
  98. package/skills/dashtaste-global/references/numbers.md +83 -0
  99. package/skills/dashtaste-global/references/tokens.md +65 -0
package/docs/rules.md ADDED
@@ -0,0 +1,735 @@
1
+ # Rules
2
+
3
+ Run checks with the intended pack and mode. Page-only rules require extracted page facts.
4
+ Errors reduce score by 10, warnings by 3, and information by 1; the minimum score is zero.
5
+
6
+ ## DT001 default-palette
7
+
8
+ Severity: error. Packs: global, cn.
9
+
10
+ Why: Implicit theme colours make series meaning unstable across charts.
11
+
12
+ Triggers: Multiple series need explicit colours. Any default palette colour is flagged.
13
+
14
+ Fix: set option.color to the pack palette (dashtaste tokens --pack <pack>).
15
+
16
+ Minimal failing input:
17
+
18
+ ```json
19
+ {
20
+ "charts": [
21
+ {
22
+ "option": {
23
+ "series": [
24
+ {
25
+ "type": "bar"
26
+ },
27
+ {
28
+ "type": "line"
29
+ }
30
+ ]
31
+ }
32
+ }
33
+ ]
34
+ }
35
+ ```
36
+
37
+ ## DT002 rainbow-palette
38
+
39
+ Severity: warn. Packs: global, cn.
40
+
41
+ Why: Too many competing hues make category comparison harder.
42
+
43
+ Triggers: More than 6 distinct categorical colours or more than 5 saturated 60-degree hue buckets.
44
+
45
+ Fix: use the pack palette and group remaining categories as Other.
46
+
47
+ Minimal failing input:
48
+
49
+ ```json
50
+ {
51
+ "charts": [
52
+ {
53
+ "option": {
54
+ "color": ["#110000", "#220000", "#330000", "#440000", "#550000", "#660000", "#770000"]
55
+ }
56
+ }
57
+ ]
58
+ }
59
+ ```
60
+
61
+ ## DT003 smooth-line
62
+
63
+ Severity: warn. Packs: global, cn.
64
+
65
+ Why: Curves can imply observations that were never measured.
66
+
67
+ Triggers: Line smoothing greater than zero draws values that do not exist.
68
+
69
+ Fix: use straight segments: smooth: false.
70
+
71
+ Minimal failing input:
72
+
73
+ ```json
74
+ {
75
+ "charts": [
76
+ {
77
+ "option": {
78
+ "series": [
79
+ {
80
+ "type": "line",
81
+ "smooth": true
82
+ }
83
+ ]
84
+ }
85
+ }
86
+ ]
87
+ }
88
+ ```
89
+
90
+ ## DT004 symbols-everywhere
91
+
92
+ Severity: warn. Packs: global, cn.
93
+
94
+ Why: Repeated symbols obscure the line on longer time series.
95
+
96
+ Triggers: Use small explicit dots only on short series; avoid default symbols everywhere.
97
+
98
+ Fix: use dots at most 5px on at most 31 points, or set showSymbol: false.
99
+
100
+ Minimal failing input:
101
+
102
+ ```json
103
+ {
104
+ "charts": [
105
+ {
106
+ "option": {
107
+ "series": [
108
+ {
109
+ "type": "line",
110
+ "data": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
111
+ }
112
+ ]
113
+ }
114
+ }
115
+ ]
116
+ }
117
+ ```
118
+
119
+ ## DT005 pie-slices
120
+
121
+ Severity: warn. Packs: global, cn.
122
+
123
+ Why: Small pie slices are hard to compare and label.
124
+
125
+ Triggers: Maximum 5 slices; cn rose charts allow 8.
126
+
127
+ Fix: use horizontal bars or combine small parts into Other.
128
+
129
+ Minimal failing input:
130
+
131
+ ```json
132
+ {
133
+ "charts": [
134
+ {
135
+ "option": {
136
+ "series": [
137
+ {
138
+ "type": "pie",
139
+ "data": [1, 1, 1, 1, 1, 1]
140
+ }
141
+ ]
142
+ }
143
+ }
144
+ ]
145
+ }
146
+ ```
147
+
148
+ ## DT006 pie-overuse
149
+
150
+ Severity: warn. Packs: global, cn.
151
+
152
+ Why: Repeated pies and narrow rings make part comparison difficult.
153
+
154
+ Triggers: More than one pie per page or radius ratio above 0.75 warns. A global pie is informational.
155
+
156
+ Fix: prefer horizontal bars; keep donut radius ratio at most 0.75.
157
+
158
+ Minimal failing input:
159
+
160
+ ```json
161
+ {
162
+ "charts": [
163
+ {
164
+ "option": {
165
+ "series": [
166
+ {
167
+ "type": "pie",
168
+ "radius": ["90%", "100%"],
169
+ "data": [1, 2]
170
+ }
171
+ ]
172
+ }
173
+ }
174
+ ]
175
+ }
176
+ ```
177
+
178
+ ## DT007 three-d
179
+
180
+ Severity: error. Packs: global, cn.
181
+
182
+ Why: Depth and perspective distort visible size.
183
+
184
+ Triggers: 3D series, axes, coordinates, and pseudo-3D pie decoration distort comparisons.
185
+
186
+ Fix: use flat 2D charts.
187
+
188
+ Minimal failing input:
189
+
190
+ ```json
191
+ {
192
+ "charts": [
193
+ {
194
+ "option": {
195
+ "series": [
196
+ {
197
+ "type": "bar3D"
198
+ }
199
+ ]
200
+ }
201
+ }
202
+ ]
203
+ }
204
+ ```
205
+
206
+ ## DT008 dual-axis
207
+
208
+ Severity: warn. Packs: global, cn.
209
+
210
+ Why: Independent scales can imply misleading relationships.
211
+
212
+ Triggers: Multiple value axes obscure comparisons, including mixed bar and line charts.
213
+
214
+ Fix: use one value axis or aligned small multiples.
215
+
216
+ Minimal failing input:
217
+
218
+ ```json
219
+ {
220
+ "charts": [
221
+ {
222
+ "option": {
223
+ "yAxis": [
224
+ {
225
+ "type": "value",
226
+ "name": "A"
227
+ },
228
+ {
229
+ "type": "value",
230
+ "name": "B"
231
+ }
232
+ ],
233
+ "series": [
234
+ {
235
+ "type": "line"
236
+ },
237
+ {
238
+ "type": "line"
239
+ }
240
+ ]
241
+ }
242
+ }
243
+ ]
244
+ }
245
+ ```
246
+
247
+ ## DT009 missing-unit
248
+
249
+ Severity: warn. Packs: global, cn.
250
+
251
+ Why: A value without a unit is ambiguous.
252
+
253
+ Triggers: Value axes need a name, formatter, unit metadata, or units in rendered ticks.
254
+
255
+ Fix: put units in axis names, rendered ticks, or formatter metadata.
256
+
257
+ Minimal failing input:
258
+
259
+ ```json
260
+ {
261
+ "charts": [
262
+ {
263
+ "option": {
264
+ "yAxis": {
265
+ "type": "value"
266
+ }
267
+ }
268
+ }
269
+ ]
270
+ }
271
+ ```
272
+
273
+ ## DT010 bar-baseline
274
+
275
+ Severity: error. Packs: global, cn.
276
+
277
+ Why: Bar length encodes magnitude relative to zero.
278
+
279
+ Triggers: Bar value axes must start at zero; positive min and scale: true distort lengths.
280
+
281
+ Fix: set min: 0 and scale: false.
282
+
283
+ Minimal failing input:
284
+
285
+ ```json
286
+ {
287
+ "charts": [
288
+ {
289
+ "option": {
290
+ "series": [
291
+ {
292
+ "type": "bar"
293
+ }
294
+ ],
295
+ "yAxis": {
296
+ "min": 10
297
+ }
298
+ }
299
+ }
300
+ ]
301
+ }
302
+ ```
303
+
304
+ ## DT011 raw-large-numbers
305
+
306
+ Severity: warn. Packs: global, cn.
307
+
308
+ Why: Unformatted large values crowd labels and hide useful scale.
309
+
310
+ Triggers: At data maximum magnitude 10000 or above, axis, visible data label, and tooltip channels need formatters.
311
+
312
+ Fix: format compact units: cn 万/亿; global K/M.
313
+
314
+ Minimal failing input:
315
+
316
+ ```json
317
+ {
318
+ "charts": [
319
+ {
320
+ "option": {
321
+ "series": [
322
+ {
323
+ "data": [10000]
324
+ }
325
+ ],
326
+ "yAxis": {}
327
+ }
328
+ }
329
+ ]
330
+ }
331
+ ```
332
+
333
+ ## DT012 unit-system
334
+
335
+ Severity: warn. Packs: global, cn.
336
+
337
+ Why: Mixed unit systems make comparisons harder to read.
338
+
339
+ Triggers: String formatter templates must use the selected compact unit system.
340
+
341
+ Fix: use cn 万/亿 or global K/M/B consistently.
342
+
343
+ Minimal failing input:
344
+
345
+ ```json
346
+ {
347
+ "charts": [
348
+ {
349
+ "option": {
350
+ "yAxis": {
351
+ "axisLabel": {
352
+ "formatter": "{value}万"
353
+ }
354
+ }
355
+ }
356
+ }
357
+ ]
358
+ }
359
+ ```
360
+
361
+ ## DT013 delta-convention
362
+
363
+ Severity: error. Packs: global, cn.
364
+
365
+ Why: Changing colour conventions reverses the apparent meaning of a delta.
366
+
367
+ Triggers: Directional deltas must follow the page convention and metric polarity: global by polarity; cn red-up by default.
368
+
369
+ Fix: apply one deltaColors convention to the whole page.
370
+
371
+ Minimal failing input:
372
+
373
+ ```json
374
+ {
375
+ "charts": [],
376
+ "deltas": [
377
+ {
378
+ "selector": "#delta",
379
+ "text": "▲ 3%",
380
+ "color": "#B4413A"
381
+ }
382
+ ]
383
+ }
384
+ ```
385
+
386
+ ## DT014 delta-colour-only
387
+
388
+ Severity: warn. Packs: global, cn.
389
+
390
+ Why: Colour alone excludes readers who cannot distinguish the chosen hues.
391
+
392
+ Triggers: Directional colour must be accompanied by an arrow or signed number.
393
+
394
+ Fix: add ▲/▼ or an explicit sign.
395
+
396
+ Minimal failing input:
397
+
398
+ ```json
399
+ {
400
+ "charts": [],
401
+ "deltas": [
402
+ {
403
+ "selector": "#delta",
404
+ "text": "3%",
405
+ "color": "#2F7D4F"
406
+ }
407
+ ]
408
+ }
409
+ ```
410
+
411
+ ## DT015 low-contrast-text
412
+
413
+ Severity: error. Packs: global, cn.
414
+
415
+ Why: Low contrast makes text difficult to read.
416
+
417
+ Triggers: Text needs 4.5:1; at least 24px or 18.66px bold needs 3:1. Chart label defaults use 4.5:1.
418
+
419
+ Fix: increase text contrast against its background.
420
+
421
+ Minimal failing input:
422
+
423
+ ```json
424
+ {
425
+ "charts": [],
426
+ "texts": [
427
+ {
428
+ "selector": "#text",
429
+ "text": "Label",
430
+ "color": "#eeeeee",
431
+ "background": "#ffffff",
432
+ "fontSize": 14,
433
+ "fontWeight": 400
434
+ }
435
+ ]
436
+ }
437
+ ```
438
+
439
+ ## DT016 thin-line-contrast
440
+
441
+ Severity: warn. Packs: global, cn.
442
+
443
+ Why: Thin marks need stronger contrast than large filled regions.
444
+
445
+ Triggers: Line colours need at least 3:1 against the chart background.
446
+
447
+ Fix: darken light-mode lines or lighten dark-mode lines.
448
+
449
+ Minimal failing input:
450
+
451
+ ```json
452
+ {
453
+ "charts": [
454
+ {
455
+ "option": {
456
+ "backgroundColor": "#ffffff",
457
+ "color": ["#eeeeee"],
458
+ "series": [
459
+ {
460
+ "type": "line"
461
+ }
462
+ ]
463
+ }
464
+ }
465
+ ]
466
+ }
467
+ ```
468
+
469
+ ## DT017 gradient-decoration
470
+
471
+ Severity: warn. Packs: global, cn.
472
+
473
+ Why: Decorative gradients add visual structure unrelated to data.
474
+
475
+ Triggers: Card/page gradients and linear or radial chart fills add decoration.
476
+
477
+ Fix: use solid backgrounds and fills.
478
+
479
+ Minimal failing input:
480
+
481
+ ```json
482
+ {
483
+ "charts": [],
484
+ "gradients": [
485
+ {
486
+ "selector": ".card",
487
+ "background": "linear-gradient(red, blue)"
488
+ }
489
+ ]
490
+ }
491
+ ```
492
+
493
+ ## DT018 glow
494
+
495
+ Severity: warn. Packs: global, cn.
496
+
497
+ Why: Glow blurs boundaries and competes with measured values.
498
+
499
+ Triggers: Coloured CSS shadows with blur at least 12px, or chart shadowBlur at least 10, create glow.
500
+
501
+ Fix: remove glow and use hairline borders.
502
+
503
+ Minimal failing input:
504
+
505
+ ```json
506
+ {
507
+ "charts": [],
508
+ "shadows": [
509
+ {
510
+ "selector": ".card",
511
+ "boxShadow": "0px 0px 14px rgb(0, 0, 255)",
512
+ "textShadow": "none"
513
+ }
514
+ ]
515
+ }
516
+ ```
517
+
518
+ ## DT019 kpi-overload
519
+
520
+ Severity: warn. Packs: global, cn.
521
+
522
+ Why: Too many primary values weaken priority.
523
+
524
+ Triggers: More than 6 global or 8 cn KPI tiles weakens priority.
525
+
526
+ Fix: keep only primary KPIs in the first view.
527
+
528
+ Minimal failing input:
529
+
530
+ ```json
531
+ {
532
+ "charts": [],
533
+ "kpis": [
534
+ {
535
+ "selector": ".kpi",
536
+ "text": "22",
537
+ "color": "#111111"
538
+ },
539
+ {
540
+ "selector": ".kpi",
541
+ "text": "22",
542
+ "color": "#111111"
543
+ },
544
+ {
545
+ "selector": ".kpi",
546
+ "text": "22",
547
+ "color": "#111111"
548
+ },
549
+ {
550
+ "selector": ".kpi",
551
+ "text": "22",
552
+ "color": "#111111"
553
+ },
554
+ {
555
+ "selector": ".kpi",
556
+ "text": "22",
557
+ "color": "#111111"
558
+ },
559
+ {
560
+ "selector": ".kpi",
561
+ "text": "22",
562
+ "color": "#111111"
563
+ },
564
+ {
565
+ "selector": ".kpi",
566
+ "text": "22",
567
+ "color": "#111111"
568
+ },
569
+ {
570
+ "selector": ".kpi",
571
+ "text": "22",
572
+ "color": "#111111"
573
+ },
574
+ {
575
+ "selector": ".kpi",
576
+ "text": "22",
577
+ "color": "#111111"
578
+ }
579
+ ]
580
+ }
581
+ ```
582
+
583
+ ## DT020 kpi-rainbow
584
+
585
+ Severity: warn. Packs: global, cn.
586
+
587
+ Why: Competing KPI accents distract from the primary measures.
588
+
589
+ Triggers: Three or more distinct accent colours on KPI values or tiles distract. Neutral greys and pack text/background tokens are excluded.
590
+
591
+ Fix: use ink for values and reserve colour for deltas.
592
+
593
+ Minimal failing input:
594
+
595
+ ```json
596
+ {
597
+ "charts": [],
598
+ "kpis": [
599
+ {
600
+ "selector": ".kpi",
601
+ "text": "22",
602
+ "color": "#ff0000"
603
+ },
604
+ {
605
+ "selector": ".kpi",
606
+ "text": "22",
607
+ "color": "#00ff00"
608
+ },
609
+ {
610
+ "selector": ".kpi",
611
+ "text": "22",
612
+ "color": "#0000ff"
613
+ }
614
+ ]
615
+ }
616
+ ```
617
+
618
+ ## DT021 emoji-icons
619
+
620
+ Severity: warn. Packs: global, cn.
621
+
622
+ Why: Emoji add inconsistent visual emphasis to functional labels.
623
+
624
+ Triggers: Emoji in headings, KPI labels, and rankings add inconsistent iconography.
625
+
626
+ Fix: use plain text and plain numerals for rankings.
627
+
628
+ Minimal failing input:
629
+
630
+ ```json
631
+ {
632
+ "charts": [],
633
+ "emojis": [
634
+ {
635
+ "selector": "h1",
636
+ "text": "\uD83C\uDFC6 Sales"
637
+ }
638
+ ]
639
+ }
640
+ ```
641
+
642
+ ## DT022 cjk-min-size
643
+
644
+ Severity: warn. Packs: cn.
645
+
646
+ Why: Small CJK glyphs lose essential detail.
647
+
648
+ Triggers: CJK text must be at least 12px.
649
+
650
+ Fix: set CJK text to at least 12px.
651
+
652
+ Minimal failing input (cn pack):
653
+
654
+ ```json
655
+ {
656
+ "charts": [],
657
+ "texts": [
658
+ {
659
+ "selector": "#text",
660
+ "text": "销量",
661
+ "color": "#111111",
662
+ "background": "#ffffff",
663
+ "fontSize": 11,
664
+ "fontWeight": 400
665
+ }
666
+ ]
667
+ }
668
+ ```
669
+
670
+ ## DT023 legend-single-series
671
+
672
+ Severity: info. Packs: global, cn.
673
+
674
+ Why: A single-series legend repeats context already available in the title.
675
+
676
+ Triggers: An explicitly visible legend on one series repeats context.
677
+
678
+ Fix: hide the legend or label the series directly.
679
+
680
+ Minimal failing input:
681
+
682
+ ```json
683
+ {
684
+ "charts": [
685
+ {
686
+ "option": {
687
+ "series": [
688
+ {
689
+ "type": "bar"
690
+ }
691
+ ],
692
+ "legend": {}
693
+ }
694
+ }
695
+ ]
696
+ }
697
+ ```
698
+
699
+ ## DT024 bar-width
700
+
701
+ Bars need an explicit width cap of 24px, or 32px on screens.
702
+
703
+ set barMaxWidth to 24 or less; use 32 or less on screens
704
+
705
+ ## DT025 legend-not-direct-label
706
+
707
+ Two to four line series need direct labels instead of a legend.
708
+
709
+ hide the legend and label series at their last point
710
+
711
+ ## DT026 line-weight
712
+
713
+ Line widths outside 1–3px obscure the data.
714
+
715
+ use 2px main lines and 1.5px comparison lines
716
+
717
+ ## DT027 tabular-numerals
718
+
719
+ KPI numbers need tabular numerals.
720
+
721
+ set font-variant-numeric to tabular-nums
722
+
723
+ ## DT028 hero-metric
724
+
725
+ Pages with at least three KPIs need a clear hero metric.
726
+
727
+ make the largest KPI at least 1.6 times the median of the others
728
+
729
+ ## Pack defaults
730
+
731
+ The cn pack uses a warm neutral page, one white sheet, steel data marks, red-up and green-down deltas. Its dark screen uses an almost black page and sky data marks.
732
+ The global pack uses one white surface, ink, one blue accent, and grey context. Its dark mode uses the same hierarchy with a pale accent. Global deltas follow metric polarity.
733
+ Use the numeral faces and exact colour tables in the skill references. Subtle is decorative; the last two cn light categorical fills need direct labels. Comparison lines use muted when other grey cannot meet 3:1.
734
+ Use 400 for body text and 500 for headings and values, at most 600. Use tabular figures everywhere. One hero is 52–64px on desktop or 112–128px on screens, at least 2.2 times the next KPI. Secondary values are 22–26px, or 32–40px on screens.
735
+ Use one sheet, hairlines, 36px table rows, and 4px share bars. Keep screen CJK at least 16px. No donuts on KPI boards, dual axes, helper sentences, gradients, glow, or in-flow shadows.