@radicool/throughline 0.23.0 → 0.25.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 (71) hide show
  1. package/README.md +7 -5
  2. package/adapters/codex/AGENTS.md +1 -0
  3. package/adapters/codex/prompts/check-figma.md +41 -0
  4. package/adapters/codex/prompts/component-builder.md +8 -4
  5. package/adapters/cursor/.cursor/commands/check-figma.md +41 -0
  6. package/adapters/cursor/.cursor/rules/component-builder.mdc +8 -4
  7. package/adapters/generic/AGENTS.md +1 -0
  8. package/adapters/generic/commands/check-figma.md +41 -0
  9. package/adapters/generic/skills/component-builder/SKILL.md +8 -4
  10. package/package.json +1 -1
  11. package/references/figma-adherence.md +442 -0
  12. package/references/figma-checks.md +432 -40
  13. package/references/figma-component-standards.md +8 -0
  14. package/references/proof-bundle.md +1 -0
  15. package/scripts/README.md +7 -4
  16. package/scripts/build-figma-checks.mjs +139 -28
  17. package/scripts/install.mjs +5 -1
  18. package/scripts/lib/contrast.mjs +5 -2
  19. package/scripts/lib/figma-adherence-read.figma.js +207 -0
  20. package/scripts/lib/figma-adherence.mjs +146 -0
  21. package/scripts/lib/figma-checks-read.figma.js +68 -14
  22. package/scripts/lib/figma-checks.mjs +173 -17
  23. package/scripts/lib/figma-variables-read.figma.js +39 -0
  24. package/scripts/lib/fixtures/figma-adherence/clean.json +90 -0
  25. package/scripts/lib/fixtures/figma-adherence/detached-library.json +116 -0
  26. package/scripts/lib/fixtures/figma-adherence/ds01-gaps.json +84 -0
  27. package/scripts/lib/fixtures/figma-adherence/inert-all.json +45 -0
  28. package/scripts/lib/fixtures/figma-adherence/mixed-text.json +61 -0
  29. package/scripts/lib/fixtures/figma-adherence/nothing-scanned.json +49 -0
  30. package/scripts/lib/fixtures/figma-adherence/off-scale-gap.json +65 -0
  31. package/scripts/lib/fixtures/figma-adherence/override-raw.json +72 -0
  32. package/scripts/lib/fixtures/figma-adherence/padding-raw.json +74 -0
  33. package/scripts/lib/fixtures/figma-adherence/raw-colour-token.json +70 -0
  34. package/scripts/lib/fixtures/figma-adherence/root-radius.json +88 -0
  35. package/scripts/lib/fixtures/figma-adherence/skip-colour.json +86 -0
  36. package/scripts/lib/fixtures/figma-adherence/zygarden-override-bound.json +85 -0
  37. package/scripts/lib/fixtures/figma-adherence/zygarden-text-raw.json +208 -0
  38. package/scripts/lib/fixtures/figma-checks/avatar-not-in-table.json +2 -1
  39. package/scripts/lib/fixtures/figma-checks/button-ios-swift-skipped.json +2 -1
  40. package/scripts/lib/fixtures/figma-checks/button-missing-default.json +2 -1
  41. package/scripts/lib/fixtures/figma-checks/button-mui-ring.json +2 -1
  42. package/scripts/lib/fixtures/figma-checks/button-no-focus-variant.json +2 -1
  43. package/scripts/lib/fixtures/figma-checks/button-plain-frames.json +2 -1
  44. package/scripts/lib/fixtures/figma-checks/button-ring-child-not-absolute.json +2 -1
  45. package/scripts/lib/fixtures/figma-checks/button-ring-hidden.json +2 -1
  46. package/scripts/lib/fixtures/figma-checks/button-ring-unbound-colour.json +4 -3
  47. package/scripts/lib/fixtures/figma-checks/button-shadcn-filled.json +2 -1
  48. package/scripts/lib/fixtures/figma-checks/button-sparse.json +2 -1
  49. package/scripts/lib/fixtures/figma-checks/button-spread-zero.json +2 -1
  50. package/scripts/lib/fixtures/figma-checks/button-transparent-ring-child.json +2 -1
  51. package/scripts/lib/fixtures/figma-checks/button-vanilla-inside.json +2 -1
  52. package/scripts/lib/fixtures/figma-checks/button-vanilla-outside.json +2 -1
  53. package/scripts/lib/fixtures/figma-checks/button-wrong-combination.json +2 -1
  54. package/scripts/lib/fixtures/figma-checks/checkbox-checked-axis.real.json +2 -1
  55. package/scripts/lib/fixtures/figma-checks/checkbox-checked-synonym.json +2 -1
  56. package/scripts/lib/fixtures/figma-checks/checkbox-missing-selected.json +2 -1
  57. package/scripts/lib/fixtures/figma-checks/indicator-failed.json +106 -0
  58. package/scripts/lib/fixtures/figma-checks/input-control-layer-missing.json +2 -1
  59. package/scripts/lib/fixtures/figma-checks/input-control-layer-ring-on-wrapper.json +2 -1
  60. package/scripts/lib/fixtures/figma-checks/input-control-layer.json +2 -1
  61. package/scripts/lib/fixtures/figma-checks/input-mui-weight-unbound.json +2 -1
  62. package/scripts/lib/fixtures/figma-checks/input-mui.json +2 -1
  63. package/scripts/lib/fixtures/figma-checks/ring-single-mode.json +112 -0
  64. package/scripts/lib/fixtures/figma-checks/ring-translucent.json +111 -0
  65. package/scripts/lib/fixtures/figma-checks/sample-button.real.json +2 -1
  66. package/scripts/lib/fixtures/figma-checks/sample-ring-on-muted-raw.json +113 -0
  67. package/scripts/lib/fixtures/figma-checks/sample-ring-passes.json +169 -0
  68. package/scripts/lib/fixtures/figma-checks/surface-missing.json +108 -0
  69. package/scripts/lib/fixtures/figma-checks/surface-single-mode.json +112 -0
  70. package/scripts/lib/fixtures/figma-checks/surface-translucent.json +112 -0
  71. package/scripts/lib/fixtures/figma-checks/zygarden-ring-light-fails.json +113 -0
@@ -0,0 +1,88 @@
1
+ {
2
+ "covers": "Zygarden 2026-10-13: the screen root radius 32 is skipped; the same radius on a child fails",
3
+ "snapshot": {
4
+ "fileKey": "FILE",
5
+ "screens": [
6
+ {
7
+ "id": "0:1",
8
+ "name": "Screen"
9
+ }
10
+ ],
11
+ "nodes": [
12
+ {
13
+ "id": "3:1",
14
+ "path": "Templates / Example 1",
15
+ "isRoot": true,
16
+ "inInstance": false,
17
+ "overridden": [],
18
+ "paints": [],
19
+ "mixedPaints": false,
20
+ "gap": null,
21
+ "padding": [],
22
+ "radius": [
23
+ {
24
+ "corner": "all",
25
+ "value": 32,
26
+ "bound": false
27
+ }
28
+ ],
29
+ "detached": null
30
+ },
31
+ {
32
+ "id": "3:2",
33
+ "path": "Templates / Example 1 / Card",
34
+ "isRoot": false,
35
+ "inInstance": false,
36
+ "overridden": [],
37
+ "paints": [],
38
+ "mixedPaints": false,
39
+ "gap": null,
40
+ "padding": [],
41
+ "radius": [
42
+ {
43
+ "corner": "all",
44
+ "value": 32,
45
+ "bound": false
46
+ }
47
+ ],
48
+ "detached": null
49
+ }
50
+ ],
51
+ "tokens": {
52
+ "colours": {
53
+ "#ffffff": [
54
+ "color/bg/canvas"
55
+ ],
56
+ "#111827": [
57
+ "color/text/primary"
58
+ ]
59
+ },
60
+ "gap": {
61
+ "8": [
62
+ "spacing/2"
63
+ ],
64
+ "12": [
65
+ "spacing/3"
66
+ ],
67
+ "16": [
68
+ "spacing/4"
69
+ ]
70
+ },
71
+ "radius": {
72
+ "8": [
73
+ "radius/md"
74
+ ],
75
+ "32": [
76
+ "radius/xl"
77
+ ]
78
+ }
79
+ }
80
+ },
81
+ "expected": {
82
+ "pass": false,
83
+ "failures": [
84
+ "raw-dimension"
85
+ ],
86
+ "advisories": 0
87
+ }
88
+ }
@@ -0,0 +1,86 @@
1
+ {
2
+ "covers": "a skipped rule runs nothing and its inert check does not fire",
3
+ "skip": [
4
+ "raw-colour"
5
+ ],
6
+ "tokens": {
7
+ "colours": {},
8
+ "gap": {
9
+ "8": [
10
+ "spacing/2"
11
+ ],
12
+ "12": [
13
+ "spacing/3"
14
+ ],
15
+ "16": [
16
+ "spacing/4"
17
+ ]
18
+ },
19
+ "radius": {
20
+ "8": [
21
+ "radius/md"
22
+ ],
23
+ "32": [
24
+ "radius/xl"
25
+ ]
26
+ }
27
+ },
28
+ "snapshot": {
29
+ "fileKey": "FILE",
30
+ "screens": [
31
+ {
32
+ "id": "0:1",
33
+ "name": "Screen"
34
+ }
35
+ ],
36
+ "nodes": [
37
+ {
38
+ "id": "1:1",
39
+ "path": "Templates / Screen / Node",
40
+ "isRoot": false,
41
+ "inInstance": false,
42
+ "overridden": [],
43
+ "paints": [
44
+ {
45
+ "kind": "fill",
46
+ "hex": "#ffffff",
47
+ "bound": false,
48
+ "styled": false
49
+ }
50
+ ],
51
+ "mixedPaints": false,
52
+ "gap": null,
53
+ "padding": [],
54
+ "radius": [],
55
+ "detached": null
56
+ }
57
+ ],
58
+ "tokens": {
59
+ "colours": {},
60
+ "gap": {
61
+ "8": [
62
+ "spacing/2"
63
+ ],
64
+ "12": [
65
+ "spacing/3"
66
+ ],
67
+ "16": [
68
+ "spacing/4"
69
+ ]
70
+ },
71
+ "radius": {
72
+ "8": [
73
+ "radius/md"
74
+ ],
75
+ "32": [
76
+ "radius/xl"
77
+ ]
78
+ }
79
+ }
80
+ },
81
+ "expected": {
82
+ "pass": true,
83
+ "failures": [],
84
+ "advisories": 0
85
+ }
86
+ }
@@ -0,0 +1,85 @@
1
+ {
2
+ "covers": "Zygarden 2026-10-13: overridden fills that are bound pass",
3
+ "snapshot": {
4
+ "fileKey": "FILE",
5
+ "screens": [
6
+ {
7
+ "id": "0:1",
8
+ "name": "Screen"
9
+ }
10
+ ],
11
+ "nodes": [
12
+ {
13
+ "id": "1:1",
14
+ "path": "Templates / Screen / Node",
15
+ "isRoot": false,
16
+ "inInstance": true,
17
+ "overridden": [
18
+ "fills"
19
+ ],
20
+ "paints": [
21
+ {
22
+ "kind": "fill",
23
+ "hex": "#111827",
24
+ "bound": true,
25
+ "styled": false
26
+ }
27
+ ],
28
+ "mixedPaints": false,
29
+ "gap": null,
30
+ "padding": [],
31
+ "radius": [],
32
+ "detached": null
33
+ },
34
+ {
35
+ "id": "1:2",
36
+ "path": "Templates / Screen / Node",
37
+ "isRoot": false,
38
+ "inInstance": true,
39
+ "overridden": [
40
+ "fills"
41
+ ],
42
+ "paints": [],
43
+ "mixedPaints": false,
44
+ "gap": null,
45
+ "padding": [],
46
+ "radius": [],
47
+ "detached": null
48
+ }
49
+ ],
50
+ "tokens": {
51
+ "colours": {
52
+ "#ffffff": [
53
+ "color/bg/canvas"
54
+ ],
55
+ "#111827": [
56
+ "color/text/primary"
57
+ ]
58
+ },
59
+ "gap": {
60
+ "8": [
61
+ "spacing/2"
62
+ ],
63
+ "12": [
64
+ "spacing/3"
65
+ ],
66
+ "16": [
67
+ "spacing/4"
68
+ ]
69
+ },
70
+ "radius": {
71
+ "8": [
72
+ "radius/md"
73
+ ],
74
+ "32": [
75
+ "radius/xl"
76
+ ]
77
+ }
78
+ }
79
+ },
80
+ "expected": {
81
+ "pass": true,
82
+ "failures": [],
83
+ "advisories": 0
84
+ }
85
+ }
@@ -0,0 +1,208 @@
1
+ {
2
+ "covers": "Zygarden 2026-10-13: 8 text fills #e8e7e7, which no token has: advisories, not failures",
3
+ "snapshot": {
4
+ "fileKey": "FILE",
5
+ "screens": [
6
+ {
7
+ "id": "0:1",
8
+ "name": "Screen"
9
+ }
10
+ ],
11
+ "nodes": [
12
+ {
13
+ "id": "10:1",
14
+ "path": "Templates / Example 1 / My tournaments",
15
+ "isRoot": false,
16
+ "inInstance": false,
17
+ "overridden": [],
18
+ "paints": [
19
+ {
20
+ "kind": "fill",
21
+ "hex": "#e8e7e7",
22
+ "bound": false,
23
+ "styled": false
24
+ }
25
+ ],
26
+ "mixedPaints": false,
27
+ "gap": null,
28
+ "padding": [],
29
+ "radius": [],
30
+ "detached": null
31
+ },
32
+ {
33
+ "id": "10:2",
34
+ "path": "Templates / Example 2 / My tournaments",
35
+ "isRoot": false,
36
+ "inInstance": false,
37
+ "overridden": [],
38
+ "paints": [
39
+ {
40
+ "kind": "fill",
41
+ "hex": "#e8e7e7",
42
+ "bound": false,
43
+ "styled": false
44
+ }
45
+ ],
46
+ "mixedPaints": false,
47
+ "gap": null,
48
+ "padding": [],
49
+ "radius": [],
50
+ "detached": null
51
+ },
52
+ {
53
+ "id": "10:3",
54
+ "path": "Templates / Example 3 / My tournaments",
55
+ "isRoot": false,
56
+ "inInstance": false,
57
+ "overridden": [],
58
+ "paints": [
59
+ {
60
+ "kind": "fill",
61
+ "hex": "#e8e7e7",
62
+ "bound": false,
63
+ "styled": false
64
+ }
65
+ ],
66
+ "mixedPaints": false,
67
+ "gap": null,
68
+ "padding": [],
69
+ "radius": [],
70
+ "detached": null
71
+ },
72
+ {
73
+ "id": "10:4",
74
+ "path": "Templates / Example 4 / My tournaments",
75
+ "isRoot": false,
76
+ "inInstance": false,
77
+ "overridden": [],
78
+ "paints": [
79
+ {
80
+ "kind": "fill",
81
+ "hex": "#e8e7e7",
82
+ "bound": false,
83
+ "styled": false
84
+ }
85
+ ],
86
+ "mixedPaints": false,
87
+ "gap": null,
88
+ "padding": [],
89
+ "radius": [],
90
+ "detached": null
91
+ },
92
+ {
93
+ "id": "10:5",
94
+ "path": "Templates / Example 5 / My tournaments",
95
+ "isRoot": false,
96
+ "inInstance": false,
97
+ "overridden": [],
98
+ "paints": [
99
+ {
100
+ "kind": "fill",
101
+ "hex": "#e8e7e7",
102
+ "bound": false,
103
+ "styled": false
104
+ }
105
+ ],
106
+ "mixedPaints": false,
107
+ "gap": null,
108
+ "padding": [],
109
+ "radius": [],
110
+ "detached": null
111
+ },
112
+ {
113
+ "id": "10:6",
114
+ "path": "Templates / Example 6 / My tournaments",
115
+ "isRoot": false,
116
+ "inInstance": false,
117
+ "overridden": [],
118
+ "paints": [
119
+ {
120
+ "kind": "fill",
121
+ "hex": "#e8e7e7",
122
+ "bound": false,
123
+ "styled": false
124
+ }
125
+ ],
126
+ "mixedPaints": false,
127
+ "gap": null,
128
+ "padding": [],
129
+ "radius": [],
130
+ "detached": null
131
+ },
132
+ {
133
+ "id": "10:7",
134
+ "path": "Templates / Example 7 / My tournaments",
135
+ "isRoot": false,
136
+ "inInstance": false,
137
+ "overridden": [],
138
+ "paints": [
139
+ {
140
+ "kind": "fill",
141
+ "hex": "#e8e7e7",
142
+ "bound": false,
143
+ "styled": false
144
+ }
145
+ ],
146
+ "mixedPaints": false,
147
+ "gap": null,
148
+ "padding": [],
149
+ "radius": [],
150
+ "detached": null
151
+ },
152
+ {
153
+ "id": "10:8",
154
+ "path": "Templates / Example 8 / My tournaments",
155
+ "isRoot": false,
156
+ "inInstance": false,
157
+ "overridden": [],
158
+ "paints": [
159
+ {
160
+ "kind": "fill",
161
+ "hex": "#e8e7e7",
162
+ "bound": false,
163
+ "styled": false
164
+ }
165
+ ],
166
+ "mixedPaints": false,
167
+ "gap": null,
168
+ "padding": [],
169
+ "radius": [],
170
+ "detached": null
171
+ }
172
+ ],
173
+ "tokens": {
174
+ "colours": {
175
+ "#ffffff": [
176
+ "color/bg/canvas"
177
+ ],
178
+ "#111827": [
179
+ "color/text/primary"
180
+ ]
181
+ },
182
+ "gap": {
183
+ "8": [
184
+ "spacing/2"
185
+ ],
186
+ "12": [
187
+ "spacing/3"
188
+ ],
189
+ "16": [
190
+ "spacing/4"
191
+ ]
192
+ },
193
+ "radius": {
194
+ "8": [
195
+ "radius/md"
196
+ ],
197
+ "32": [
198
+ "radius/xl"
199
+ ]
200
+ }
201
+ }
202
+ },
203
+ "expected": {
204
+ "pass": true,
205
+ "failures": [],
206
+ "advisories": 8
207
+ }
208
+ }
@@ -38,6 +38,7 @@
38
38
  "expected": {
39
39
  "structural-read-back": "pass",
40
40
  "state-baseline": "skip",
41
- "focus-indicator": "skip"
41
+ "focus-indicator": "skip",
42
+ "focus-contrast": "skip"
42
43
  }
43
44
  }
@@ -80,6 +80,7 @@
80
80
  "expected": {
81
81
  "structural-read-back": "pass",
82
82
  "state-baseline": "pass",
83
- "focus-indicator": "skip"
83
+ "focus-indicator": "skip",
84
+ "focus-contrast": "skip"
84
85
  }
85
86
  }
@@ -66,6 +66,7 @@
66
66
  "expected": {
67
67
  "structural-read-back": "pass",
68
68
  "state-baseline": "fail",
69
- "focus-indicator": "pass"
69
+ "focus-indicator": "pass",
70
+ "focus-contrast": "skip"
70
71
  }
71
72
  }
@@ -87,6 +87,7 @@
87
87
  "expected": {
88
88
  "structural-read-back": "pass",
89
89
  "state-baseline": "pass",
90
- "focus-indicator": "pass"
90
+ "focus-indicator": "pass",
91
+ "focus-contrast": "skip"
91
92
  }
92
93
  }
@@ -66,6 +66,7 @@
66
66
  "expected": {
67
67
  "structural-read-back": "pass",
68
68
  "state-baseline": "fail",
69
- "focus-indicator": "fail"
69
+ "focus-indicator": "fail",
70
+ "focus-contrast": "skip"
70
71
  }
71
72
  }
@@ -87,6 +87,7 @@
87
87
  "expected": {
88
88
  "structural-read-back": "fail",
89
89
  "state-baseline": "pass",
90
- "focus-indicator": "fail"
90
+ "focus-indicator": "fail",
91
+ "focus-contrast": "skip"
91
92
  }
92
93
  }
@@ -89,6 +89,7 @@
89
89
  "expected": {
90
90
  "structural-read-back": "pass",
91
91
  "state-baseline": "pass",
92
- "focus-indicator": "fail"
92
+ "focus-indicator": "fail",
93
+ "focus-contrast": "skip"
93
94
  }
94
95
  }
@@ -87,6 +87,7 @@
87
87
  "expected": {
88
88
  "structural-read-back": "pass",
89
89
  "state-baseline": "pass",
90
- "focus-indicator": "fail"
90
+ "focus-indicator": "fail",
91
+ "focus-contrast": "skip"
91
92
  }
92
93
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "covers": "shadow with a raw colour and a variable with no focus segment",
2
+ "covers": "shadow with a raw colour and a variable with no focus word",
3
3
  "expect": {
4
4
  "name": "Button",
5
5
  "matrix": {
@@ -66,7 +66,7 @@
66
66
  "type": "DROP_SHADOW",
67
67
  "visible": true,
68
68
  "spread": 3,
69
- "colorVariable": "color/focusRing"
69
+ "colorVariable": "color/border/default"
70
70
  }
71
71
  ],
72
72
  "strokeAlign": "INSIDE",
@@ -93,6 +93,7 @@
93
93
  "expected": {
94
94
  "structural-read-back": "pass",
95
95
  "state-baseline": "pass",
96
- "focus-indicator": "fail"
96
+ "focus-indicator": "fail",
97
+ "focus-contrast": "skip"
97
98
  }
98
99
  }
@@ -87,6 +87,7 @@
87
87
  "expected": {
88
88
  "structural-read-back": "pass",
89
89
  "state-baseline": "pass",
90
- "focus-indicator": "pass"
90
+ "focus-indicator": "pass",
91
+ "focus-contrast": "skip"
91
92
  }
92
93
  }
@@ -129,6 +129,7 @@
129
129
  "expected": {
130
130
  "structural-read-back": "fail",
131
131
  "state-baseline": "pass",
132
- "focus-indicator": "pass"
132
+ "focus-indicator": "pass",
133
+ "focus-contrast": "skip"
133
134
  }
134
135
  }
@@ -87,6 +87,7 @@
87
87
  "expected": {
88
88
  "structural-read-back": "pass",
89
89
  "state-baseline": "pass",
90
- "focus-indicator": "fail"
90
+ "focus-indicator": "fail",
91
+ "focus-contrast": "skip"
91
92
  }
92
93
  }
@@ -89,6 +89,7 @@
89
89
  "expected": {
90
90
  "structural-read-back": "pass",
91
91
  "state-baseline": "pass",
92
- "focus-indicator": "pass"
92
+ "focus-indicator": "pass",
93
+ "focus-contrast": "skip"
93
94
  }
94
95
  }
@@ -82,6 +82,7 @@
82
82
  "expected": {
83
83
  "structural-read-back": "pass",
84
84
  "state-baseline": "pass",
85
- "focus-indicator": "fail"
85
+ "focus-indicator": "fail",
86
+ "focus-contrast": "skip"
86
87
  }
87
88
  }
@@ -82,6 +82,7 @@
82
82
  "expected": {
83
83
  "structural-read-back": "pass",
84
84
  "state-baseline": "pass",
85
- "focus-indicator": "pass"
85
+ "focus-indicator": "pass",
86
+ "focus-contrast": "skip"
86
87
  }
87
88
  }
@@ -87,6 +87,7 @@
87
87
  "expected": {
88
88
  "structural-read-back": "fail",
89
89
  "state-baseline": "fail",
90
- "focus-indicator": "pass"
90
+ "focus-indicator": "pass",
91
+ "focus-contrast": "skip"
91
92
  }
92
93
  }
@@ -22,7 +22,8 @@
22
22
  "expected": {
23
23
  "structural-read-back": "pass",
24
24
  "state-baseline": "pass",
25
- "focus-indicator": "pass"
25
+ "focus-indicator": "pass",
26
+ "focus-contrast": "skip"
26
27
  },
27
28
  "snapshot": {
28
29
  "type": "COMPONENT_SET",
@@ -82,6 +82,7 @@
82
82
  "expected": {
83
83
  "structural-read-back": "pass",
84
84
  "state-baseline": "pass",
85
- "focus-indicator": "pass"
85
+ "focus-indicator": "pass",
86
+ "focus-contrast": "skip"
86
87
  }
87
88
  }
@@ -74,6 +74,7 @@
74
74
  "expected": {
75
75
  "structural-read-back": "pass",
76
76
  "state-baseline": "fail",
77
- "focus-indicator": "pass"
77
+ "focus-indicator": "pass",
78
+ "focus-contrast": "skip"
78
79
  }
79
80
  }