@multiplatform.one/forms 7.5.0 → 7.6.1

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 (233) hide show
  1. package/dist/cjs/Button/index.cjs +6 -10
  2. package/dist/cjs/Button/index.native.js +6 -10
  3. package/dist/cjs/Button/index.native.js.map +1 -1
  4. package/dist/cjs/ContextualSaveBar/UnsavedChangesDialog.cjs +0 -1
  5. package/dist/cjs/ContextualSaveBar/UnsavedChangesDialog.native.js +0 -1
  6. package/dist/cjs/ContextualSaveBar/UnsavedChangesDialog.native.js.map +1 -1
  7. package/dist/cjs/FieldDisplay/index.cjs +2 -1
  8. package/dist/cjs/FieldDisplay/index.native.js +9 -2
  9. package/dist/cjs/FieldDisplay/index.native.js.map +1 -1
  10. package/dist/cjs/FloatingPanel/index.cjs +8 -1
  11. package/dist/cjs/InputParts/index.cjs +64 -2
  12. package/dist/cjs/InputParts/index.native.js +70 -4
  13. package/dist/cjs/InputParts/index.native.js.map +1 -1
  14. package/dist/cjs/fields/Barcode/BarcodePreview.cjs +15 -9
  15. package/dist/cjs/fields/Barcode/BarcodePreview.native.js +15 -9
  16. package/dist/cjs/fields/Barcode/BarcodePreview.native.js.map +1 -1
  17. package/dist/cjs/fields/Combobox/index.cjs +14 -11
  18. package/dist/cjs/fields/Combobox/index.native.js +16 -11
  19. package/dist/cjs/fields/Combobox/index.native.js.map +1 -1
  20. package/dist/cjs/fields/FileUpload/index.cjs +66 -5
  21. package/dist/cjs/fields/FileUpload/index.native.js +85 -5
  22. package/dist/cjs/fields/FileUpload/index.native.js.map +1 -1
  23. package/dist/cjs/fields/Geolocation/geoValue.cjs +122 -0
  24. package/dist/cjs/fields/Geolocation/geoValue.native.js +151 -0
  25. package/dist/cjs/fields/Geolocation/geoValue.native.js.map +1 -0
  26. package/dist/cjs/fields/Geolocation/index.cjs +16 -29
  27. package/dist/cjs/fields/Geolocation/index.native.js +16 -29
  28. package/dist/cjs/fields/Geolocation/index.native.js.map +1 -1
  29. package/dist/cjs/fields/Icon/index.cjs +1 -0
  30. package/dist/cjs/fields/Icon/index.native.js +1 -0
  31. package/dist/cjs/fields/Icon/index.native.js.map +1 -1
  32. package/dist/cjs/fields/Input/index.cjs +175 -95
  33. package/dist/cjs/fields/Input/index.native.js +83 -4
  34. package/dist/cjs/fields/Input/index.native.js.map +1 -1
  35. package/dist/cjs/fields/MentionInput/index.cjs +6 -1
  36. package/dist/cjs/fields/MentionInput/index.native.js +6 -1
  37. package/dist/cjs/fields/MentionInput/index.native.js.map +1 -1
  38. package/dist/cjs/fields/OTPInput/index.cjs +7 -2
  39. package/dist/cjs/fields/OTPInput/index.native.js +7 -2
  40. package/dist/cjs/fields/OTPInput/index.native.js.map +1 -1
  41. package/dist/cjs/fields/PhoneInput/index.native.js +1 -0
  42. package/dist/cjs/fields/PhoneInput/index.native.js.map +1 -1
  43. package/dist/cjs/fields/SearchInput/index.native.js.map +1 -1
  44. package/dist/cjs/shared/utils.cjs +4 -14
  45. package/dist/cjs/shared/utils.native.js +6 -14
  46. package/dist/cjs/shared/utils.native.js.map +1 -1
  47. package/dist/esm/Button/index.mjs +6 -9
  48. package/dist/esm/Button/index.mjs.map +1 -1
  49. package/dist/esm/Button/index.native.js +6 -9
  50. package/dist/esm/Button/index.native.js.map +1 -1
  51. package/dist/esm/ContextualSaveBar/UnsavedChangesDialog.mjs +0 -1
  52. package/dist/esm/ContextualSaveBar/UnsavedChangesDialog.mjs.map +1 -1
  53. package/dist/esm/ContextualSaveBar/UnsavedChangesDialog.native.js +0 -1
  54. package/dist/esm/ContextualSaveBar/UnsavedChangesDialog.native.js.map +1 -1
  55. package/dist/esm/FieldDisplay/index.mjs +2 -1
  56. package/dist/esm/FieldDisplay/index.mjs.map +1 -1
  57. package/dist/esm/FieldDisplay/index.native.js +9 -2
  58. package/dist/esm/FieldDisplay/index.native.js.map +1 -1
  59. package/dist/esm/FloatingPanel/index.mjs +8 -1
  60. package/dist/esm/FloatingPanel/index.mjs.map +1 -1
  61. package/dist/esm/InputParts/index.mjs +64 -2
  62. package/dist/esm/InputParts/index.mjs.map +1 -1
  63. package/dist/esm/InputParts/index.native.js +70 -4
  64. package/dist/esm/InputParts/index.native.js.map +1 -1
  65. package/dist/esm/fields/Barcode/BarcodePreview.mjs +15 -9
  66. package/dist/esm/fields/Barcode/BarcodePreview.mjs.map +1 -1
  67. package/dist/esm/fields/Barcode/BarcodePreview.native.js +15 -9
  68. package/dist/esm/fields/Barcode/BarcodePreview.native.js.map +1 -1
  69. package/dist/esm/fields/Combobox/index.mjs +14 -11
  70. package/dist/esm/fields/Combobox/index.mjs.map +1 -1
  71. package/dist/esm/fields/Combobox/index.native.js +16 -11
  72. package/dist/esm/fields/Combobox/index.native.js.map +1 -1
  73. package/dist/esm/fields/FileUpload/index.mjs +66 -5
  74. package/dist/esm/fields/FileUpload/index.mjs.map +1 -1
  75. package/dist/esm/fields/FileUpload/index.native.js +85 -5
  76. package/dist/esm/fields/FileUpload/index.native.js.map +1 -1
  77. package/dist/esm/fields/Geolocation/geoValue.mjs +95 -0
  78. package/dist/esm/fields/Geolocation/geoValue.mjs.map +1 -0
  79. package/dist/esm/fields/Geolocation/geoValue.native.js +121 -0
  80. package/dist/esm/fields/Geolocation/geoValue.native.js.map +1 -0
  81. package/dist/esm/fields/Geolocation/index.mjs +15 -28
  82. package/dist/esm/fields/Geolocation/index.mjs.map +1 -1
  83. package/dist/esm/fields/Geolocation/index.native.js +15 -28
  84. package/dist/esm/fields/Geolocation/index.native.js.map +1 -1
  85. package/dist/esm/fields/Icon/index.mjs +1 -0
  86. package/dist/esm/fields/Icon/index.mjs.map +1 -1
  87. package/dist/esm/fields/Icon/index.native.js +1 -0
  88. package/dist/esm/fields/Icon/index.native.js.map +1 -1
  89. package/dist/esm/fields/Input/index.mjs +177 -98
  90. package/dist/esm/fields/Input/index.mjs.map +1 -1
  91. package/dist/esm/fields/Input/index.native.js +85 -7
  92. package/dist/esm/fields/Input/index.native.js.map +1 -1
  93. package/dist/esm/fields/MentionInput/index.mjs +6 -1
  94. package/dist/esm/fields/MentionInput/index.mjs.map +1 -1
  95. package/dist/esm/fields/MentionInput/index.native.js +6 -1
  96. package/dist/esm/fields/MentionInput/index.native.js.map +1 -1
  97. package/dist/esm/fields/OTPInput/index.mjs +7 -3
  98. package/dist/esm/fields/OTPInput/index.mjs.map +1 -1
  99. package/dist/esm/fields/OTPInput/index.native.js +7 -3
  100. package/dist/esm/fields/OTPInput/index.native.js.map +1 -1
  101. package/dist/esm/fields/PhoneInput/index.mjs.map +1 -1
  102. package/dist/esm/fields/PhoneInput/index.native.js +1 -0
  103. package/dist/esm/fields/PhoneInput/index.native.js.map +1 -1
  104. package/dist/esm/fields/SearchInput/index.mjs.map +1 -1
  105. package/dist/esm/fields/SearchInput/index.native.js.map +1 -1
  106. package/dist/esm/shared/utils.mjs +4 -14
  107. package/dist/esm/shared/utils.mjs.map +1 -1
  108. package/dist/esm/shared/utils.native.js +6 -14
  109. package/dist/esm/shared/utils.native.js.map +1 -1
  110. package/dist/jsx/Button/index.mjs +6 -9
  111. package/dist/jsx/Button/index.mjs.map +1 -1
  112. package/dist/jsx/Button/index.native.js +6 -10
  113. package/dist/jsx/Button/index.native.js.map +1 -1
  114. package/dist/jsx/ContextualSaveBar/UnsavedChangesDialog.mjs +0 -1
  115. package/dist/jsx/ContextualSaveBar/UnsavedChangesDialog.mjs.map +1 -1
  116. package/dist/jsx/ContextualSaveBar/UnsavedChangesDialog.native.js +0 -1
  117. package/dist/jsx/ContextualSaveBar/UnsavedChangesDialog.native.js.map +1 -1
  118. package/dist/jsx/FieldDisplay/index.mjs +2 -1
  119. package/dist/jsx/FieldDisplay/index.mjs.map +1 -1
  120. package/dist/jsx/FieldDisplay/index.native.js +9 -2
  121. package/dist/jsx/FieldDisplay/index.native.js.map +1 -1
  122. package/dist/jsx/FloatingPanel/index.mjs +8 -1
  123. package/dist/jsx/FloatingPanel/index.mjs.map +1 -1
  124. package/dist/jsx/InputParts/index.mjs +64 -2
  125. package/dist/jsx/InputParts/index.mjs.map +1 -1
  126. package/dist/jsx/InputParts/index.native.js +70 -4
  127. package/dist/jsx/InputParts/index.native.js.map +1 -1
  128. package/dist/jsx/fields/Barcode/BarcodePreview.mjs +15 -9
  129. package/dist/jsx/fields/Barcode/BarcodePreview.mjs.map +1 -1
  130. package/dist/jsx/fields/Barcode/BarcodePreview.native.js +15 -9
  131. package/dist/jsx/fields/Barcode/BarcodePreview.native.js.map +1 -1
  132. package/dist/jsx/fields/Combobox/index.mjs +14 -11
  133. package/dist/jsx/fields/Combobox/index.mjs.map +1 -1
  134. package/dist/jsx/fields/Combobox/index.native.js +16 -11
  135. package/dist/jsx/fields/Combobox/index.native.js.map +1 -1
  136. package/dist/jsx/fields/FileUpload/index.mjs +66 -5
  137. package/dist/jsx/fields/FileUpload/index.mjs.map +1 -1
  138. package/dist/jsx/fields/FileUpload/index.native.js +85 -5
  139. package/dist/jsx/fields/FileUpload/index.native.js.map +1 -1
  140. package/dist/jsx/fields/Geolocation/geoValue.mjs +95 -0
  141. package/dist/jsx/fields/Geolocation/geoValue.mjs.map +1 -0
  142. package/dist/jsx/fields/Geolocation/geoValue.native.js +151 -0
  143. package/dist/jsx/fields/Geolocation/geoValue.native.js.map +1 -0
  144. package/dist/jsx/fields/Geolocation/index.mjs +15 -28
  145. package/dist/jsx/fields/Geolocation/index.mjs.map +1 -1
  146. package/dist/jsx/fields/Geolocation/index.native.js +16 -29
  147. package/dist/jsx/fields/Geolocation/index.native.js.map +1 -1
  148. package/dist/jsx/fields/Icon/index.mjs +1 -0
  149. package/dist/jsx/fields/Icon/index.mjs.map +1 -1
  150. package/dist/jsx/fields/Icon/index.native.js +1 -0
  151. package/dist/jsx/fields/Icon/index.native.js.map +1 -1
  152. package/dist/jsx/fields/Input/index.mjs +177 -98
  153. package/dist/jsx/fields/Input/index.mjs.map +1 -1
  154. package/dist/jsx/fields/Input/index.native.js +83 -4
  155. package/dist/jsx/fields/Input/index.native.js.map +1 -1
  156. package/dist/jsx/fields/MentionInput/index.mjs +6 -1
  157. package/dist/jsx/fields/MentionInput/index.mjs.map +1 -1
  158. package/dist/jsx/fields/MentionInput/index.native.js +6 -1
  159. package/dist/jsx/fields/MentionInput/index.native.js.map +1 -1
  160. package/dist/jsx/fields/OTPInput/index.mjs +7 -3
  161. package/dist/jsx/fields/OTPInput/index.mjs.map +1 -1
  162. package/dist/jsx/fields/OTPInput/index.native.js +7 -2
  163. package/dist/jsx/fields/OTPInput/index.native.js.map +1 -1
  164. package/dist/jsx/fields/PhoneInput/index.mjs.map +1 -1
  165. package/dist/jsx/fields/PhoneInput/index.native.js +1 -0
  166. package/dist/jsx/fields/PhoneInput/index.native.js.map +1 -1
  167. package/dist/jsx/fields/SearchInput/index.mjs.map +1 -1
  168. package/dist/jsx/fields/SearchInput/index.native.js.map +1 -1
  169. package/dist/jsx/shared/utils.mjs +4 -14
  170. package/dist/jsx/shared/utils.mjs.map +1 -1
  171. package/dist/jsx/shared/utils.native.js +6 -14
  172. package/dist/jsx/shared/utils.native.js.map +1 -1
  173. package/package.json +7 -7
  174. package/src/Button/Button.density.spec.tsx +118 -32
  175. package/src/Button/Button.labelInk.spec.tsx +72 -19
  176. package/src/Button/Button.lc71.spec.tsx +10 -3
  177. package/src/Button/Button.stories.tsx +97 -7
  178. package/src/Button/index.tsx +22 -12
  179. package/src/ContextualSaveBar/UnsavedChangesDialog.tsx +0 -1
  180. package/src/FieldDisplay/FieldDisplay.spec.tsx +11 -2
  181. package/src/FieldDisplay/index.tsx +5 -0
  182. package/src/FloatingPanel/index.tsx +22 -1
  183. package/src/FloatingPanel/panelWidthContract.spec.ts +1 -0
  184. package/src/InputParts/index.tsx +100 -3
  185. package/src/fields/Barcode/BarcodePreview.spec.tsx +153 -5
  186. package/src/fields/Barcode/BarcodePreview.stories.tsx +104 -0
  187. package/src/fields/Barcode/BarcodePreview.tsx +47 -15
  188. package/src/fields/ColorPicker/PresetSwatches.spec.tsx +71 -0
  189. package/src/fields/ColorPicker/PresetSwatches.stories.tsx +94 -0
  190. package/src/fields/Combobox/Combobox.spec.tsx +64 -1
  191. package/src/fields/Combobox/index.tsx +22 -11
  192. package/src/fields/FileUpload/FileUpload.spec.tsx +71 -0
  193. package/src/fields/FileUpload/index.tsx +100 -3
  194. package/src/fields/Geolocation/Geolocation.spec.tsx +126 -0
  195. package/src/fields/Geolocation/geoValue.spec.ts +169 -0
  196. package/src/fields/Geolocation/geoValue.ts +149 -0
  197. package/src/fields/Geolocation/index.tsx +15 -24
  198. package/src/fields/Icon/Icon.stories.tsx +1 -1
  199. package/src/fields/Icon/index.tsx +1 -0
  200. package/src/fields/Input/index.tsx +180 -81
  201. package/src/fields/MentionInput/index.tsx +9 -1
  202. package/src/fields/OTPInput/OtpInput.spec.tsx +36 -4
  203. package/src/fields/OTPInput/index.tsx +18 -3
  204. package/src/fields/OTPInput/otpCellHeightTouch.spec.ts +44 -0
  205. package/src/fields/PhoneInput/index.tsx +1 -0
  206. package/src/fields/SearchInput/index.tsx +5 -3
  207. package/src/shared/utils.spec.ts +46 -0
  208. package/src/shared/utils.ts +31 -16
  209. package/types/Button/index.d.ts +8 -3
  210. package/types/Button/index.d.ts.map +1 -1
  211. package/types/ContextualSaveBar/UnsavedChangesDialog.d.ts.map +1 -1
  212. package/types/FieldDisplay/index.d.ts.map +1 -1
  213. package/types/InputParts/index.d.ts +9 -5
  214. package/types/InputParts/index.d.ts.map +1 -1
  215. package/types/fields/Barcode/BarcodePreview.d.ts +20 -9
  216. package/types/fields/Barcode/BarcodePreview.d.ts.map +1 -1
  217. package/types/fields/Checkbox/index.d.ts +3 -3
  218. package/types/fields/Combobox/index.d.ts.map +1 -1
  219. package/types/fields/FileUpload/index.d.ts.map +1 -1
  220. package/types/fields/Geolocation/geoValue.d.ts +45 -0
  221. package/types/fields/Geolocation/geoValue.d.ts.map +1 -0
  222. package/types/fields/Geolocation/index.d.ts.map +1 -1
  223. package/types/fields/Icon/index.d.ts.map +1 -1
  224. package/types/fields/Input/index.d.ts +18 -6
  225. package/types/fields/Input/index.d.ts.map +1 -1
  226. package/types/fields/MentionInput/index.d.ts.map +1 -1
  227. package/types/fields/OTPInput/index.d.ts +12 -0
  228. package/types/fields/OTPInput/index.d.ts.map +1 -1
  229. package/types/fields/PhoneInput/index.d.ts.map +1 -1
  230. package/types/fields/RadioGroup/index.d.ts +2 -2
  231. package/types/fields/SearchInput/index.d.ts.map +1 -1
  232. package/types/fields/ToggleGroup/index.d.ts +1 -1
  233. package/types/shared/utils.d.ts.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"names":["__defProp","Object","defineProperty","__getOwnPropDesc","getOwnPropertyDescriptor","__getOwnPropNames","getOwnPropertyNames","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","name","get","enumerable","__copyProps","to","from","except","desc","key","call","__toCommonJS","mod","value","utils_exports","clampRadiusForControl","clampRadiusForLargeComponent","clampRadiusInStateProps","getElevationWrapperProps","getFieldError","getFieldHeight","getOTPCellRadius","mergeFieldHandler","stripRadiusFromStateProps","useFormField","useResolvedValidators","module","exports","import_react","require","import_theme","import_Form","import_validateOn","_type_of","obj","Symbol","constructor","sizeToken","rows","arguments","length","sizeRecipeForToken","String","touch","isTouchSurface","height","clampRadius","radiusToken","maxTokenNum","fallback","startsWith","num","Number","parseInt","slice","OTP_RADIUS_STOPS","keys","borderRadiusMap","otpRadiusStop","_iteratorNormalCompletion","_didIteratorError","_iteratorError","_iterator","iterator","_step","next","done","stop","err","return","Error","heightPx","resolveRadiusClass","knobProps","elevationRecipe","_elevationRecipe_hoverKnobProps","_elevationRecipe_pressKnobProps","_elevationRecipe_focusVisibleKnobProps","elevation","hoverKnobProps","pressKnobProps","focusVisibleKnobProps","props","overflow","elevationChrome","hoverStyle","pressStyle","focusVisibleStyle","transition","animation","formContext","useFormContext","resolvedForm","form","resolved","useResolvedKnobs","compact","generatedId","useId","id","field","propError","fieldError","state","meta","errors","defaultIsEmpty","defaultIsEmpty2","v","required","validators","label","isEmpty","validateOn","hasSubmitted","useFormValidate","effective","resolveEffectiveValidateOn","useMemo","requiredValidator","requiredValidator2","param","onSubmit","existingOnSubmit","composedOnSubmit","_existingOnSubmit"],"sources":["../../../src/shared/utils.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["__defProp","Object","defineProperty","__getOwnPropDesc","getOwnPropertyDescriptor","__getOwnPropNames","getOwnPropertyNames","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","name","get","enumerable","__copyProps","to","from","except","desc","key","call","__toCommonJS","mod","value","utils_exports","clampRadiusForControl","clampRadiusForLargeComponent","clampRadiusInStateProps","getElevationWrapperProps","getFieldError","getFieldHeight","getOTPCellRadius","mergeFieldHandler","stripRadiusFromStateProps","useFormField","useResolvedValidators","module","exports","import_react","require","import_theme","import_Form","import_validateOn","_type_of","obj","Symbol","constructor","sizeToken","rows","arguments","length","sizeRecipeForToken","String","touch","isTouchSurface","height","clampRadius","radiusToken","maxTokenNum","fallback","startsWith","num","Number","parseInt","slice","OTP_RADIUS_STOPS","keys","borderRadiusMap","otpRadiusStop","_iteratorNormalCompletion","_didIteratorError","_iteratorError","_iterator","iterator","_step","next","done","stop","err","return","Error","heightPx","resolveRadiusClass","knobProps","elevationRecipe","_elevationRecipe_hoverKnobProps","_elevationRecipe_pressKnobProps","_elevationRecipe_focusVisibleKnobProps","elevation","hoverKnobProps","pressKnobProps","focusVisibleKnobProps","props","overflow","elevationChrome","hoverStyle","pressStyle","focusVisibleStyle","transition","animation","formContext","useFormContext","resolvedForm","form","resolved","useResolvedKnobs","compact","generatedId","useId","id","field","propError","fieldError","state","meta","errors","defaultIsEmpty","defaultIsEmpty2","v","required","validators","label","isEmpty","validateOn","hasSubmitted","useFormValidate","effective","resolveEffectiveValidateOn","useMemo","requiredValidator","requiredValidator2","param","onSubmit","existingOnSubmit","composedOnSubmit","_existingOnSubmit"],"sources":["../../../src/shared/utils.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multiplatform.one/forms",
3
- "version": "7.5.0",
3
+ "version": "7.6.1",
4
4
  "description": "Cross-platform form components with URL state management",
5
5
  "keywords": [
6
6
  "cross-platform",
@@ -70,9 +70,9 @@
70
70
  "perfect-freehand": "^1.2.3",
71
71
  "react-native-svg": "15.15.4",
72
72
  "tamagui": "2.7.6",
73
- "@multiplatform.one/table-primitives": "7.5.0",
74
- "@multiplatform.one/theme": "7.5.0",
75
- "@multiplatform.one/router": "7.5.0"
73
+ "@multiplatform.one/router": "7.6.1",
74
+ "@multiplatform.one/table-primitives": "7.6.1",
75
+ "@multiplatform.one/theme": "7.6.1"
76
76
  },
77
77
  "devDependencies": {
78
78
  "@tamagui/build": "2.7.6",
@@ -95,9 +95,9 @@
95
95
  "react-i18next": "^16.6.6",
96
96
  "react-native-web": "^0.21.2",
97
97
  "vitest": "^4.1.5",
98
- "@multiplatform.one/test-utils": "7.5.0",
99
- "@multiplatform.one/config": "7.5.0",
100
- "@multiplatform.one/storybook": "7.5.0"
98
+ "@multiplatform.one/config": "7.6.1",
99
+ "@multiplatform.one/storybook": "7.6.1",
100
+ "@multiplatform.one/test-utils": "7.6.1"
101
101
  },
102
102
  "peerDependencies": {
103
103
  "expo-haptics": "55.0.17",
@@ -2,17 +2,25 @@
2
2
  * @vitest-environment jsdom
3
3
  *
4
4
  * MPO-42 / LC-76: the density knob and the `compact` PROP must land the
5
- * SAME padX. Height stays 44 at both stops — density moves pad/gap only.
5
+ * SAME padX. Height stays 44 at both stops — density moves space, not size.
6
+ *
7
+ * LC-79 / LC-80: the internals come from the size recipe, so density picks
8
+ * the recipe FAMILY (`control` -> `controlCompact`) and never subtracts a
9
+ * hand-set delta. The delta drifted from the generated table at every token
10
+ * but `$4`, and at `$2` it INVERTED — `max(8, 7 - 2)` painted a compact
11
+ * Button 8px wider than the comfortable 7px one.
6
12
  */
7
13
 
8
14
  import { renderWithProviders } from "@multiplatform.one/test-utils";
9
- import { Preset } from "@multiplatform.one/theme";
15
+ import { Preset, recipeFamilies } from "@multiplatform.one/theme";
10
16
  import { cleanup, screen } from "@testing-library/react";
11
17
  import { afterEach, describe, expect, it } from "vitest";
12
- import { Button } from "./index";
18
+ import { Button, buttonDensityFamily, resolveButtonSizeRecipe } from "./index";
13
19
 
14
20
  afterEach(cleanup);
15
21
 
22
+ const sizes = ["$2", "$3", "$4"] as const;
23
+
16
24
  /** Last `_pl-Npx` / `_pr-Npx` class wins (paddingHorizontal expands to both). */
17
25
  function paddingPxFromClass(el: HTMLElement): number | null {
18
26
  let found: number | null = null;
@@ -23,56 +31,134 @@ function paddingPxFromClass(el: HTMLElement): number | null {
23
31
  return found;
24
32
  }
25
33
 
34
+ function gapPxFromClass(el: HTMLElement): number | null {
35
+ for (const c of String(el.className || "").split(/\s+/)) {
36
+ const m = c.match(/^_gap-(\d+)px$/);
37
+ if (m) return Number(m[1]);
38
+ }
39
+ return null;
40
+ }
41
+
42
+ function renderProp(size: (typeof sizes)[number]) {
43
+ renderWithProviders(
44
+ <Button compact size={size} icon={<span>i</span>}>
45
+ Save
46
+ </Button>,
47
+ );
48
+ return screen.getByRole("button");
49
+ }
50
+
51
+ function renderKnob(size: (typeof sizes)[number]) {
52
+ renderWithProviders(
53
+ <Preset overrides={{ density: "compact" }}>
54
+ <Button size={size} icon={<span>i</span>}>
55
+ Save
56
+ </Button>
57
+ </Preset>,
58
+ );
59
+ return screen.getByRole("button");
60
+ }
61
+
62
+ function renderComfortable(size: (typeof sizes)[number]) {
63
+ renderWithProviders(
64
+ <Button size={size} icon={<span>i</span>}>
65
+ Save
66
+ </Button>,
67
+ );
68
+ return screen.getByRole("button");
69
+ }
70
+
26
71
  describe("Button density knob = compact PROP (MPO-42)", () => {
27
72
  it("compact PROP and density:compact knob land the SAME padX", () => {
28
- renderWithProviders(
29
- <Button compact size="$4">
30
- Save
31
- </Button>,
32
- );
33
- const viaProp = screen.getByRole("button");
73
+ const viaProp = renderProp("$4");
34
74
  const propPad = paddingPxFromClass(viaProp);
35
75
  expect(viaProp.getAttribute("data-mp-density")).toBe("compact");
36
76
  expect(propPad).toBe(16);
37
77
  cleanup();
38
78
 
39
- renderWithProviders(
40
- <Preset overrides={{ density: "compact" }}>
41
- <Button size="$4">Save</Button>
42
- </Preset>,
43
- );
44
- const viaKnob = screen.getByRole("button");
45
- const knobPad = paddingPxFromClass(viaKnob);
79
+ const viaKnob = renderKnob("$4");
46
80
  expect(viaKnob.getAttribute("data-mp-density")).toBe("compact");
47
- expect(knobPad).toBe(propPad);
48
- expect(knobPad).toBe(16);
81
+ expect(paddingPxFromClass(viaKnob)).toBe(propPad);
82
+ expect(paddingPxFromClass(viaKnob)).toBe(16);
83
+ });
84
+
85
+ it("both channels land the SAME padX at every size step, not just $4", () => {
86
+ for (const size of sizes) {
87
+ const viaProp = paddingPxFromClass(renderProp(size));
88
+ cleanup();
89
+ const viaKnob = paddingPxFromClass(renderKnob(size));
90
+ cleanup();
91
+ expect(viaKnob, `padX at ${size}`).toBe(viaProp);
92
+ }
49
93
  });
50
94
 
51
95
  it("height stays 44 at comfortable and compact density stops", () => {
52
- renderWithProviders(<Button size="$4">Save</Button>);
53
- const comfortable = screen.getByRole("button");
96
+ const comfortable = renderComfortable("$4");
54
97
  expect(comfortable.getAttribute("data-mp-button-height")).toBe("44");
55
98
  expect(comfortable.className).toMatch(/_h-44px/);
56
99
  expect(paddingPxFromClass(comfortable)).toBe(18);
57
100
  cleanup();
58
101
 
59
- renderWithProviders(
60
- <Preset overrides={{ density: "compact" }}>
61
- <Button size="$4">Save</Button>
62
- </Preset>,
63
- );
64
- const viaKnob = screen.getByRole("button");
102
+ const viaKnob = renderKnob("$4");
65
103
  expect(viaKnob.getAttribute("data-mp-button-height")).toBe("44");
66
104
  expect(viaKnob.className).toMatch(/_h-44px/);
67
105
  cleanup();
68
106
 
69
- renderWithProviders(
70
- <Button compact size="$4">
71
- Save
72
- </Button>,
73
- );
74
- const viaProp = screen.getByRole("button");
107
+ const viaProp = renderProp("$4");
75
108
  expect(viaProp.getAttribute("data-mp-button-height")).toBe("44");
76
109
  expect(viaProp.className).toMatch(/_h-44px/);
77
110
  });
111
+
112
+ it("density never changes the painted height at any size step (LC-76)", () => {
113
+ for (const size of sizes) {
114
+ const relaxed = renderComfortable(size).getAttribute("data-mp-button-height");
115
+ cleanup();
116
+ const tight = renderKnob(size).getAttribute("data-mp-button-height");
117
+ cleanup();
118
+ expect(tight, `height at ${size}`).toBe(relaxed);
119
+ }
120
+ });
121
+ });
122
+
123
+ describe("Button density comes from the recipe family (LC-79 / LC-80)", () => {
124
+ it("density picks the family, it does not subtract from the recipe", () => {
125
+ expect(buttonDensityFamily("control", true)).toBe("controlCompact");
126
+ expect(buttonDensityFamily("control", false)).toBe("control");
127
+ // An explicit controlCompact consumer (Chip) does not tighten twice.
128
+ expect(buttonDensityFamily("controlCompact", true)).toBe("controlCompact");
129
+ });
130
+
131
+ it("compact padX is the generated controlCompact table, at every size", () => {
132
+ for (const size of sizes) {
133
+ const expected = resolveButtonSizeRecipe(size, "controlCompact").paddingHorizontal;
134
+ const viaKnob = paddingPxFromClass(renderKnob(size));
135
+ cleanup();
136
+ expect(viaKnob, `padX at ${size}`).toBe(expected);
137
+ }
138
+ });
139
+
140
+ it("compact is never WIDER than comfortable — the $2 inversion stays fixed", () => {
141
+ for (const size of sizes) {
142
+ const relaxed = paddingPxFromClass(renderComfortable(size)) ?? 0;
143
+ cleanup();
144
+ const tight = paddingPxFromClass(renderKnob(size)) ?? 0;
145
+ cleanup();
146
+ expect(tight, `padX at ${size}`).toBeLessThan(relaxed);
147
+ }
148
+ });
149
+
150
+ it("the inner gap stays the recipe gap — gap == radius at both stops", () => {
151
+ for (const size of sizes) {
152
+ const radius = recipeFamilies.controlCompact.radii[size];
153
+ const relaxed = gapPxFromClass(renderComfortable(size));
154
+ cleanup();
155
+ const tight = gapPxFromClass(renderKnob(size));
156
+ cleanup();
157
+ expect(tight, `gap at ${size}`).toBe(resolveButtonSizeRecipe(size, "controlCompact").gap);
158
+ expect(tight, `gap == radius at ${size}`).toBe(radius);
159
+ expect(relaxed, `comfortable gap at ${size}`).toBe(
160
+ resolveButtonSizeRecipe(size, "control").gap,
161
+ );
162
+ }
163
+ });
78
164
  });
@@ -9,21 +9,20 @@
9
9
  *
10
10
  * jsdom cannot cascade Tamagui CSS, so the DOM arm reads the atomic
11
11
  * classes the text node emits (`_col-color11`, `_fow-400`). Contrast is
12
- * the theme-table arm: intent `$color11` vs the page `$background` in
13
- * both schemes.
12
+ * the theme-table arm: intent `$color11` against EVERY page surface the
13
+ * house theme paints, in both schemes — `$background`/`$color1` (white)
14
+ * and `$color2` (#f9f9fa, what the gallery boards are shot on).
14
15
  */
15
16
 
16
17
  import { render, cleanup, screen } from "@testing-library/react";
17
18
  import {
18
19
  aaTextContrastRatio,
19
- contrastRatio,
20
20
  createDefaultThemeConfig,
21
21
  createThemesBuilder,
22
22
  defaultAccentTheme,
23
23
  defaultBaseTheme,
24
24
  defaultBuilderOptions,
25
- normalizeToHex,
26
- relativeLuminance,
25
+ measureContrast,
27
26
  } from "@multiplatform.one/theme";
28
27
  import { afterEach, describe, expect, it } from "vitest";
29
28
  import { TamaguiProvider, YStack } from "tamagui";
@@ -40,6 +39,15 @@ const houseThemes = createThemesBuilder(
40
39
 
41
40
  const INTENTS = ["error", "warning", "success"] as const;
42
41
  const SCHEMES = ["light", "dark"] as const;
42
+ /**
43
+ * The house theme has THREE page surfaces and they are not interchangeable:
44
+ * `$background` and `$color1` are pure white, `$color2` is #f9f9fa. Every
45
+ * gallery board paints `$color2` (actionFeedbackMain.tsx board root), so a
46
+ * contrast arm that only reads `$background` certifies on a surface the
47
+ * boards never shoot — the same shape of hole as measuring the FRAME
48
+ * instead of the text node. All three are measured here.
49
+ */
50
+ const WHITE_PAGE_SURFACES = ["background", "color1"] as const;
43
51
 
44
52
  function houseRender(ui: React.ReactElement, theme: "light" | "dark" = "light") {
45
53
  return render(
@@ -53,12 +61,13 @@ function labelNode(text: string): HTMLElement {
53
61
  return screen.getByText(text);
54
62
  }
55
63
 
56
- function contrastAgainstPage(ink: string, ground: string): number {
57
- const inkHex = normalizeToHex(ink);
58
- const groundHex = normalizeToHex(ground);
59
- expect(inkHex, `ink ${ink}`).toBeTruthy();
60
- expect(groundHex, `ground ${ground}`).toBeTruthy();
61
- return contrastRatio(relativeLuminance(inkHex!), relativeLuminance(groundHex!));
64
+ /**
65
+ * One implementation of the arithmetic (the point of exporting
66
+ * `measureContrast`), and the REPORTED 2-decimal ratio, so the number a
67
+ * failure prints is the number the verdict used.
68
+ */
69
+ function pageRatio(ink: string, ground: string, label: string): number {
70
+ return measureContrast({ foreground: ink, background: ground, label }).ratio;
62
71
  }
63
72
 
64
73
  describe("Button MPO-40 outlined intent label ink", () => {
@@ -121,23 +130,67 @@ describe("Button MPO-40 outlined intent label ink", () => {
121
130
  }
122
131
  });
123
132
 
124
- it("intent $color11 vs page ground is >= 4.5:1 in both schemes; on-fill $color is not", () => {
133
+ it("intent $color11 clears 4.5:1 on the white page surfaces in both schemes; on-fill $color never does", () => {
125
134
  for (const scheme of SCHEMES) {
126
- const ground = houseThemes[scheme].background;
127
135
  for (const intent of INTENTS) {
128
136
  const ink = houseThemes[`${scheme}_${intent}`].color11;
129
- const ratio = contrastAgainstPage(ink, ground);
130
- expect(
131
- ratio,
132
- `${scheme}_${intent}.color11 ${ink} vs ${ground}`,
133
- ).toBeGreaterThanOrEqual(aaTextContrastRatio);
137
+ for (const surface of WHITE_PAGE_SURFACES) {
138
+ const ground = houseThemes[scheme][surface];
139
+ const label = `${scheme}_${intent}.color11 ${ink} on ${scheme}.${surface} ${ground}`;
140
+ expect(pageRatio(ink, ground, label), label).toBeGreaterThanOrEqual(aaTextContrastRatio);
141
+ }
134
142
 
135
143
  const onFill = houseThemes[`${scheme}_${intent}_Button`].color;
144
+ const ground = houseThemes[scheme].background;
136
145
  expect(
137
- contrastAgainstPage(onFill, ground),
146
+ pageRatio(onFill, ground, `${scheme}_${intent}_Button.color`),
138
147
  `${scheme}_${intent}_Button.color is the on-fill foreground`,
139
148
  ).toBeLessThan(2);
140
149
  }
141
150
  }
142
151
  });
152
+
153
+ it("dark intent $color11 clears 4.5:1 on the $color2 page surface too", () => {
154
+ for (const intent of INTENTS) {
155
+ const ink = houseThemes[`dark_${intent}`].color11;
156
+ const ground = houseThemes.dark.color2;
157
+ const label = `dark_${intent}.color11 ${ink} on dark.color2 ${ground}`;
158
+ expect(pageRatio(ink, ground, label), label).toBeGreaterThanOrEqual(aaTextContrastRatio);
159
+ }
160
+ });
161
+
162
+ // PINNED MISS — MPO-40's own secondary finding, still OPEN.
163
+ //
164
+ // Fixing the plumbing (the arm above) was necessary, not sufficient. On
165
+ // $color2 — the surface every gallery board is shot on — light warning
166
+ // and success $color11 are under mpo's own floor, and those are exactly
167
+ // the numbers button-anatomy already prints for its CONTRACT row. The
168
+ // step that would close them is a PALETTE change (Radix yellow-11
169
+ // #9e6c00 / green-11 #218358), owned by the contrast-floor lane, not by
170
+ // the Button. colorRules.ts pins the same warning miss on the intent's
171
+ // own color1 (#fcfcf7, 4.44:1); this pins it on the page ground.
172
+ //
173
+ // These numbers are asserted EXACTLY on purpose. Step the palette and
174
+ // this test fails, which is the reminder to re-measure the board and
175
+ // tick MPO-40's contrast criterion — never a regression to re-baseline.
176
+ it("light warning/success $color11 are STILL under 4.5:1 on the $color2 page surface", () => {
177
+ const ground = houseThemes.light.color2;
178
+ const measured = Object.fromEntries(
179
+ INTENTS.map((intent): [string, number] => [
180
+ intent,
181
+ pageRatio(
182
+ houseThemes[`light_${intent}`].color11,
183
+ ground,
184
+ `light_${intent}.color11 on light.color2`,
185
+ ),
186
+ ]),
187
+ );
188
+ expect(measured, `light.color2 is ${ground}`).toEqual({
189
+ error: 4.95,
190
+ warning: 4.34,
191
+ success: 4.48,
192
+ });
193
+ expect(measured.warning).toBeLessThan(aaTextContrastRatio);
194
+ expect(measured.success).toBeLessThan(aaTextContrastRatio);
195
+ });
143
196
  });
@@ -6,7 +6,7 @@ import { cleanup, fireEvent, screen } from "@testing-library/react";
6
6
  import { renderWithProviders } from "@multiplatform.one/test-utils";
7
7
  import { Preset } from "@multiplatform.one/theme";
8
8
  import { afterEach, describe, expect, it, vi } from "vitest";
9
- import { Button, buttonDensityPad, buttonSizeRecipes, resolveButtonSizeRecipe } from "./index";
9
+ import { Button, buttonDensityFamily, buttonSizeRecipes, resolveButtonSizeRecipe } from "./index";
10
10
 
11
11
  afterEach(cleanup);
12
12
 
@@ -52,8 +52,15 @@ describe("Button size recipes", () => {
52
52
 
53
53
  describe("Button density ≠ size", () => {
54
54
  it("compact tightens pad without shrinking recipe height", () => {
55
- expect(buttonDensityPad(12, true)).toBe(10);
56
- expect(buttonDensityPad(12, false)).toBe(12);
55
+ // Density picks the FAMILY; it never subtracts from the recipe (LC-79).
56
+ expect(buttonDensityFamily("control", true)).toBe("controlCompact");
57
+ expect(buttonDensityFamily("control", false)).toBe("control");
58
+ expect(resolveButtonSizeRecipe("$4", "controlCompact").paddingHorizontal).toBeLessThan(
59
+ resolveButtonSizeRecipe("$4", "control").paddingHorizontal,
60
+ );
61
+ expect(resolveButtonSizeRecipe("$4", "controlCompact").height).toBe(
62
+ resolveButtonSizeRecipe("$4", "control").height,
63
+ );
57
64
  renderWithProviders(
58
65
  <Button compact size="$4">
59
66
  Save
@@ -1,7 +1,12 @@
1
1
  import { action } from "@multiplatform.one/storybook";
2
2
  import type { Meta, StoryObj } from "@storybook/react-native-web-vite";
3
3
  import { Activity, Airplay, Download, Trash } from "@tamagui/lucide-icons-2";
4
- import { Button as TamaguiButton, Paragraph, XStack, YStack } from "tamagui";
4
+ import {
5
+ FOCUS_RING_CLIPPED_OFFSET,
6
+ FOCUS_RING_MIN_CONTRAST,
7
+ ensureFocusVisibleRing,
8
+ } from "@multiplatform.one/theme";
9
+ import { Button as TamaguiButton, Paragraph, XStack, YStack, useTheme } from "tamagui";
5
10
  import { Button } from "./index";
6
11
 
7
12
  const pressed = (name: string) => () => action(name)();
@@ -57,11 +62,12 @@ export const Basic: Story = {
57
62
  options: ["$2", "$3", "$4", "$5", "$6"],
58
63
  description: "Tamagui size token",
59
64
  },
60
- variant: {
61
- control: "select",
62
- options: [undefined, "outlined"],
63
- description: "Structural variant",
64
- },
65
+ // The house fill channel is the `outlined` boolean. `variant="outlined"`
66
+ // is raw-tamagui API this component does not read (only the
67
+ // `@multiplatform.one/components` shim maps it), so a `variant` control
68
+ // here drove nothing — MPO-19 X3 measured the board's own "Outlined"
69
+ // specimen rendering as a filled button with a transparent edge.
70
+ outlined: { control: "boolean", description: "Outlined fill style" },
65
71
  chromeless: { control: "boolean", description: "Transparent, text-only style" },
66
72
  disabled: { control: "boolean", description: "Disable the button" },
67
73
  skeleton: { control: "boolean", description: "Show skeleton placeholder" },
@@ -210,7 +216,7 @@ export const Comparison: Story = {
210
216
  <Button icon={Activity} onPress={pressed("custom.icon")}>
211
217
  With Icon
212
218
  </Button>
213
- <Button variant="outlined" onPress={pressed("custom.outlined")}>
219
+ <Button outlined onPress={pressed("custom.outlined")}>
214
220
  Outlined
215
221
  </Button>
216
222
  <Button disabled disabledReason="Needs a completed draft">
@@ -241,3 +247,87 @@ export const Comparison: Story = {
241
247
  </XStack>
242
248
  ),
243
249
  };
250
+
251
+ /**
252
+ * MPO-41 FOCUS-RING BOARD. Mirrors design-mockups-v3
253
+ * gallery/html/action-and-feedback/button-state-focus.html so the board is
254
+ * re-shot from the repo instead of the retired scratchpad harness
255
+ * (mpo-main-wt @ 8bd3a90da, which no longer exists).
256
+ *
257
+ * `forceStyle="focusVisible"` paints the REAL focus-visible ring. Button reads
258
+ * `pageOutlineColor` outside the intent Theme and spreads it into
259
+ * focusVisibleStyle, so the ring stays neutral on an accent or error fill.
260
+ * Never spread FOCUS_VISIBLE_RING onto an intent Button here: inside the
261
+ * sub-theme `$outlineColor` re-resolves to the fill and measures 1.00:1.
262
+ *
263
+ * Row 3 is the deliberate FOCUS_VISIBLE_RING_INSET specimen (offset -2, the
264
+ * clipped-composite carve-out). On an intent fill it is EXPECTED to miss the
265
+ * 3:1 floor, which `parameters.focusRingBoard.expectedBelowFloor` tells the
266
+ * shooter; the old board reported those as severe findings because the
267
+ * specimen was built from a bare ensureFocusVisibleRing() that now returns 0.
268
+ * The shooter measures the painted composition (computed outline colour and
269
+ * offset, then the surface the band actually lands on), never the declaration.
270
+ */
271
+ /**
272
+ * The LC-71 §4 clipping carve-out at page scope. A consumer `focusVisibleStyle`
273
+ * REPLACES Button's own ring (it rides the `{...props}` spread), so the inset
274
+ * row has to carry the whole ring itself, and it resolves `$outlineColor` at
275
+ * PAGE scope the way Button does (DG-A11Y-01): inside the intent sub-theme the
276
+ * token derives a ring for the inverted surface, which is the 1.00:1 trap.
277
+ */
278
+ function InsetRow() {
279
+ const pageOutlineColor = useTheme().outlineColor?.val as string | undefined;
280
+ const inset = ensureFocusVisibleRing({
281
+ outlineOffset: FOCUS_RING_CLIPPED_OFFSET,
282
+ ...(pageOutlineColor ? { outlineColor: pageOutlineColor } : undefined),
283
+ });
284
+ return (
285
+ <XStack gap="$3" alignItems="center" flexWrap="wrap">
286
+ <Button testID="inset-accent" accent forceStyle="focusVisible" focusVisibleStyle={inset}>
287
+ Accent, offset -2
288
+ </Button>
289
+ <Button testID="inset-error" error forceStyle="focusVisible" focusVisibleStyle={inset}>
290
+ Error, offset -2
291
+ </Button>
292
+ </XStack>
293
+ );
294
+ }
295
+
296
+ export const StateFocus: Story = {
297
+ name: "State: focus (MPO-41 board)",
298
+ parameters: {
299
+ focusRingBoard: {
300
+ floor: FOCUS_RING_MIN_CONTRAST,
301
+ specimens: ["focus-default", "focus-accent", "focus-error", "inset-accent", "inset-error"],
302
+ expectedBelowFloor: ["inset-accent", "inset-error"],
303
+ },
304
+ docs: {
305
+ description: {
306
+ story:
307
+ 'MPO-41 board. Rings forced on with forceStyle="focusVisible" so light and dark ' +
308
+ "shoot in one pass. Rows: rest vs focus on the neutral fill; focus on accent and " +
309
+ "error (offset 0, band on the page ground, must be >= 3:1); the inset carve-out " +
310
+ "(offset -2, band on the control's own fill, expected below the floor on intent).",
311
+ },
312
+ },
313
+ },
314
+ render: () => (
315
+ <YStack testID="focus-ring-board" gap="$4" padding="$4" backgroundColor="$background">
316
+ <XStack gap="$3" alignItems="center" flexWrap="wrap">
317
+ <Button testID="rest">Rest</Button>
318
+ <Button testID="focus-default" forceStyle="focusVisible">
319
+ Focus
320
+ </Button>
321
+ </XStack>
322
+ <XStack gap="$3" alignItems="center" flexWrap="wrap">
323
+ <Button testID="focus-accent" accent forceStyle="focusVisible">
324
+ Accent focus
325
+ </Button>
326
+ <Button testID="focus-error" error forceStyle="focusVisible">
327
+ Error focus
328
+ </Button>
329
+ </XStack>
330
+ <InsetRow />
331
+ </YStack>
332
+ ),
333
+ };
@@ -76,13 +76,18 @@ export function resolveButtonSizeRecipe(
76
76
  return sizeRecipeForToken(size == null ? "$true" : String(size), { family });
77
77
  }
78
78
 
79
- /** Density tightens pad/gap only — never height, type, or icon (density ≠ size). */
80
- export function buttonDensityPad(padX: number, densityTight: boolean): number {
81
- return densityTight ? Math.max(8, padX - 2) : padX;
82
- }
83
-
84
- export function buttonDensityGap(gap: number, densityTight: boolean): number {
85
- return densityTight ? Math.max(4, gap - 1) : gap;
79
+ /**
80
+ * Density selects the recipe FAMILY, it does not subtract from the recipe
81
+ * (LC-79 / LC-80): control -> controlCompact, same heights, tighter ratios.
82
+ * A hand-set delta is a second density writer and it drifts from the
83
+ * generated table at every token but `$4` — it inverted at `$2`, where
84
+ * `max(8, 7 - 2)` painted a compact Button WIDER than a comfortable one.
85
+ */
86
+ export function buttonDensityFamily(
87
+ family: RecipeFamilyName,
88
+ densityTight: boolean,
89
+ ): RecipeFamilyName {
90
+ return densityTight ? "controlCompact" : family;
86
91
  }
87
92
 
88
93
  /**
@@ -416,9 +421,11 @@ export function Button({
416
421
  (warning && "warning") ||
417
422
  (success && "success") ||
418
423
  undefined;
419
- // compact is density (pad/gap), not a size step — do not pass it through
424
+ // compact is density, not a size step — do not pass it through
420
425
  // useResolvedKnobs (size stays on the size axis; LC-76). The PROP and the
421
- // density knob must share this channel so both land the same padX/gap.
426
+ // density knob share one channel: both pick the recipe FAMILY, the same
427
+ // way resolveKnobs picks it for every other control, so both land the
428
+ // same padX and the internals stay recipe-generated (LC-75 / LC-79).
422
429
  const { knobProps, control, text, disabledState } = useResolvedKnobs({
423
430
  intent,
424
431
  component: "Button",
@@ -430,10 +437,13 @@ export function Button({
430
437
  const grouped = useControlGrouped();
431
438
  const sizeProp = (props as { size?: SizeTokens | number }).size;
432
439
  const resolvedSize = sizeProp ?? knobProps.sizeToken;
433
- const recipe = resolveButtonSizeRecipe(resolvedSize, recipeFamily);
434
440
  const densityTight = compact !== undefined ? compact === true : knobProps.density === "compact";
435
- const padX = buttonDensityPad(recipe.paddingHorizontal, densityTight);
436
- const innerGap = buttonDensityGap(recipe.gap, densityTight);
441
+ const recipe = resolveButtonSizeRecipe(
442
+ resolvedSize,
443
+ buttonDensityFamily(recipeFamily, densityTight),
444
+ );
445
+ const padX = recipe.paddingHorizontal;
446
+ const innerGap = recipe.gap;
437
447
  if (skeleton) {
438
448
  return (
439
449
  <View
@@ -102,7 +102,6 @@ export function UnsavedChangesDialog({
102
102
  </Button>
103
103
  <Button
104
104
  error
105
- theme="red"
106
105
  actionRole="primary"
107
106
  data-mp-savebar-discard-confirm=""
108
107
  onPress={() => {