@hjmds/design-contracts 1.12.1 → 1.13.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 (288) hide show
  1. package/dist/avatar-fallback.d.ts +11 -0
  2. package/dist/avatar-fallback.d.ts.map +1 -1
  3. package/dist/avatar-fallback.js +21 -0
  4. package/dist/avatar-fallback.js.map +1 -1
  5. package/dist/base-recipes.d.ts +17 -0
  6. package/dist/base-recipes.d.ts.map +1 -1
  7. package/dist/base-recipes.js +17 -0
  8. package/dist/base-recipes.js.map +1 -1
  9. package/dist/catalog.d.ts +25 -0
  10. package/dist/catalog.d.ts.map +1 -1
  11. package/dist/command-palette.d.ts +14 -9
  12. package/dist/command-palette.d.ts.map +1 -1
  13. package/dist/command-palette.js +8 -9
  14. package/dist/command-palette.js.map +1 -1
  15. package/dist/component-recipes.d.ts +17 -0
  16. package/dist/component-recipes.d.ts.map +1 -1
  17. package/dist/component-recipes.js +5 -0
  18. package/dist/component-recipes.js.map +1 -1
  19. package/dist/provider-button.d.ts.map +1 -1
  20. package/dist/provider-button.js +3 -0
  21. package/dist/provider-button.js.map +1 -1
  22. package/dist/reactions.d.ts +10 -0
  23. package/dist/reactions.d.ts.map +1 -1
  24. package/dist/reactions.js +7 -0
  25. package/dist/reactions.js.map +1 -1
  26. package/dist/screen-patterns.d.ts +147 -0
  27. package/dist/screen-patterns.d.ts.map +1 -0
  28. package/dist/screen-patterns.js +149 -0
  29. package/dist/screen-patterns.js.map +1 -0
  30. package/dist/slider.d.ts +8 -0
  31. package/dist/slider.d.ts.map +1 -1
  32. package/dist/slider.js +6 -1
  33. package/dist/slider.js.map +1 -1
  34. package/dist/upload-item.d.ts +5 -0
  35. package/dist/upload-item.d.ts.map +1 -1
  36. package/dist/upload-item.js +5 -0
  37. package/dist/upload-item.js.map +1 -1
  38. package/dist/version.d.ts +1 -1
  39. package/dist/version.js +1 -1
  40. package/dist/version.js.map +1 -1
  41. package/docs/action-session.md +3 -3
  42. package/docs/agreement.md +5 -0
  43. package/docs/avatar-fallback.md +7 -0
  44. package/docs/bottom-navigation.md +6 -0
  45. package/docs/brand-boundary.md +1 -1
  46. package/docs/button-label.md +6 -0
  47. package/docs/clipboard.md +3 -0
  48. package/docs/command-palette.md +45 -2
  49. package/docs/consumer-policy.md +5 -1
  50. package/docs/data-table.md +6 -4
  51. package/docs/dialog.md +8 -2
  52. package/docs/form.md +51 -0
  53. package/docs/generated/component-maturity.md +1 -1
  54. package/docs/generated/renderer-evidence.json +3 -3
  55. package/docs/generated/renderer-evidence.md +1 -1
  56. package/docs/generated/showcase-manifest.json +1 -1
  57. package/docs/link.md +8 -0
  58. package/docs/migration-native-legacy-removal.md +45 -1
  59. package/docs/optional-adapters.md +1 -1
  60. package/docs/password-field.md +5 -0
  61. package/docs/product-composition-adoption.md +40 -0
  62. package/docs/progress.md +19 -1
  63. package/docs/provider-button.md +13 -0
  64. package/docs/result.md +3 -0
  65. package/docs/screen-chrome.md +10 -0
  66. package/docs/screen-patterns.md +376 -0
  67. package/docs/sheet.md +12 -0
  68. package/docs/splitter.md +8 -2
  69. package/docs/theming.md +36 -29
  70. package/docs/toggle-group.md +7 -0
  71. package/docs/tour.md +7 -1
  72. package/docs/tree.md +5 -2
  73. package/docs/upload-item.md +7 -0
  74. package/docs/usage/README.md +236 -0
  75. package/docs/usage/STANDARD.md +108 -0
  76. package/docs/usage/components/accordion.md +107 -0
  77. package/docs/usage/components/activity-heatmap.md +104 -0
  78. package/docs/usage/components/affix.md +86 -0
  79. package/docs/usage/components/agreement.md +129 -0
  80. package/docs/usage/components/alert-dialog.md +130 -0
  81. package/docs/usage/components/anchor.md +96 -0
  82. package/docs/usage/components/aspect-ratio.md +89 -0
  83. package/docs/usage/components/asset.md +126 -0
  84. package/docs/usage/components/auth-provider-button.md +116 -0
  85. package/docs/usage/components/auth-screen-layout.md +129 -0
  86. package/docs/usage/components/avatar.md +114 -0
  87. package/docs/usage/components/badge.md +84 -0
  88. package/docs/usage/components/bottom-cta.md +125 -0
  89. package/docs/usage/components/bottom-info.md +99 -0
  90. package/docs/usage/components/bottom-navigation.md +136 -0
  91. package/docs/usage/components/breadcrumb.md +81 -0
  92. package/docs/usage/components/button.md +118 -0
  93. package/docs/usage/components/calendar.md +122 -0
  94. package/docs/usage/components/card.md +110 -0
  95. package/docs/usage/components/carousel.md +113 -0
  96. package/docs/usage/components/celebration.md +96 -0
  97. package/docs/usage/components/chat-message.md +122 -0
  98. package/docs/usage/components/chat-screen.md +112 -0
  99. package/docs/usage/components/checkbox-group.md +104 -0
  100. package/docs/usage/components/checkbox.md +103 -0
  101. package/docs/usage/components/chip.md +104 -0
  102. package/docs/usage/components/code-block.md +111 -0
  103. package/docs/usage/components/collapsible.md +112 -0
  104. package/docs/usage/components/color-picker.md +86 -0
  105. package/docs/usage/components/combobox.md +137 -0
  106. package/docs/usage/components/command-palette.md +125 -0
  107. package/docs/usage/components/comment-thread-screen.md +125 -0
  108. package/docs/usage/components/container.md +98 -0
  109. package/docs/usage/components/content-transition.md +101 -0
  110. package/docs/usage/components/context-menu.md +136 -0
  111. package/docs/usage/components/counter-badge.md +107 -0
  112. package/docs/usage/components/data-table.md +122 -0
  113. package/docs/usage/components/date-picker.md +142 -0
  114. package/docs/usage/components/date-range-picker.md +111 -0
  115. package/docs/usage/components/description-list.md +103 -0
  116. package/docs/usage/components/design-system-provider.md +124 -0
  117. package/docs/usage/components/dialog.md +176 -0
  118. package/docs/usage/components/divider.md +89 -0
  119. package/docs/usage/components/editor-screen.md +126 -0
  120. package/docs/usage/components/effect-surface.md +120 -0
  121. package/docs/usage/components/empty-state.md +114 -0
  122. package/docs/usage/components/field.md +129 -0
  123. package/docs/usage/components/file-picker.md +114 -0
  124. package/docs/usage/components/floating-action-button.md +138 -0
  125. package/docs/usage/components/form.md +162 -0
  126. package/docs/usage/components/grid.md +99 -0
  127. package/docs/usage/components/heading.md +87 -0
  128. package/docs/usage/components/icon-button.md +126 -0
  129. package/docs/usage/components/icon.md +105 -0
  130. package/docs/usage/components/image.md +122 -0
  131. package/docs/usage/components/keyboard-avoiding.md +93 -0
  132. package/docs/usage/components/keyboard-dock.md +110 -0
  133. package/docs/usage/components/keyboard-form-scroll-view.md +95 -0
  134. package/docs/usage/components/keyboard-motion-provider.md +86 -0
  135. package/docs/usage/components/layout.md +117 -0
  136. package/docs/usage/components/link.md +121 -0
  137. package/docs/usage/components/list-detail-screen.md +103 -0
  138. package/docs/usage/components/list-row.md +124 -0
  139. package/docs/usage/components/list.md +119 -0
  140. package/docs/usage/components/load-more.md +115 -0
  141. package/docs/usage/components/masonry.md +109 -0
  142. package/docs/usage/components/media-selection-screen.md +119 -0
  143. package/docs/usage/components/mentions.md +119 -0
  144. package/docs/usage/components/menu.md +129 -0
  145. package/docs/usage/components/menubar.md +93 -0
  146. package/docs/usage/components/message-composer.md +124 -0
  147. package/docs/usage/components/moderation-screen.md +113 -0
  148. package/docs/usage/components/notice.md +106 -0
  149. package/docs/usage/components/notification-inbox-screen.md +97 -0
  150. package/docs/usage/components/notification-item.md +98 -0
  151. package/docs/usage/components/number-field.md +131 -0
  152. package/docs/usage/components/onboarding-screen.md +106 -0
  153. package/docs/usage/components/otp-field.md +101 -0
  154. package/docs/usage/components/pagination.md +82 -0
  155. package/docs/usage/components/password-field.md +137 -0
  156. package/docs/usage/components/permission-screen.md +107 -0
  157. package/docs/usage/components/photo-source-sheet.md +119 -0
  158. package/docs/usage/components/popover.md +108 -0
  159. package/docs/usage/components/profile-screen.md +89 -0
  160. package/docs/usage/components/progress.md +122 -0
  161. package/docs/usage/components/qr-code.md +122 -0
  162. package/docs/usage/components/radio-group.md +124 -0
  163. package/docs/usage/components/radio.md +104 -0
  164. package/docs/usage/components/result.md +116 -0
  165. package/docs/usage/components/saved-items-screen.md +126 -0
  166. package/docs/usage/components/screen-layout.md +119 -0
  167. package/docs/usage/components/search-field.md +120 -0
  168. package/docs/usage/components/search-screen.md +215 -0
  169. package/docs/usage/components/section.md +111 -0
  170. package/docs/usage/components/segmented-control.md +138 -0
  171. package/docs/usage/components/select.md +142 -0
  172. package/docs/usage/components/settings-screen.md +126 -0
  173. package/docs/usage/components/shared-transition-element.md +111 -0
  174. package/docs/usage/components/shared-transition-screen.md +86 -0
  175. package/docs/usage/components/sheet.md +151 -0
  176. package/docs/usage/components/side-panel.md +104 -0
  177. package/docs/usage/components/sidebar.md +107 -0
  178. package/docs/usage/components/skeleton.md +105 -0
  179. package/docs/usage/components/skip-nav.md +76 -0
  180. package/docs/usage/components/slider.md +121 -0
  181. package/docs/usage/components/sortable-collection.md +127 -0
  182. package/docs/usage/components/spinner.md +86 -0
  183. package/docs/usage/components/splitter.md +103 -0
  184. package/docs/usage/components/stack.md +93 -0
  185. package/docs/usage/components/statistic.md +123 -0
  186. package/docs/usage/components/steps.md +110 -0
  187. package/docs/usage/components/surface.md +91 -0
  188. package/docs/usage/components/swipe-actions.md +124 -0
  189. package/docs/usage/components/switch.md +120 -0
  190. package/docs/usage/components/tabs.md +134 -0
  191. package/docs/usage/components/tag.md +84 -0
  192. package/docs/usage/components/tags-input.md +111 -0
  193. package/docs/usage/components/text-area.md +112 -0
  194. package/docs/usage/components/text-format.md +75 -0
  195. package/docs/usage/components/text-transition.md +104 -0
  196. package/docs/usage/components/text.md +101 -0
  197. package/docs/usage/components/thinking-orb.md +105 -0
  198. package/docs/usage/components/timeline.md +105 -0
  199. package/docs/usage/components/toast.md +145 -0
  200. package/docs/usage/components/toggle-group.md +95 -0
  201. package/docs/usage/components/tooltip.md +103 -0
  202. package/docs/usage/components/top-bar.md +124 -0
  203. package/docs/usage/components/top.md +89 -0
  204. package/docs/usage/components/tour.md +118 -0
  205. package/docs/usage/components/transfer-list.md +115 -0
  206. package/docs/usage/components/tree.md +91 -0
  207. package/docs/usage/components/upload-item.md +99 -0
  208. package/docs/usage/components/virtual-list.md +105 -0
  209. package/docs/usage/components/visually-hidden.md +72 -0
  210. package/docs/usage/components/watermark.md +78 -0
  211. package/docs/usage/compositions/action-recovery-optimistic.md +180 -0
  212. package/docs/usage/compositions/action-recovery-save.md +235 -0
  213. package/docs/usage/compositions/action-recovery-undo.md +193 -0
  214. package/docs/usage/compositions/common-message.md +132 -0
  215. package/docs/usage/compositions/common-notification.md +101 -0
  216. package/docs/usage/compositions/compound-controls.md +186 -0
  217. package/docs/usage/compositions/data-layouts.md +157 -0
  218. package/docs/usage/compositions/disclosure.md +144 -0
  219. package/docs/usage/compositions/environment-matrix.md +139 -0
  220. package/docs/usage/compositions/expo-interactions.md +149 -0
  221. package/docs/usage/compositions/family-drawer.md +201 -0
  222. package/docs/usage/compositions/floating-action-button.md +197 -0
  223. package/docs/usage/compositions/input-sheet.md +148 -0
  224. package/docs/usage/compositions/interaction-adapters.md +190 -0
  225. package/docs/usage/compositions/interaction-flow-apply.md +205 -0
  226. package/docs/usage/compositions/interaction-flow-draft.md +188 -0
  227. package/docs/usage/compositions/interaction-flow-search.md +171 -0
  228. package/docs/usage/compositions/native-renderers.md +106 -0
  229. package/docs/usage/compositions/navigation-bar-collection.md +164 -0
  230. package/docs/usage/compositions/optional-adapters.md +169 -0
  231. package/docs/usage/compositions/optional-motion.md +109 -0
  232. package/docs/usage/compositions/photo-source.md +104 -0
  233. package/docs/usage/compositions/purpose-input-comment.md +110 -0
  234. package/docs/usage/compositions/purpose-input-message.md +119 -0
  235. package/docs/usage/compositions/reference-first.md +96 -0
  236. package/docs/usage/compositions/reference-review.md +107 -0
  237. package/docs/usage/compositions/reference-settings.md +107 -0
  238. package/docs/usage/compositions/selection-scope.md +174 -0
  239. package/docs/usage/compositions/stea-event-ticket.md +166 -0
  240. package/docs/usage/compositions/stea-flip-card.md +162 -0
  241. package/docs/usage/compositions/stea-order-progress.md +184 -0
  242. package/docs/usage/compositions/stea-otp-verify.md +215 -0
  243. package/docs/usage/compositions/stea-pixel-empty.md +140 -0
  244. package/docs/usage/compositions/stea-schedule-card.md +169 -0
  245. package/docs/usage/compositions/stea-stat-summary.md +154 -0
  246. package/docs/usage/compositions/time-selection.md +174 -0
  247. package/docs/usage/compositions/toast-layout.md +128 -0
  248. package/docs/usage/compositions/visual-foundations.md +185 -0
  249. package/docs/usage/compositions/web-additions.md +146 -0
  250. package/docs/usage/compositions/web-navigation.md +143 -0
  251. package/docs/usage/screens/common-chat.md +127 -0
  252. package/docs/usage/screens/common-comments.md +108 -0
  253. package/docs/usage/screens/common-inbox.md +110 -0
  254. package/docs/usage/screens/common-login.md +98 -0
  255. package/docs/usage/screens/common-profile.md +221 -0
  256. package/docs/usage/screens/common-saved.md +127 -0
  257. package/docs/usage/screens/common-search.md +274 -0
  258. package/docs/usage/screens/common-settings.md +126 -0
  259. package/docs/usage/screens/common-shell.md +108 -0
  260. package/docs/usage/screens/dashboard.md +245 -0
  261. package/docs/usage/screens/discovery-gallery.md +306 -0
  262. package/docs/usage/screens/flow-collection.md +96 -0
  263. package/docs/usage/screens/flow-editor.md +120 -0
  264. package/docs/usage/screens/flow-media.md +111 -0
  265. package/docs/usage/screens/flow-moderation.md +120 -0
  266. package/docs/usage/screens/flow-onboarding.md +193 -0
  267. package/docs/usage/screens/flow-permission.md +103 -0
  268. package/docs/usage/screens/landing.md +347 -0
  269. package/docs/usage/screens/mockup-studio.md +190 -0
  270. package/docs/usage/screens/notification-settings.md +206 -0
  271. package/docs/usage/screens/reference-comparison.md +159 -0
  272. package/docs/usage/templates/component.md +61 -0
  273. package/docs/usage/templates/composition.md +47 -0
  274. package/docs/usage/templates/screen.md +56 -0
  275. package/docs/usage/templates/token.md +32 -0
  276. package/docs/usage/tokens/color.md +142 -0
  277. package/docs/usage/tokens/elevation-opacity.md +86 -0
  278. package/docs/usage/tokens/layers.md +98 -0
  279. package/docs/usage/tokens/layout.md +114 -0
  280. package/docs/usage/tokens/motion.md +88 -0
  281. package/docs/usage/tokens/radius.md +53 -0
  282. package/docs/usage/tokens/size.md +74 -0
  283. package/docs/usage/tokens/spacing.md +73 -0
  284. package/docs/usage/tokens/stroke.md +50 -0
  285. package/docs/usage/tokens/theme-studio.md +70 -0
  286. package/docs/usage/tokens/typography-studio.md +70 -0
  287. package/docs/usage/tokens/typography.md +89 -0
  288. package/package.json +7 -1
@@ -1 +1 @@
1
- {"version":3,"file":"upload-item.js","sourceRoot":"","sources":["../src/upload-item.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ColorReference } from \"./color-references.js\";\nimport type { BehaviorContract } from \"./behaviors.js\";\nimport { focusIndicatorContract } from \"./component-contracts.js\";\nimport {\n progressRecipe,\n type ProgressSize,\n type ProgressTone,\n} from \"./progress-recipe.js\";\nimport { control, layout, opacity, radius, spacing, type TextVariant } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\nexport type UploadItemStatus = \"pending\" | \"uploading\" | \"success\" | \"error\";\n\nexport type UploadItemPendingState = Readonly<{ status: \"pending\" }>;\n\nexport type UploadItemUploadingState = Readonly<{\n status: \"uploading\";\n /** Product-measured transfer fraction. `null` means indeterminate — size unknown. */\n progress: number | null;\n /** Product-formatted announcement, e.g. \"1.6MB / 3.2MB 업로드 중\". */\n progressLabel?: string;\n}>;\n\nexport type UploadItemSuccessState = Readonly<{ status: \"success\" }>;\n\nexport type UploadItemErrorState = Readonly<{\n status: \"error\";\n /** States the problem and the next action, e.g. \"네트워크 오류입니다. 다시 시도해주세요.\" */\n message: string;\n}>;\n\n/**\n * Cancel and retry are never stored as separate booleans: deriving them from\n * `status` (see `getUploadItemAvailableAction`) makes \"cancel while error\" or\n * \"retry while uploading\" unrepresentable, the same way `SheetOpenState` and\n * `LoadMoreState` close off invalid combinations by construction.\n */\nexport type UploadItemState =\n | UploadItemPendingState\n | UploadItemUploadingState\n | UploadItemSuccessState\n | UploadItemErrorState;\n\nexport type UploadItemDescriptor = Readonly<{\n id: string;\n name: string;\n /** Product-formatted size, e.g. \"1.2 MB\" — HJM does not format bytes. */\n sizeLabel?: string;\n state: UploadItemState;\n}>;\n\nexport type UploadItemLabels = Readonly<{\n pending: string;\n /** Fallback when `progress` is `null` or `progressLabel` is absent. */\n uploading: string;\n success: string;\n cancel: string;\n retry: string;\n}>;\n\nfunction assertNonEmpty(value: string, field: string): void {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TypeError(`UploadItem ${field} must not be empty`);\n }\n}\n\nexport function validateUploadItemState(state: UploadItemState): void {\n const status = (state as Readonly<{ status?: unknown }>).status;\n if (\n status !== \"pending\" &&\n status !== \"uploading\" &&\n status !== \"success\" &&\n status !== \"error\"\n ) {\n throw new TypeError(`Unsupported UploadItem status: ${String(status)}`);\n }\n if (status === \"uploading\") {\n const { progress, progressLabel } = state as UploadItemUploadingState;\n if (progress !== null && (!Number.isFinite(progress) || progress < 0 || progress > 1)) {\n throw new RangeError(\"UploadItem progress must be null or a number between 0 and 1\");\n }\n if (progressLabel !== undefined) assertNonEmpty(progressLabel, \"progressLabel\");\n }\n if (status === \"error\") {\n assertNonEmpty((state as UploadItemErrorState).message, \"error state message\");\n }\n}\n\nexport function validateUploadItemDescriptor(descriptor: UploadItemDescriptor): void {\n assertNonEmpty(descriptor.id, \"id\");\n assertNonEmpty(descriptor.name, \"name\");\n if (descriptor.sizeLabel !== undefined) assertNonEmpty(descriptor.sizeLabel, \"sizeLabel\");\n validateUploadItemState(descriptor.state);\n}\n\nexport function validateUploadItemLabels(labels: UploadItemLabels): void {\n for (const field of [\"pending\", \"uploading\", \"success\", \"cancel\", \"retry\"] as const) {\n assertNonEmpty(labels[field], `labels.${field}`);\n }\n}\n\n/** Rejects duplicate stable ids so a rendered list never desyncs a row's identity. */\nexport function validateUploadItemList(items: readonly UploadItemDescriptor[]): void {\n const ids = new Set<string>();\n for (const item of items) {\n validateUploadItemDescriptor(item);\n if (ids.has(item.id)) {\n throw new TypeError(`Duplicate UploadItem id: ${item.id}`);\n }\n ids.add(item.id);\n }\n}\n\nexport type UploadItemAction = \"cancel\" | \"retry\";\n\n/**\n * The only place cancel/retry availability is computed. Renderers must not\n * keep their own copy of this rule — it would drift from `status` the first\n * time a new state is added.\n */\nexport function getUploadItemAvailableAction(state: UploadItemState): UploadItemAction | null {\n if (state.status === \"uploading\") return \"cancel\";\n if (state.status === \"error\") return \"retry\";\n return null;\n}\n\nexport type UploadItemAnnouncement = Readonly<{\n /** Accessible name — the file name, kept independent of status (Statistic precedent). */\n label: string;\n /** Accessible description/live-region text. Never color-only. */\n description: string;\n}>;\n\n/**\n * Falls back to a rounded percent when a product supplies numeric `progress`\n * but no `progressLabel` — the one place this module computes visible copy\n * itself, mirroring Slider's raw-value fallback rather than Statistic's\n * strict \"product always formats\" rule, because an uploading row with no\n * numeric feedback at all is a worse default than a plain percentage.\n */\nexport function resolveUploadItemAnnouncement(\n descriptor: UploadItemDescriptor,\n labels: UploadItemLabels,\n): UploadItemAnnouncement {\n validateUploadItemDescriptor(descriptor);\n validateUploadItemLabels(labels);\n const { state } = descriptor;\n if (state.status === \"pending\") {\n return { label: descriptor.name, description: labels.pending };\n }\n if (state.status === \"uploading\") {\n if (state.progressLabel) {\n return { label: descriptor.name, description: state.progressLabel };\n }\n const description =\n state.progress === null ? labels.uploading : `${Math.round(state.progress * 100)}%`;\n return { label: descriptor.name, description };\n }\n if (state.status === \"success\") {\n return { label: descriptor.name, description: labels.success };\n }\n return { label: descriptor.name, description: state.message };\n}\n\nexport const uploadItemRecipe = {\n slots: [\"root\", \"icon\", \"name\", \"meta\", \"progress\", \"statusText\", \"cancel\", \"retry\"] as const,\n defaults: { size: \"medium\" },\n row: {\n minHeight: layout.rowHeight.twoLine,\n paddingHorizontal: spacing.md,\n gap: spacing.sm,\n radius: \"md\",\n },\n name: { color: semanticColors.content.body, textVariant: \"body\" },\n meta: { color: semanticColors.content.secondary, textVariant: \"label\" },\n statusTones: {\n pending: semanticColors.content.secondary,\n uploading: semanticColors.content.brand,\n success: semanticColors.feedback.success.foreground,\n error: semanticColors.content.danger,\n },\n /** Reuses `progressRecipe` verbatim — no second progress bar. */\n progress: {\n size: progressRecipe.defaults.size,\n tone: progressRecipe.defaults.tone,\n errorTone: \"danger\",\n },\n action: {\n minTarget: control.minTouchTarget,\n color: semanticColors.content.brand,\n dangerColor: semanticColors.content.danger,\n },\n states: {\n focus: focusIndicatorContract,\n disabledOpacity: opacity.disabled,\n },\n} as const satisfies {\n slots: readonly [\n \"root\",\n \"icon\",\n \"name\",\n \"meta\",\n \"progress\",\n \"statusText\",\n \"cancel\",\n \"retry\",\n ];\n defaults: { size: \"medium\" };\n row: { minHeight: number; paddingHorizontal: number; gap: number; radius: keyof typeof radius };\n name: { color: ColorReference; textVariant: TextVariant };\n meta: { color: ColorReference; textVariant: TextVariant };\n statusTones: Record<UploadItemStatus, ColorReference>;\n progress: { size: ProgressSize; tone: ProgressTone; errorTone: ProgressTone };\n action: { minTarget: number; color: ColorReference; dangerColor: ColorReference };\n states: { focus: typeof focusIndicatorContract; disabledOpacity: number };\n};\n\n/**\n * `stateAxes.content` maps status onto the common axis\n * (pending→idle, uploading→loading, success→complete, error→error).\n * `loadingMore`/`empty` do not apply — one row is never paginated.\n */\nexport const uploadItemBehavior = {\n controlled: [],\n inputs: [\"descriptor\", \"labels\"],\n events: [\"onCancel\", \"onRetry\"],\n stateAxes: {\n content: [\"idle\", \"loading\", \"complete\", \"error\"],\n },\n web: {\n roles: [\"group\", \"progressbar\", \"button\"],\n keyboard: [\"Tab\", \"Enter\", \"Space\"],\n focus: \"native\",\n },\n native: {\n roles: [\"progressbar\", \"button\"],\n states: [\"busy\"],\n actions: [\"cancel\", \"retry\"],\n },\n scenarios: [\n \"progress-is-announced-as-a-sentence-not-only-a-filled-bar\",\n \"cancel-is-reachable-only-while-uploading\",\n \"retry-is-reachable-only-while-error\",\n \"pending-and-success-expose-no-destructive-or-retry-action\",\n \"indeterminate-progress-falls-back-to-a-static-uploading-label\",\n \"reuses-the-shared-progress-recipe-instead-of-a-new-bar\",\n \"duplicate-ids-in-a-rendered-list-are-rejected\",\n ],\n} as const satisfies BehaviorContract;\n"]}
1
+ {"version":3,"file":"upload-item.js","sourceRoot":"","sources":["../src/upload-item.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ColorReference } from \"./color-references.js\";\nimport type { BehaviorContract } from \"./behaviors.js\";\nimport { focusIndicatorContract } from \"./component-contracts.js\";\nimport {\n progressRecipe,\n type ProgressSize,\n type ProgressTone,\n} from \"./progress-recipe.js\";\nimport { control, layout, opacity, radius, spacing, type TextVariant } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\nexport type UploadItemStatus = \"pending\" | \"uploading\" | \"success\" | \"error\";\n\nexport type UploadItemPendingState = Readonly<{ status: \"pending\" }>;\n\nexport type UploadItemUploadingState = Readonly<{\n status: \"uploading\";\n /** Product-measured transfer fraction. `null` means indeterminate — size unknown. */\n progress: number | null;\n /** Product-formatted announcement, e.g. \"1.6MB / 3.2MB 업로드 중\". */\n progressLabel?: string;\n}>;\n\nexport type UploadItemSuccessState = Readonly<{ status: \"success\" }>;\n\nexport type UploadItemErrorState = Readonly<{\n status: \"error\";\n /** States the problem and the next action, e.g. \"네트워크 오류입니다. 다시 시도해주세요.\" */\n message: string;\n}>;\n\n/**\n * Cancel and retry are never stored as separate booleans: deriving them from\n * `status` (see `getUploadItemAvailableAction`) makes \"cancel while error\" or\n * \"retry while uploading\" unrepresentable, the same way `SheetOpenState` and\n * `LoadMoreState` close off invalid combinations by construction.\n */\nexport type UploadItemState =\n | UploadItemPendingState\n | UploadItemUploadingState\n | UploadItemSuccessState\n | UploadItemErrorState;\n\nexport type UploadItemDescriptor = Readonly<{\n id: string;\n name: string;\n /** Product-formatted size, e.g. \"1.2 MB\" — HJM does not format bytes. */\n sizeLabel?: string;\n state: UploadItemState;\n}>;\n\nexport type UploadItemLabels = Readonly<{\n pending: string;\n /** Fallback when `progress` is `null` or `progressLabel` is absent. */\n uploading: string;\n success: string;\n cancel: string;\n retry: string;\n}>;\n\nfunction assertNonEmpty(value: string, field: string): void {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TypeError(`UploadItem ${field} must not be empty`);\n }\n}\n\nexport function validateUploadItemState(state: UploadItemState): void {\n const status = (state as Readonly<{ status?: unknown }>).status;\n if (\n status !== \"pending\" &&\n status !== \"uploading\" &&\n status !== \"success\" &&\n status !== \"error\"\n ) {\n throw new TypeError(`Unsupported UploadItem status: ${String(status)}`);\n }\n if (status === \"uploading\") {\n const { progress, progressLabel } = state as UploadItemUploadingState;\n if (progress !== null && (!Number.isFinite(progress) || progress < 0 || progress > 1)) {\n throw new RangeError(\"UploadItem progress must be null or a number between 0 and 1\");\n }\n if (progressLabel !== undefined) assertNonEmpty(progressLabel, \"progressLabel\");\n }\n if (status === \"error\") {\n assertNonEmpty((state as UploadItemErrorState).message, \"error state message\");\n }\n}\n\nexport function validateUploadItemDescriptor(descriptor: UploadItemDescriptor): void {\n assertNonEmpty(descriptor.id, \"id\");\n assertNonEmpty(descriptor.name, \"name\");\n if (descriptor.sizeLabel !== undefined) assertNonEmpty(descriptor.sizeLabel, \"sizeLabel\");\n validateUploadItemState(descriptor.state);\n}\n\nexport function validateUploadItemLabels(labels: UploadItemLabels): void {\n for (const field of [\"pending\", \"uploading\", \"success\", \"cancel\", \"retry\"] as const) {\n assertNonEmpty(labels[field], `labels.${field}`);\n }\n}\n\n/** Rejects duplicate stable ids so a rendered list never desyncs a row's identity. */\nexport function validateUploadItemList(items: readonly UploadItemDescriptor[]): void {\n const ids = new Set<string>();\n for (const item of items) {\n validateUploadItemDescriptor(item);\n if (ids.has(item.id)) {\n throw new TypeError(`Duplicate UploadItem id: ${item.id}`);\n }\n ids.add(item.id);\n }\n}\n\nexport type UploadItemAction = \"cancel\" | \"retry\";\n\n/**\n * The only place cancel/retry availability is computed. Renderers must not\n * keep their own copy of this rule — it would drift from `status` the first\n * time a new state is added.\n */\nexport function getUploadItemAvailableAction(state: UploadItemState): UploadItemAction | null {\n if (state.status === \"uploading\") return \"cancel\";\n if (state.status === \"error\") return \"retry\";\n return null;\n}\n\nexport type UploadItemAnnouncement = Readonly<{\n /** Accessible name — the file name, kept independent of status (Statistic precedent). */\n label: string;\n /** Accessible description/live-region text. Never color-only. */\n description: string;\n}>;\n\n/**\n * Falls back to a rounded percent when a product supplies numeric `progress`\n * but no `progressLabel` — the one place this module computes visible copy\n * itself, mirroring Slider's raw-value fallback rather than Statistic's\n * strict \"product always formats\" rule, because an uploading row with no\n * numeric feedback at all is a worse default than a plain percentage.\n */\nexport function resolveUploadItemAnnouncement(\n descriptor: UploadItemDescriptor,\n labels: UploadItemLabels,\n): UploadItemAnnouncement {\n validateUploadItemDescriptor(descriptor);\n validateUploadItemLabels(labels);\n const { state } = descriptor;\n if (state.status === \"pending\") {\n return { label: descriptor.name, description: labels.pending };\n }\n if (state.status === \"uploading\") {\n if (state.progressLabel) {\n return { label: descriptor.name, description: state.progressLabel };\n }\n const description =\n state.progress === null ? labels.uploading : `${Math.round(state.progress * 100)}%`;\n return { label: descriptor.name, description };\n }\n if (state.status === \"success\") {\n return { label: descriptor.name, description: labels.success };\n }\n return { label: descriptor.name, description: state.message };\n}\n\nexport const uploadItemRecipe = {\n slots: [\"root\", \"icon\", \"name\", \"meta\", \"progress\", \"statusText\", \"cancel\", \"retry\"] as const,\n defaults: { size: \"medium\" },\n row: {\n minHeight: layout.rowHeight.twoLine,\n paddingHorizontal: spacing.md,\n /**\n * Added 2026-10-06: Native hard-coded 8 and Web used 12 with a 64px floor, so the same row\n * measured 68 on Native and 64-ish+ on Web. One value now drives both renderers.\n */\n paddingVertical: spacing.xs,\n gap: spacing.sm,\n radius: \"md\",\n },\n name: { color: semanticColors.content.body, textVariant: \"body\" },\n meta: { color: semanticColors.content.secondary, textVariant: \"label\" },\n statusTones: {\n pending: semanticColors.content.secondary,\n uploading: semanticColors.content.brand,\n success: semanticColors.feedback.success.foreground,\n error: semanticColors.content.danger,\n },\n /** Reuses `progressRecipe` verbatim — no second progress bar. */\n progress: {\n size: progressRecipe.defaults.size,\n tone: progressRecipe.defaults.tone,\n errorTone: \"danger\",\n },\n action: {\n minTarget: control.minTouchTarget,\n color: semanticColors.content.brand,\n dangerColor: semanticColors.content.danger,\n },\n states: {\n focus: focusIndicatorContract,\n disabledOpacity: opacity.disabled,\n },\n} as const satisfies {\n slots: readonly [\n \"root\",\n \"icon\",\n \"name\",\n \"meta\",\n \"progress\",\n \"statusText\",\n \"cancel\",\n \"retry\",\n ];\n defaults: { size: \"medium\" };\n row: { minHeight: number; paddingHorizontal: number; paddingVertical: number; gap: number; radius: keyof typeof radius };\n name: { color: ColorReference; textVariant: TextVariant };\n meta: { color: ColorReference; textVariant: TextVariant };\n statusTones: Record<UploadItemStatus, ColorReference>;\n progress: { size: ProgressSize; tone: ProgressTone; errorTone: ProgressTone };\n action: { minTarget: number; color: ColorReference; dangerColor: ColorReference };\n states: { focus: typeof focusIndicatorContract; disabledOpacity: number };\n};\n\n/**\n * `stateAxes.content` maps status onto the common axis\n * (pending→idle, uploading→loading, success→complete, error→error).\n * `loadingMore`/`empty` do not apply — one row is never paginated.\n */\nexport const uploadItemBehavior = {\n controlled: [],\n inputs: [\"descriptor\", \"labels\"],\n events: [\"onCancel\", \"onRetry\"],\n stateAxes: {\n content: [\"idle\", \"loading\", \"complete\", \"error\"],\n },\n web: {\n roles: [\"group\", \"progressbar\", \"button\"],\n keyboard: [\"Tab\", \"Enter\", \"Space\"],\n focus: \"native\",\n },\n native: {\n roles: [\"progressbar\", \"button\"],\n states: [\"busy\"],\n actions: [\"cancel\", \"retry\"],\n },\n scenarios: [\n \"progress-is-announced-as-a-sentence-not-only-a-filled-bar\",\n \"cancel-is-reachable-only-while-uploading\",\n \"retry-is-reachable-only-while-error\",\n \"pending-and-success-expose-no-destructive-or-retry-action\",\n \"indeterminate-progress-falls-back-to-a-static-uploading-label\",\n \"reuses-the-shared-progress-recipe-instead-of-a-new-bar\",\n \"duplicate-ids-in-a-rendered-list-are-rejected\",\n ],\n} as const satisfies BehaviorContract;\n"]}
package/dist/version.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  /** Package release shown by documentation surfaces. Kept in sync by a test. */
2
- export declare const designSystemVersion: "1.12.1";
2
+ export declare const designSystemVersion: "1.13.0";
3
3
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  /** Package release shown by documentation surfaces. Kept in sync by a test. */
2
- export const designSystemVersion = "1.12.1";
2
+ export const designSystemVersion = "1.13.0";
3
3
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAiB,CAAC","sourcesContent":["/** Package release shown by documentation surfaces. Kept in sync by a test. */\nexport const designSystemVersion = \"1.12.1\" as const;\n"]}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAiB,CAAC","sourcesContent":["/** Package release shown by documentation surfaces. Kept in sync by a test. */\nexport const designSystemVersion = \"1.13.0\" as const;\n"]}
@@ -1,6 +1,6 @@
1
1
  # 공통 실행과 실패 복구
2
2
 
3
- 검토일: 2026-10-02 · 상태: 선택적 행동 API, Storybook 실험 단계
3
+ 검토일: 2026-10-02 · 상태: 선택적 행동 API, Storybook 배포(2026-10-06 사용자 승인, npm 게시와 별개)
4
4
 
5
5
  ## 문제와 기존 API의 관계
6
6
 
@@ -59,9 +59,9 @@ UI를 되돌렸다는 이유로 서버 취소 완료를 선언하거나 금융·
59
59
 
60
60
  ## 검증 화면과 범위
61
61
 
62
- Web/Native Storybook `실험/구성/공통 동작`의 저장과 재시도, 즉시 반영과 복구, 보관과 실행 취소.
62
+ Web/Native Storybook `배포/구성/피드백과 복구`의 저장과 재시도, 즉시 반영과 복구, 보관과 실행 취소.
63
63
  각각 기본·어두운 테마·큰 글자 상태. 데모만 350ms 인위 지연과 실패 주입을 제공하며 실제 API나
64
- 영속 저장소는 연결하지 않는다. 보관 데모에 자동 만료는 없다. 실험의 스토리북 배포는 사용자 승인 후다.
64
+ 영속 저장소는 연결하지 않는다. 보관 데모에 자동 만료는 없다. 세 예제는 2026-10-06 사용자 승인으로 `실험/구성/공통 동작`에서 스토리북 배포로 옮겼고, 이는 API 안정화·npm 게시와 별개다.
65
65
 
66
66
  회귀 검사는 중복 실행 차단, sync throw, 명시적인 재시도 허용, 낙관적 rollback, 새 세대 이후
67
67
  역순 성공/실패 무시, 구독 해제, 실패한 역연산의 상태 보존을 검증한다. 실기기·소비 앱 채택과는 별개다.
package/docs/agreement.md CHANGED
@@ -31,3 +31,8 @@
31
31
  **플랫폼 번역.** Web은 `group` + `checkbox` + 별도 링크/버튼, Native는 checkbox
32
32
  accessibility state와 `openDetail` action이다. 두 표면 모두 전체 동의 행이 목록의 제목
33
33
  역할을 하도록 `surface.sunken` 위에 놓는다.
34
+
35
+ **첫 상태도 알린다(2026-10-06).** `onStateChange`는 마운트 때 한 번 초기 상태로 불리고, 그 뒤 토글마다 불린다.
36
+ 이전에는 토글에서만 불려 `defaultCheckedIds`·`checkedIds`가 처음부터 필수를 채운 경우 첫 `satisfied: true`가 오지 않아,
37
+ 이 콜백에 연결한 제출 버튼이 꺼진 채 남았다. 파생 상태가 바뀔 때마다 부르는 대안은 호출 수가 렌더 횟수에 묶여 버렸다.
38
+ 호출 수를 세던 소비자는 마운트 1회가 늘어난다(Changeset `recipe-parity-followups`).
@@ -72,3 +72,10 @@ adds installed iPhone 17 / iOS 27 simulator captures and active/stopped CPU samp
72
72
  from the real upstream runtime. The inactive frame-loop correction is backed by
73
73
  those samples. This does not establish physical-device GPU timing, battery use,
74
74
  or exhaustive geometry and screen-reader behavior.
75
+
76
+ ## Initials (2026-10-06)
77
+
78
+ `resolveAvatarInitials(name, provided?)` is the one initials rule for both renderers: the first character of the first and
79
+ the last word, counted in code points. Web had taken the first two words and Native the first and last, so "Kim Min Jun"
80
+ read "KM" on Web and "KJ" on Native; Native also split surrogate pairs. First+last was kept because it shows the family and
81
+ given names instead of a middle name. Native `initials` still overrides (at most three code points).
@@ -1,5 +1,7 @@
1
1
  # BottomNavigation contract
2
2
 
3
+ 검토일: 2026-10-05
4
+
3
5
  `BottomNavigation`은 콘텐츠 panel을 바꾸는 `Tabs`가 아니라 앱의 안정된 최상위 route를
4
6
  이동합니다. 같은 destination 의미를 Web의 link와 React Native navigator tab으로 적응시키며,
5
7
  route state는 제품 router 한 곳에서만 소유합니다. 각 renderer의 환경 matrix, Web route-link
@@ -108,6 +110,10 @@ status/live role이나 별도 accessibility label을 추가하면 같은 정보
108
110
  - item target은 최소 44×44입니다. label은 항상 보이고 font scaling을 허용하며 고정 item 높이와
109
111
  한 줄 clipping을 사용하지 않습니다. 모든 destination을 동시에 유지해야 하는 persistent chrome의
110
112
  visual label은 최대 `1.4×`까지만 커지고, 원문 전체는 item의 접근성 이름으로 유지합니다.
113
+ Native에서는 명시적 Provider/value가 글자 배율을 style에 굽는 경로에도 같은 제한을
114
+ 적용합니다. 2026-10-05 번뚝 최대 글자 QA에서 native multiplier prop만 전달하면
115
+ controlled Provider의 3배 style에는 제한이 작동하지 않았습니다(BT-QA-021).
116
+ 기존 Provider API로 라벨에만 recipe 배율을 적용하며 본문·목적지 접근성 이름은 보존합니다.
111
117
  - safe-area bottom inset은 recipe의 최소 padding에 더합니다. `max(base, inset)`으로 대체하지
112
118
  않습니다.
113
119
  - 긴 label은 320px viewport의 Web geometry matrix와 React Native renderer matrix에서 확인합니다.
@@ -27,7 +27,7 @@ CSS 변수 재정의, 제품 자체 토큰 생성기)였고, 문서 세 개가
27
27
  ## 2. 모든 브랜드 팔레트는 대비 검사를 통과해야 한다
28
28
 
29
29
  `@hjmds/design-contracts/palette-contrast`의 `checkBrandPaletteContrast(brandPalette)`가 Provider와 같은 방식으로
30
- theme별 병합 결과를 검사하고, 기준 미달 쌍을 돌려준다. 빈 배열이어야 한다(MUST). 제품은 이 호출을
30
+ theme별 병합 결과를 검사하고, theme별 기준 미달 쌍을 돌려준다. 반환 객체의 `light`와 `dark` 배열이 모두 비어 있어야 한다(MUST). 제품은 이 호출을
31
31
  자기 테스트에 두어 팔레트 변경마다 실행한다. 전체 팔레트는 `checkPaletteContrast(palette)`를 쓴다.
32
32
 
33
33
  | 쌍 | 최소 | 근거 |
@@ -18,3 +18,9 @@ Web은 `-webkit-line-clamp`, Native는 `numberOfLines`로 같은 답을 쓴다.
18
18
 
19
19
  **숫자는 recipe에만 있다.** 스타일시트는 `--hjm-button-label-lines`를 읽고 렌더러가 그
20
20
  변수를 recipe에서 채운다 — 두 벌이 되면 언젠가 어긋난다.
21
+
22
+
23
+ 2026-10-06 독립 재구현의 Native 2배 글자에서 `numberOfLines` 상한만 풀고 버튼 높이는
24
+ 44로 고정해 긴 문구가 잘리는 것을 확인했다. 문자열·숫자 라벨은 recipe 높이를 최소값으로만
25
+ 사용하고 row 안에서 폭을 줄여 줄바꿈한다. 로딩은 같은 라벨의 공간을 유지한다. 이미지 등
26
+ 사용자 콘텐츠의 기본 고정 높이는 유지하며 `growWithContent`로 확장한다.
package/docs/clipboard.md CHANGED
@@ -11,3 +11,6 @@
11
11
  - **값이 바뀌면 "복사됨"을 지운다.** 그 표시는 이전 문자열을 가리키던 것이라, 남겨 두면
12
12
  사실이 아닌 말을 화면에 두는 셈이다.
13
13
  - 두 상태의 문구는 제품이 준다.
14
+
15
+ - **기본 tone은 `secondary`다(2026-10-06).** 복사는 화면의 주 행동 옆 보조 행동인데, Button 기본값 `primary`를 물려받아
16
+ 한 화면에 primary가 둘이 됐다. 복사가 곧 화면의 주 행동이면 `tone="primary"`를 명시한다.
@@ -1,5 +1,7 @@
1
1
  # CommandPalette contract
2
2
 
3
+ 검토일: 2026-10-06 (Web renderer의 local/external 필터링·빈 결과·section 이름·닫기 버튼 반영)
4
+
3
5
  ## 문제
4
6
 
5
7
  키보드로 전체 앱의 행동을 검색해 실행한다(⌘K 스타일). antd에는 이 문제에 직접
@@ -45,6 +47,16 @@ CommandPalette는 "Combobox + 모달 표면 + 전역 단축키"로 보일 수
45
47
  `CommandPaletteInput`/`CommandPaletteQueryState`는 각각 `ComboboxInput`/
46
48
  `ComboboxCollectionState`의 별칭이다.
47
49
 
50
+ - `filtering`을 생략하거나 `"local"`이면 renderer가 `query`로 `source`를 거른다. 규칙은
51
+ `label`·`textValue`에 대한 대소문자 무시 부분 일치이며 Native Combobox의 local 규칙과 같다. 접두 일치(typeahead 규칙)는 "지우기"처럼 단어 중간으로 찾는 명령
52
+ 검색에 맞지 않아 쓰지 않았다.
53
+ - `"external"`이면 제품이 이미 거르거나 순위를 매긴 결과(서버·fuzzy 검색)를 그대로 보여 주고,
54
+ `queryValue !== resultQuery`인 낡은 결과는 표시하되 실행할 수 없다(Native Combobox와 같은 규칙).
55
+ - 거른 뒤 비는 section은 제목째 뺀다.
56
+ - 이 해석은 contracts helper가 아니라 Web renderer 내부 함수에 둔다. 소비하는 renderer가 Web 하나뿐이고
57
+ (Native unsupported), 이 모듈을 재수출하는 contracts `./behaviors` 묶음이 2026-10-06 측정에서 바이트 상한
58
+ 바로 아래(약 345.0 kB)라 helper 약 2 kB가 경보를 냈기 때문이다. Native 대응이 생기면 contracts로 올린다.
59
+
48
60
  ### 실행(activate)은 Menu의 onAction 모양을 따르되 자급자족한다
49
61
 
50
62
  `onActivate`(즉시 실행)와 `onActivateAfterDismiss`(퇴장 전환이 끝난 뒤 실행)로
@@ -75,7 +87,14 @@ surface를 연다")과 같은 순서가 필요하고, `onActivateAfterDismiss`
75
87
  ### 설명
76
88
 
77
89
  `CommandPaletteDescriptor`는 `accessibilityLabel`과 `searchPlaceholder` 둘 다
78
- **필수**다. Popover의 `accessibilityLabel`은 선택 사항이었다(콘텐츠가 보통 자체
90
+ **필수**다. 2026-10-06에 선택 필드 두 개를 더했다. 기존 descriptor가 그대로 유효하도록 선택으로 두었고,
91
+ 값이 있으면 비어 있지 않아야 한다.
92
+
93
+ - `emptyMessage` — 보이는 결과가 0개일 때 목록 전체에 **한 번** 알리는 문구. `asyncState`가
94
+ `idle`이 아니면 그 message가 우선한다(제품 어댑터가 더 많이 안다). 없으면 빈 상태 문구를 표시하지 않는다.
95
+ renderer가 번역되지 않은 대체 문구를 만들지 않기 위해서다.
96
+ - `closeLabel` — 보이는 닫기 버튼의 접근 가능한 이름. 있으면 버튼이 `"close-action"`으로 닫는다
97
+ (정책상 `dismissible: false`면 막힌다). 없으면 이전처럼 Escape·바깥·실행으로만 닫힌다. Popover의 `accessibilityLabel`은 선택 사항이었다(콘텐츠가 보통 자체
79
98
  heading을 가지므로) — CommandPalette는 다르다: `role="dialog"` 표면에 보이는 제목이
80
99
  없고 검색 입력 하나뿐이라, 검색창 placeholder만으로 렌더러마다 다른 접근 가능한
81
100
  이름을 만들 위험이 있다. 그래서 명시적으로 요구한다.
@@ -132,6 +151,30 @@ Native는 `unsupported`다. 전역 단축키는 제품 소유다.
132
151
  방향키는 계약의 `getCollectionNavigationTarget`(disabled 건너뜀)을 쓴다.
133
152
  - **query가 바뀌면 활성 행이 첫 결과로 되돌아간다.** Enter의 대상이 언제나 분명해야 한다.
134
153
  - **전역 단축키는 제품 소유다.** 이 renderer는 여는 키를 정하지 않는다.
135
- - 로컬 검증: `test/command-palette.browser.test.tsx` 5개(이름·초점·배경 inert, 활성 행과
154
+ - 로컬 검증: `test/command-palette.browser.test.tsx`(이름·초점·배경 inert, 좁은 화면 긴 문구, 활성 행과
136
155
  disabled 건너뜀·재필터, 실행 시 강제 종료와 사유, 종료 후 후속 명령 순서, Escape·바깥
137
156
  pointer 종료)와 `컴포넌트/탐색/Command Palette`.
157
+
158
+ ### 2026-10-06 보강
159
+
160
+ 사용 지침 작성 중 이 계약이 말하는 local/external 필터링과 빈 결과 상태가 renderer에 없다는 것이
161
+ 드러났다. 그때까지 Web renderer는 받은 `source`를 거르지 않고 그대로 그렸고(제품이 직접 걸렀다),
162
+ 빈 결과는 `asyncState` message로만 표시했으며, section의 `accessibilityLabel`을 쓰지 않았고,
163
+ `close-action` 사유를 낼 닫기 버튼이 없었다. 지금은 다음과 같다.
164
+
165
+ - **local 필터링이 기본이다.** `ComboboxCollectionState`의 기본값이 원래 local이었으므로 계약 쪽으로
166
+ 맞췄다. 이미 직접 거르던 제품은 그대로 동작한다(부분 집합을 다시 거를 뿐). 자체 순위·fuzzy·서버
167
+ 검색처럼 local 규칙보다 넓은 결과를 주는 제품은 `queryState={{ filtering: "external", asyncState,
168
+ queryValue, resultQuery }}`를 넘겨야 결과가 줄지 않는다.
169
+ - **빈 결과는 목록 전체에 한 번** `role="status"`로 알린다(`descriptor.emptyMessage` 또는 asyncState).
170
+ 활성 행이 없으므로 `aria-activedescendant`도 비운다.
171
+ - **section 이름**은 `accessibilityLabel ?? label`로 `role="group"`의 `aria-label`에 연결한다.
172
+ 보이는 제목은 group 이름과 겹쳐 두 번 읽히지 않도록 `aria-hidden`이다.
173
+ - **닫기 버튼**은 `descriptor.closeLabel`이 있을 때만 검색 행에 그린다.
174
+ - **typeahead는 별도로 구현하지 않는다.** 초점이 항상 검색 입력에 있어 입력한 글자가 곧 query다.
175
+ `getCollectionTypeaheadMatch`를 겹쳐 쓰면 같은 키 입력이 필터와 활성 행 점프를 동시에 일으킨다.
176
+ 표의 "typeahead 재사용"은 이 renderer에서는 검색 입력이 그 역할을 대신하는 것으로 읽는다.
177
+ - **Native는 여전히 `unsupported`다.** catalog가 `platform: web`이고 Native renderer 파일이 없다.
178
+ 이번 보강은 Web만 바꿨다.
179
+ - 검증: 위 파일에 local 필터링·빈 section 제거, 단일 빈 상태, external 결과 유지와 낡은 결과 실행 차단,
180
+ 닫기 버튼 사유 4개를 더했고 contracts `test/command-palette.test.ts`에 선택 문구 검증을 더했다.
@@ -2,7 +2,7 @@
2
2
 
3
3
  상태: **Normative**
4
4
  정책 버전: **2.0.0**
5
- 검토일: 2026-10-02 · 전체 소비 이관에 따른 deprecated API 제거
5
+ 검토일: 2026-10-06 · Web `layoutStyle` 전수 제공(이전: 2026-10-02 deprecated API 제거)
6
6
  적용 대상: 신규 HJM Web·React Native 앱과 기존 앱의 새 화면
7
7
 
8
8
  이 원문은 `@hjmds/design-contracts/consumer-policy.md`에서 제공한다. 소비 앱은 설치한
@@ -98,6 +98,10 @@ foundation이 모두 stable이 됐으므로, 중앙 app profile의 다음 개정
98
98
  1.x 설치본의 caller-last 호환 동작은 역사적 동작이며 현재 소스에 유지되지 않습니다.
99
99
 
100
100
  - 배치에는 `layoutStyle`을 쓰고, 제공하지 않는 컴포넌트는 외부 composition wrapper를 씁니다.
101
+ Web은 2026-10-06부터 화면 흐름 안에 루트가 있는 모든 공개 컴포넌트가 `layoutStyle`을 받습니다.
102
+ 사용 지침을 쓰다가 일부 컴포넌트에만 이 prop이 있어 wrapper나 내부 input에 걸리는 `style`로
103
+ 배치하던 것을 확인해 넓혔습니다. 제외 대상은 provider·portal/viewport layer·recipe가 위치를 고정하는
104
+ 요소이며 그 목록과 이유는 `@hjmds/react`의 `composition-style.ts`와 각 props 타입 주석에 있습니다.
101
105
  - `HjmCompositionStyle` / `layoutStyle`은 margin, width, flex 계열과 `alignSelf`만 제공합니다. color,
102
106
  typography, padding, gap, border, radius, height, opacity, transform은 recipe/semantic API 소유입니다.
103
107
  - 내부 조합의 스타일은 비공개 renderer에 두며 앱에서 해당 내부 파일을 import하지 않습니다.
@@ -96,14 +96,16 @@ selection·320px long-copy proof를 통과해 2026-09-29 stable로 승격한다.
96
96
  제품이 한다.
97
97
  - **선택은 공용 collection 모델 그대로다.** multiple은 checkbox, single은 radio 의미를
98
98
  쓰고, 머리글의 tri-state는 `resolveDataTableSelectAllState`가 파생한다. disabled 행은
99
- 분모·분자 양쪽에서 빠진다.
99
+ 분모·분자 양쪽에서 빠진다. `selectedKey(s)`가 있으면 controlled, `defaultSelectedKey(s)`만 있으면
100
+ renderer가 첫 값을 내부 상태로 보관하고 행·전체 선택으로 갱신한다(2026-10-06 정정: default를 매 렌더 다시
101
+ 읽어 클릭 후에도 표시가 바뀌지 않았다. Tree와 같은 결함·같은 수정).
100
102
  - **셀 하나에 focusable 컨트롤은 최대 하나다.** roving tabindex grid 탐색을 도입하지 않고
101
103
  기본 tab 순서를 쓴다.
102
104
  - **페이지네이션은 표 아래에 조합한다.** `footer` slot은 제품이 채우고 표가 소유하지 않는다.
103
105
  - 키보드는 표 전체를 하나의 ARIA grid로 만들지 않고 native Tab 순서를 쓴다. 실제 브라우저 입력으로
104
106
  select-all·정렬·행 선택 버튼이 Tab 순서에 있고 Space/Enter로 동작하는지 확인한다. 방향키 셀 탐색은
105
107
  계약에 없다 — 제품 수요가 확인되지 않은 full grid interaction은 별도 계약으로 다룬다.
106
- - 로컬 검증: `test/data-table.browser.test.tsx` 5개(header 안 정렬 버튼과 aria-sort 3단계,
107
- tri-state 선택과 disabled 제외, 단일 선택 radio 의미, 셀당 컨트롤 하나와 기본 tab 순서,
108
- async 상태 발표와 footer 조합), `test/data-table.keyboard.browser.test.tsx` 1개(실제 Tab·Enter·Space
108
+ - 로컬 검증: `test/data-table.browser.test.tsx` 8개(header 안 정렬 버튼과 aria-sort 3단계,
109
+ tri-state 선택과 disabled 제외, 단일 선택 radio 의미, 비제어 single·multiple 선택 보관과
110
+ controlled 소유자 우선, 셀당 컨트롤 하나와 기본 tab 순서, async 상태 발표와 footer 조합, 44px hit area), `test/data-table.keyboard.browser.test.tsx` 1개(실제 Tab·Enter·Space
109
111
  입력), 그리고 `컴포넌트/탐색/Command Palette`의 표 화면.
package/docs/dialog.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Dialog
2
2
 
3
3
  Dialog provides a modal boundary for a task that needs the user's attention. This
4
+ Reviewed: 2026-10-05. The original
4
5
  contract was recorded during the 2026-09-29 promotion audit because Web and Native
5
6
  already had different title APIs and dismissal sources, while there was no single
6
7
  component guide describing their shared behavior.
@@ -10,7 +11,10 @@ component guide describing their shared behavior.
10
11
  - Web renders `role="dialog"`, `aria-modal="true"`, a title reference, and an
11
12
  optional description reference. When opened, focus enters the dialog, stays in
12
13
  the active modal, and returns to the trigger (or the explicit return-focus
13
- target) after dismissal.
14
+ target) after dismissal. A real backdrop click must not let the browser's
15
+ default blur undo that return, or move focus outside a busy modal. Diairy QA
16
+ W16 reproduced this after outside dismissal while Escape and the close action
17
+ returned correctly; synthetic mousedown dispatch had omitted the browser default.
14
18
  - Native renders a `role="dialog"` modal boundary with
15
19
  `accessibilityViewIsModal`. A string `title` names the dialog. If `title` is a
16
20
  React element, pass `accessibilityTitle`; native renderers cannot derive a
@@ -32,10 +36,12 @@ set `busy` so repeated action and dismissal attempts are disabled.
32
36
  ## Renderer proof
33
37
 
34
38
  The React browser tests cover focus entry, Tab containment, Escape dismissal,
35
- focus restoration, busy dismissal guards, and long title/description/body copy in
39
+ focus restoration (including real pointer backdrop clicks), busy dismissal guards, and long title/description/body copy in
36
40
  a narrow viewport. React Native renderer tests cover modal role/name/state,
37
41
  action labels and close requests, back/outside dismissal, busy guards, and long
38
42
  title/description copy. These tests exercise renderer contracts; device and
39
43
  screen-reader behavior remains a separate consumer validation concern.
40
44
 
41
45
  Native accessibility follow-up (2026-10-01): at 200% text scale, the close glyph was clipped inside the fixed IconButton frame. Dialog and Sheet now render that decorative glyph at a fixed icon size, matching Toast; title/body text still scales and the named close action and touch target are preserved. `sheet-viewport.test.tsx` checks both renderers and close callbacks.
46
+
47
+ Native viewport candidate (BT-QA-027, 2026-10-05): BurnTok local QA exposed a long confirmation extending beyond the visible screen. Dialog and AlertDialog now constrain the content surface and provide a shrinking scroll body, with actions outside that body and provider safe-area padding. Host tests cover those structural contracts and cancellation; they cannot prove Yoga geometry. Ordinary-size iOS confirmation and cancellation were checked. Enlarged-text device verification remains pending: the user stopped that QA scope before the candidate was confirmed, so this is not a completed device fix. Font caps and clipped copy were rejected because they remove readable content.
package/docs/form.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Form contract
2
2
 
3
+ 검토일: 2026-10-06 (세션 API와 renderer `Form`의 경계를 명시)
4
+
3
5
  ## 문제
4
6
 
5
7
  antd `Form`은 레이아웃과 상태 관리 라이브러리를 겸합니다(`useForm`, `dependencies`,
@@ -24,6 +26,22 @@ gap, control-to-support-text gap)을 `fieldRecipe`로 소유합니다. `Form`은
24
26
  정의합니다. 한 필드 내부의 리듬을 바꾸고 싶다면 그것은 Field의 변경이지 Form의
25
27
  prop이 아닙니다.
26
28
 
29
+ Native 한 줄 입력의 최소 높이는 실제 입력의 글자 배율과 line height, recipe padding,
30
+ border를 함께 포함한다. 2026-10-05 번뚝 폴더 이름 입력에서 OS 최대 글자는 커졌지만
31
+ 고정 프레임은 그대로여서 글자가 넘쳤다(BT-QA-020). `TextField`와 `SearchField`가 공유하는
32
+ renderer에서 프레임을 키우며, 제품에서 글자를 제한하거나 Form에 별도 높이를 주지 않는다.
33
+ OS 배율·명시적 Provider 배율·입력의 `allowFontScaling`/`maxFontSizeMultiplier`를 같은
34
+ 방식으로 반영하고, 일반 배율로 돌아오면 높이도 돌아온다. 옆의 제품 버튼이 입력 폭을
35
+ 좁히면 제품이 행을 세로로 재배치한다. Web은 CSS 폰트·레이아웃 경계를 사용하므로
36
+ Native의 OS 배율 계산을 추가하지 않는다. 공개 API는 바뀌지 않는다.
37
+
38
+ iOS multiline 입력은 글자 배율을 바꿔도 기존 attributed 본문이 이전 크기에 남는 host 제약을 갖는다.
39
+ 2026-10-05 번뚝 TextArea에서 일반↔최대 전환 후 재진입해야만 본문 크기가 바뀌었다(BT-QA-025).
40
+ Native renderer는 실제 배율이 바뀔 때 iOS multiline native editor만 교체하고 상위 draft state와
41
+ forwarded ref, 현재 focus/selection을 보존한다. 스타일 변경만으로는 기존 attributed 본문을
42
+ 갱신하지 못하며, field 전체 교체는 제품 초안을 버리므로 사용하지 않는다. Android·일반 한 줄
43
+ 입력은 이 교체 경로를 사용하지 않는다. Web은 브라우저 폰트 layout을 유지하며 공개 API는 같다.
44
+
27
45
  ### 제출 세션
28
46
 
29
47
  `AlertDialog`가 이미 `idle → busy → error/closing → closed` 세션을 갖고 있습니다.
@@ -52,6 +70,33 @@ dispose()가 submitting 중 호출되면 그 attempt는 interrupted로 한 번
52
70
  - Form은 값을 들고 있지 않습니다. `submit(values)`가 호출될 때마다 그 순간의 값을
53
71
  전달받을 뿐이며, 언제 제출을 시작해도 되는지(dirty/valid 여부)는 제품이 판단합니다.
54
72
 
73
+ ### 세션 API와 renderer `Form`의 관계
74
+
75
+ 위의 `submit()`/`reset()`/`dispose()`는 `@hjmds/design-contracts/components/form`의
76
+ `createFormSubmitSession(options)`이 돌려주는 객체(`FormSubmitSession`)의 API입니다.
77
+ **renderer의 `Form` 컴포넌트는 이 세션을 노출하지 않습니다.** 2026-10-06 사용 지침 작성 중 이 절이
78
+ renderer `Form`에서 `submit()`을 부를 수 있는 것처럼 읽힌다는 지적이 있어 구분을 명시합니다.
79
+
80
+ | 표면 | 제출 진입 | 중복 차단 | 상태·오류 |
81
+ | --- | --- | --- | --- |
82
+ | Web `@hjmds/react` `Form` | `<form>` submit 이벤트 → `onSubmit(event)` | `onSubmit`이 Promise를 돌려주면 정산까지 `fieldset disabled`·`aria-busy` | 제품이 `busy`·`formError`를 prop으로 넘긴다 |
83
+ | Native `@hjmds/react-native` `Form` | 내장 제출 버튼 → `onSubmit(values)` | 진행 중 재진입 무시, `accessibilityState.busy` | `status`/`defaultStatus`/`onStatusChange`, 거부 시 `error` 또는 `fallbackErrorMessage` |
84
+ | contracts `createFormSubmitSession` | 제품이 `session.submit(values)` 호출 | `{ outcome: "blocked" }` 반환 | `getSnapshot()`/`subscribe()`, `reset()`, `dispose()`의 `interrupted` 정산 |
85
+
86
+ 두 renderer는 위 상태기계와 같은 규칙(재진입 차단, 실패 후 재시도 가능)을 각자 구현하지만,
87
+ `reset()`·`dispose()`·`blocked`/`interrupted` 결과 객체는 제공하지 않습니다. 제품이 세션을
88
+ 직접 만드는 경우는 다음입니다.
89
+
90
+ - 제출 결과(`succeeded`/`failed`/`blocked`/`interrupted`)를 값으로 받아 후속 흐름을 분기해야 할 때.
91
+ - 화면을 떠날 때(언마운트·라우트 전환) 진행 중 제출을 `dispose()`로 한 번만 정산해야 할 때.
92
+ - 제출 상태를 폼 밖(헤더 저장 버튼, 다른 화면)과 공유하거나, 제품 폼 라이브러리(React Hook Form 등)의
93
+ submit handler에 같은 규칙을 걸어야 할 때.
94
+
95
+ 이때 세션의 `getSnapshot()`을 renderer에 연결합니다. Web은 `busy={phase.status === "submitting"}`과
96
+ `formError`(failed의 `message`)를, Native는 `status`와 `error`를 controlled로 넘깁니다. 세션과
97
+ renderer 내장 상태를 동시에 진실의 원천으로 두지 않습니다. 단순한 폼은 renderer `Form`만으로 충분하며
98
+ 세션을 만들 필요가 없습니다.
99
+
55
100
  ### 오류 소유권
56
101
 
57
102
  - **필드 오류**는 각 Field의 `validation`(`valid`/`invalid`) 축이 소유합니다. Form은
@@ -144,3 +189,9 @@ HJM renderer의 Native action 회귀와 사용 가능한 자동 검증을 기준
144
189
  Stack이 아직 실제 사용 사례 전까지 `planned` 이상으로 승격하지 않은 것과 같은
145
190
  이유로, Form도 actions 슬롯의 위치만 고정하고 그 안의 레이아웃 토큰은 만들지
146
191
  않습니다.
192
+
193
+ ### TextArea의 입력창 안 행동
194
+
195
+ 2026-10-05 사진/메시지 작성에서 별도 TextArea 구현이 생기지 않도록 Web/Native `TextArea.trailing`
196
+ 슬롯을 추가했다. 자동 높이·IME·값 변경 계약은 그대로이며, 입력 필드와 아이콘 버튼의 접근성 이름은
197
+ 각각 제공한다. 메시지에서는 `MessageComposer.sendIcon`이 이 슬롯을 합성한다.
@@ -1,6 +1,6 @@
1
1
  # Component maturity and evidence manifest
2
2
 
3
- > Generated from `@hjmds/design-contracts` 1.12.1. Do not edit this file directly; run `pnpm contracts:sync`.
3
+ > Generated from `@hjmds/design-contracts` 1.13.0. Do not edit this file directly; run `pnpm contracts:sync`.
4
4
 
5
5
  This document is the human-readable projection of the same catalog and Showcase manifest consumed by product CI.
6
6
 
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "designSystemVersion": "1.12.1",
3
+ "designSystemVersion": "1.13.0",
4
4
  "source": "packages/design-contracts/docs/generated/showcase-manifest.json",
5
5
  "surfaces": {
6
6
  "web": {
7
7
  "packageName": "@hjmds/react",
8
- "packageVersion": "1.12.1",
8
+ "packageVersion": "1.13.0",
9
9
  "activeComponents": 103,
10
10
  "claimedComponents": 103,
11
11
  "completeScenarioComponents": 103,
@@ -5642,7 +5642,7 @@
5642
5642
  },
5643
5643
  "native": {
5644
5644
  "packageName": "@hjmds/react-native",
5645
- "packageVersion": "1.12.1",
5645
+ "packageVersion": "1.13.0",
5646
5646
  "activeComponents": 83,
5647
5647
  "claimedComponents": 83,
5648
5648
  "completeScenarioComponents": 83,
@@ -1,6 +1,6 @@
1
1
  # Renderer evidence coverage
2
2
 
3
- > Generated for HJM 1.12.1. Do not edit directly; run `pnpm evidence:sync`.
3
+ > Generated for HJM 1.13.0. Do not edit directly; run `pnpm evidence:sync`.
4
4
 
5
5
  This projection joins the canonical surface maturity manifest with first-party renderer claims. Missing scenarios are explicit beta promotion debt; stable surfaces are blocked by CI until none remain.
6
6
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
- "designSystemVersion": "1.12.1",
3
+ "designSystemVersion": "1.13.0",
4
4
  "packageName": "@hjmds/design-contracts",
5
5
  "components": [
6
6
  {
package/docs/link.md CHANGED
@@ -50,6 +50,14 @@ icon은 HJM semantic name만 고릅니다. decorative 처리와 size/tone/weight
50
50
  color, stroke, fixed direction을 넘겨 이 문법을 바꾸지 못합니다. arbitrary ReactNode와 중첩
51
51
  button/link도 열지 않습니다.
52
52
 
53
+ Native는 전역 icon registry가 없으므로 `Link`가 `renderIcon`(semantic name → glyph) 경계를 받고,
54
+ descriptor의 `leadingIcon`/`trailingIcon`을 `Icon`으로 감싸 `linkRecipe.icon.glyph` 크기·link tone·
55
+ decorative·RTL mirroring을 HJM이 정합니다. 2026-10-06 사용 지침 점검에서 Native가 아이콘을 검증만 하고
56
+ 그리지 않아 Web과 공유한 descriptor의 chevron이 사라지는 것을 확인해 고쳤습니다. `renderIcon` 없이
57
+ icon을 넘기면 개발 모드에서 한 번 경고하고 그리지 않습니다. 이미 아이콘을 넘기던 1.x 앱이 실행 중
58
+ 죽지 않도록 throw 대신 경고를 택했습니다. descriptor icon과 `leading`/`trailing` node를 함께 주면 descriptor가
59
+ slot을 가집니다. Native `style`은 deprecated이며 배치는 `layoutStyle`로 합니다.
60
+
53
61
  core descriptor와 destination은 허용된 key만 받으며 renderer의 `className`, `target`, `replace`
54
62
  같은 플랫폼 prop을 섞지 않습니다. resolver는 검증된 label, canonical `{ kind, href }`, semantic
55
63
  icon identity만 새 객체로 반환해 입력 객체의 숨은 확장 필드를 플랫폼 사이에 전달하지 않습니다.
@@ -1,6 +1,6 @@
1
1
  # 1.11 호환 API 제거와 소비 이관
2
2
 
3
- 검토일: 2026-10-02 · 1.11 minor 소스 변경 · npm 게시와 소비 설치는 별도 확인
3
+ 검토일: 2026-10-06 · 1.11 minor 소스 변경 · npm 게시와 소비 설치는 별도 확인
4
4
 
5
5
  사용자가 전체 소비 앱을 함께 버전업하므로 구형 호환 API도 제거하라고 요청했다.
6
6
  별칭과 deprecated 스타일 통로를 유지하는 대신 기존 canonical API로 이관한다.
@@ -46,6 +46,50 @@ renderer에 주입하는 이미지 스타일, 제품 소유 슬롯과 private re
46
46
  LoadMore의 내부 Button 조합은 비공개 RecipeButton을 써서 recipe의 크기·색·그림자를 보존한다.
47
47
  해당 구현을 패키지 entry에서 export하지 않는다.
48
48
 
49
+ ## 1.13 deprecated 시각 style (제거는 다음 major)
50
+
51
+ 2026-10-06 사용 지침 작성 중 위 표 밖의 Native 컴포넌트가 아직 raw `StyleProp` 시각 통로를
52
+ 받는 것을 확인했다. 1.11처럼 minor에서 지우거나 Button처럼 throw하면 1.x 소비 앱이 깨지므로
53
+ 이번 minor는 동작을 유지한 채 `@deprecated` JSDoc과 개발 모드 1회 `console.warn`
54
+ (`internal/deprecated-style.ts`, 컴포넌트·prop마다 한 번, production 무음)만 단다. 없던 곳에는
55
+ `layoutStyle`을 추가했다. 실제 삭제는 [consumer-policy §3.1](consumer-policy.md#31-react-native-legacy-style-compatibility-boundary)의
56
+ 네 조건을 채운 다음 major에서 한다.
57
+
58
+ | 컴포넌트 | deprecated prop | 대체 |
59
+ | --- | --- | --- |
60
+ | IconButton, Link, BottomCTA, Agreement, Asset, AssetGroup, AuthProviderButton, BottomInfo, Carousel, Collapsible, Container, Section, Icon, Steps, Top, UploadItem | `style` | 배치는 `layoutStyle`, 외형은 각 recipe·semantic prop |
61
+ | Heading | `style`(TextStyle) | `layoutStyle`, 크기·굵기는 `level` |
62
+ | Badge, Tag | `style`, `labelStyle` | `layoutStyle`, `tone`/`size`/`variant` |
63
+ | ListRow | `titleStyle`, `descriptionStyle` | `density`·`selected`, listRowRecipe typography |
64
+ | Avatar | `style`, `imageStyle` | `layoutStyle`, `size`/`renderFallback` |
65
+ | Divider, CounterBadge, List, Timeline | `style` | `layoutStyle`, 각 컴포넌트 축 |
66
+ | Accordion | `style`, `itemStyle`, `triggerStyle`, `titleStyle`, `indicatorStyle`, `panelStyle` | `layoutStyle`, `density`/`renderIndicator` |
67
+ | DescriptionList, StatisticGroup | `style`, `itemStyle` | `layoutStyle`, descriptor·`density` |
68
+ | Statistic | `style`, `labelStyle`, `valueStyle`, `affixStyle`, `trendStyle`, `hintStyle` | `layoutStyle`, `density`/`presentation` |
69
+ | Notice, Result, Spinner, Skeleton, Toast | `style` | `layoutStyle`, `tone`/`status`/`size`/`shape`·`width`·`height` |
70
+ | EmptyState | `style`, `illustrationStyle`, `titleStyle`, `descriptionStyle`, `actionStyle` | `layoutStyle`, `density`/`align` |
71
+ | Progress | `style`, `labelStyle`, `valueStyle`, `trackStyle`, `indicatorStyle` | `layoutStyle`, `tone`/`size`/`shape` |
72
+ | ToastRegion | `style`, `toastStyle` | `layoutStyle`, `placement`/`safeAreaInsets` |
73
+ | AlertDialog, Sheet | `contentStyle`의 배치 밖 key | 배치 key만 사용. 다음 major에서 Dialog처럼 `HjmCompositionStyleProp`으로 좁힘(배치 key만 넘기면 경고 없음) |
74
+ | Tabs | `style`, `tabListStyle` / TabPanel `style` | `layoutStyle`, `size`/`layout`/`overflow`/`appearance` |
75
+ | BottomNavigation | `style`, `surfaceStyle`, `listStyle`, `primaryActionStyle` | `layoutStyle`, `configuration` |
76
+ | TopBar | `style`, `leadingStyle`, `titleStyle`, `trailingStyle` / TopBar action `style`, `labelStyle` | `layoutStyle`, `centered`/`labelVisibility` |
77
+ | Menu | `style` | `layoutStyle`, `density` |
78
+ | Checkbox, Radio, CheckboxGroup, RadioGroup | `style`, `controlStyle`, `indicatorStyle`, `leadingStyle`, `contentStyle`, `labelStyle`, `descriptionStyle` | `layoutStyle`(행 또는 그룹), selection control recipe |
79
+ | Switch, SegmentedControl, Form, Select, Combobox, TagsInput, ToggleGroup, DatePicker, FilePicker, TransferList | `style` | `layoutStyle`, 각 컴포넌트 `size`·descriptor |
80
+ | Chip | `labelStyle` | `tone`/`size` |
81
+ | OtpField | `slotStyle`, `slotTextStyle` | `size`/`presentation`. 1.13부터 상속된 `layoutStyle`이 숨은 TextInput이 아니라 바깥 frame에 적용된다 |
82
+ | NumberField | `inputStyle`, `containerStyle` | `layoutStyle`, `size` |
83
+ | Slider | `containerStyle`, `controlStyle` | `layoutStyle` |
84
+ | Mentions | `listStyle` | 후보 목록 recipe. Mentions의 상속 `layoutStyle`은 입력 영역만 배치한다 |
85
+
86
+ 제외(정식 host·renderer 스타일): KeyboardAvoiding·KeyboardDock(플랫폼 host), Image·ImageAdapter `style`(renderer 주입),
87
+ EffectSurface·ThinkingOrb·SharedTransition(optional motion host frame), Text·Stack·AspectRatio·Grid·Layout(내부 조합 전반이
88
+ 쓰는 layout primitive라 경고하면 HJM 자신이 경고를 낸다. 내부 renderer 분리 후 별도 train에서 다룬다).
89
+
90
+ 이관: 경고 문구의 prop을 찾아 margin·width·flex·alignSelf만 남긴 값은 `layoutStyle`로 옮기고, 색·글꼴·padding·
91
+ 높이·radius는 semantic prop으로 옮긴다. 표현할 축이 없으면 `.hjm-*`·style 우회 대신 계약 공백으로 올린다.
92
+
49
93
  ## 관리 소비 저장소
50
94
 
51
95
  번뚝·다에리·비행중·모펀의 직접 호출과 제품 래퍼를 함께 이관한다. 스핀트·유틸버스·포트폴리오
@@ -93,7 +93,7 @@ Expo Go cannot verify these linked native modules.
93
93
 
94
94
  ## Evidence and promotion
95
95
 
96
- Web stories: 갤러리/모션 연동. Native stories: 실험실/선택적 연동.
96
+ Web stories: `배포/구성/직접 조작과 모션/숫자 변화와 메뉴 변형`. Native stories: `배포/구성/직접 조작과 모션/이미지·시트·키보드 조작` (the earlier 갤러리/모션 연동 and 실험실/선택적 연동 menus; moved by the 2026-10-06 user-approved Storybook promotion, which is not an API promotion).
97
97
  Tests cover adaptation logic and web browser behavior; native mocks cannot prove OS gestures,
98
98
  keyboard animation, accessibility focus, native linking or installed-device health. Keep native
99
99
  adapters experimental until iOS/Android device evidence exists. No consumer migration or publication
@@ -34,6 +34,11 @@
34
34
  `play`/`pause`처럼 서로 다른 아이콘 이름이 지금 상태가 아니라 누르면 일어날 동작을
35
35
  가리킨다(`semanticIconNames`).
36
36
 
37
+ **renderer 번역(2026-10-06 정정).** Web 토글은 `aria-pressed` 없는 `<button>`, Native 토글은
38
+ `accessibilityState`에 `selected` 없이 `disabled`만 싣는다. 이전 구현은 Web에 `aria-pressed`, Native에
39
+ `selected: revealed`를 붙여 "비밀번호 숨기기, 눌림/선택됨"처럼 다음 행동과 현재 상태를 함께 읽혀 위 판정 1과
40
+ 어긋났다. 두 renderer 테스트가 상태 속성의 부재를 고정한다.
41
+
37
42
  **타입으로 강제한 지점.** `PasswordToggleAccessibleNameInfo`는 `revealed`가 아니라
38
43
  `willReveal`(= `!revealed`)을 담아 제품에 넘긴다. 필드 이름을 "현재 상태"로 지어
39
44
  어차피 뒤집어야 하는 계산을 제품 composer 안에 숨기지 않고, 호출부(`resolvePasswordFieldDescriptor`)에서
@@ -0,0 +1,40 @@
1
+ # 제품별 테마를 유지하는 UI 채택
2
+
3
+ 검토일: 2026-10-06
4
+
5
+ 사용자 요청: 공통 기능을 가져다 쓰듯 UI 구조와 행동을 재사용한다. 예제 복사와 공개 라이브러리 사용을 혼동하지 않도록 선택 경로와 소유권을 정한다.
6
+
7
+ ## 선택 순서
8
+
9
+ [사용 지침 색인](usage/README.md)에서 화면을 먼저 고른다. 화면이 없을 때 구성, 구성의 슬롯 안에서 컴포넌트, 마지막으로 토큰을 고른다. Storybook 배포·실험은 검토 상태이며 이 계층과 별개다.
10
+
11
+ | 목적 | 공개 API | 앱이 연결할 것 | 예제를 그대로 복사하지 않을 것 |
12
+ | --- | --- | --- | --- |
13
+ | 검색 화면 | SearchScreen (`/screen-flows`) + SearchField | query, onSearch, 결과·최근 검색·필터 슬롯, 번역된 전체 삭제 라벨 | 가짜 결과, 지연, 한국어 고정 문구 |
14
+ | 댓글 | CommentThreadScreen (`/screen-flows`) + MessageComposer (`/screens`) | 댓글·권한·답글 대상, 확정된 전송 결과 | 낙관적 성공 확정, 샘플 사람·사진 |
15
+ | 메시지 | ChatScreen + MessageComposer (`/screens`) | 타임라인, 초안, 업로드·전송·실패 | 타이머를 서버 완료로 사용 |
16
+ | 프로필 | ProfileScreen + EditorScreen (`/screen-flows`) | summary·fields·dirty·submit·discard | 공통 저장/이탈 확인을 별도 구현 |
17
+ | 저장 컬렉션 | SavedItemsScreen (`/saved-items`) | items, collections, 현재 컬렉션·항목, 사진·상세·저장 해제/Undo | 컬렉션/격자/상세를 앱마다 새로 조립 |
18
+ | 카테고리 필터 | SegmentedControl (`/selection`, Native `/inputs`)의 pills | 항목·현재 값·선택 콜백 | 단일 선택을 개별 Button selected로 중복 구현 |
19
+ | 일반 목록과 상세 | ListDetailScreen (`/screen-flows`) | list, detail, back, 검색·해제·Undo 도메인 로직 | 상세 진입 때 목록/검색 상태를 버리는 새 화면 |
20
+ | 설정 | SettingsScreen (`/screens`) | sections, 설정 값, 저장/API | 메뉴·저장 버튼의 위치를 앱마다 재정의 |
21
+
22
+ Web은 `@hjmds/react`, Native는 `@hjmds/react-native`의 표에 명시된 subpath로 가져온다. 사용 지침에서 해당 버전의 타입과 지원 여부를 확인한다. 위 표는 현재 작업 트리 API 기준이며 npm 게시 완료를 뜻하지 않는다.
23
+
24
+ ## 공통 계약과 제품 소유권
25
+
26
+ | HJM이 고정 | 제품이 공급 |
27
+ | --- | --- |
28
+ | 영역 구조, 읽기 순서, 버튼 위치·타깃, 간격·크기 토큰 | 제품 목적에 맞는 공개 구성 선택 |
29
+ | pending·disabled·error, focus·keyboard·back·취소·복구 계약 | API·권한·저장 시점, 서버 operation ID |
30
+ | 테마 의미와 대비, 모션 감소 | `HjmProvider.brandPalette`의 light/dark semantic 값 |
31
+ | 슬롯 구조와 접근성 이름 요구 | 번역 문자열, 로고·사진·아이콘, 날짜 형식 |
32
+
33
+ 제품 팔레트는 [브랜드 경계](brand-boundary.md)를 따른다. 앱마다 스타일 파일로 내부 모양을 덮거나 Showcase 테마를 제품 기본값으로 복사하지 않는다. 공개 옵션으로 표현할 수 없는 차이는 해당 API의 새 축이 필요한지 검토한다.
34
+
35
+ ## 채택 검증
36
+
37
+ - 같은 공개 구성에 서로 다른 두 제품 팔레트를 적용한다. 기본·다크·큰 글자에서 입력·CTA·오류의 의미가 유지돼야 한다.
38
+ - 정상 실행뿐 아니라 중복 입력, 요청 실패, 재시도, 취소, 상세에서 복귀, 초안 보존을 확인한다.
39
+ - Web 키보드와 Native 키보드·safe area·뒤로가기는 각각 확인한다. 웹 캡처를 Native 검증으로 대체하지 않는다.
40
+ - 문서 연결 검사만으로 지침 준수나 앱 적용을 선언하지 않는다. 공개 진입점과 소비 제품의 실제 import·콜백·테마 연결을 확인한다.