@hjmds/design-contracts 1.26.0 → 1.27.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 (278) hide show
  1. package/dist/agreement.d.ts +1 -0
  2. package/dist/agreement.d.ts.map +1 -1
  3. package/dist/agreement.js +2 -1
  4. package/dist/agreement.js.map +1 -1
  5. package/dist/auth-screen.d.ts +1 -1
  6. package/dist/behaviors.d.ts +1 -1
  7. package/dist/calendar.d.ts +1 -0
  8. package/dist/calendar.d.ts.map +1 -1
  9. package/dist/calendar.js +4 -1
  10. package/dist/calendar.js.map +1 -1
  11. package/dist/catalog.d.ts +78 -30
  12. package/dist/catalog.d.ts.map +1 -1
  13. package/dist/catalog.js +10 -5
  14. package/dist/catalog.js.map +1 -1
  15. package/dist/chart.d.ts +2 -0
  16. package/dist/chart.d.ts.map +1 -1
  17. package/dist/chart.js +1 -1
  18. package/dist/chart.js.map +1 -1
  19. package/dist/component-definitions.d.ts +1 -0
  20. package/dist/component-definitions.d.ts.map +1 -1
  21. package/dist/component-definitions.js +1 -0
  22. package/dist/component-definitions.js.map +1 -1
  23. package/dist/component-recipes.d.ts +53 -6
  24. package/dist/component-recipes.d.ts.map +1 -1
  25. package/dist/component-recipes.js +64 -10
  26. package/dist/component-recipes.js.map +1 -1
  27. package/dist/data-table.d.ts +11 -0
  28. package/dist/data-table.d.ts.map +1 -1
  29. package/dist/data-table.js +5 -0
  30. package/dist/data-table.js.map +1 -1
  31. package/dist/description-list.d.ts +3 -0
  32. package/dist/description-list.d.ts.map +1 -1
  33. package/dist/description-list.js +5 -1
  34. package/dist/description-list.js.map +1 -1
  35. package/dist/design-profile-layout.d.ts +28 -0
  36. package/dist/design-profile-layout.d.ts.map +1 -1
  37. package/dist/design-profile-layout.js +27 -3
  38. package/dist/design-profile-layout.js.map +1 -1
  39. package/dist/design-profile.d.ts.map +1 -1
  40. package/dist/design-profile.js +40 -8
  41. package/dist/design-profile.js.map +1 -1
  42. package/dist/field-group.d.ts +13 -0
  43. package/dist/field-group.d.ts.map +1 -1
  44. package/dist/field-group.js +17 -2
  45. package/dist/field-group.js.map +1 -1
  46. package/dist/foundations.d.ts +24 -11
  47. package/dist/foundations.d.ts.map +1 -1
  48. package/dist/foundations.js +40 -7
  49. package/dist/foundations.js.map +1 -1
  50. package/dist/heading.d.ts +5 -5
  51. package/dist/image-choice.d.ts +23 -0
  52. package/dist/image-choice.d.ts.map +1 -0
  53. package/dist/image-choice.js +26 -0
  54. package/dist/image-choice.js.map +1 -0
  55. package/dist/index.d.ts +1 -1
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +1 -1
  58. package/dist/index.js.map +1 -1
  59. package/dist/masonry.d.ts +8 -0
  60. package/dist/masonry.d.ts.map +1 -1
  61. package/dist/masonry.js +12 -1
  62. package/dist/masonry.js.map +1 -1
  63. package/dist/number-field.d.ts +2 -2
  64. package/dist/number-field.d.ts.map +1 -1
  65. package/dist/number-field.js +4 -2
  66. package/dist/number-field.js.map +1 -1
  67. package/dist/qr-code-recipe.d.ts +1 -0
  68. package/dist/qr-code-recipe.d.ts.map +1 -1
  69. package/dist/qr-code-recipe.js +3 -1
  70. package/dist/qr-code-recipe.js.map +1 -1
  71. package/dist/recipes.d.ts +1 -1
  72. package/dist/recipes.d.ts.map +1 -1
  73. package/dist/recipes.js +1 -1
  74. package/dist/recipes.js.map +1 -1
  75. package/dist/result.d.ts +9 -0
  76. package/dist/result.d.ts.map +1 -1
  77. package/dist/result.js +5 -0
  78. package/dist/result.js.map +1 -1
  79. package/dist/screen-patterns.d.ts +139 -2
  80. package/dist/screen-patterns.d.ts.map +1 -1
  81. package/dist/screen-patterns.js +140 -1
  82. package/dist/screen-patterns.js.map +1 -1
  83. package/dist/sheet.d.ts +15 -1
  84. package/dist/sheet.d.ts.map +1 -1
  85. package/dist/sheet.js +20 -1
  86. package/dist/sheet.js.map +1 -1
  87. package/dist/showcase.d.ts +27 -27
  88. package/dist/showcase.js +27 -27
  89. package/dist/showcase.js.map +1 -1
  90. package/dist/tag.d.ts +10 -2
  91. package/dist/tag.d.ts.map +1 -1
  92. package/dist/tag.js +3 -2
  93. package/dist/tag.js.map +1 -1
  94. package/dist/tags-input.d.ts +6 -0
  95. package/dist/tags-input.d.ts.map +1 -1
  96. package/dist/tags-input.js +3 -1
  97. package/dist/tags-input.js.map +1 -1
  98. package/dist/text-formats.d.ts +2 -2
  99. package/dist/top.d.ts +2 -2
  100. package/dist/transfer-list.d.ts +1 -1
  101. package/dist/transfer-list.d.ts.map +1 -1
  102. package/dist/transfer-list.js +2 -1
  103. package/dist/transfer-list.js.map +1 -1
  104. package/dist/upload-item.d.ts +1 -10
  105. package/dist/upload-item.d.ts.map +1 -1
  106. package/dist/upload-item.js +1 -2
  107. package/dist/upload-item.js.map +1 -1
  108. package/dist/version.d.ts +1 -1
  109. package/dist/version.js +1 -1
  110. package/dist/version.js.map +1 -1
  111. package/docs/activity-chip.md +4 -3
  112. package/docs/catalog-freeze.json +5 -2
  113. package/docs/chart.md +1 -1
  114. package/docs/consumer-policy.md +13 -6
  115. package/docs/description-list.md +4 -0
  116. package/docs/design-profile.md +28 -2
  117. package/docs/font-roles.md +1 -1
  118. package/docs/generated/component-maturity.md +5 -4
  119. package/docs/generated/renderer-evidence.json +92 -6
  120. package/docs/generated/renderer-evidence.md +5 -3
  121. package/docs/generated/showcase-manifest.json +49 -1
  122. package/docs/gravity-letters.md +7 -2
  123. package/docs/masonry.md +4 -2
  124. package/docs/screen-patterns.md +5 -2
  125. package/docs/showcase.md +7 -9
  126. package/docs/stable-promotion.md +13 -1
  127. package/docs/usage/README.md +40 -10
  128. package/docs/usage/SELECTION.md +30 -4
  129. package/docs/usage/STANDARD.md +11 -2
  130. package/docs/usage/components/account-switcher.md +98 -0
  131. package/docs/usage/components/activity-chip.md +4 -4
  132. package/docs/usage/components/badge.md +1 -1
  133. package/docs/usage/components/bezier-editor.md +99 -0
  134. package/docs/usage/components/bottom-cta.md +2 -1
  135. package/docs/usage/components/button.md +26 -12
  136. package/docs/usage/components/carousel.md +4 -4
  137. package/docs/usage/components/chart.md +1 -0
  138. package/docs/usage/components/chat-message.md +18 -12
  139. package/docs/usage/components/chat-screen.md +6 -3
  140. package/docs/usage/components/cohort-retention-table.md +83 -0
  141. package/docs/usage/components/collapsible.md +7 -2
  142. package/docs/usage/components/comment-thread-row.md +102 -0
  143. package/docs/usage/components/comment-thread-screen.md +27 -13
  144. package/docs/usage/components/composition-layout.md +1 -0
  145. package/docs/usage/components/context-menu.md +32 -15
  146. package/docs/usage/components/conversation-row.md +11 -4
  147. package/docs/usage/components/conversion-funnel.md +85 -0
  148. package/docs/usage/components/correlation-matrix.md +80 -0
  149. package/docs/usage/components/data-table.md +4 -0
  150. package/docs/usage/components/description-list.md +2 -1
  151. package/docs/usage/components/editor-screen.md +9 -5
  152. package/docs/usage/components/effect-surface.md +4 -2
  153. package/docs/usage/components/empty-state.md +2 -2
  154. package/docs/usage/components/event-attendance-card.md +104 -0
  155. package/docs/usage/components/expandable-search.md +105 -0
  156. package/docs/usage/components/file-picker.md +5 -0
  157. package/docs/usage/components/filter-recovery.md +97 -0
  158. package/docs/usage/components/follow-profile-card.md +100 -0
  159. package/docs/usage/components/heading.md +6 -2
  160. package/docs/usage/components/histogram-explorer.md +89 -0
  161. package/docs/usage/components/icon-button.md +18 -2
  162. package/docs/usage/components/icon-tile.md +12 -4
  163. package/docs/usage/components/image-choice-group.md +81 -0
  164. package/docs/usage/components/import-field-mapping.md +94 -0
  165. package/docs/usage/components/inline-edit.md +105 -0
  166. package/docs/usage/components/keyboard-avoidance-context.md +60 -0
  167. package/docs/usage/components/keyboard-avoiding.md +5 -1
  168. package/docs/usage/components/list-detail-screen.md +4 -4
  169. package/docs/usage/components/list.md +1 -1
  170. package/docs/usage/components/masonry.md +6 -7
  171. package/docs/usage/components/media-selection-screen.md +7 -5
  172. package/docs/usage/components/media-transport.md +104 -0
  173. package/docs/usage/components/message-composer.md +13 -3
  174. package/docs/usage/components/moderation-screen.md +5 -2
  175. package/docs/usage/components/notification-inbox-screen.md +3 -3
  176. package/docs/usage/components/notification-row.md +64 -8
  177. package/docs/usage/components/onboarding-screen.md +1 -1
  178. package/docs/usage/components/overview-screen.md +9 -3
  179. package/docs/usage/components/pagination.md +18 -3
  180. package/docs/usage/components/password-rules-field.md +90 -0
  181. package/docs/usage/components/permission-screen.md +1 -1
  182. package/docs/usage/components/photo-source-sheet.md +1 -1
  183. package/docs/usage/components/popover.md +6 -3
  184. package/docs/usage/components/pricing-choice.md +85 -0
  185. package/docs/usage/components/profile-summary.md +56 -8
  186. package/docs/usage/components/progress.md +3 -3
  187. package/docs/usage/components/qr-code.md +5 -9
  188. package/docs/usage/components/repeatable-field-group.md +92 -0
  189. package/docs/usage/components/reply-composer.md +83 -0
  190. package/docs/usage/components/reply-message.md +85 -0
  191. package/docs/usage/components/result.md +3 -3
  192. package/docs/usage/components/saved-items-screen.md +7 -5
  193. package/docs/usage/components/screen-layout.md +21 -10
  194. package/docs/usage/components/search-field.md +1 -1
  195. package/docs/usage/components/section.md +2 -2
  196. package/docs/usage/components/selection-action-bar.md +89 -0
  197. package/docs/usage/components/settings-screen.md +13 -11
  198. package/docs/usage/components/sheet.md +19 -7
  199. package/docs/usage/components/shortcut-recorder.md +77 -0
  200. package/docs/usage/components/split-action.md +94 -0
  201. package/docs/usage/components/streaming-answer.md +94 -0
  202. package/docs/usage/components/switch.md +6 -2
  203. package/docs/usage/components/table-column-settings.md +88 -0
  204. package/docs/usage/components/target-summary.md +79 -0
  205. package/docs/usage/components/text.md +11 -5
  206. package/docs/usage/components/toggle-feedback-button.md +94 -0
  207. package/docs/usage/components/top-bar.md +8 -12
  208. package/docs/usage/components/transfer-task.md +87 -0
  209. package/docs/usage/compositions/action-recovery-optimistic.md +18 -14
  210. package/docs/usage/compositions/action-recovery-save.md +18 -14
  211. package/docs/usage/compositions/action-recovery-undo.md +18 -14
  212. package/docs/usage/compositions/activity-status.md +1 -1
  213. package/docs/usage/compositions/adaptive-content.md +2 -1
  214. package/docs/usage/compositions/collection-detail.md +6 -5
  215. package/docs/usage/compositions/collection-editing.md +13 -6
  216. package/docs/usage/compositions/command-records.md +5 -4
  217. package/docs/usage/compositions/communication.md +7 -5
  218. package/docs/usage/compositions/compound-controls.md +7 -3
  219. package/docs/usage/compositions/content-transition-comparison.md +10 -5
  220. package/docs/usage/compositions/data-layouts.md +12 -16
  221. package/docs/usage/compositions/data-workflows.md +7 -4
  222. package/docs/usage/compositions/date-time-selection.md +5 -5
  223. package/docs/usage/compositions/design-profile-comparison.md +22 -6
  224. package/docs/usage/compositions/disclosure.md +14 -9
  225. package/docs/usage/compositions/document-resource.md +1 -1
  226. package/docs/usage/compositions/environment-matrix.md +16 -12
  227. package/docs/usage/compositions/expo-interactions.md +9 -8
  228. package/docs/usage/compositions/family-drawer.md +9 -5
  229. package/docs/usage/compositions/field-group.md +7 -3
  230. package/docs/usage/compositions/interaction-adapters.md +11 -9
  231. package/docs/usage/compositions/interaction-flow-apply.md +17 -15
  232. package/docs/usage/compositions/interaction-flow-search.md +11 -7
  233. package/docs/usage/compositions/live-list.md +12 -8
  234. package/docs/usage/compositions/navigation-bar-collection.md +8 -3
  235. package/docs/usage/compositions/optional-motion.md +2 -2
  236. package/docs/usage/compositions/paper-surface.md +11 -6
  237. package/docs/usage/compositions/purpose-input-comment.md +10 -4
  238. package/docs/usage/compositions/purpose-input-message.md +10 -4
  239. package/docs/usage/compositions/reference-first.md +4 -4
  240. package/docs/usage/compositions/reference-review.md +4 -4
  241. package/docs/usage/compositions/reference-settings.md +4 -4
  242. package/docs/usage/compositions/selection-motion.md +2 -1
  243. package/docs/usage/compositions/social-compositions.md +7 -6
  244. package/docs/usage/compositions/stea-event-ticket.md +2 -4
  245. package/docs/usage/compositions/stea-order-progress.md +19 -21
  246. package/docs/usage/compositions/stea-pixel-empty.md +26 -26
  247. package/docs/usage/compositions/task-controls.md +7 -2
  248. package/docs/usage/compositions/texture-comparison.md +5 -5
  249. package/docs/usage/compositions/toast-layout.md +7 -3
  250. package/docs/usage/compositions/toggle-feedback.md +1 -1
  251. package/docs/usage/compositions/visual-foundations.md +10 -5
  252. package/docs/usage/compositions/web-additions.md +5 -1
  253. package/docs/usage/screens/common-chat.md +5 -3
  254. package/docs/usage/screens/common-comments.md +14 -9
  255. package/docs/usage/screens/common-conversation-list.md +109 -0
  256. package/docs/usage/screens/common-inbox.md +5 -3
  257. package/docs/usage/screens/common-login.md +7 -4
  258. package/docs/usage/screens/common-profile.md +16 -12
  259. package/docs/usage/screens/common-saved.md +12 -8
  260. package/docs/usage/screens/common-search.md +16 -8
  261. package/docs/usage/screens/common-settings.md +12 -11
  262. package/docs/usage/screens/common-shell.md +12 -10
  263. package/docs/usage/screens/dashboard.md +59 -56
  264. package/docs/usage/screens/discovery-gallery.md +18 -9
  265. package/docs/usage/screens/flow-collection.md +10 -6
  266. package/docs/usage/screens/flow-editor.md +9 -6
  267. package/docs/usage/screens/flow-media.md +12 -7
  268. package/docs/usage/screens/flow-moderation.md +9 -3
  269. package/docs/usage/screens/flow-onboarding.md +13 -8
  270. package/docs/usage/screens/flow-permission.md +5 -3
  271. package/docs/usage/screens/landing.md +20 -14
  272. package/docs/usage/screens/mockup-studio.md +100 -88
  273. package/docs/usage/screens/notification-settings.md +9 -7
  274. package/docs/usage/screens/product-bento.md +22 -10
  275. package/docs/usage/screens/reference-comparison.md +2 -2
  276. package/docs/usage/tokens/font-roles.md +18 -2
  277. package/docs/usage/tokens/typography.md +22 -11
  278. package/package.json +7 -1
@@ -24,7 +24,23 @@ export const textRecipe = {
24
24
  medium: fontWeight.semibold,
25
25
  strong: fontWeight.bold,
26
26
  },
27
+ /**
28
+ * Variants whose omitted `emphasis` renders the variant's typography weight even without a design profile.
29
+ * Label's 500 has no emphasis step (`medium` is 600), and the shared `regular` default drew form labels at
30
+ * body weight (2026-10-17 user decision: label 500 on Web and Native). Other variants keep `regular`.
31
+ */
32
+ variantWeight: ["label"],
27
33
  };
34
+ /**
35
+ * Weight Text renders when the caller omits `emphasis`; `undefined` means `textRecipe.defaults.emphasis` applies.
36
+ * Inside a design profile every variant follows the profile's typography weight; outside one only
37
+ * `textRecipe.variantWeight` variants do. An explicit `emphasis` always wins and never reaches this resolver.
38
+ */
39
+ export function resolveTextDefaultWeight(variant, typographyTokens, designProfileActive) {
40
+ return designProfileActive || textRecipe.variantWeight.includes(variant)
41
+ ? typographyTokens[variant].fontWeight
42
+ : undefined;
43
+ }
28
44
  export const iconRecipe = {
29
45
  slots: ["root"],
30
46
  defaults: {
@@ -129,11 +145,13 @@ export const badgeRecipe = {
129
145
  },
130
146
  },
131
147
  tones: {
148
+ // A canvas-colored plate is invisible on the canvas, so neutral and brand badges read as plain text beside a
149
+ // tinted success badge (2026-10-17 review E3). The plate stays white (canvas) and the neutral border draws it.
132
150
  neutral: {
133
151
  content: semanticColors.content.secondary,
134
152
  outlineContent: semanticColors.content.secondary,
135
153
  background: semanticColors.canvas,
136
- border: null,
154
+ border: semanticColors.border.default,
137
155
  },
138
156
  strong: {
139
157
  content: semanticColors.content.inverse,
@@ -151,7 +169,7 @@ export const badgeRecipe = {
151
169
  // with one option switched on. The plate goes neutral and the brand
152
170
  // stays in the copy.
153
171
  background: semanticColors.canvas,
154
- border: null,
172
+ border: semanticColors.border.default,
155
173
  },
156
174
  info: {
157
175
  content: semanticColors.feedback.info.foreground,
@@ -326,7 +344,9 @@ export const listRecipe = {
326
344
  separators: {
327
345
  none: null,
328
346
  full: { insetStart: 0, insetEnd: 0 },
329
- indented: { insetStart: 40 + spacing.sm, insetEnd: 0 },
347
+ // Starts where the row text starts: row padding (xs, every ListRow density) + leading 40 + gap sm. Without the row
348
+ // padding the line began 8px before the text (2026-10-17 review E5).
349
+ indented: { insetStart: spacing.xs + 40 + spacing.sm, insetEnd: 0 },
330
350
  },
331
351
  background: null,
332
352
  };
@@ -687,9 +707,10 @@ export const switchRecipe = {
687
707
  rowLabelTextVariant: "body",
688
708
  slots: ["root", "track", "thumb", "label", "description"],
689
709
  defaults: { size: "medium" },
690
- // Preserve existing inline Web and full-row Native layouts. Settings can opt in
691
- // to one shared row without wrapping a switch in a second interactive target.
692
- presentationDefaults: { web: "inline", native: "row" },
710
+ // One default on both renderers: label/description first, track at the trailing edge
711
+ // (2026-10-17 web/native comparison: Web defaulted to an inline track-first switch, so the
712
+ // same settings toggle rendered mirrored). Compact toolbars can still pass `inline`.
713
+ presentationDefaults: { web: "row", native: "row" },
693
714
  // Descriptions beside a fixed track become a narrow column at larger text sizes.
694
715
  // Reuse the provider's large-text boundary so Web CSS and Native reflow together.
695
716
  stackedTextScale: largeTextThreshold,
@@ -777,6 +798,8 @@ export const selectionControlRecipe = {
777
798
  },
778
799
  },
779
800
  shapes: { checkbox: "sm", radio: "full" },
801
+ // Web drew 2px and Native 1px; Agreement and TransferList now reuse this indicator, so the width is one value.
802
+ indicatorBorderWidth: stroke.strong,
780
803
  presentations: {
781
804
  plain: {
782
805
  background: null,
@@ -890,6 +913,7 @@ export const bottomNavigationRecipe = {
890
913
  radius: "full",
891
914
  shadow: floatingSurfaceContract.shadow,
892
915
  maxWidth: 480,
916
+ contentMaxWidth: null,
893
917
  outerPaddingHorizontal: spacing.md,
894
918
  outerPaddingTop: spacing.xs,
895
919
  },
@@ -901,6 +925,11 @@ export const bottomNavigationRecipe = {
901
925
  radius: null,
902
926
  shadow: null,
903
927
  maxWidth: null,
928
+ /**
929
+ * The bar surface and its top edge span the viewport, but the destinations stay in a centered 480 column
930
+ * (the capsule width): on a 1280 desktop two items otherwise sat at opposite screen edges (2026-10-17 review D8).
931
+ */
932
+ contentMaxWidth: 480,
904
933
  outerPaddingHorizontal: 0,
905
934
  outerPaddingTop: 0,
906
935
  },
@@ -912,6 +941,7 @@ export const bottomNavigationRecipe = {
912
941
  radius: "xl",
913
942
  shadow: floatingSurfaceContract.shadow,
914
943
  maxWidth: 384,
944
+ contentMaxWidth: null,
915
945
  outerPaddingHorizontal: spacing.md,
916
946
  outerPaddingTop: spacing.xs,
917
947
  },
@@ -1189,7 +1219,20 @@ export const avatarRecipe = {
1189
1219
  background: semanticColors.canvas,
1190
1220
  content: semanticColors.content.secondary,
1191
1221
  border: semanticColors.border.default,
1222
+ /** Initials are bold; their size follows the avatar (Web `.hjm-avatar[data-size]` steps). */
1223
+ initialsWeight: "strong",
1192
1224
  };
1225
+ /**
1226
+ * Initials text step for an avatar diameter, shared by Web size tokens and Native numeric sizes
1227
+ * (2026-10-17 comparison: Native drew 12pt brand-colored initials at every size).
1228
+ */
1229
+ export function resolveAvatarInitialsTextVariant(size) {
1230
+ if (size < avatarRecipe.sizes.medium)
1231
+ return "label";
1232
+ if (size < avatarRecipe.sizes.xlarge)
1233
+ return "body";
1234
+ return "title";
1235
+ }
1193
1236
  export const statisticRecipe = {
1194
1237
  slots: [
1195
1238
  "group",
@@ -1268,6 +1311,12 @@ export const statisticRecipe = {
1268
1311
  },
1269
1312
  },
1270
1313
  };
1314
+ /**
1315
+ * Gap between adjacent buttons in one action group (2026-10-17 user rule: stacked bottom buttons looked
1316
+ * loose at 12). Stacked (column) groups use 8, inline (row) groups keep 12 so tap targets stay apart.
1317
+ * AlertDialog already used these values; Sheet/Dialog footers and composed screens now share them.
1318
+ */
1319
+ export const actionGroupRecipe = { inlineGap: spacing.sm, stackedGap: spacing.xs };
1271
1320
  export const sheetRecipe = {
1272
1321
  slots: ["backdrop", "positioner", "content", "handle", "header", "title", "body", "footer", "close"],
1273
1322
  defaults: { placement: "bottom", size: "auto" },
@@ -1307,7 +1356,8 @@ export const sheetRecipe = {
1307
1356
  height: 4,
1308
1357
  radius: "full",
1309
1358
  color: semanticColors.content.secondary,
1310
- visibleByDefault: false,
1359
+ // Bottom sheets now always offer drag-to-dismiss, so the handle is visible whenever it is allowed.
1360
+ visibleByDefault: true,
1311
1361
  showWhen: "swipe-dismiss-enabled",
1312
1362
  },
1313
1363
  header: { minHeight: control.minTouchTarget, gap: spacing.sm },
@@ -1325,7 +1375,8 @@ export const sheetRecipe = {
1325
1375
  footer: {
1326
1376
  color: semanticColors.content.body,
1327
1377
  textVariant: "body",
1328
- gap: spacing.sm,
1378
+ // Native stacks footer actions in a column.
1379
+ gap: actionGroupRecipe.stackedGap,
1329
1380
  paddingTop: spacing.sm,
1330
1381
  },
1331
1382
  transition: { enter: motionPreset.enter, exit: motionPreset.exit },
@@ -1408,8 +1459,8 @@ export const alertDialogRecipe = {
1408
1459
  error: formSupportContract.error,
1409
1460
  // Stacked: confirm on top, cancel below, tighter gap (reported 2026-09-27). Row stays [cancel][confirm].
1410
1461
  actions: {
1411
- gap: spacing.sm,
1412
- stackedGap: spacing.xs,
1462
+ gap: actionGroupRecipe.inlineGap,
1463
+ stackedGap: actionGroupRecipe.stackedGap,
1413
1464
  stackedOrder: "confirm-first",
1414
1465
  stackBelow: breakpoint.medium,
1415
1466
  minButtonWidth: 96,
@@ -1627,6 +1678,9 @@ export const bottomCtaRecipe = {
1627
1678
  paddingTop: spacing.sm,
1628
1679
  paddingBottom: spacing.sm,
1629
1680
  gap: spacing.sm,
1681
+ // The footer surface spans the viewport, but its copy and actions stop at the reading width (ScreenLayout's body
1682
+ // width): on a 1200px desktop or tablet stage each half-width button had stretched to ~440px (2026-10-17 review).
1683
+ contentMaxWidth: layout.readingMaxWidth,
1630
1684
  // A footer painted in the canvas tone left only a 1pt hairline between it
1631
1685
  // and the content scrolling underneath — a product author found that the
1632
1686
  // line cut a glyph in half at whatever scroll position the reader stopped