roast-my-design-system 3.6.0 → 3.7.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/README.md CHANGED
@@ -24,11 +24,11 @@ Your AI agent (Claude, Cursor, Copilot) builds UI by imitating what's already in
24
24
 
25
25
  The full report for vercel/ai-chatbot, top to bottom:
26
26
 
27
- ![The full diagnosis report for vercel/ai-chatbot in dark mode: health score, three-yardstick tiles, palette forensics, spacing receipts, typography, offenders, duplicates, and the where-to-start close](https://raw.githubusercontent.com/pencilrebel/roast-my-design-system/main/assets/report-full-dark.png?v=3.6.0)
27
+ ![The full diagnosis report for vercel/ai-chatbot in dark mode: health score, three-yardstick tiles, palette forensics, spacing receipts, typography, offenders, duplicates, and the where-to-start close](https://raw.githubusercontent.com/pencilrebel/roast-my-design-system/main/assets/report-full-dark.png?v=3.7.0)
28
28
 
29
29
  The same report in light mode (one file, built-in toggle):
30
30
 
31
- ![The diagnosis report in light mode](https://raw.githubusercontent.com/pencilrebel/roast-my-design-system/main/assets/report-light-hero.png?v=3.6.0)
31
+ ![The diagnosis report in light mode](https://raw.githubusercontent.com/pencilrebel/roast-my-design-system/main/assets/report-light-hero.png?v=3.7.0)
32
32
 
33
33
  ## What makes the numbers trustworthy
34
34
 
@@ -120,6 +120,9 @@ Three real roasts of public repos, hosted as-is (the same self-contained HTML th
120
120
  | Duplicated components | 0 | 14 | 10 |
121
121
  | Inline style blocks | 0 | 42 | 12 |
122
122
  | Arbitrary Tailwind values | ~20 | 91 | 0 |
123
+ | Near-identical colour pairs | 0 | 2 | 1 |
124
+ | !important declarations | 0 | 5 | 3 |
125
+ | Components never imported | 0 | 0 | 0 |
123
126
 
124
127
  Yes, the median repo is already a mess. That's the point.
125
128
 
@@ -127,4 +130,4 @@ Yes, the median repo is already a mess. That's the point.
127
130
 
128
131
  MIT
129
132
 
130
- Built and designed by <a href="https://gregkozakiewicz.com"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/pencilrebel/roast-my-design-system/main/assets/gk-mark-dark.png?v=3.6.0"><img src="https://raw.githubusercontent.com/pencilrebel/roast-my-design-system/main/assets/gk-mark.png?v=3.6.0" height="15" alt="GK mark"></picture> Greg Kozakiewicz</a>.
133
+ Built and designed by <a href="https://gregkozakiewicz.com"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/pencilrebel/roast-my-design-system/main/assets/gk-mark-dark.png?v=3.7.0"><img src="https://raw.githubusercontent.com/pencilrebel/roast-my-design-system/main/assets/gk-mark.png?v=3.7.0" height="15" alt="GK mark"></picture> Greg Kozakiewicz</a>.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roast-my-design-system",
3
- "version": "3.6.0",
3
+ "version": "3.7.0",
4
4
  "description": "Roast your design system with real data. A deterministic, zero-dependency scanner that counts every colour, grey, spacing value, typeface, duplicated component, inline style and arbitrary Tailwind value in your repo, scores it 0-100 against Ideal Design System norms and a 30-repo benchmark, generates a shareable HTML diagnosis, and with --rules writes the agent rules file (CLAUDE.md, .cursor/rules) that stops the mess coming back.",
5
5
  "keywords": [
6
6
  "design-system",
@@ -1,5 +1,5 @@
1
1
  {
2
- "builtAt": "2026-08-01T17:38:24.498Z",
2
+ "builtAt": "2026-08-03T16:33:32.652Z",
3
3
  "repoCount": 30,
4
4
  "ideal2026": {
5
5
  "colors": {
@@ -38,6 +38,18 @@
38
38
  "value": 0,
39
39
  "note": "styling belongs to the system"
40
40
  },
41
+ "nearPairs": {
42
+ "value": 0,
43
+ "note": "near-identical colours are copy-paste, not decisions"
44
+ },
45
+ "important": {
46
+ "value": 0,
47
+ "note": "!important is the cascade admitting defeat"
48
+ },
49
+ "neverImported": {
50
+ "value": 0,
51
+ "note": "a system component nobody imports is dead weight"
52
+ },
41
53
  "arbitrary": {
42
54
  "value": 20,
43
55
  "note": "bracket escape hatches; a handful of deliberate exceptions"
@@ -104,7 +116,7 @@
104
116
  174,
105
117
  206,
106
118
  246,
107
- 302,
119
+ 303,
108
120
  385,
109
121
  403,
110
122
  1645
@@ -112,7 +124,7 @@
112
124
  "p25": 38,
113
125
  "median": 96,
114
126
  "p75": 170,
115
- "p90": 310
127
+ "p90": 311
116
128
  },
117
129
  "greys": {
118
130
  "values": [
@@ -373,7 +385,7 @@
373
385
  95,
374
386
  103,
375
387
  115,
376
- 187
388
+ 189
377
389
  ],
378
390
  "p25": 1,
379
391
  "median": 14,
@@ -397,7 +409,7 @@
397
409
  23,
398
410
  37,
399
411
  41,
400
- 42,
412
+ 43,
401
413
  46,
402
414
  47,
403
415
  51,
@@ -411,7 +423,7 @@
411
423
  122,
412
424
  173,
413
425
  1178,
414
- 2914
426
+ 2923
415
427
  ],
416
428
  "p25": 8,
417
429
  "median": 42,
@@ -443,7 +455,7 @@
443
455
  234,
444
456
  271,
445
457
  321,
446
- 374,
458
+ 380,
447
459
  407,
448
460
  468,
449
461
  470,
@@ -456,6 +468,120 @@
456
468
  "p75": 309,
457
469
  "p90": 472
458
470
  },
471
+ "nearPairs": {
472
+ "values": [
473
+ 0,
474
+ 0,
475
+ 0,
476
+ 0,
477
+ 0,
478
+ 0,
479
+ 0,
480
+ 0,
481
+ 0,
482
+ 0,
483
+ 0,
484
+ 1,
485
+ 1,
486
+ 2,
487
+ 2,
488
+ 2,
489
+ 9,
490
+ 12,
491
+ 13,
492
+ 13,
493
+ 15,
494
+ 16,
495
+ 23,
496
+ 26,
497
+ 30,
498
+ 37,
499
+ 47,
500
+ 49,
501
+ 70,
502
+ 146
503
+ ],
504
+ "p25": 0,
505
+ "median": 2,
506
+ "p75": 21,
507
+ "p90": 47
508
+ },
509
+ "important": {
510
+ "values": [
511
+ 0,
512
+ 0,
513
+ 0,
514
+ 0,
515
+ 0,
516
+ 0,
517
+ 0,
518
+ 0,
519
+ 0,
520
+ 0,
521
+ 0,
522
+ 1,
523
+ 1,
524
+ 2,
525
+ 4,
526
+ 5,
527
+ 5,
528
+ 6,
529
+ 9,
530
+ 9,
531
+ 11,
532
+ 12,
533
+ 20,
534
+ 35,
535
+ 42,
536
+ 42,
537
+ 74,
538
+ 83,
539
+ 88,
540
+ 161
541
+ ],
542
+ "p25": 0,
543
+ "median": 5,
544
+ "p75": 18,
545
+ "p90": 75
546
+ },
547
+ "neverImported": {
548
+ "values": [
549
+ 0,
550
+ 0,
551
+ 0,
552
+ 0,
553
+ 0,
554
+ 0,
555
+ 0,
556
+ 0,
557
+ 0,
558
+ 0,
559
+ 0,
560
+ 0,
561
+ 0,
562
+ 0,
563
+ 0,
564
+ 0,
565
+ 0,
566
+ 0,
567
+ 5,
568
+ 7,
569
+ 10,
570
+ 13,
571
+ 22,
572
+ 22,
573
+ 26,
574
+ 36,
575
+ 39,
576
+ 45,
577
+ 94,
578
+ 100
579
+ ],
580
+ "p25": 0,
581
+ "median": 0,
582
+ "p75": 20,
583
+ "p90": 40
584
+ },
459
585
  "components": {
460
586
  "values": [
461
587
  9,
@@ -471,27 +597,27 @@
471
597
  342,
472
598
  350,
473
599
  417,
474
- 546,
475
- 629,
600
+ 555,
601
+ 637,
476
602
  777,
477
- 790,
478
- 834,
603
+ 796,
604
+ 836,
479
605
  936,
480
606
  940,
481
- 961,
607
+ 962,
482
608
  970,
483
- 1043,
609
+ 1052,
484
610
  1081,
485
611
  1216,
486
612
  1379,
487
613
  1414,
488
614
  1619,
489
- 2133,
490
- 2549
615
+ 2134,
616
+ 2566
491
617
  ],
492
618
  "p25": 233,
493
- "median": 703,
494
- "p75": 1025,
619
+ "median": 707,
620
+ "p75": 1032,
495
621
  "p90": 1435
496
622
  }
497
623
  },
@@ -511,6 +637,9 @@
511
637
  "exactDuplicates": 115,
512
638
  "inlineStyles": 79,
513
639
  "arbitrary": 321,
640
+ "nearPairs": 47,
641
+ "important": 161,
642
+ "neverImported": 39,
514
643
  "components": 1414
515
644
  }
516
645
  },
@@ -529,6 +658,9 @@
529
658
  "exactDuplicates": 3,
530
659
  "inlineStyles": 82,
531
660
  "arbitrary": 0,
661
+ "nearPairs": 49,
662
+ "important": 88,
663
+ "neverImported": 0,
532
664
  "components": 342
533
665
  }
534
666
  },
@@ -547,13 +679,16 @@
547
679
  "exactDuplicates": 4,
548
680
  "inlineStyles": 23,
549
681
  "arbitrary": 0,
682
+ "nearPairs": 37,
683
+ "important": 4,
684
+ "neverImported": 0,
550
685
  "components": 50
551
686
  }
552
687
  },
553
688
  {
554
689
  "repo": "outline/outline",
555
690
  "designSystem": "custom",
556
- "codeFiles": 2100,
691
+ "codeFiles": 2122,
557
692
  "metrics": {
558
693
  "colors": 89,
559
694
  "greys": 15,
@@ -565,7 +700,10 @@
565
700
  "exactDuplicates": 23,
566
701
  "inlineStyles": 76,
567
702
  "arbitrary": 0,
568
- "components": 629
703
+ "nearPairs": 1,
704
+ "important": 0,
705
+ "neverImported": 0,
706
+ "components": 637
569
707
  }
570
708
  },
571
709
  {
@@ -583,6 +721,9 @@
583
721
  "exactDuplicates": 2,
584
722
  "inlineStyles": 0,
585
723
  "arbitrary": 4,
724
+ "nearPairs": 0,
725
+ "important": 5,
726
+ "neverImported": 0,
586
727
  "components": 220
587
728
  }
588
729
  },
@@ -601,6 +742,9 @@
601
742
  "exactDuplicates": 0,
602
743
  "inlineStyles": 0,
603
744
  "arbitrary": 0,
745
+ "nearPairs": 0,
746
+ "important": 0,
747
+ "neverImported": 0,
604
748
  "components": 25
605
749
  }
606
750
  },
@@ -619,6 +763,9 @@
619
763
  "exactDuplicates": 0,
620
764
  "inlineStyles": 0,
621
765
  "arbitrary": 60,
766
+ "nearPairs": 0,
767
+ "important": 1,
768
+ "neverImported": 94,
622
769
  "components": 182
623
770
  }
624
771
  },
@@ -637,6 +784,9 @@
637
784
  "exactDuplicates": 0,
638
785
  "inlineStyles": 0,
639
786
  "arbitrary": 0,
787
+ "nearPairs": 0,
788
+ "important": 0,
789
+ "neverImported": 0,
640
790
  "components": 9
641
791
  }
642
792
  },
@@ -655,6 +805,9 @@
655
805
  "exactDuplicates": 1,
656
806
  "inlineStyles": 2,
657
807
  "arbitrary": 16,
808
+ "nearPairs": 0,
809
+ "important": 2,
810
+ "neverImported": 0,
658
811
  "components": 60
659
812
  }
660
813
  },
@@ -673,6 +826,9 @@
673
826
  "exactDuplicates": 1,
674
827
  "inlineStyles": 7,
675
828
  "arbitrary": 103,
829
+ "nearPairs": 0,
830
+ "important": 9,
831
+ "neverImported": 0,
676
832
  "components": 315
677
833
  }
678
834
  },
@@ -691,13 +847,16 @@
691
847
  "exactDuplicates": 1,
692
848
  "inlineStyles": 11,
693
849
  "arbitrary": 148,
850
+ "nearPairs": 0,
851
+ "important": 0,
852
+ "neverImported": 100,
694
853
  "components": 272
695
854
  }
696
855
  },
697
856
  {
698
857
  "repo": "documenso/documenso",
699
858
  "designSystem": "custom",
700
- "codeFiles": 1906,
859
+ "codeFiles": 1912,
701
860
  "metrics": {
702
861
  "colors": 36,
703
862
  "greys": 13,
@@ -709,13 +868,16 @@
709
868
  "exactDuplicates": 17,
710
869
  "inlineStyles": 69,
711
870
  "arbitrary": 234,
712
- "components": 790
871
+ "nearPairs": 15,
872
+ "important": 0,
873
+ "neverImported": 45,
874
+ "components": 796
713
875
  }
714
876
  },
715
877
  {
716
878
  "repo": "dubinc/dub",
717
879
  "designSystem": "custom",
718
- "codeFiles": 3523,
880
+ "codeFiles": 3526,
719
881
  "metrics": {
720
882
  "colors": 102,
721
883
  "greys": 14,
@@ -727,6 +889,9 @@
727
889
  "exactDuplicates": 17,
728
890
  "inlineStyles": 47,
729
891
  "arbitrary": 470,
892
+ "nearPairs": 13,
893
+ "important": 1,
894
+ "neverImported": 22,
730
895
  "components": 1379
731
896
  }
732
897
  },
@@ -745,6 +910,9 @@
745
910
  "exactDuplicates": 59,
746
911
  "inlineStyles": 18,
747
912
  "arbitrary": 164,
913
+ "nearPairs": 13,
914
+ "important": 83,
915
+ "neverImported": 0,
748
916
  "components": 940
749
917
  }
750
918
  },
@@ -763,13 +931,16 @@
763
931
  "exactDuplicates": 92,
764
932
  "inlineStyles": 119,
765
933
  "arbitrary": 164,
934
+ "nearPairs": 0,
935
+ "important": 0,
936
+ "neverImported": 5,
766
937
  "components": 1081
767
938
  }
768
939
  },
769
940
  {
770
941
  "repo": "lukevella/rallly",
771
942
  "designSystem": "custom",
772
- "codeFiles": 689,
943
+ "codeFiles": 692,
773
944
  "metrics": {
774
945
  "colors": 26,
775
946
  "greys": 4,
@@ -781,7 +952,10 @@
781
952
  "exactDuplicates": 10,
782
953
  "inlineStyles": 23,
783
954
  "arbitrary": 27,
784
- "components": 546
955
+ "nearPairs": 2,
956
+ "important": 0,
957
+ "neverImported": 22,
958
+ "components": 555
785
959
  }
786
960
  },
787
961
  {
@@ -799,13 +973,16 @@
799
973
  "exactDuplicates": 20,
800
974
  "inlineStyles": 46,
801
975
  "arbitrary": 407,
976
+ "nearPairs": 0,
977
+ "important": 42,
978
+ "neverImported": 13,
802
979
  "components": 777
803
980
  }
804
981
  },
805
982
  {
806
983
  "repo": "elie222/inbox-zero",
807
984
  "designSystem": "custom",
808
- "codeFiles": 1808,
985
+ "codeFiles": 1821,
809
986
  "metrics": {
810
987
  "colors": 154,
811
988
  "greys": 24,
@@ -817,7 +994,10 @@
817
994
  "exactDuplicates": 21,
818
995
  "inlineStyles": 5,
819
996
  "arbitrary": 125,
820
- "components": 834
997
+ "nearPairs": 26,
998
+ "important": 6,
999
+ "neverImported": 0,
1000
+ "components": 836
821
1001
  }
822
1002
  },
823
1003
  {
@@ -835,6 +1015,9 @@
835
1015
  "exactDuplicates": 7,
836
1016
  "inlineStyles": 41,
837
1017
  "arbitrary": 78,
1018
+ "nearPairs": 0,
1019
+ "important": 0,
1020
+ "neverImported": 0,
838
1021
  "components": 190
839
1022
  }
840
1023
  },
@@ -853,13 +1036,16 @@
853
1036
  "exactDuplicates": 0,
854
1037
  "inlineStyles": 173,
855
1038
  "arbitrary": 1,
1039
+ "nearPairs": 2,
1040
+ "important": 5,
1041
+ "neverImported": 0,
856
1042
  "components": 417
857
1043
  }
858
1044
  },
859
1045
  {
860
1046
  "repo": "twentyhq/twenty",
861
1047
  "designSystem": "library",
862
- "codeFiles": 15125,
1048
+ "codeFiles": 15132,
863
1049
  "metrics": {
864
1050
  "colors": 1645,
865
1051
  "greys": 100,
@@ -871,13 +1057,16 @@
871
1057
  "exactDuplicates": 22,
872
1058
  "inlineStyles": 122,
873
1059
  "arbitrary": 0,
874
- "components": 2133
1060
+ "nearPairs": 146,
1061
+ "important": 9,
1062
+ "neverImported": 0,
1063
+ "components": 2134
875
1064
  }
876
1065
  },
877
1066
  {
878
1067
  "repo": "langfuse/langfuse",
879
1068
  "designSystem": "custom",
880
- "codeFiles": 2758,
1069
+ "codeFiles": 2774,
881
1070
  "metrics": {
882
1071
  "colors": 170,
883
1072
  "greys": 4,
@@ -887,9 +1076,12 @@
887
1076
  "radii": 10,
888
1077
  "shadows": 0,
889
1078
  "exactDuplicates": 7,
890
- "inlineStyles": 42,
891
- "arbitrary": 374,
892
- "components": 1043
1079
+ "inlineStyles": 43,
1080
+ "arbitrary": 380,
1081
+ "nearPairs": 2,
1082
+ "important": 12,
1083
+ "neverImported": 10,
1084
+ "components": 1052
893
1085
  }
894
1086
  },
895
1087
  {
@@ -907,6 +1099,9 @@
907
1099
  "exactDuplicates": 45,
908
1100
  "inlineStyles": 37,
909
1101
  "arbitrary": 468,
1102
+ "nearPairs": 70,
1103
+ "important": 42,
1104
+ "neverImported": 0,
910
1105
  "components": 1216
911
1106
  }
912
1107
  },
@@ -925,6 +1120,9 @@
925
1120
  "exactDuplicates": 95,
926
1121
  "inlineStyles": 22,
927
1122
  "arbitrary": 561,
1123
+ "nearPairs": 16,
1124
+ "important": 74,
1125
+ "neverImported": 7,
928
1126
  "components": 1619
929
1127
  }
930
1128
  },
@@ -943,31 +1141,37 @@
943
1141
  "exactDuplicates": 19,
944
1142
  "inlineStyles": 1178,
945
1143
  "arbitrary": 0,
1144
+ "nearPairs": 23,
1145
+ "important": 35,
1146
+ "neverImported": 36,
946
1147
  "components": 350
947
1148
  }
948
1149
  },
949
1150
  {
950
1151
  "repo": "lobehub/lobe-chat",
951
1152
  "designSystem": "library",
952
- "codeFiles": 8299,
1153
+ "codeFiles": 8344,
953
1154
  "metrics": {
954
- "colors": 302,
1155
+ "colors": 303,
955
1156
  "greys": 37,
956
1157
  "spacing": 61,
957
1158
  "typefaces": 1,
958
1159
  "fontSizes": 2,
959
1160
  "radii": 1,
960
1161
  "shadows": 0,
961
- "exactDuplicates": 187,
962
- "inlineStyles": 2914,
1162
+ "exactDuplicates": 189,
1163
+ "inlineStyles": 2923,
963
1164
  "arbitrary": 0,
964
- "components": 2549
1165
+ "nearPairs": 9,
1166
+ "important": 0,
1167
+ "neverImported": 0,
1168
+ "components": 2566
965
1169
  }
966
1170
  },
967
1171
  {
968
1172
  "repo": "danny-avila/LibreChat",
969
1173
  "designSystem": "custom",
970
- "codeFiles": 2424,
1174
+ "codeFiles": 2430,
971
1175
  "metrics": {
972
1176
  "colors": 174,
973
1177
  "greys": 30,
@@ -979,7 +1183,10 @@
979
1183
  "exactDuplicates": 38,
980
1184
  "inlineStyles": 71,
981
1185
  "arbitrary": 492,
982
- "components": 961
1186
+ "nearPairs": 30,
1187
+ "important": 20,
1188
+ "neverImported": 0,
1189
+ "components": 962
983
1190
  }
984
1191
  },
985
1192
  {
@@ -997,13 +1204,16 @@
997
1204
  "exactDuplicates": 103,
998
1205
  "inlineStyles": 117,
999
1206
  "arbitrary": 1751,
1207
+ "nearPairs": 12,
1208
+ "important": 0,
1209
+ "neverImported": 26,
1000
1210
  "components": 970
1001
1211
  }
1002
1212
  },
1003
1213
  {
1004
1214
  "repo": "triggerdotdev/trigger.dev",
1005
1215
  "designSystem": "custom",
1006
- "codeFiles": 2354,
1216
+ "codeFiles": 2366,
1007
1217
  "metrics": {
1008
1218
  "colors": 137,
1009
1219
  "greys": 18,
@@ -1015,6 +1225,9 @@
1015
1225
  "exactDuplicates": 8,
1016
1226
  "inlineStyles": 51,
1017
1227
  "arbitrary": 271,
1228
+ "nearPairs": 1,
1229
+ "important": 11,
1230
+ "neverImported": 0,
1018
1231
  "components": 936
1019
1232
  }
1020
1233
  },
@@ -1033,12 +1246,15 @@
1033
1246
  "exactDuplicates": 0,
1034
1247
  "inlineStyles": 0,
1035
1248
  "arbitrary": 26,
1249
+ "nearPairs": 0,
1250
+ "important": 0,
1251
+ "neverImported": 0,
1036
1252
  "components": 36
1037
1253
  }
1038
1254
  }
1039
1255
  ],
1040
1256
  "referenceSystems": {
1041
- "builtAt": "2026-08-01T17:38:26.401Z",
1257
+ "builtAt": "2026-08-03T16:33:34.617Z",
1042
1258
  "count": 10,
1043
1259
  "note": "Curated scoped scans of reputable public design systems. Not comparable head-to-head with product repos: see per-system notes.",
1044
1260
  "stats": {
@@ -1192,6 +1408,51 @@
1192
1408
  ],
1193
1409
  "median": 0
1194
1410
  },
1411
+ "nearPairs": {
1412
+ "values": [
1413
+ 0,
1414
+ 0,
1415
+ 0,
1416
+ 0,
1417
+ 1,
1418
+ 1,
1419
+ 1,
1420
+ 5,
1421
+ 33,
1422
+ 50
1423
+ ],
1424
+ "median": 1
1425
+ },
1426
+ "important": {
1427
+ "values": [
1428
+ 0,
1429
+ 0,
1430
+ 0,
1431
+ 1,
1432
+ 1,
1433
+ 4,
1434
+ 4,
1435
+ 30,
1436
+ 33,
1437
+ 61
1438
+ ],
1439
+ "median": 3
1440
+ },
1441
+ "neverImported": {
1442
+ "values": [
1443
+ 0,
1444
+ 0,
1445
+ 0,
1446
+ 0,
1447
+ 0,
1448
+ 0,
1449
+ 0,
1450
+ 0,
1451
+ 0,
1452
+ 0
1453
+ ],
1454
+ "median": 0
1455
+ },
1195
1456
  "components": {
1196
1457
  "values": [
1197
1458
  0,
@@ -1226,6 +1487,9 @@
1226
1487
  "exactDuplicates": 0,
1227
1488
  "inlineStyles": 1,
1228
1489
  "arbitrary": 35,
1490
+ "nearPairs": 0,
1491
+ "important": 0,
1492
+ "neverImported": 0,
1229
1493
  "components": 331
1230
1494
  }
1231
1495
  },
@@ -1246,6 +1510,9 @@
1246
1510
  "exactDuplicates": 13,
1247
1511
  "inlineStyles": 14,
1248
1512
  "arbitrary": 0,
1513
+ "nearPairs": 5,
1514
+ "important": 33,
1515
+ "neverImported": 0,
1249
1516
  "components": 228
1250
1517
  }
1251
1518
  },
@@ -1266,6 +1533,9 @@
1266
1533
  "exactDuplicates": 15,
1267
1534
  "inlineStyles": 10,
1268
1535
  "arbitrary": 0,
1536
+ "nearPairs": 0,
1537
+ "important": 30,
1538
+ "neverImported": 0,
1269
1539
  "components": 243
1270
1540
  }
1271
1541
  },
@@ -1286,6 +1556,9 @@
1286
1556
  "exactDuplicates": 6,
1287
1557
  "inlineStyles": 17,
1288
1558
  "arbitrary": 0,
1559
+ "nearPairs": 1,
1560
+ "important": 4,
1561
+ "neverImported": 0,
1289
1562
  "components": 314
1290
1563
  }
1291
1564
  },
@@ -1306,6 +1579,9 @@
1306
1579
  "exactDuplicates": 2,
1307
1580
  "inlineStyles": 10,
1308
1581
  "arbitrary": 0,
1582
+ "nearPairs": 50,
1583
+ "important": 0,
1584
+ "neverImported": 0,
1309
1585
  "components": 170
1310
1586
  }
1311
1587
  },
@@ -1326,6 +1602,9 @@
1326
1602
  "exactDuplicates": 0,
1327
1603
  "inlineStyles": 1,
1328
1604
  "arbitrary": 0,
1605
+ "nearPairs": 1,
1606
+ "important": 0,
1607
+ "neverImported": 0,
1329
1608
  "components": 711
1330
1609
  }
1331
1610
  },
@@ -1346,6 +1625,9 @@
1346
1625
  "exactDuplicates": 17,
1347
1626
  "inlineStyles": 24,
1348
1627
  "arbitrary": 0,
1628
+ "nearPairs": 1,
1629
+ "important": 1,
1630
+ "neverImported": 0,
1349
1631
  "components": 246
1350
1632
  }
1351
1633
  },
@@ -1366,6 +1648,9 @@
1366
1648
  "exactDuplicates": 0,
1367
1649
  "inlineStyles": 0,
1368
1650
  "arbitrary": 0,
1651
+ "nearPairs": 33,
1652
+ "important": 61,
1653
+ "neverImported": 0,
1369
1654
  "components": 0
1370
1655
  }
1371
1656
  },
@@ -1386,6 +1671,9 @@
1386
1671
  "exactDuplicates": 170,
1387
1672
  "inlineStyles": 14,
1388
1673
  "arbitrary": 0,
1674
+ "nearPairs": 0,
1675
+ "important": 1,
1676
+ "neverImported": 0,
1389
1677
  "components": 819
1390
1678
  }
1391
1679
  },
@@ -1406,6 +1694,9 @@
1406
1694
  "exactDuplicates": 28,
1407
1695
  "inlineStyles": 22,
1408
1696
  "arbitrary": 0,
1697
+ "nearPairs": 0,
1698
+ "important": 4,
1699
+ "neverImported": 0,
1409
1700
  "components": 508
1410
1701
  }
1411
1702
  }
@@ -13,6 +13,7 @@ import { fileURLToPath } from 'node:url';
13
13
  import { distinctTypefaces } from '../lib/typefaces.mjs';
14
14
  import { rulesMarkdown } from '../rules/build.mjs';
15
15
  import { nearColorPairs } from '../lib/nearpairs.mjs';
16
+ import { neverImportedComponents } from '../lib/neverimported.mjs';
16
17
 
17
18
  // The benchmark (Ideal-2026 norms + scanned-repo stats) ships next to the
18
19
  // code so the page works offline; degrade gracefully when absent.
@@ -35,7 +36,7 @@ const GK_MASK = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqa
35
36
  const GK_MARK = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAACXBIWXMAAAAAAAAAAQCEeRdzAAACPklEQVR4nOVWTYh5URR/PlJYEKV8LKwkZalsmI2dZmMnK8pKmaZ87JSanexEkayUhSxsZyfJNBQ7UYqVhSShfM+v/6mXmtU81+p/Frfze+/d3++ce88993Hcf2cikehZ1GazWafTPUVDLBZjLBQKnU6HMTVvcrl8Op3ebrdsNgsokUiYUVP4Pp/v9s96vd79cwZGK95sNkkgGAwC2u12lhrY3u12C/b1ei2TyV5eXjabjV6vZ6BBa51MJin8arUKWKlU4H99fSkUCo5JUQ2HQxJwuVzIYLVawcdIVSs8CQoNpMQ+Go0Aw+EwwXw+D+h2u4Vr0LRSqUSMqVQKsN1uE3Q4HEql8ng8BgIBTnDhqlSqxWIBuvP5bDAYLBbL5XIBHAwGeBuJRODvdjun0/nnPOjraDRK8TYaDcBMJkMwFosBfn9/E0wkEn9OgjYgnU6fTidQwAGcTCbw9/u9VqvFUYB/vV5RskajkRNcTiaTKR6PI9j5fE7xlstlPM/lcgTr9ToneJ/5oOB4PB70O8Tb7/f9fv9sNqPkXl9fhQsQ9f1ktVqNvUVCxA6ZRxsf5pMAxnsuVBSOwvv7+6Phk0PsBNn0OOLC9haLReqdvBL/wUNKNPnt7Q0LjeVutVqhUEij0TwU9W8BVA4JUEUul8tarcZGhpbIarWiSeA0HQ4Hkvn4+GB56VMvov6DA+z1eplR89btdsE+Ho9tNhv3jOv+8/MTFz06M3ypVMqMnTfcwPhn4djG/tue+Mf4RGom9gMt6lAx16huIwAAAABJRU5ErkJggg==';
36
37
 
37
38
  // Shown in the report footer; keep in step with plugin.json when releasing.
38
- const VERSION = '3.6.0';
39
+ const VERSION = '3.7.0';
39
40
 
40
41
  // Two shipped skins, same layout: 'dark' (navy glass, mint accent) and
41
42
  // 'light' (lilac wash, white glass, violet accent). --theme picks one.
@@ -157,16 +158,7 @@ const important = h.tokens.important ?? { count: 0, files: [] };
157
158
 
158
159
  const nearPairs = nearColorPairs(colors);
159
160
 
160
- // Components defined but never imported — scoped to the design-system
161
- // directory, where "nobody imports this" is meaningful. File-local
162
- // subcomponents elsewhere legitimately never get imported by name.
163
- const dsDirRe = h.profile?.uiDir
164
- ? new RegExp(`^${h.profile.uiDir.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/`)
165
- : /(^|\/)(packages\/ui|design-system|ui-kit)\//;
166
- const usedFiles = new Set(reusable.filter((c) => c.usageCount > 0).map((c) => c.file));
167
- const neverImported = reusable.filter((c) => !c.usageCount && dsDirRe.test(c.file)
168
- && !/(^|\/)icons?\//.test(c.file) // glyph sets are deliberately complete
169
- && !usedFiles.has(c.file)); // compound API: an adopted file's sub-exports are surface, not dead
161
+ const neverImported = neverImportedComponents(h.components, h.profile?.uiDir);
170
162
 
171
163
  // Benchmark helpers: for a metric value, where does this repo sit among the
172
164
  // scanned fleet? ("more colours than 90% of scanned repos")
@@ -254,8 +246,8 @@ if (bench && findings.length) {
254
246
  // Where the median is missing or sits at/below ideal, warn caps at 1.5x ideal.
255
247
  // Zero-ideal metrics (duplicates, inline styles): 0 is good; a small tolerance
256
248
  // is warn; beyond that bad.
257
- const ZERO_IDEAL = new Set(['exactDuplicates', 'inlineStyles']);
258
- const WARN_TOLERANCE = { exactDuplicates: 2, inlineStyles: 10 };
249
+ const ZERO_IDEAL = new Set(['exactDuplicates', 'inlineStyles', 'nearPairs', 'important', 'neverImported']);
250
+ const WARN_TOLERANCE = { exactDuplicates: 2, inlineStyles: 10, nearPairs: 2, important: 5, neverImported: 2 };
259
251
  // Absence rule also skipped for arbitrary values: zero brackets is discipline
260
252
  // (nine of ten reputable systems sit at 0, including Tailwind-native shadcn/ui).
261
253
  const NO_ABSENCE_RULE = new Set(['colors', 'greys', 'arbitrary']);
@@ -308,6 +300,9 @@ const bigStats = [
308
300
  tile(spacingTotal, 'off-scale spacing values', 'spacing', 'a dozen deliberate exceptions'),
309
301
  tile(hardDupes.length, 'duplicated components', 'exactDuplicates', 'should be 0'),
310
302
  tile(inline.count, 'inline style blocks', 'inlineStyles', 'invisible to any system'),
303
+ tile(nearPairs.length, 'near-identical colour pairs', 'nearPairs', 'copy-paste, not decisions'),
304
+ tile(important.count, '!important declarations', 'important', 'the cascade admitting defeat'),
305
+ tile(neverImported.length, 'components never imported', 'neverImported', 'the system nobody found'),
311
306
  tile(arbitraryCount, 'arbitrary bracket values', 'arbitrary', 'a handful of deliberate exceptions'),
312
307
  ];
313
308
 
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Design-system components defined but never imported — scoped so the claim
3
+ * is honest. File-local subcomponents elsewhere legitimately never get
4
+ * imported by name; icon sets are deliberately complete; sub-exports of an
5
+ * adopted file are API surface, not dead code. Shared by diagnose, rules and
6
+ * the benchmark builder.
7
+ */
8
+ export function neverImportedComponents(components, uiDir) {
9
+ const reusable = (components ?? []).filter((c) => !c.isPage);
10
+ const dsDirRe = uiDir
11
+ ? new RegExp(`^${uiDir.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/`)
12
+ : /(^|\/)(packages\/ui|design-system|ui-kit)\//;
13
+ const usedFiles = new Set(reusable.filter((c) => c.usageCount > 0).map((c) => c.file));
14
+ return reusable.filter((c) => !c.usageCount && dsDirRe.test(c.file)
15
+ && !/(^|\/)icons?\//.test(c.file)
16
+ && !usedFiles.has(c.file));
17
+ }
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { distinctTypefaces } from '../lib/typefaces.mjs';
8
8
  import { nearColorPairs } from './../lib/nearpairs.mjs';
9
+ import { neverImportedComponents } from '../lib/neverimported.mjs';
9
10
 
10
11
  export function rulesMarkdown(h) {
11
12
  const repoName = h.profile?.name ?? 'this repo';
@@ -88,13 +89,7 @@ const repoName = h.profile?.name ?? 'this repo';
88
89
  }
89
90
 
90
91
  // ---------- never-imported components ----------
91
- const dsDirRe = h.profile?.uiDir
92
- ? new RegExp(`^${h.profile.uiDir.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}/`)
93
- : /(^|\/)(packages\/ui|design-system|ui-kit)\//;
94
- const usedFiles = new Set(reusable.filter((c) => c.usageCount > 0).map((c) => c.file));
95
- const neverImported = reusable.filter((c) => !c.usageCount && dsDirRe.test(c.file)
96
- && !/(^|\/)icons?\//.test(c.file) // glyph sets are deliberately complete
97
- && !usedFiles.has(c.file)); // compound API: an adopted file's sub-exports are surface, not dead
92
+ const neverImported = neverImportedComponents(h.components, h.profile?.uiDir);
98
93
  if (neverImported.length >= 3) {
99
94
  section('Components nobody imports');
100
95
  rule(`${neverImported.length} components are defined but never imported (${neverImported.slice(0, 3).map((c) => `\`<${c.name}>\``).join(', ')}…). Before writing any new component, check this list first; adopt one or flag it for deletion instead of adding another.`);