@multiplatform.one/theme 7.34.3 → 7.35.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 (280) hide show
  1. package/package.json +20 -5
  2. package/src/audit/constraintAudit.spec.ts +274 -87
  3. package/src/audit/constraintAudit.ts +135 -120
  4. package/src/audit/constraintScope.spec.ts +1 -1
  5. package/src/audit/designTokens.ts +42 -0
  6. package/src/audit/themeMatrix.spec.ts +83 -2
  7. package/src/audit/themeMatrix.ts +33 -1
  8. package/src/figma/figmaTokens.spec.ts +26 -34
  9. package/src/figma/figmaTokens.ts +26 -50
  10. package/src/fonts/createFontLoader.ts +4 -1
  11. package/src/menuRow.ts +4 -1
  12. package/src/theme/FontKnobStyles.tsx +2 -1
  13. package/src/theme/Preset.stories.tsx +0 -1
  14. package/src/theme/Preset.tsx +6 -1
  15. package/src/theme/PresetContext.ts +4 -0
  16. package/src/theme/Surface.spec.tsx +114 -67
  17. package/src/theme/Surface.stories.tsx +8 -25
  18. package/src/theme/Surface.tsx +87 -43
  19. package/src/theme/accentFloor.spec.ts +37 -11
  20. package/src/theme/activateThemeConfig.ts +22 -6
  21. package/src/theme/animations/animations.spec.ts +62 -0
  22. package/src/theme/animations/css.ts +9 -35
  23. package/src/theme/animations/reactNative.ts +98 -25
  24. package/src/theme/boundaryFloor.spec.ts +57 -0
  25. package/src/theme/chartPalette.spec.ts +56 -27
  26. package/src/theme/chartPalette.ts +33 -28
  27. package/src/theme/colorRules.spec.ts +32 -72
  28. package/src/theme/cookies.spec.ts +49 -18
  29. package/src/theme/cookies.ts +36 -7
  30. package/src/theme/cookiesPersistence.spec.ts +5 -5
  31. package/src/theme/createDefaultThemeConfig.spec.ts +182 -20
  32. package/src/theme/createDefaultThemeConfig.ts +34 -28
  33. package/src/theme/createThemes.ts +169 -218
  34. package/src/theme/defaults/accent.ts +3 -35
  35. package/src/theme/defaults/base.ts +14 -53
  36. package/src/theme/defaults/builderOptions.ts +68 -85
  37. package/src/theme/defaults/categoryFonts.ts +10 -0
  38. package/src/theme/defaults/fonts.spec.ts +84 -4
  39. package/src/theme/defaults/fonts.ts +166 -124
  40. package/src/theme/defaults/house.ts +48 -0
  41. package/src/theme/defaults/index.ts +1 -0
  42. package/src/theme/defaults/neutralRamp.spec.ts +52 -47
  43. package/src/theme/devtools/ColorLineVisualizer.tsx +6 -6
  44. package/src/theme/devtools/ThemeDevtoolsPanel.setMarkers.spec.tsx +13 -6
  45. package/src/theme/devtools/ThemeDevtoolsPanel.tsx +14 -77
  46. package/src/theme/devtools/animationOptions.spec.ts +23 -37
  47. package/src/theme/devtools/animationOptions.ts +4 -6
  48. package/src/theme/devtools/buildSnapshot.ts +0 -3
  49. package/src/theme/devtools/knobIcons.tsx +4 -24
  50. package/src/theme/focusState.spec.ts +9 -30
  51. package/src/theme/index.ts +17 -0
  52. package/src/theme/intentFill.spec.ts +42 -0
  53. package/src/theme/knobs.ts +14 -42
  54. package/src/theme/layoutTokens.ts +15 -1
  55. package/src/theme/layoutTokensHooks.spec.tsx +8 -4
  56. package/src/theme/mergedTokens.ts +20 -0
  57. package/src/theme/motion.spec.ts +84 -0
  58. package/src/theme/motion.ts +53 -0
  59. package/src/theme/motionStops.ts +17 -0
  60. package/src/theme/motionTiming.ts +59 -0
  61. package/src/theme/mpo13Proof.spec.ts +43 -46
  62. package/src/theme/mpo19Proof.spec.ts +13 -14
  63. package/src/theme/namedInkFloor.spec.ts +60 -0
  64. package/src/theme/overlaySurfaces.tsx +330 -0
  65. package/src/theme/preset.types.ts +16 -0
  66. package/src/theme/presetPreview.ts +32 -0
  67. package/src/theme/presetRoot.spec.tsx +89 -0
  68. package/src/theme/presetStore.persistence.spec.ts +141 -0
  69. package/src/theme/presetStore.spec.ts +205 -0
  70. package/src/theme/presetStore.ts +286 -0
  71. package/src/theme/presetUserSync.spec.tsx +168 -0
  72. package/src/theme/presetUserSync.ts +47 -0
  73. package/src/theme/presets.spec.ts +22 -15
  74. package/src/theme/presets.ts +22 -10
  75. package/src/theme/radiusClass.spec.ts +60 -41
  76. package/src/theme/radiusClass.ts +17 -4
  77. package/src/theme/radiusClassTable.generated.ts +3 -3
  78. package/src/theme/readableColor.spec.ts +59 -152
  79. package/src/theme/readableColor.ts +34 -174
  80. package/src/theme/recipeInputs.ts +153 -295
  81. package/src/theme/recipes.ts +58 -18
  82. package/src/theme/resolveKnobs.spec.ts +528 -588
  83. package/src/theme/resolveKnobs.ts +250 -325
  84. package/src/theme/scale/ScaleStyles.native.tsx +19 -0
  85. package/src/theme/scale/ScaleStyles.tsx +48 -0
  86. package/src/theme/scale/activeScale.ts +30 -0
  87. package/src/theme/scale/deriveRecipeInputs.ts +110 -0
  88. package/src/theme/scale/generateScale.spec.ts +260 -0
  89. package/src/theme/scale/generateScale.ts +147 -0
  90. package/src/theme/scale/index.ts +54 -0
  91. package/src/theme/scale/installScaleConfig.native.spec.ts +77 -0
  92. package/src/theme/scale/installScaleConfig.native.ts +57 -0
  93. package/src/theme/scale/installScaleConfig.ts +7 -0
  94. package/src/theme/scale/scaleAttribute.ts +21 -0
  95. package/src/theme/scale/scaleCss.probe.spec.tsx +64 -0
  96. package/src/theme/scale/scaleCss.spec.ts +132 -0
  97. package/src/theme/scale/scaleCss.ts +95 -0
  98. package/src/theme/scale/scaleFonts.ts +106 -0
  99. package/src/theme/scale/scaleInputs.ts +322 -0
  100. package/src/theme/scale/themeScales.ts +181 -0
  101. package/src/theme/scale/useActiveScale.spec.tsx +129 -0
  102. package/src/theme/scale/useActiveScale.ts +95 -0
  103. package/src/theme/schemeStore.spec.ts +81 -0
  104. package/src/theme/schemeStore.ts +67 -0
  105. package/src/theme/shared.spec.ts +7 -6
  106. package/src/theme/shared.tsx +37 -3
  107. package/src/theme/sizeLadder.spec.ts +37 -36
  108. package/src/theme/sizeRecipes.spec.ts +179 -85
  109. package/src/theme/sizeRecipes.ts +48 -25
  110. package/src/theme/subsetThemes.spec.ts +9 -3
  111. package/src/theme/switchThumb.spec.ts +69 -0
  112. package/src/theme/syntaxFloor.spec.ts +11 -8
  113. package/src/theme/theme.hydration.native.spec.tsx +51 -0
  114. package/src/theme/theme.native.tsx +59 -21
  115. package/src/theme/theme.palette.spec.tsx +76 -0
  116. package/src/theme/theme.preset.native.spec.tsx +73 -0
  117. package/src/theme/theme.provider.spec.ts +143 -1
  118. package/src/theme/theme.tsx +61 -25
  119. package/src/theme/tileSizes.ts +6 -7
  120. package/src/theme/transitionProps.native.ts +25 -2
  121. package/src/theme/transitionProps.spec.ts +34 -0
  122. package/src/theme/transitionProps.ts +26 -2
  123. package/src/theme/usePresetSwitch.native.spec.tsx +68 -0
  124. package/src/theme/usePresetSwitch.native.ts +32 -0
  125. package/src/theme/usePresetSwitch.spec.tsx +103 -0
  126. package/src/theme/usePresetSwitch.ts +54 -0
  127. package/src/theme/useResolvedKnobs.hydration.spec.tsx +2 -2
  128. package/src/theme/useResolvedKnobs.ts +23 -90
  129. package/src/theme/useResolvedKnobsBehavior.spec.tsx +25 -30
  130. package/src/theme/useSchemePreference.native.ts +33 -0
  131. package/src/theme/useSchemePreference.spec.tsx +51 -0
  132. package/src/theme/useSchemePreference.ts +32 -0
  133. package/src/theme/useSchemePreference.web.spec.tsx +32 -0
  134. package/src/theme/useTheme.native.spec.tsx +68 -0
  135. package/src/theme/useTheme.native.ts +49 -24
  136. package/src/theme/useTheme.ts +7 -4
  137. package/types/audit/constraintAudit.d.ts +6 -5
  138. package/types/audit/constraintAudit.d.ts.map +1 -1
  139. package/types/audit/designTokens.d.ts +13 -0
  140. package/types/audit/designTokens.d.ts.map +1 -0
  141. package/types/audit/themeMatrix.d.ts +10 -0
  142. package/types/audit/themeMatrix.d.ts.map +1 -1
  143. package/types/figma/figmaTokens.d.ts +11 -28
  144. package/types/figma/figmaTokens.d.ts.map +1 -1
  145. package/types/fonts/createFontLoader.d.ts.map +1 -1
  146. package/types/menuRow.d.ts +4 -1
  147. package/types/menuRow.d.ts.map +1 -1
  148. package/types/theme/FontKnobStyles.d.ts.map +1 -1
  149. package/types/theme/Preset.d.ts.map +1 -1
  150. package/types/theme/PresetContext.d.ts +4 -0
  151. package/types/theme/PresetContext.d.ts.map +1 -1
  152. package/types/theme/Surface.d.ts +57 -26
  153. package/types/theme/Surface.d.ts.map +1 -1
  154. package/types/theme/activateThemeConfig.d.ts +5 -3
  155. package/types/theme/activateThemeConfig.d.ts.map +1 -1
  156. package/types/theme/animations/css.d.ts +27 -34
  157. package/types/theme/animations/css.d.ts.map +1 -1
  158. package/types/theme/animations/reactNative.d.ts +196 -45
  159. package/types/theme/animations/reactNative.d.ts.map +1 -1
  160. package/types/theme/chartPalette.d.ts +11 -3
  161. package/types/theme/chartPalette.d.ts.map +1 -1
  162. package/types/theme/colorRules.d.ts +1 -1
  163. package/types/theme/cookies.d.ts +12 -1
  164. package/types/theme/cookies.d.ts.map +1 -1
  165. package/types/theme/createDefaultThemeConfig.d.ts +13 -13
  166. package/types/theme/createDefaultThemeConfig.d.ts.map +1 -1
  167. package/types/theme/createThemes.d.ts +58 -2
  168. package/types/theme/createThemes.d.ts.map +1 -1
  169. package/types/theme/defaults/accent.d.ts.map +1 -1
  170. package/types/theme/defaults/base.d.ts.map +1 -1
  171. package/types/theme/defaults/builderOptions.d.ts.map +1 -1
  172. package/types/theme/defaults/categoryFonts.d.ts +10 -0
  173. package/types/theme/defaults/categoryFonts.d.ts.map +1 -1
  174. package/types/theme/defaults/fonts.d.ts +330 -30
  175. package/types/theme/defaults/fonts.d.ts.map +1 -1
  176. package/types/theme/defaults/house.d.ts +8 -0
  177. package/types/theme/defaults/house.d.ts.map +1 -0
  178. package/types/theme/defaults/index.d.ts +1 -0
  179. package/types/theme/defaults/index.d.ts.map +1 -1
  180. package/types/theme/devtools/ThemeDevtoolsPanel.d.ts +3 -3
  181. package/types/theme/devtools/ThemeDevtoolsPanel.d.ts.map +1 -1
  182. package/types/theme/devtools/animationOptions.d.ts.map +1 -1
  183. package/types/theme/devtools/buildSnapshot.d.ts.map +1 -1
  184. package/types/theme/devtools/knobIcons.d.ts +0 -3
  185. package/types/theme/devtools/knobIcons.d.ts.map +1 -1
  186. package/types/theme/index.d.ts +11 -0
  187. package/types/theme/index.d.ts.map +1 -1
  188. package/types/theme/knobs.d.ts +8 -36
  189. package/types/theme/knobs.d.ts.map +1 -1
  190. package/types/theme/layoutTokens.d.ts +13 -1
  191. package/types/theme/layoutTokens.d.ts.map +1 -1
  192. package/types/theme/mergedTokens.d.ts +11 -0
  193. package/types/theme/mergedTokens.d.ts.map +1 -0
  194. package/types/theme/motion.d.ts +17 -0
  195. package/types/theme/motion.d.ts.map +1 -0
  196. package/types/theme/motionStops.d.ts +3 -0
  197. package/types/theme/motionStops.d.ts.map +1 -0
  198. package/types/theme/motionTiming.d.ts +36 -0
  199. package/types/theme/motionTiming.d.ts.map +1 -0
  200. package/types/theme/overlaySurfaces.d.ts +70 -0
  201. package/types/theme/overlaySurfaces.d.ts.map +1 -0
  202. package/types/theme/preset.types.d.ts +16 -0
  203. package/types/theme/preset.types.d.ts.map +1 -1
  204. package/types/theme/presetPreview.d.ts +14 -0
  205. package/types/theme/presetPreview.d.ts.map +1 -0
  206. package/types/theme/presetStore.d.ts +40 -0
  207. package/types/theme/presetStore.d.ts.map +1 -0
  208. package/types/theme/presetUserSync.d.ts +7 -0
  209. package/types/theme/presetUserSync.d.ts.map +1 -0
  210. package/types/theme/presets.d.ts +12 -4
  211. package/types/theme/presets.d.ts.map +1 -1
  212. package/types/theme/radiusClass.d.ts +5 -2
  213. package/types/theme/radiusClass.d.ts.map +1 -1
  214. package/types/theme/radiusClassTable.generated.d.ts +3 -3
  215. package/types/theme/readableColor.d.ts +11 -49
  216. package/types/theme/readableColor.d.ts.map +1 -1
  217. package/types/theme/recipeInputs.d.ts +39 -70
  218. package/types/theme/recipeInputs.d.ts.map +1 -1
  219. package/types/theme/recipes.d.ts +79 -18
  220. package/types/theme/recipes.d.ts.map +1 -1
  221. package/types/theme/resolveKnobs.d.ts +64 -32
  222. package/types/theme/resolveKnobs.d.ts.map +1 -1
  223. package/types/theme/scale/ScaleStyles.d.ts +18 -0
  224. package/types/theme/scale/ScaleStyles.d.ts.map +1 -0
  225. package/types/theme/scale/ScaleStyles.native.d.ts +10 -0
  226. package/types/theme/scale/ScaleStyles.native.d.ts.map +1 -0
  227. package/types/theme/scale/activeScale.d.ts +13 -0
  228. package/types/theme/scale/activeScale.d.ts.map +1 -0
  229. package/types/theme/scale/deriveRecipeInputs.d.ts +10 -0
  230. package/types/theme/scale/deriveRecipeInputs.d.ts.map +1 -0
  231. package/types/theme/scale/generateScale.d.ts +38 -0
  232. package/types/theme/scale/generateScale.d.ts.map +1 -0
  233. package/types/theme/scale/index.d.ts +17 -0
  234. package/types/theme/scale/index.d.ts.map +1 -0
  235. package/types/theme/scale/installScaleConfig.d.ts +4 -0
  236. package/types/theme/scale/installScaleConfig.d.ts.map +1 -0
  237. package/types/theme/scale/installScaleConfig.native.d.ts +13 -0
  238. package/types/theme/scale/installScaleConfig.native.d.ts.map +1 -0
  239. package/types/theme/scale/scaleAttribute.d.ts +14 -0
  240. package/types/theme/scale/scaleAttribute.d.ts.map +1 -0
  241. package/types/theme/scale/scaleCss.d.ts +25 -0
  242. package/types/theme/scale/scaleCss.d.ts.map +1 -0
  243. package/types/theme/scale/scaleFonts.d.ts +16 -0
  244. package/types/theme/scale/scaleFonts.d.ts.map +1 -0
  245. package/types/theme/scale/scaleInputs.d.ts +66 -0
  246. package/types/theme/scale/scaleInputs.d.ts.map +1 -0
  247. package/types/theme/scale/themeScales.d.ts +52 -0
  248. package/types/theme/scale/themeScales.d.ts.map +1 -0
  249. package/types/theme/scale/useActiveScale.d.ts +31 -0
  250. package/types/theme/scale/useActiveScale.d.ts.map +1 -0
  251. package/types/theme/schemeStore.d.ts +6 -0
  252. package/types/theme/schemeStore.d.ts.map +1 -0
  253. package/types/theme/shared.d.ts +18 -0
  254. package/types/theme/shared.d.ts.map +1 -1
  255. package/types/theme/sizeRecipes.d.ts +20 -15
  256. package/types/theme/sizeRecipes.d.ts.map +1 -1
  257. package/types/theme/theme.d.ts +2 -1
  258. package/types/theme/theme.d.ts.map +1 -1
  259. package/types/theme/theme.native.d.ts +10 -1
  260. package/types/theme/theme.native.d.ts.map +1 -1
  261. package/types/theme/tileSizes.d.ts +6 -7
  262. package/types/theme/tileSizes.d.ts.map +1 -1
  263. package/types/theme/transitionProps.d.ts +3 -1
  264. package/types/theme/transitionProps.d.ts.map +1 -1
  265. package/types/theme/transitionProps.native.d.ts +3 -1
  266. package/types/theme/transitionProps.native.d.ts.map +1 -1
  267. package/types/theme/usePresetSwitch.d.ts +10 -0
  268. package/types/theme/usePresetSwitch.d.ts.map +1 -0
  269. package/types/theme/usePresetSwitch.native.d.ts +10 -0
  270. package/types/theme/usePresetSwitch.native.d.ts.map +1 -0
  271. package/types/theme/useResolvedKnobs.d.ts +13 -13
  272. package/types/theme/useResolvedKnobs.d.ts.map +1 -1
  273. package/types/theme/useSchemePreference.d.ts +8 -0
  274. package/types/theme/useSchemePreference.d.ts.map +1 -0
  275. package/types/theme/useSchemePreference.native.d.ts +8 -0
  276. package/types/theme/useSchemePreference.native.d.ts.map +1 -0
  277. package/types/theme/useTheme.d.ts +4 -2
  278. package/types/theme/useTheme.d.ts.map +1 -1
  279. package/types/theme/useTheme.native.d.ts +10 -1
  280. package/types/theme/useTheme.native.d.ts.map +1 -1
@@ -5,10 +5,14 @@
5
5
  * Tamagui token steps defined by the knob system. Off-scale values indicate
6
6
  * styles that bypassed the constraint system.
7
7
  *
8
- * Token values are from @tamagui/themes v5 + resolveKnobs.ts mappings.
8
+ * Token values are the active generated scale (`globalThis.__MPO_SCALE__`,
9
+ * else the default `generateScale`) + resolveKnobs.ts mappings.
9
10
  * Run this in-browser via Playwright's page.evaluate(runConstraintAudit).
10
11
  */
11
12
 
13
+ import { type GeneratedScale, type ScaleTokens, generateScale } from "../theme/scale/generateScale";
14
+ import { defaultScaleInputs } from "../theme/scale/scaleInputs";
15
+
12
16
  // ── Types ─────────────────────────────────────────────────────────────────────
13
17
 
14
18
  export interface PropertyAudit {
@@ -84,55 +88,41 @@ export interface AuditReport {
84
88
  }
85
89
 
86
90
  // ── Token scale definitions ───────────────────────────────────────────────────
87
- // These match @tamagui/themes v5 tokens + resolveKnobs.ts mapping tables.
88
-
89
- /** Radius token values: token key → px (from @tamagui/themes v5 radius tokens) */
90
- const RADIUS_TOKENS: Record<number, number> = {
91
- 0: 0,
92
- 1: 3,
93
- 2: 5,
94
- 3: 7,
95
- 4: 9,
96
- 5: 10,
97
- 6: 16,
98
- 7: 19,
99
- 8: 22,
100
- 9: 26,
101
- 10: 34,
102
- 11: 42,
103
- 12: 50,
104
- };
91
+ // The active generated scale's tokens + resolveKnobs.ts mapping tables.
105
92
 
106
- /** Space token values: token key → px (from @tamagui/themes v5 space tokens) */
107
- const SPACE_TOKENS: Record<number, number> = {
108
- 0: 0,
109
- 0.25: 0.5,
110
- 0.5: 1,
111
- 0.75: 1.5,
112
- 1: 2,
113
- 1.5: 4,
114
- 2: 7,
115
- 2.5: 10,
116
- 3: 13,
117
- 3.5: 16,
118
- 4: 18,
119
- 4.5: 21,
120
- 5: 24,
121
- 6: 32,
122
- 7: 39,
123
- 8: 46,
124
- };
93
+ function activeScaleTokens(): ScaleTokens {
94
+ const published = (globalThis as { __MPO_SCALE__?: GeneratedScale }).__MPO_SCALE__;
95
+ return published?.tokens ?? generateScale(defaultScaleInputs).tokens;
96
+ }
97
+
98
+ /** Whole and half-step keys of a token group as numbers (`$1.5` → 1.5); `true` and negatives dropped. */
99
+ function numericTokens(group: Record<string, number>): Record<number, number> {
100
+ const out: Record<number, number> = {};
101
+ for (const [key, px] of Object.entries(group)) {
102
+ const stop = Number(key.replace(/^\$/, ""));
103
+ if (Number.isFinite(stop) && stop >= 0) out[stop] = px;
104
+ }
105
+ return out;
106
+ }
107
+
108
+ const scaleTokens = activeScaleTokens();
109
+
110
+ /** Radius token values: token key → px. */
111
+ const RADIUS_TOKENS: Record<number, number> = numericTokens(scaleTokens.radius);
112
+
113
+ /** Space token values: token key → px. */
114
+ const SPACE_TOKENS: Record<number, number> = numericTokens(scaleTokens.space);
125
115
 
126
116
  /**
127
117
  * Inner border radius knob → pixel value
128
118
  * Source: borderRadiusMap in resolveKnobs.ts
129
119
  */
130
120
  const BORDER_RADIUS_KNOB: Record<string, number> = {
131
- none: RADIUS_TOKENS[0], // 0px
132
- small: RADIUS_TOKENS[2], // 5px
133
- medium: RADIUS_TOKENS[4], // 9px
134
- large: RADIUS_TOKENS[6], // 16px
135
- full: RADIUS_TOKENS[12], // 50px
121
+ none: RADIUS_TOKENS[0],
122
+ small: RADIUS_TOKENS[2],
123
+ medium: RADIUS_TOKENS[4],
124
+ large: RADIUS_TOKENS[6],
125
+ full: RADIUS_TOKENS[12],
136
126
  };
137
127
 
138
128
  /**
@@ -140,11 +130,11 @@ const BORDER_RADIUS_KNOB: Record<string, number> = {
140
130
  * Source: outerRadiusMap in resolveKnobs.ts
141
131
  */
142
132
  const OUTER_RADIUS_KNOB: Record<string, number> = {
143
- none: RADIUS_TOKENS[0], // 0px
144
- small: RADIUS_TOKENS[2], // 5px
145
- medium: RADIUS_TOKENS[3], // 7px
146
- large: RADIUS_TOKENS[4], // 9px
147
- full: RADIUS_TOKENS[5], // 10px
133
+ none: RADIUS_TOKENS[0],
134
+ small: RADIUS_TOKENS[2],
135
+ medium: RADIUS_TOKENS[3],
136
+ large: RADIUS_TOKENS[4],
137
+ full: RADIUS_TOKENS[5],
148
138
  };
149
139
 
150
140
  /**
@@ -159,33 +149,29 @@ const BORDER_WIDTH_KNOB: Record<string, number> = {
159
149
  };
160
150
 
161
151
  /**
162
- * Gap (inner spacing) knob → pixel value
163
- * Source: gapMap in resolveKnobs.ts ($2, $4, $5 space tokens)
152
+ * Within-group gap roles → pixel value on the scale.
153
+ * Source: spacingRoles in resolveKnobs.ts (gapSm $2, gap $3).
164
154
  */
165
155
  export const GAP_KNOB: Record<string, number> = {
166
- small: SPACE_TOKENS[2], // 7px
167
- medium: SPACE_TOKENS[4], // 18px
168
- large: SPACE_TOKENS[5], // 24px
156
+ gapSm: SPACE_TOKENS[2],
157
+ gap: SPACE_TOKENS[3],
169
158
  };
170
159
 
171
160
  /**
172
- * Gap large (gapLg) knob → pixel value
173
- * Source: gapLgMap in resolveKnobs.ts ($3, $5, $6 space tokens)
161
+ * Between-groups gap role → pixel value on the scale.
162
+ * Source: spacingRoles in resolveKnobs.ts (gapLg $5).
174
163
  */
175
164
  export const GAP_LG_KNOB: Record<string, number> = {
176
- small: SPACE_TOKENS[3], // 13px
177
- medium: SPACE_TOKENS[5], // 24px
178
- large: SPACE_TOKENS[6], // 32px
165
+ gapLg: SPACE_TOKENS[5],
179
166
  };
180
167
 
181
168
  /**
182
- * Panel padding knob → pixel value
183
- * Source: panelPaddingMap in resolveKnobs.ts ($3, $4, $6 space tokens)
169
+ * Inset roles → pixel value on the scale.
170
+ * Source: spacingRoles in resolveKnobs.ts (panelPadding $4, featurePadding $6).
184
171
  */
185
172
  const PANEL_PADDING_KNOB: Record<string, number> = {
186
- small: SPACE_TOKENS[3], // 13px
187
- medium: SPACE_TOKENS[4], // 18px
188
- large: SPACE_TOKENS[6], // 32px
173
+ panelPadding: SPACE_TOKENS[4],
174
+ featurePadding: SPACE_TOKENS[6],
189
175
  };
190
176
 
191
177
  /**
@@ -225,6 +211,7 @@ const VALID_FONT_WEIGHT = new Set(Object.values(FONT_WEIGHT_KNOB));
225
211
  const FONT_FAMILY_KNOB: Record<string, string> = {
226
212
  inter: "sans-serif ($body/$heading)",
227
213
  georgia: "serif",
214
+ "ui-monospace": "mono",
228
215
  monospace: "mono",
229
216
  "roboto slab": "slab",
230
217
  rockwell: "slab (fallback)",
@@ -335,37 +322,55 @@ function reverseMapFontFamily(computed: string): { knob: string; offScale: boole
335
322
  */
336
323
  export function runConstraintAudit(rootSelector?: string): AuditReport {
337
324
  // ── Inline token definitions (must be self-contained for page.evaluate) ──
325
+ // The page's published scale wins; the second argument is the default
326
+ // scale's value, for pages that publish none.
327
+
328
+ const _publishedScale = (
329
+ globalThis as {
330
+ __MPO_SCALE__?: {
331
+ controlGaps?: number[];
332
+ tokens?: {
333
+ radius?: Record<string, number>;
334
+ space?: Record<string, number>;
335
+ size?: Record<string, number>;
336
+ };
337
+ };
338
+ }
339
+ ).__MPO_SCALE__;
340
+ const _scale = _publishedScale?.tokens;
341
+ const _radius = (key: string, px: number): number => _scale?.radius?.[key] ?? px;
342
+ const _space = (key: string, px: number): number => _scale?.space?.[`$${key}`] ?? px;
343
+ const _size = (key: string, px: number): number => _scale?.size?.[`$${key}`] ?? px;
344
+
345
+ // Token-internal values components paint outside the knob maps, each named
346
+ // by the token it is so a preset on another scale checks its own value.
347
+ const _SPACE_1 = _space("1", 2);
348
+ const _SPACE_1_5 = _space("1.5", 4);
349
+ const _SPACE_2 = _space("2", 6);
350
+ const _SPACE_3 = _space("3", 12);
351
+ const _SIZE_0_5 = _size("0.5", 4);
352
+ const _CONTROL_GAPS = new Set<number>(_publishedScale?.controlGaps ?? [3, 4, 5, 6, 7, 8, 9, 14]);
338
353
 
339
- const _RADIUS_TOKENS: Record<number, number> = {
340
- 0: 0,
341
- 2: 5,
342
- 3: 7,
343
- 4: 9,
344
- 5: 10,
345
- 6: 16,
346
- 12: 50,
347
- };
348
- const _SPACE_TOKENS: Record<number, number> = {
349
- 0: 0,
350
- 2: 7,
351
- 3: 13,
352
- 4: 18,
353
- 5: 24,
354
- 6: 32,
355
- };
356
354
  const _BORDER_RADIUS_KNOB: Record<string, number> = {
357
- none: 0,
358
- small: 5,
359
- medium: 9,
360
- large: 16,
361
- full: 50,
355
+ none: _radius("0", 0),
356
+ small: _radius("2", 4),
357
+ medium: _radius("4", 8),
358
+ large: _radius("6", 14),
359
+ full: _radius("12", 44),
360
+ };
361
+ const _CONTAINER_RADIUS_KNOB: Record<string, number> = {
362
+ none: _radius("0", 0),
363
+ small: _radius("4", 8),
364
+ medium: _radius("6", 14),
365
+ large: _radius("8", 20),
366
+ full: _radius("12", 44),
362
367
  };
363
368
  const _OUTER_RADIUS_KNOB: Record<string, number> = {
364
- none: 0,
365
- small: 5,
366
- medium: 7,
367
- large: 9,
368
- full: 10,
369
+ none: _radius("0", 0),
370
+ small: _radius("2", 4),
371
+ medium: _radius("3", 6),
372
+ large: _radius("4", 8),
373
+ full: _radius("5", 9),
369
374
  };
370
375
  const _BORDER_WIDTH_KNOB: Record<string, number> = {
371
376
  none: 0,
@@ -374,19 +379,15 @@ export function runConstraintAudit(rootSelector?: string): AuditReport {
374
379
  large: 2,
375
380
  };
376
381
  const _GAP_KNOB: Record<string, number> = {
377
- small: 7,
378
- medium: 18,
379
- large: 24,
382
+ gapSm: _space("2", 6),
383
+ gap: _space("3", 12),
380
384
  };
381
385
  const _GAP_LG_KNOB: Record<string, number> = {
382
- small: 13,
383
- medium: 24,
384
- large: 32,
386
+ gapLg: _space("5", 21),
385
387
  };
386
388
  const _PANEL_PADDING_KNOB: Record<string, number> = {
387
- small: 13,
388
- medium: 18,
389
- large: 32,
389
+ panelPadding: _space("4", 16),
390
+ featurePadding: _space("6", 28),
390
391
  };
391
392
  const _FONT_WEIGHT_KNOB: Record<string, number> = {
392
393
  light: 300, // Inter body default weight
@@ -397,24 +398,29 @@ export function runConstraintAudit(rootSelector?: string): AuditReport {
397
398
  const _VALID_RADIUS = new Set([
398
399
  ...Object.values(_BORDER_RADIUS_KNOB),
399
400
  ...Object.values(_OUTER_RADIUS_KNOB),
401
+ ...Object.values(_CONTAINER_RADIUS_KNOB),
402
+ ...Object.values(_CONTAINER_RADIUS_KNOB).map((radius) =>
403
+ Math.min(radius, _PANEL_PADDING_KNOB.panelPadding),
404
+ ),
400
405
  1000, // Tamagui pill radius (Switch, RadioGroup.Item)
401
406
  100000, // Tamagui circular button radius (Button circular variant)
402
407
  ]);
403
408
  const _VALID_BORDER_WIDTH = new Set(Object.values(_BORDER_WIDTH_KNOB));
404
409
  const _VALID_GAP = new Set([
405
410
  0,
406
- 2, // $space.1 Tamagui space token
411
+ _SPACE_1,
407
412
  3.9, // Tamagui InputContainerFrame size-proportional gap
408
- 4, // $size.$0.5 sub-token used by Tamagui button internals
409
- 5, // $size.$1
410
- 9, // $size.$2
413
+ _SPACE_1_5,
414
+ _SIZE_0_5, // Tamagui button internals
415
+ ..._CONTROL_GAPS,
411
416
  ...Object.values(_GAP_KNOB),
412
417
  ...Object.values(_GAP_LG_KNOB),
413
418
  ]);
414
419
  const _VALID_PADDING = new Set([
415
420
  0,
416
- 2, // $space.1 Tamagui space token
417
- 7, // $size.$1.5 sub-token (button padding at $size.$2)
421
+ _SPACE_1,
422
+ _SPACE_2, // button padding at $size.$2
423
+ _SPACE_3, // a fixed-token inset (chips, OwnInset rows)
418
424
  ...Object.values(_PANEL_PADDING_KNOB),
419
425
  ]);
420
426
  const _VALID_FONT_WEIGHT = new Set(Object.values(_FONT_WEIGHT_KNOB));
@@ -422,6 +428,7 @@ export function runConstraintAudit(rootSelector?: string): AuditReport {
422
428
  const _FONT_FAMILY_KNOB: Record<string, string> = {
423
429
  inter: "sans-serif ($body/$heading)",
424
430
  georgia: "serif",
431
+ "ui-monospace": "mono",
425
432
  monospace: "mono",
426
433
  "roboto slab": "slab",
427
434
  rockwell: "slab (fallback)",
@@ -470,6 +477,10 @@ export function runConstraintAudit(rootSelector?: string): AuditReport {
470
477
  for (const [k, v] of Object.entries(_OUTER_RADIUS_KNOB)) {
471
478
  if (v === px) return `${k} (outer)`;
472
479
  }
480
+ for (const [k, v] of Object.entries(_CONTAINER_RADIUS_KNOB)) {
481
+ if (v === px) return `${k} (container)`;
482
+ if (Math.min(v, _PANEL_PADDING_KNOB.panelPadding) === px) return `${k} (container cap)`;
483
+ }
473
484
  return "off-scale";
474
485
  }
475
486
 
@@ -489,9 +500,10 @@ export function runConstraintAudit(rootSelector?: string): AuditReport {
489
500
  if (v === px) return `${k} (lg)`;
490
501
  }
491
502
  // Tamagui space/size sub-tokens used internally by components
492
- if (px === 2) return "space-token(2px)";
503
+ if (px === _SPACE_1) return `space-token(${px}px)`;
493
504
  if (px === 3.9) return "input-internal(3.9px)";
494
- if (px === 4 || px === 5 || px === 9) return `size-token(${px}px)`;
505
+ if (px === _SPACE_1_5 || px === _SIZE_0_5 || _CONTROL_GAPS.has(px))
506
+ return `size-token(${px}px)`;
495
507
  return "off-scale";
496
508
  }
497
509
 
@@ -501,8 +513,8 @@ export function runConstraintAudit(rootSelector?: string): AuditReport {
501
513
  if (v === px) return k;
502
514
  }
503
515
  // Tamagui space/size sub-tokens
504
- if (px === 2) return "space-token(2px)";
505
- if (px === 7) return "size-token(7px)";
516
+ if (px === _SPACE_1) return `space-token(${px}px)`;
517
+ if (px === _SPACE_2 || px === _SPACE_3) return `size-token(${px}px)`;
506
518
  return "off-scale";
507
519
  }
508
520
 
@@ -778,19 +790,15 @@ export function runConstraintAudit(rootSelector?: string): AuditReport {
778
790
  }
779
791
  if (capContainer) {
780
792
  const radiusKnob = el.getAttribute("data-radius-knob") ?? "";
781
- const spaceKnob = el.getAttribute("data-space-knob") ?? "";
782
- const radiusStop = Object.hasOwn(_BORDER_RADIUS_KNOB, radiusKnob)
783
- ? _BORDER_RADIUS_KNOB[radiusKnob]
784
- : undefined;
793
+ const radiusMap = containerName === "TextArea" ? _BORDER_RADIUS_KNOB : _CONTAINER_RADIUS_KNOB;
794
+ const radiusStop = Object.hasOwn(radiusMap, radiusKnob) ? radiusMap[radiusKnob] : undefined;
785
795
  const paddingSides = ["paddingTop", "paddingRight", "paddingBottom", "paddingLeft"] as const;
786
796
  const measuredInsets = paddingSides.map((prop) => parsePx(style[prop]));
787
797
  const inset = ownInset
788
798
  ? measuredInsets.some((v) => v === null)
789
799
  ? undefined
790
800
  : Math.min(...(measuredInsets as number[]))
791
- : Object.hasOwn(_PANEL_PADDING_KNOB, spaceKnob)
792
- ? _PANEL_PADDING_KNOB[spaceKnob]
793
- : undefined;
801
+ : _PANEL_PADDING_KNOB.panelPadding;
794
802
  if (ownsInset) {
795
803
  for (const [index, prop] of paddingSides.entries()) {
796
804
  const value = measuredInsets[index];
@@ -798,12 +806,12 @@ export function runConstraintAudit(rootSelector?: string): AuditReport {
798
806
  entry[prop] = {
799
807
  value: value ?? 0,
800
808
  unit: "px",
801
- knob: `${containerName} panelPadding:${spaceKnob}`,
809
+ knob: `${containerName} panelPadding`,
802
810
  offScale,
803
811
  };
804
812
  if (offScale)
805
813
  entry.violations.push(
806
- `${prop}: ${style[prop]} does not match ${containerName} panelPadding:${spaceKnob} (${inset ?? "unknown"}px)`,
814
+ `${prop}: ${style[prop]} does not match ${containerName} panelPadding (${inset ?? "unknown"}px)`,
807
815
  );
808
816
  }
809
817
  }
@@ -830,7 +838,7 @@ export function runConstraintAudit(rootSelector?: string): AuditReport {
830
838
  ? `${containerName} ${stackPosition} inner corner 0`
831
839
  : ownInset
832
840
  ? `${containerName} min(radius:${radiusKnob}, own padding)`
833
- : `${containerName} min(radius:${radiusKnob}, panelPadding:${spaceKnob})`,
841
+ : `${containerName} min(radius:${radiusKnob}, panelPadding)`,
834
842
  offScale,
835
843
  };
836
844
  entry[prop] = result;
@@ -947,7 +955,14 @@ export function runConstraintAudit(rootSelector?: string): AuditReport {
947
955
  // font_heading while changing its family. These are typography defaults,
948
956
  // not alternatives to an explicitly declared regular/bold recipe.
949
957
  const configuredHeading =
950
- el.classList.contains("font_heading") && [600, 700, 800].includes(fw);
958
+ (/^H[1-6]$/.test(el.tagName) ||
959
+ el.getAttribute("role") === "heading" ||
960
+ Array.from(el.classList).some((name) =>
961
+ /^font_heading(?:Serif|Mono|Slab|Rounded|Condensed|Cursive|Handwriting|Pixel|Blackletter|Geometric)?$/.test(
962
+ name,
963
+ ),
964
+ )) &&
965
+ [600, 700, 800].includes(fw);
951
966
  const expected =
952
967
  weightKnob !== null && ["regular", "bold"].includes(weightKnob)
953
968
  ? _FONT_WEIGHT_KNOB[weightKnob]
@@ -11,7 +11,7 @@ afterEach(() => {
11
11
 
12
12
  describe("constraint audit scope and painted visibility", () => {
13
13
  it("audits the requested surface while keeping the document-wide default", () => {
14
- document.body.innerHTML = `<div id="outside" style="border-radius:14px"></div><main id="story"><button id="inside" style="border-radius:15px">Inside</button></main>`;
14
+ document.body.innerHTML = `<div id="outside" style="border-radius:17px"></div><main id="story"><button id="inside" style="border-radius:15px">Inside</button></main>`;
15
15
  expect(audit("#story").violations.map((entry) => entry.id)).toEqual(["inside"]);
16
16
  expect(audit().violations.map((entry) => entry.id)).toEqual(["outside", "inside"]);
17
17
  });
@@ -0,0 +1,42 @@
1
+ import { generateScale } from "../theme/scale/generateScale";
2
+ import { defaultPreset } from "../theme/presets";
3
+ import { defaultFonts } from "../theme/defaults/fonts";
4
+ import { createThemesBuilder } from "../theme/createThemes";
5
+ import { defaultBaseTheme } from "../theme/defaults/base";
6
+ import { defaultAccentTheme } from "../theme/defaults/accent";
7
+ import { defaultBuilderOptions } from "../theme/defaults/builderOptions";
8
+
9
+ export function exportDesignTokens() {
10
+ const scale = generateScale(defaultPreset.scale, "web");
11
+ const themes = createThemesBuilder(
12
+ defaultBaseTheme,
13
+ defaultAccentTheme,
14
+ defaultBuilderOptions,
15
+ ).themes();
16
+ const values = (table: Record<string, unknown>) =>
17
+ Object.values(table).map((value) =>
18
+ value && typeof value === "object" && "val" in value ? value.val : value,
19
+ );
20
+ const unique = <T>(items: T[]) => [...new Set(items)];
21
+ const colors = (scheme: "light" | "dark") =>
22
+ unique(
23
+ Object.entries(themes)
24
+ .filter(([name]) => name === scheme || name.startsWith(`${scheme}_`))
25
+ .flatMap(([, theme]) => values(theme))
26
+ .filter((value): value is string => typeof value === "string"),
27
+ );
28
+ return {
29
+ preset: "default",
30
+ inputs: scale.inputs,
31
+ space: unique(Object.values(scale.tokens.space)),
32
+ radius: unique(Object.values(scale.tokens.radius)),
33
+ fontSize: unique([
34
+ ...Object.values(scale.fonts.bodySizes),
35
+ ...Object.values(scale.fonts.headingSizes),
36
+ ]),
37
+ fontWeight: unique(
38
+ Object.values(defaultFonts).flatMap((font) => values(font.weight ?? {}).map(String)),
39
+ ),
40
+ color: { light: colors("light"), dark: colors("dark") },
41
+ };
42
+ }
@@ -156,7 +156,7 @@ describe("theme-axis geometry invariance (LC-80 tripwire)", () => {
156
156
  // theme colour. Geometry becomes a function of theme and the theme
157
157
  // axis is no longer paint-only.
158
158
  const inputsForTheme = (theme: string): Partial<SizeRecipeInputs> =>
159
- theme === "blue" ? { baseHeight: 48 } : {};
159
+ theme === "blue" ? { control: 48, type: 16 } : {};
160
160
 
161
161
  const defaultCell = snapshotFromRecipeInputs(inputsForTheme(""));
162
162
  const blueCell = snapshotFromRecipeInputs(inputsForTheme("blue"));
@@ -205,7 +205,7 @@ describe("preset-axis no-breakage (two assertions, never one)", () => {
205
205
  // still passes no-breakage as long as nothing broke. Geometry equality
206
206
  // belongs exclusively to the theme axis.
207
207
  const defaultCell = snapshotFromRecipeInputs({});
208
- const boldCell = snapshotFromRecipeInputs({ baseHeight: 52, radiusPx: 50 });
208
+ const boldCell = snapshotFromRecipeInputs({ control: 52, radiusPx: 50 });
209
209
  expect(diffGeometry(defaultCell, boldCell).identical).toBe(false);
210
210
 
211
211
  const result = evaluateNoBreakage({
@@ -296,6 +296,87 @@ describe("preset-axis no-breakage (two assertions, never one)", () => {
296
296
  ).toBe(true);
297
297
  });
298
298
 
299
+ it("reads a known off-scale part at a scaled px as the same finding, not a new one", () => {
300
+ const input = {
301
+ storyRendered: true,
302
+ offScaleViolations: 1,
303
+ baselineOffScaleViolations: 1,
304
+ baselineOffScaleSignatures: [
305
+ "[k1] field :: paddingRight: 15px is off-scale (no matching token)",
306
+ ],
307
+ offScaleSignatures: ["[k1] field :: paddingRight: 11px is off-scale (no matching token)"],
308
+ contrast: cleanContrast,
309
+ baselineContrastViolations: 0,
310
+ };
311
+ expect(evaluateNoBreakage(input).ok).toBe(true);
312
+ const moved = evaluateNoBreakage({
313
+ ...input,
314
+ offScaleViolations: 2,
315
+ offScaleSignatures: [
316
+ ...input.offScaleSignatures,
317
+ "[k2] field :: paddingLeft: 11px is off-scale (no matching token)",
318
+ ],
319
+ });
320
+ expect(moved.ok).toBe(false);
321
+ expect(moved.failures[0]).toContain("[k2] field :: paddingLeft: 11px");
322
+ expect(moved.failures[0]).not.toContain("[k1]");
323
+ });
324
+
325
+ it("reads a known inline colour at a re-themed value as the same finding, not a new one", () => {
326
+ const input = {
327
+ storyRendered: true,
328
+ offScaleViolations: 1,
329
+ baselineOffScaleViolations: 1,
330
+ baselineOffScaleSignatures: [
331
+ '[k1] body :: inlineColor: backgroundColor="rgb(248, 248, 248)" bypasses theme tokens',
332
+ ],
333
+ offScaleSignatures: [
334
+ '[k1] body :: inlineColor: backgroundColor="rgb(254, 246, 250)" bypasses theme tokens',
335
+ ],
336
+ contrast: cleanContrast,
337
+ baselineContrastViolations: 0,
338
+ };
339
+ expect(evaluateNoBreakage(input).ok).toBe(true);
340
+ const added = evaluateNoBreakage({
341
+ ...input,
342
+ offScaleViolations: 2,
343
+ offScaleSignatures: [
344
+ ...input.offScaleSignatures,
345
+ '[k2] chip :: inlineColor: color="#ff0000" bypasses theme tokens',
346
+ ],
347
+ });
348
+ expect(added.ok).toBe(false);
349
+ expect(added.failures[0]).toContain('[k2] chip :: inlineColor: color="#ff0000"');
350
+ });
351
+
352
+ it("reads a known off-scale text node in a preset's own font as the same finding, not a new one", () => {
353
+ const input = {
354
+ storyRendered: true,
355
+ offScaleViolations: 1,
356
+ baselineOffScaleViolations: 1,
357
+ baselineOffScaleSignatures: [
358
+ "[k1] span.is_BadgeText.is_Text.font_body :: fontWeight: 600 does not match a configured typography weight",
359
+ ],
360
+ offScaleSignatures: [
361
+ "[k1] span.is_BadgeText.is_Text.font_condensed :: fontWeight: 600 does not match a configured typography weight",
362
+ ],
363
+ contrast: cleanContrast,
364
+ baselineContrastViolations: 0,
365
+ };
366
+ expect(evaluateNoBreakage(input).ok).toBe(true);
367
+ const added = evaluateNoBreakage({
368
+ ...input,
369
+ offScaleViolations: 2,
370
+ offScaleSignatures: [
371
+ ...input.offScaleSignatures,
372
+ "[k2] a.is_Anchor.is_Text.font_condensed :: paddingTop: 10px is off-scale (no matching token)",
373
+ ],
374
+ });
375
+ expect(added.ok).toBe(false);
376
+ expect(added.failures[0]).toContain("[k2] a.is_Anchor.is_Text.font_condensed :: paddingTop");
377
+ expect(added.failures[0]).not.toContain("[k1]");
378
+ });
379
+
299
380
  it("names replacement text failures regardless of count and preserves repeated identities", () => {
300
381
  const old = {
301
382
  key: "old text",
@@ -1548,6 +1548,38 @@ function newEntries(before: string[], after: string[]): string[] {
1548
1548
  return added;
1549
1549
  }
1550
1550
 
1551
+ /**
1552
+ * An off-scale signature with its px, colour and font family blanked: the
1553
+ * element and property that left the scale, not the value it landed on. A
1554
+ * preset with its own token scale moves every px, one with its own colour
1555
+ * theme moves every colour, and one with its own body or heading font swaps
1556
+ * every text node's `font_<family>` class, so a part the default cell already
1557
+ * reports is the same finding at a scaled, re-themed or re-typed value, not a
1558
+ * new breakage.
1559
+ */
1560
+ export function offScaleIdentity(signature: string): string {
1561
+ return signature
1562
+ .replace(/-?\d+(?:\.\d+)?px/g, "#px")
1563
+ .replace(/(inlineColor: \w+=)"[^"]*"/g, '$1"#colour"')
1564
+ .replace(/\.font_[\w-]+/g, ".font_#family");
1565
+ }
1566
+
1567
+ /** `newEntries` by off-scale identity, reported as this cell's own signatures. */
1568
+ function newOffScaleEntries(before: string[], after: string[]): string[] {
1569
+ const added = newEntries(before.map(offScaleIdentity), after.map(offScaleIdentity));
1570
+ const pending = new Map<string, number>();
1571
+ for (const identity of added) pending.set(identity, (pending.get(identity) ?? 0) + 1);
1572
+ const named: string[] = [];
1573
+ for (const signature of after) {
1574
+ const identity = offScaleIdentity(signature);
1575
+ const count = pending.get(identity) ?? 0;
1576
+ if (count === 0) continue;
1577
+ pending.set(identity, count - 1);
1578
+ named.push(signature);
1579
+ }
1580
+ return named;
1581
+ }
1582
+
1551
1583
  const namedList = (entries: string[], limit = 6): string => {
1552
1584
  if (entries.length === 0) return "";
1553
1585
  const shown = entries.slice(0, limit).join("; ");
@@ -1575,7 +1607,7 @@ export function evaluateNoBreakage(input: NoBreakageInput): NoBreakageResult {
1575
1607
  }
1576
1608
  const structuralAdded =
1577
1609
  input.offScaleSignatures && input.baselineOffScaleSignatures
1578
- ? newEntries(input.baselineOffScaleSignatures, input.offScaleSignatures)
1610
+ ? newOffScaleEntries(input.baselineOffScaleSignatures, input.offScaleSignatures)
1579
1611
  : [];
1580
1612
  if (structuralAdded.length || input.offScaleViolations > input.baselineOffScaleViolations) {
1581
1613
  failures.push(