@hjmds/design-contracts 1.17.2 → 1.19.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 (274) hide show
  1. package/dist/action-session.d.ts +16 -0
  2. package/dist/action-session.d.ts.map +1 -1
  3. package/dist/action-session.js +18 -0
  4. package/dist/action-session.js.map +1 -1
  5. package/dist/activity-chip.d.ts +127 -0
  6. package/dist/activity-chip.d.ts.map +1 -0
  7. package/dist/activity-chip.js +65 -0
  8. package/dist/activity-chip.js.map +1 -0
  9. package/dist/base-recipes.d.ts +1 -1
  10. package/dist/base-recipes.d.ts.map +1 -1
  11. package/dist/base-recipes.js +2 -1
  12. package/dist/base-recipes.js.map +1 -1
  13. package/dist/behaviors.d.ts +9 -6
  14. package/dist/behaviors.d.ts.map +1 -1
  15. package/dist/behaviors.js +5 -3
  16. package/dist/behaviors.js.map +1 -1
  17. package/dist/bottom-navigation.d.ts +16 -0
  18. package/dist/bottom-navigation.d.ts.map +1 -1
  19. package/dist/bottom-navigation.js +18 -0
  20. package/dist/bottom-navigation.js.map +1 -1
  21. package/dist/card.d.ts +4 -0
  22. package/dist/card.d.ts.map +1 -1
  23. package/dist/card.js +4 -1
  24. package/dist/card.js.map +1 -1
  25. package/dist/carousel.d.ts +3 -0
  26. package/dist/carousel.d.ts.map +1 -1
  27. package/dist/carousel.js.map +1 -1
  28. package/dist/catalog.d.ts +80 -9
  29. package/dist/catalog.d.ts.map +1 -1
  30. package/dist/component-recipes.d.ts +74 -3
  31. package/dist/component-recipes.d.ts.map +1 -1
  32. package/dist/component-recipes.js +38 -6
  33. package/dist/component-recipes.js.map +1 -1
  34. package/dist/content-transition.d.ts +1 -1
  35. package/dist/content-transition.d.ts.map +1 -1
  36. package/dist/content-transition.js +2 -2
  37. package/dist/content-transition.js.map +1 -1
  38. package/dist/field-group.d.ts +14 -0
  39. package/dist/field-group.d.ts.map +1 -1
  40. package/dist/field-group.js +9 -0
  41. package/dist/field-group.js.map +1 -1
  42. package/dist/form.d.ts.map +1 -1
  43. package/dist/form.js +10 -3
  44. package/dist/form.js.map +1 -1
  45. package/dist/icon-button-recipe.d.ts +1 -5
  46. package/dist/icon-button-recipe.d.ts.map +1 -1
  47. package/dist/icon-button-recipe.js +2 -1
  48. package/dist/icon-button-recipe.js.map +1 -1
  49. package/dist/index.d.ts +3 -2
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +2 -0
  52. package/dist/index.js.map +1 -1
  53. package/dist/screen-patterns.d.ts +10 -0
  54. package/dist/screen-patterns.d.ts.map +1 -1
  55. package/dist/screen-patterns.js +17 -3
  56. package/dist/screen-patterns.js.map +1 -1
  57. package/dist/toast-liquid.d.ts +1 -1
  58. package/dist/toast-liquid.d.ts.map +1 -1
  59. package/dist/toast-liquid.js +3 -2
  60. package/dist/toast-liquid.js.map +1 -1
  61. package/dist/toast.d.ts +29 -2
  62. package/dist/toast.d.ts.map +1 -1
  63. package/dist/toast.js +59 -3
  64. package/dist/toast.js.map +1 -1
  65. package/dist/version.d.ts +1 -1
  66. package/dist/version.js +1 -1
  67. package/dist/version.js.map +1 -1
  68. package/docs/action-session.md +2 -0
  69. package/docs/activity-chip.md +34 -0
  70. package/docs/asset.md +7 -0
  71. package/docs/bottom-navigation.md +16 -1
  72. package/docs/brand-boundary.md +3 -3
  73. package/docs/collection-rail.md +4 -0
  74. package/docs/compound-controls.md +8 -3
  75. package/docs/consumer-policy.md +39 -120
  76. package/docs/generated/component-maturity.md +1 -1
  77. package/docs/generated/renderer-evidence.json +3 -3
  78. package/docs/generated/renderer-evidence.md +1 -1
  79. package/docs/generated/showcase-manifest.json +1 -1
  80. package/docs/gravity-letters.md +2 -0
  81. package/docs/grid-reveal.md +2 -0
  82. package/docs/layout.md +2 -2
  83. package/docs/load-more.md +5 -0
  84. package/docs/migration-native-legacy-removal.md +30 -17
  85. package/docs/optional-adapters.md +4 -0
  86. package/docs/screen-patterns.md +23 -1
  87. package/docs/toast.md +64 -2
  88. package/docs/usage/README.md +248 -243
  89. package/docs/usage/SELECTION.md +43 -0
  90. package/docs/usage/STANDARD.md +22 -10
  91. package/docs/usage/components/accordion.md +5 -3
  92. package/docs/usage/components/activity-chip.md +109 -0
  93. package/docs/usage/components/agreement.md +6 -9
  94. package/docs/usage/components/alert-dialog.md +12 -6
  95. package/docs/usage/components/asset.md +10 -4
  96. package/docs/usage/components/auth-provider-button.md +2 -2
  97. package/docs/usage/components/avatar.md +8 -6
  98. package/docs/usage/components/badge.md +4 -4
  99. package/docs/usage/components/bottom-cta.md +4 -4
  100. package/docs/usage/components/bottom-info.md +2 -2
  101. package/docs/usage/components/bottom-navigation.md +18 -6
  102. package/docs/usage/components/button.md +6 -3
  103. package/docs/usage/components/card.md +28 -7
  104. package/docs/usage/components/carousel.md +12 -4
  105. package/docs/usage/components/chat-message.md +10 -2
  106. package/docs/usage/components/chat-screen.md +5 -5
  107. package/docs/usage/components/checkbox-group.md +1 -1
  108. package/docs/usage/components/checkbox.md +2 -2
  109. package/docs/usage/components/chip.md +2 -2
  110. package/docs/usage/components/code-block.md +3 -4
  111. package/docs/usage/components/collapsible.md +6 -4
  112. package/docs/usage/components/combobox.md +2 -2
  113. package/docs/usage/components/command-palette.md +1 -1
  114. package/docs/usage/components/comment-thread-screen.md +7 -4
  115. package/docs/usage/components/container.md +1 -1
  116. package/docs/usage/components/content-transition.md +3 -2
  117. package/docs/usage/components/context-menu.md +2 -2
  118. package/docs/usage/components/counter-badge.md +3 -3
  119. package/docs/usage/components/data-table.md +1 -1
  120. package/docs/usage/components/date-picker.md +4 -4
  121. package/docs/usage/components/description-list.md +3 -3
  122. package/docs/usage/components/design-system-provider.md +2 -2
  123. package/docs/usage/components/dialog.md +14 -3
  124. package/docs/usage/components/divider.md +2 -2
  125. package/docs/usage/components/editor-screen.md +5 -3
  126. package/docs/usage/components/effect-surface.md +1 -1
  127. package/docs/usage/components/empty-state.md +6 -6
  128. package/docs/usage/components/field.md +5 -3
  129. package/docs/usage/components/file-picker.md +6 -3
  130. package/docs/usage/components/form.md +1 -1
  131. package/docs/usage/components/heading.md +3 -3
  132. package/docs/usage/components/icon-button.md +6 -3
  133. package/docs/usage/components/icon.md +1 -1
  134. package/docs/usage/components/image.md +5 -4
  135. package/docs/usage/components/keyboard-dock.md +1 -1
  136. package/docs/usage/components/layout.md +1 -1
  137. package/docs/usage/components/link.md +2 -2
  138. package/docs/usage/components/list-detail-screen.md +6 -6
  139. package/docs/usage/components/list-row.md +13 -4
  140. package/docs/usage/components/list.md +5 -4
  141. package/docs/usage/components/masonry.md +2 -1
  142. package/docs/usage/components/media-selection-screen.md +11 -5
  143. package/docs/usage/components/mentions.md +2 -2
  144. package/docs/usage/components/menu.md +3 -2
  145. package/docs/usage/components/menubar.md +2 -2
  146. package/docs/usage/components/message-composer.md +3 -3
  147. package/docs/usage/components/moderation-screen.md +3 -2
  148. package/docs/usage/components/notice.md +2 -2
  149. package/docs/usage/components/notification-inbox-screen.md +1 -1
  150. package/docs/usage/components/notification-item.md +2 -2
  151. package/docs/usage/components/number-field.md +3 -3
  152. package/docs/usage/components/otp-field.md +1 -1
  153. package/docs/usage/components/overview-screen.md +1 -1
  154. package/docs/usage/components/permission-screen.md +1 -1
  155. package/docs/usage/components/photo-source-sheet.md +1 -1
  156. package/docs/usage/components/popover.md +2 -2
  157. package/docs/usage/components/profile-screen.md +1 -1
  158. package/docs/usage/components/progress.md +2 -2
  159. package/docs/usage/components/qr-code.md +2 -2
  160. package/docs/usage/components/radio-group.md +1 -2
  161. package/docs/usage/components/radio.md +1 -2
  162. package/docs/usage/components/result.md +1 -1
  163. package/docs/usage/components/saved-items-screen.md +4 -2
  164. package/docs/usage/components/screen-layout.md +4 -3
  165. package/docs/usage/components/search-field.md +1 -1
  166. package/docs/usage/components/search-screen.md +4 -4
  167. package/docs/usage/components/section.md +3 -3
  168. package/docs/usage/components/segmented-control.md +4 -4
  169. package/docs/usage/components/select.md +2 -3
  170. package/docs/usage/components/settings-screen.md +18 -56
  171. package/docs/usage/components/sheet.md +10 -5
  172. package/docs/usage/components/sidebar.md +1 -1
  173. package/docs/usage/components/skeleton.md +1 -1
  174. package/docs/usage/components/skip-nav.md +1 -1
  175. package/docs/usage/components/slider.md +2 -2
  176. package/docs/usage/components/spinner.md +2 -2
  177. package/docs/usage/components/splitter.md +1 -1
  178. package/docs/usage/components/statistic.md +5 -4
  179. package/docs/usage/components/steps.md +2 -2
  180. package/docs/usage/components/surface.md +2 -3
  181. package/docs/usage/components/switch.md +1 -1
  182. package/docs/usage/components/tabs.md +4 -4
  183. package/docs/usage/components/tag.md +2 -2
  184. package/docs/usage/components/tags-input.md +4 -4
  185. package/docs/usage/components/text-area.md +5 -3
  186. package/docs/usage/components/text.md +85 -4
  187. package/docs/usage/components/timeline.md +2 -2
  188. package/docs/usage/components/toast.md +41 -13
  189. package/docs/usage/components/toggle-group.md +3 -3
  190. package/docs/usage/components/tooltip.md +1 -1
  191. package/docs/usage/components/top-bar.md +5 -4
  192. package/docs/usage/components/top.md +3 -3
  193. package/docs/usage/components/transfer-list.md +5 -4
  194. package/docs/usage/components/upload-item.md +3 -3
  195. package/docs/usage/components/virtual-list.md +1 -0
  196. package/docs/usage/compositions/action-feedback.md +42 -6
  197. package/docs/usage/compositions/action-recovery-optimistic.md +6 -1
  198. package/docs/usage/compositions/action-recovery-save.md +19 -23
  199. package/docs/usage/compositions/action-recovery-undo.md +6 -1
  200. package/docs/usage/compositions/activity-status.md +168 -0
  201. package/docs/usage/compositions/collection-detail.md +2 -0
  202. package/docs/usage/compositions/command-records.md +15 -5
  203. package/docs/usage/compositions/common-message.md +4 -2
  204. package/docs/usage/compositions/common-notification.md +5 -3
  205. package/docs/usage/compositions/compound-controls.md +13 -5
  206. package/docs/usage/compositions/content-transition-comparison.md +2 -2
  207. package/docs/usage/compositions/date-entry.md +10 -5
  208. package/docs/usage/compositions/date-time-selection.md +3 -3
  209. package/docs/usage/compositions/design-profile-comparison.md +7 -5
  210. package/docs/usage/compositions/disclosure.md +1 -1
  211. package/docs/usage/compositions/document-resource.md +10 -3
  212. package/docs/usage/compositions/expo-interactions.md +3 -2
  213. package/docs/usage/compositions/family-drawer.md +9 -4
  214. package/docs/usage/compositions/field-group.md +7 -1
  215. package/docs/usage/compositions/floating-action-button.md +18 -18
  216. package/docs/usage/compositions/illustrated-outcome.md +3 -2
  217. package/docs/usage/compositions/input-sheet.md +6 -2
  218. package/docs/usage/compositions/interaction-adapters.md +1 -1
  219. package/docs/usage/compositions/interaction-flow-apply.md +6 -3
  220. package/docs/usage/compositions/interaction-flow-draft.md +5 -2
  221. package/docs/usage/compositions/interaction-flow-search.md +5 -2
  222. package/docs/usage/compositions/live-list.md +7 -5
  223. package/docs/usage/compositions/navigation-bar-collection.md +8 -7
  224. package/docs/usage/compositions/optional-adapters.md +1 -1
  225. package/docs/usage/compositions/origin-dialog.md +5 -5
  226. package/docs/usage/compositions/paper-surface.md +1 -1
  227. package/docs/usage/compositions/photo-source.md +1 -1
  228. package/docs/usage/compositions/purpose-input-comment.md +5 -3
  229. package/docs/usage/compositions/purpose-input-message.md +5 -3
  230. package/docs/usage/compositions/reference-first.md +63 -42
  231. package/docs/usage/compositions/reference-review.md +60 -50
  232. package/docs/usage/compositions/reference-settings.md +60 -50
  233. package/docs/usage/compositions/selection-scope.md +48 -35
  234. package/docs/usage/compositions/stea-order-progress.md +1 -0
  235. package/docs/usage/compositions/stea-otp-verify.md +5 -3
  236. package/docs/usage/compositions/stea-pixel-empty.md +3 -3
  237. package/docs/usage/compositions/time-selection.md +6 -5
  238. package/docs/usage/compositions/upload-recovery.md +10 -1
  239. package/docs/usage/compositions/video-dialog.md +10 -3
  240. package/docs/usage/compositions/web-additions.md +5 -5
  241. package/docs/usage/compositions/web-navigation.md +1 -1
  242. package/docs/usage/screens/common-chat.md +21 -8
  243. package/docs/usage/screens/common-comments.md +13 -1
  244. package/docs/usage/screens/common-inbox.md +19 -6
  245. package/docs/usage/screens/common-login.md +19 -7
  246. package/docs/usage/screens/common-profile.md +22 -9
  247. package/docs/usage/screens/common-saved.md +16 -2
  248. package/docs/usage/screens/common-search.md +24 -4
  249. package/docs/usage/screens/common-settings.md +22 -50
  250. package/docs/usage/screens/common-shell.md +14 -2
  251. package/docs/usage/screens/dashboard.md +52 -109
  252. package/docs/usage/screens/discovery-gallery.md +57 -148
  253. package/docs/usage/screens/flow-collection.md +38 -9
  254. package/docs/usage/screens/flow-editor.md +25 -7
  255. package/docs/usage/screens/flow-media.md +23 -7
  256. package/docs/usage/screens/flow-moderation.md +15 -2
  257. package/docs/usage/screens/flow-onboarding.md +3 -3
  258. package/docs/usage/screens/flow-permission.md +17 -3
  259. package/docs/usage/screens/landing.md +37 -20
  260. package/docs/usage/screens/mockup-studio.md +22 -7
  261. package/docs/usage/screens/notification-settings.md +18 -4
  262. package/docs/usage/screens/product-bento.md +18 -9
  263. package/docs/usage/screens/reference-comparison.md +29 -10
  264. package/docs/usage/tokens/color.md +6 -3
  265. package/docs/usage/tokens/elevation-opacity.md +4 -2
  266. package/docs/usage/tokens/font-roles.md +2 -2
  267. package/docs/usage/tokens/layers.md +3 -3
  268. package/docs/usage/tokens/motion.md +2 -2
  269. package/docs/usage/tokens/radius.md +4 -2
  270. package/docs/usage/tokens/spacing.md +3 -3
  271. package/docs/usage/tokens/theme-studio.md +2 -2
  272. package/docs/usage/tokens/typography-studio.md +14 -8
  273. package/docs/usage/tokens/typography.md +15 -5
  274. package/package.json +7 -1
@@ -0,0 +1,43 @@
1
+ # 화면과 구성 선택
2
+
3
+ 검토일: 2026-10-13
4
+
5
+ 2026-10-13 설계 점검에서 화면 이름만으로 공통 화면을 강제하면 저장·스크롤·권한 흐름이
6
+ 다른 제품까지 같은 틀에 맞출 수 있음을 확인했다. 화면 → 구성 → 컴포넌트 → 토큰 순으로
7
+ 찾되, 아래 행동과 소유권이 맞는지 먼저 판단한다.
8
+
9
+ ## 선택 기준
10
+
11
+ | 선택 | 조건 | 구현 경로 |
12
+ | --- | --- | --- |
13
+ | 그대로 사용 | 영역 순서, 저장 시점, 상태 교체, 스크롤 소유가 제품과 맞는다 | 공통 화면에 데이터·문구·콜백을 연결 |
14
+ | 공개 슬롯으로 확장 | 공통 행동은 같고 헤더·본문 콘텐츠·보조 행동만 다르다 | header·notice·footer 등 해당 API의 공개 슬롯 사용 |
15
+ | 제품 전용 구성 | 저장·탐색·권한 흐름 또는 가상화·스크롤 구조가 공개 API로 표현되지 않는다 | 기존 HJM 구성·컴포넌트로 제품에서 조합하고 차이를 기존 코드/작업 기록에 설명 |
16
+
17
+ 공개 슬롯으로 해결할 수 있는 틀·상태 엔진은 복제하지 않는다. 제품 전용 구성도 semantic token,
18
+ 접근성·입력·실패 복구 계약을 유지한다. 내부 selector나 제거된 style prop은 확장 경로가 아니다.
19
+ 여러 제품이 공유하는 공백이면 공통 API의 의미 있는 축으로 검토한다. 별도 승인·ADR은 자동 요구하지 않는다.
20
+
21
+ ## 선택 전에 확인할 것
22
+
23
+ - **저장:** 즉시 저장인지 일괄 저장인지, pending의 소유자와 실패 시 마지막 확인값·초안이 어떻게 남는지.
24
+ - **스크롤:** host와 본문 중 누가 스크롤하는지, 목록 가상화·페이지 추가·위치 복구가 필요한지.
25
+ - **상태:** 최초 조회의 본문 교체와 저장/재조회 오류의 비차단 notice가 구분되는지.
26
+ - **권한:** 접근 제한에서 작성·전송을 숨길지, 읽기와 쓰기 권한이 다른지.
27
+ - **플랫폼:** Web 포커스와 Native 키보드·safe area를 누가 처리하는지. 같은 inset·스크롤을 중복 적용하지 않는다.
28
+
29
+ SettingsScreen의 행별 즉시 저장은 행 콜백에 제품 mutation을 연결한다. 일괄 저장도 공개 footer와
30
+ notice로 표현되면 같은 API를 사용한다. CommentThreadScreen의 데이터 구조나 스크롤이 대규모
31
+ 가상화에 맞지 않으면 이름만 보고 강제하지 않고 ScreenLayout과 적합한 목록을 조합한다.
32
+
33
+ ## 상태와 버전 읽기
34
+
35
+ | 정보 | 원본 | 해석 |
36
+ | --- | --- | --- |
37
+ | 제공 기준 | 컴포넌트 지침의 적용 버전과 설치 패키지 exports/타입 | 확인한 기본 API의 게시 버전이며 최초 출시 버전이나 후속 prop 전체의 제공 보증은 아님 |
38
+ | 플랫폼 지원·API 안정성 | 설치 버전 catalog의 surfaceStatus와 선택형 API 계약 | canonical과 supplemental·optional API를 구분. catalog 밖 API를 자동 stable로 간주하지 않음 |
39
+ | Storybook 분류 | 스토리 제목의 배포/실험 | 예제 분류. npm 게시·소비 앱 반영을 의미하지 않음 |
40
+ | 검증 | 해당 SHA·플랫폼·환경의 실행 결과 | 타입·브라우저·host mock·기기 결과의 범위를 구분 |
41
+
42
+ 구성·화면은 여러 API를 사용하므로 별도 라이브러리 버전을 복제하지 않고 각 API 지침을 참조한다.
43
+ 설치 버전보다 새 문서의 prop은 설치된 타입과 export에서 다시 확인한다.
@@ -1,11 +1,11 @@
1
1
  # 사용 지침 규격
2
2
 
3
- 검토일: 2026-10-06 · 강제: `pnpm usage:check`(`scripts/check-usage-docs.mjs`)
3
+ 검토일: 2026-10-13 · 선택적 형식 감사: `pnpm usage:check`(`scripts/check-usage-docs.mjs`)
4
4
 
5
5
  2026-10-06 사용자 요청: 지침은 단계마다 같은 규격으로 나와야 한다. 그래야 에이전트가 어느 지침을 열어도
6
6
  같은 자리에서 같은 정보(위치·크기·간격·순서·상태)를 찾고, 빠진 정보를 검사기가 잡는다. 자유 형식이던
7
- 첫 작성본은 같은 정보가 절마다 다른 이름·표 모양으로 흩어져 있었다. 이 규격을 바꾸면 검사기·템플릿·
8
- 전체 지침을 같은 변경에서 바꾼다.
7
+ 첫 작성본은 같은 정보가 절마다 다른 이름·표 모양으로 흩어져 있었다. 형식은 탐색과 자동 색인 생성을 위한 작성 관례다. 제품 계약과 예제의 정확성은 유지하되,
8
+ 절 순서·표 모양을 개발·npm 게시의 차단 조건으로 삼지 않는다. 규격 변경은 영향받는 생성기와 지침만 함께 갱신한다.
9
9
 
10
10
  ## 1. 공통
11
11
 
@@ -15,9 +15,9 @@
15
15
 
16
16
  ```markdown
17
17
  - 단계: 컴포넌트 # 토큰 | 컴포넌트 | 구성 | 화면
18
- - 상태: 배포 # 담당 스토리에 배포가 하나라도 있으면 배포, 모두 실험이면 실험(검사기가 대조)
18
+ - 상태: 배포 # Storybook 분류만 의미. API 안정성·npm 게시 상태가 아님
19
19
  - 지원: Web · Native # Web · Native | Web | Native
20
- - 적용: 1.12.1 # 확인한 게시 버전 x.y.z, 아직 게시 안 된 API는 `미게시(1.12.1 이후)`
20
+ - 적용: 1.17.1 # 확인한 기본 API의 게시 버전. 최초 출시 버전이 아님; 조합 예제는 `구성 API 지침 참조`
21
21
  - 검토일: 2026-10-06
22
22
  - 근거: `../../button-label.md`(링크), `src/base-recipes.ts` # 계약 문서·소스
23
23
  - 스토리북: `배포/컴포넌트/동작/버튼` # 담당 제목 전부, 백틱. 구성·화면·토큰은 검사기가 이 줄로 담당을 판정
@@ -36,17 +36,20 @@
36
36
  - `- 스토리북: 없음`은 어느 단계에도 그 API를 보여 주는 스토리가 없는 보조 API에만 쓴다.
37
37
  - 색인의 `분류` 열은 `스토리북:` 줄의 담당 제목 셋째 마디로 만든다(다른 단계 예제만 참조하는 컴포넌트는 `<단계>/<분류>`).
38
38
  머리말에 분류 키를 따로 두지 않는다. 2026-10-06 분류 어휘를 고정하면서 색인을 메뉴 순서로 묶었고, 키를 두면 제목과 두 벌이 된다.
39
- - `##` 절은 아래 단계 표의 이름·순서만 쓴다. 필수(●)는 반드시, 선택(○)은 해당될 때만 그 자리에 둔다.
39
+ - 색인과 필수 정보의 기준 절은 아래 이름·순서를 쓴다. 필요한 추가 설명 절은 허용한다. 필수(●)와 선택(○)은 정보의 유무를 구분한다.
40
40
  절 안에서는 `###`를 자유롭게 쓴다.
41
41
  - 표는 단계 표에 적힌 **열 이름 그대로** 쓴다. 해당 없는 칸은 `—`.
42
42
  - 숫자는 토큰 이름과 값을 함께 쓴다(`spacing.md` 16). 소스에서 확인한 것만 쓴다.
43
43
  - **규칙과 관찰을 구분한다.** 스토리·소스가 다른 지침이나 토큰 규칙과 어긋나면(예: 제목을 `Text`로 그림, Native
44
44
  ScrollView에 여백을 직접 줌) 지침 본문은 규칙대로 쓰고 어긋남은 `## 함정`에 "현재 스토리는 …"으로 적는다.
45
45
  2026-10-06 블라인드 검증에서 화면 지침이 스토리 관찰값을 규칙처럼 옮겨 컴포넌트·토큰 지침과 충돌했다.
46
- - **코드 예는 타입 검사로 확인한 것만 쓴다**(`strict` + `exactOptionalPropertyTypes`, 설치 버전의 타입). 같은 검증에서
47
- `grid.md`의 `gap="md"`와 Web `onChange` 예가 실제 타입과 달라 그대로 쓰면 컴파일이 깨졌다.
46
+ - 코드 예는 `strict` + `exactOptionalPropertyTypes`로 실제 공개 타입을 확인한다. `usage:check`는 문서 형식 검사이며 타입 검사 결과가 아니다.
47
+ - 같은 예제를 문서와 Storybook에 복사하지 않는다. 실행되는 TSX 원본을 참조할 수 있으며 플랫폼마다
48
+ `<!-- usage-example: Web 상대/경로.tsx -->` 또는 `Native` 표시와 읽을 수 있는 링크를 함께 둔다.
49
+ 검사기는 참조 파일의 존재만 확인한다. 변경한 원본은 해당 Showcase 타입 검사 또는 파일 지정 타입 검사로 확인한다.
50
+ 2026-10-13 설정 화면의 중복 코드 골격을 실제 Web/Native preview 참조로 교체했다. 제품은 예제를 읽고 공개 API를 사용하며 showcase를 import하지 않는다.
48
51
  - 배치도는 ```text 블록 ASCII. 위→아래, 주 행동·보조 행동·고정 영역·스크롤 영역·안전 영역을 표시한다.
49
- - 코드 예는 ```tsx 블록, 첫 줄 주석 `// Web` 또는 `// Native`. `지원`에 적은 플랫폼마다 예를 하나 이상 둔다. 문구는 `t("...")`.
52
+ - 인라인 코드 예는 ```tsx 블록과 첫 줄 `// Web` 또는 `// Native`를 쓴다. 실행 원본 참조도 코드 예로 인정한다. 제품 문구는 i18n으로 공급한다.
50
53
  - 규격 절 이름을 `###`로 내려 쓰지 않는다(표·행 검사를 피하게 된다). 다른 내용은 해당 규격 절 안의 `###`로 둔다.
51
54
  - 링크: 같은 단계 `name.md`, 다른 단계 `../<폴더>/name.md`, 계약 문서 `../../<doc>.md`.
52
55
  - 문체: 한국어 평서체(-다).
@@ -103,6 +106,15 @@
103
106
  | `## 플랫폼 차이` | ○ | `\| 항목 \| Web \| Native \|` |
104
107
  | `## 함정` | ○ | 목록 |
105
108
 
106
- ## 3. 템플릿
109
+ ## 3. 원본과 확인 범위
110
+
111
+ 선택은 [화면과 구성 선택](SELECTION.md)을 따른다. canonical API 안정성은 생성된 manifest에서 색인으로
112
+ 읽고 별도 숫자나 stable 목록을 복제하지 않는다. 구성·화면의 `적용: 구성 API 지침 참조`는
113
+ 본문의 API 링크를 따라 확인한다. `미게시(x.y.z 이후)`는 실제 미게시 추가분에만 쓴다.
114
+
115
+ `node scripts/check-usage-docs.mjs --files <지침 경로> ...`는 지정한 지침만 감사한다.
116
+ `--write`를 함께 주면 색인을 갱신한다. 전체 지침 감사는 명시적으로 선택할 때만 실행한다.
117
+
118
+ ## 4. 템플릿
107
119
 
108
120
  [`templates/`](templates/)의 네 파일을 복사해 시작한다. 템플릿은 검사 대상이 아니다.
@@ -72,7 +72,7 @@ import { Text } from "@hjmds/react-native/primitives";
72
72
  | Native `label` | `string` | 필수 | 그룹 접근성 이름 |
73
73
  | Native `renderIndicator` | `(props: { value, expanded, disabled, color, size }) => ReactNode` | `+`/`−` | 펼침 표시만 교체한다 |
74
74
  | `layoutStyle` | margin·width·flex·`alignSelf` | — | 바깥 배치 전용 |
75
- | Native `style`·`itemStyle`·`triggerStyle`·`titleStyle`·`indicatorStyle`·`panelStyle` | — | — | deprecated — `layoutStyle` 또는 `density`/`renderIndicator`(개발 모드 1회 경고, 다음 major 제거) |
75
+ | Native `style`·`itemStyle`·`triggerStyle`·`titleStyle`·`indicatorStyle`·`panelStyle` | — | — | 제거된 속성 — `layoutStyle` 또는 `density`/`renderIndicator` |
76
76
 
77
77
  ## 배치
78
78
 
@@ -86,12 +86,14 @@ import { Text } from "@hjmds/react-native/primitives";
86
86
 
87
87
  ## 꼭 지킬 것
88
88
 
89
- - 항목은 하나 이상, 식별자(Web `id`, Native `value`)는 고유해야 한다. 빈 목록·중복 id·없는 값·
89
+ - 항목은 하나 이상, 식별자(Web `id`, Native `value`)는 고유해야 한다. 빈 목록·중복 id·최초 기본값 또는 제어 값에 없는 식별자·
90
90
  단일 모드에서 둘 이상 펼침은 렌더 중 `TypeError`/`RangeError`로 실패한다.
91
+ - 비제어 상태에서 펼쳐진 항목을 삭제하면 그 식별자를 펼침 값에서 제거하고 변경 콜백을 한 번 알린다. 재삽입해도 저절로 다시 열리지 않는다. 삭제로 오류 화면이 되거나 이전 선택이 되살아나는 문제를 막기 위한 규칙이다.
92
+ - 펼쳐진 항목의 `disabled` 전환은 추가 토글만 막고 읽던 본문은 유지한다. 웹에서 삭제된 항목 또는 새로 비활성화된 트리거에 포커스가 있었다면 다음 활성 트리거로 옮긴다(없으면 그룹). 사용자가 이미 외부로 옮긴 포커스는 유지한다.
91
93
  - 제목·본문은 i18n 키로 넣는다. Native는 그룹 `label`이 필수이며 접근성 이름이 된다.
92
94
  - 펼침 표시(`+`/`−`)와 trigger·panel 접근성 관계는 HJM이 소유한다. 직접 버튼과 패널을 다시 조립하지 않는다.
93
95
  - 배치는 `layoutStyle`로만 한다. Native의 `style`·`itemStyle`·`triggerStyle`·`titleStyle`·`indicatorStyle`·`panelStyle`은
94
- deprecated다. 새 코드에 쓰지 않는다([소비 정책 §3](../../consumer-policy.md), [Native 이관](../../migration-native-legacy-removal.md)).
96
+ 제거된 속성이다. 새 코드에 쓰지 않는다([소비 정책 §3](../../consumer-policy.md), [Native 이관](../../migration-native-legacy-removal.md)).
95
97
 
96
98
  ## 플랫폼 차이
97
99
 
@@ -0,0 +1,109 @@
1
+ # ActivityChip
2
+
3
+ - 단계: 컴포넌트
4
+ - 상태: 실험
5
+ - 지원: Web · Native
6
+ - 적용: 미게시(1.17.2 이후)
7
+ - 검토일: 2026-10-10
8
+ - 근거: [ActivityChip 계약](../../activity-chip.md), `src/activity-chip.ts`(`activityChipRecipe`)
9
+ - 스토리북: `실험/컴포넌트/상태와 알림/작업 상태 칩`, `실험/구성/피드백과 복구/백그라운드 작업 상태`
10
+
11
+ ## 언제 쓰나
12
+
13
+ 백그라운드에서 돌고 있는 작업 하나(검수·업로드·동기화)의 상태를 헤더의 빈 자리에 짧게 보여 주고, 누르면 그 작업의
14
+ 진행 패널([Toast](toast.md) 패널)을 여닫을 때 쓴다. 작업이 끝나면 결과 문구와 `emphasis`로 잠깐 바뀐다.
15
+ 흐름 전체는 [백그라운드 작업 상태](../compositions/activity-status.md) 구성을 따른다.
16
+
17
+ ## 쓰지 않을 때
18
+
19
+ | 상황 | 대신 쓸 것 |
20
+ | --- | --- |
21
+ | 필터·선택처럼 켜고 끄는 값 | [Chip](chip.md)(`selectionMode`) |
22
+ | 누르면 바로 동작하는 짧은 행동 | [Button](button.md)·[Chip](chip.md)(action) |
23
+ | 개수만 알리는 표시 | [CounterBadge](counter-badge.md) |
24
+ | 진행 상황을 화면 안에 계속 보여야 함 | [Progress](progress.md)·[Steps](steps.md) |
25
+
26
+ 선택 기준: 누르면 **상세 패널이 열리고 닫히는** 상태 표시면 ActivityChip이다. 값을 고르면 Chip, 실행이면 Button이다.
27
+
28
+ ## 공개 이름과 import
29
+
30
+ | 이름 | 역할 | Web | Native |
31
+ | --- | --- | --- | --- |
32
+ | `ActivityChip` | 보조(작업 상태 칩, 펼침 트리거) | `@hjmds/react/activity-chip` | `@hjmds/react-native/activity-chip` |
33
+ | `resolveActivityChipDescriptor`·`activityChipRecipe` | 계약(검증·크기·색) | `@hjmds/design-contracts/activity-chip` | 같음 |
34
+
35
+ ## 최소 사용 예
36
+
37
+ ```tsx
38
+ // Web
39
+ import { ActivityChip } from "@hjmds/react/activity-chip";
40
+
41
+ <ActivityChip
42
+ label={t("review.chip.running")}
43
+ accessibilityLabel={t("review.chip.runningSpoken")}
44
+ expanded={panelOpen}
45
+ media={<img src={mascotUrl} alt="" />}
46
+ onClick={togglePanel}
47
+ />;
48
+ ```
49
+
50
+ ```tsx
51
+ // Native
52
+ import { Image } from "react-native";
53
+ import { ActivityChip } from "@hjmds/react-native/activity-chip";
54
+
55
+ <ActivityChip
56
+ label={t("review.chip.running")}
57
+ accessibilityLabel={t("review.chip.runningSpoken")}
58
+ expanded={panelOpen}
59
+ media={<Image source={mascot} style={{ width: 28, height: 28 }} />}
60
+ onPress={togglePanel}
61
+ />;
62
+ ```
63
+
64
+ ## 축과 기본값
65
+
66
+ | prop | 값 | 기본값 | 설명 |
67
+ | --- | --- | --- | --- |
68
+ | `label` | `string` | — | 필수. 보이는 짧은 상태(한 줄, 넘치면 줄임표). 빈 문자열이면 throw |
69
+ | `accessibilityLabel` | `string` | `label` | 화면 낭독용 전체 문장. Web은 줄 때만 `aria-label`을 둔다 |
70
+ | `emphasis` | `default` · `emphasis` | `default` | `emphasis`는 결과가 사용자를 기다릴 때(brand fill) |
71
+ | `expanded` | `boolean` | `false` | 이 칩이 여닫는 패널이 열렸는지. Web `aria-expanded`, Native `accessibilityState.expanded` |
72
+ | `media` | `ReactNode` | — | 장식 시각물(마스코트·아바타). 28×28 원 안에 그리고 보조기술에서 숨긴다 |
73
+ | `onClick`(Web) · `onPress`(Native) | 함수 | — | Web은 `<button>` 속성(`onClick`·`disabled`·`id`·`data-*`)을 받는다. Native는 `onPress` 필수, `disabled`·`accessibilityHint`·`testID` 선택 |
74
+ | `layoutStyle` | `HjmCompositionStyleProp` | — | 배치만. Web은 raw `style`을 받지 않는다 |
75
+
76
+ ## 배치
77
+
78
+ | 항목 | 값 | 근거 |
79
+ | --- | --- | --- |
80
+ | 크기 | 최소 높이 44(`control.minTouchTarget`), 최대 폭 180, 미디어 28, 모서리 full | `activityChipRecipe.root`·`media` |
81
+ | 간격 | 미디어와 라벨 사이 `spacing.xxs` 4. 안쪽 여백 시작 `spacing.xxs` 4(미디어 있을 때, 없으면 `spacing.sm` 12)·끝 `spacing.sm` 12 | `activityChipRecipe.root`, `.hjm-activity-chip` |
82
+ | 순서·정렬 | [미디어][라벨]. TopBar의 `trailing` 첫 자리 또는 제목이 없는 헤더의 빈 자리에 하나만 둔다 | 구성 [백그라운드 작업 상태](../compositions/activity-status.md) |
83
+ | 고정·스크롤 | 헤더와 함께 고정된다. 작업이 없으면 칩을 렌더하지 않는다(빈 자리 유지) | 구성 예제 |
84
+ | 좁은 폭·큰 글자 | 폭은 180에서 멈추고 라벨은 한 줄 줄임표. 큰 글자에서는 높이가 늘어난다(44는 바닥값) | `label.maxLines` 1, `minHeight` |
85
+
86
+ ```text
87
+ ┌ TopBar ───────────────────────────────────────────┐
88
+ │ [←] 내 작업 ( (🐥) 다듬는 중 ) │ ← ActivityChip, 높이 44 · 최대 폭 180
89
+ └───────────────────────────────────────────────────┘
90
+ ```
91
+
92
+ ## 꼭 지킬 것
93
+
94
+ - 칩은 상태 표시 겸 펼침 트리거다. 누르면 같은 작업의 패널 Toast를 열고, 다시 누르면 닫는다. 다른 화면 이동에 쓰지 않는다.
95
+ - `expanded`는 실제 패널 상태와 같아야 한다. 패널이 닫기 버튼·스와이프·Escape로 닫혀도 Toast `onDismiss`에서 `false`로 되돌린다.
96
+ - 문구는 i18n 문자열, 미디어는 제품 자산이다. HJM은 칩 모양·펼침 의미·색 쌍을 소유한다. 색을 `layoutStyle`로 덮지 않는다.
97
+
98
+ ## 플랫폼 차이
99
+
100
+ | 항목 | Web | Native |
101
+ | --- | --- | --- |
102
+ | 누름 | `onClick`(button 속성 전부) | `onPress` 필수 |
103
+ | 펼침 상태 | `aria-expanded` | `accessibilityState.expanded` |
104
+ | 낭독 이름 | `accessibilityLabel`이 있을 때만 `aria-label` | `accessibilityLabel ?? label` |
105
+
106
+ ## 함정
107
+
108
+ - 새 컴포넌트라 canonical catalog 행이 없다(동결 중). 공개 API 대응표에서는 supplemental로 분류된다.
109
+ - 실험 항목이다. 기기 시각·VoiceOver/TalkBack 확인은 아직 하지 않았다.
@@ -74,11 +74,11 @@ import { Agreement } from "@hjmds/react-native/agreement";
74
74
  | 전체 동의 | tri-state(파생) | — | 저장되는 값이 아니라 개별 항목에서 파생한다. 비활성 항목은 분모에서 빠진다 |
75
75
  | `checkedIds` · `defaultCheckedIds` | `ReadonlySet<Id>` | 비제어 빈 Set | 제어·비제어. 목록에서 빠진 항목의 체크는 자동으로 버려진다(`reconcileAgreementSelection`) |
76
76
  | `onCheckedIdsChange` | `(ids: ReadonlySet<Id>) => void` | — | 체크 목록이 바뀔 때 |
77
- | `onStateChange` | `(state: AgreementState) => void`, `state` = `{ all: boolean \| "mixed", satisfied: boolean, missingRequiredIds: readonly Id[] }` | — | 마운트 때 초기 상태로 한 번, 그 뒤 체크를 바꿀 때마다 호출된다(마운트 호출은 미게시(1.12.1 이후)) |
77
+ | `onStateChange` | `(state: AgreementState) => void`, `state` = `{ all: boolean \| "mixed", satisfied: boolean, missingRequiredIds: readonly Id[] }` | — | 마운트와 실제 체크/필수 항목 판정 변경 때 호출된다. 제어 요청 거부는 가상의 동의 완료를 알리지 않는다 |
78
78
  | `onDetail` | `(id: Id) => void` | — | 전문 보기. Web은 `detail.href`가 없을 때만 호출 |
79
79
  | `requiredLabel` · `optionalLabel` | `string` | 필수 | 행 끝 "(필수)"·"(선택)" 문구 |
80
80
  | `layoutStyle` | margin·width·flex·`alignSelf` | — | 바깥 배치 전용 |
81
- | Native `style` | — | — | deprecated — `layoutStyle` 또는 recipe(개발 모드 1회 경고, 다음 major 제거) |
81
+ | Native `style` | — | — | 제거된 속성 — `layoutStyle` 또는 recipe |
82
82
 
83
83
  ## 배치
84
84
 
@@ -110,27 +110,24 @@ Native의 전체 동의 프레임 `md`와 선택 표시 `sm`은 Provider의 `tok
110
110
  ## 꼭 지킬 것
111
111
 
112
112
  - 필수 동의 판정은 `resolveAgreementState`의 `satisfied`를 사용하고 제출 가능 여부는 제품의 입력 검증·진행 중 상태와 함께 결정한다. 묶음 잠금은 동의 사실을 바꾸지 않으므로 `satisfied`가 true인 채 잠길 수 있다. 남은 필수 항목 안내는 `missingRequiredIds`로 제품이 문장을 만든다.
113
- - `onStateChange`는 마운트와 사용자 토글 때 알린다. 제품이 약관 버전이나 제어 `checkedIds`를 직접 바꾸면 같은 렌더에서 `resolveAgreementState(descriptor, checkedIds)`로 판정한다. 문서 버전 변경 시 이전 동의를 초기화하는 책임은 제품에 있다.
113
+ - `onStateChange`는 마운트와 실제 동의/필수 항목 판정 변경 때 알린다. 외부 `checkedIds`·필수 항목 변경도 반영하며, 부모가 제어 요청을 거부하면 미수락 상태를 완료로 알리지 않는다. 같은 렌더에서 판정이 필요하면 `resolveAgreementState(descriptor, checkedIds)`를 사용한다. 문서 버전 변경 시 이전 동의를 초기화하는 책임은 제품에 있다.
114
114
  - `accessibilityLabel`·`allLabel`·항목 `label`·`requiredLabel`·`optionalLabel`은 모두 제품의 i18n 문구다.
115
115
  빈 문자열, 빈 목록, 중복 id, 필수이면서 `disabled`인 항목은 throw다.
116
116
  - 약관 문구·링크 주소·법적 유효성·동의 기록 저장은 제품 소유다. HJM은 판정과 배치만 가진다.
117
117
  - 전문 보기 컨트롤은 체크박스와 별개이며 눌러도 체크되지 않는다. 같은 행에 다른 누름 영역을 덧붙이지 않는다.
118
- - 색·간격은 recipe가 정한다. 배치는 `layoutStyle`로만 한다. Native `style`은 deprecated다([소비 정책 §3](../../consumer-policy.md)).
118
+ - 색·간격은 recipe가 정한다. 배치는 `layoutStyle`로만 한다. Native `style`은 제거된 속성이다([소비 정책 §3](../../consumer-policy.md)).
119
119
 
120
120
  ## 플랫폼 차이
121
121
 
122
122
  | 항목 | Web | Native |
123
123
  | --- | --- | --- |
124
124
  | 전문 열기 | `detail.href`가 있으면 `<a>`, 없으면 버튼이 `onDetail(id)` 호출 | `href`를 쓰지 않고 항상 `onDetail(id)` |
125
- | 배치 prop | `layoutStyle`(+`className`) | `layoutStyle`(`style`은 deprecated) |
125
+ | 배치 prop | `layoutStyle`(+`className`) | `layoutStyle`(`style`은 제거된 속성) |
126
126
  | `ref` | root `div`로 전달 | 없음 |
127
127
 
128
128
  ## 함정
129
129
 
130
- - 1.12.1까지 `onStateChange`는 사용자가 체크를 바꿀 때만 호출돼, `defaultCheckedIds`로 필수 항목을 미리 채운 경우 첫
131
- `satisfied`를 받지 못했다. 미게시(1.12.1 이후) 버전은 마운트 때 초기 상태를 한 번 알린다. 1.12.1에서는 제출 버튼의 첫 상태를
132
- `resolveAgreementState(descriptor, checkedIds)`(`@hjmds/design-contracts/components/agreement`)로 직접 계산한다.
133
- 호출 횟수를 세는 코드는 마운트 1회가 늘어난다.
130
+ - `onStateChange`는 마운트 때 초기 상태와 이후 실제 체크/필수 항목 판정 변경을 알린다. 제출 버튼의 활성 상태는 이 결과의 `satisfied`에 연결한다. 사용자 조작 횟수로 해석하지 않는다.
134
131
 
135
132
  2026-10-07 Utilverse 가입은 요청 중 필수 Checkbox 둘을 잠그는데, 개별 필수 item에 disabled를
136
133
  주면 Agreement 검증이 거절했다. 묶음 `descriptor.disabled`를 추가해 필수 항목을 분모에서
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-07
7
+ - 검토일: 2026-10-13
8
8
  - 근거: `AlertDialogRequest`·`createAlertDialogSession`(`src/alert-dialog.ts`), recipe `alertDialogRecipe`, Popover와의 경계 [ConfirmPopover 결정](../../confirm-popover.md), Native 긴 문구 처리 [Dialog](../../dialog.md)
9
9
  - 스토리북: `배포/컴포넌트/오버레이/확인 대화상자`
10
10
 
@@ -39,6 +39,7 @@ import { AlertDialog } from "@hjmds/react/overlays";
39
39
  <AlertDialog
40
40
  open={open}
41
41
  onOpenChange={(next) => setOpen(next)}
42
+ returnFocusRef={openButtonRef}
42
43
  request={{
43
44
  mode: "confirm",
44
45
  tone: "danger",
@@ -52,6 +53,10 @@ import { AlertDialog } from "@hjmds/react/overlays";
52
53
  />
53
54
  ```
54
55
 
56
+ Web의 외부 열기 버튼과 `returnFocusRef`에 같은 `useRef<HTMLButtonElement>(null)`을 연결한다.
57
+ macOS WebKit에서는 버튼 클릭만으로 호출 위치에 초점이 남지 않을 수 있다. 삭제 성공으로 버튼도
58
+ 제거될 때는 목록 제목 등 작업을 이어갈 유효한 위치를 복귀 대상으로 정한다([Dialog 초점 복귀](dialog.md#최소-사용-예)).
59
+
55
60
  ```tsx
56
61
  // Native
57
62
  import { AlertDialog } from "@hjmds/react-native/overlays";
@@ -60,6 +65,7 @@ import { AlertDialog } from "@hjmds/react-native/overlays";
60
65
  open={open}
61
66
  onOpenChange={(next) => setOpen(next)}
62
67
  onResult={(result) => { if (result.outcome === "confirmed") router.back(); }}
68
+ size="small"
63
69
  request={request}
64
70
  />
65
71
  ```
@@ -79,9 +85,9 @@ import { AlertDialog } from "@hjmds/react-native/overlays";
79
85
  | 닫기 | — | — | 바깥 누름으로 닫히지 않는다. Escape(Web)·뒤로(Native)는 취소로 끝난다. 처리 중(`busy`)에는 닫을 수 없다 |
80
86
  | `request.onConfirm` 실패 | — | — | 열린 채로 `resolveErrorMessage` 또는 `fallbackErrorMessage`를 보여 준다 |
81
87
  | Web `trigger` | `ReactNode` | — | 비제어면 필수 |
82
- | Web `size` | `small` · `medium` · `large` | `medium`(Dialog recipe) | — |
88
+ | `size` | `small` · `medium` · `large` | Web `medium` · Native `small` | 각 플랫폼 recipe의 최대 폭·안쪽 여백을 선택한다 |
83
89
  | Web `modalPriority` | `number` | `0` | 높은 우선순위 모달이 뒤에 열린 낮은 모달 위에서 동작한다 |
84
- | Native `contentStyle` | 배치 key(margin·width·flex·`alignSelf`)만 | — | 색·radius·padding 등 시각 key는 deprecated(개발 모드 1회 경고, 다음 major에서 배치 key로 좁힘) |
90
+ | Native `contentStyle` | 배치 key(margin·width·flex·`alignSelf`)만 | — | `HjmCompositionStyleProp` 배치 전용 타입. 색·radius·padding은 받지 않으며 크기는 `size`로 지정한다 |
85
91
 
86
92
  ### 디자인 프로필 상속
87
93
 
@@ -96,8 +102,8 @@ import { AlertDialog } from "@hjmds/react-native/overlays";
96
102
 
97
103
  | 항목 | 값 | 근거 |
98
104
  | --- | --- | --- |
99
- | 크기 | Web `size` `medium` `min(36rem, 100%)` · `small` 28rem · `large` 48rem, 최대 높이 `100dvh − 32`. Native는 recipe 기본 `small`로 최대 폭 320, 폭 100%. 아이콘 원 44(`control.minTouchTarget`). Native 행동 버튼 최소 폭 96 | `.hjm-alert-dialog`, `alertDialogRecipe`·`dialogRecipe.sizes`, `react-native/src/overlays.tsx` |
100
- | 간격 | 화면 가장자리와 최소 `spacing.md` 16. 안쪽 여백 `spacing.lg` 20(Web·Native small). 요소 사이 Web `spacing.sm` 12 · Native `dialogRecipe.content.gap` `spacing.md` 16. 행동 사이 `spacing.sm` 12, 세로로 쌓이면 `spacing.xs` 8. Native 버튼 좌우 `spacing.md` 16 | `.hjm-overlay`, `.hjm-alert-dialog__actions`, `alertDialogRecipe.actions` |
105
+ | 크기 | Web `size` `medium` `min(36rem, 100%)` · `small` 28rem · `large` 48rem, 최대 높이 `100dvh − 32`. Native는 폭 100%, 최대 폭은 `small` 320 · `medium` 420 · `large` 640이며 기본은 `small`이다. 아이콘 원 44(`control.minTouchTarget`). Native 행동 버튼 최소 폭 96 | `.hjm-alert-dialog`, `alertDialogRecipe`·`dialogRecipe.sizes`, `react-native/src/overlays.tsx` |
106
+ | 간격 | 화면 가장자리와 최소 `spacing.md` 16. 안쪽 여백은 Native `small` `spacing.lg` 20, `medium`·`large` `spacing.xl` 24이다. 요소 사이 Web `spacing.sm` 12 · Native `dialogRecipe.content.gap` `spacing.md` 16. 행동 사이 `spacing.sm` 12, 세로로 쌓이면 `spacing.xs` 8. Native 버튼 좌우 `spacing.md` 16 | `.hjm-overlay`, `.hjm-alert-dialog__actions`, `alertDialogRecipe.actions` |
101
107
  | 순서·정렬 | 화면 가운데. 위→아래 [아이콘] → 제목 → 설명 → 오류 문구 → 행동. 행동은 끝 정렬 한 줄 [취소][확인]. 버튼 수는 `mode`가 정한다(alert 1 · confirm 2) | `.hjm-overlay`, `alertDialogRecipe.slots` |
102
108
  | 고정·스크롤 | 배경막(`backdrop.modal`)이 화면 전체를 덮고 하단 고정 바보다 위에 쌓인다(Web z-index `layer.modal` 900). 내용이 길면 대화상자 안에서 스크롤한다 | `.hjm-overlay`, `.hjm-alert-dialog` |
103
109
  | 좁은 폭·큰 글자 | 폭 < 600(`breakpoint.medium`)이면 행동을 세로로 쌓고 **확인이 위**에 온다(Web DOM은 [취소][확인] 유지). Native는 글자 배율 1.6 이상(`largeTextThreshold`)에서도 쌓는다 | `alertDialogRecipe.actions.stackBelow`·`stackedOrder`, `react-native/src/overlays.tsx` |
@@ -129,7 +135,7 @@ import { AlertDialog } from "@hjmds/react-native/overlays";
129
135
  | 여는 요소 | `trigger`(선택, 비제어면 필수) | 없음. `open`/`defaultOpen`으로만 연다 |
130
136
  | 결과 콜백 | 없음(`onOpenChange` 사유 `confirm`/`cancel-action`/`escape`) | `onResult({ outcome })` |
131
137
  | 아이콘 | `icon` | 없음 |
132
- | 크기·우선순위·portal | `size`, `modalPriority`, `portalContainer` | 없음(RN `Modal` props 일부를 그대로 받는다) |
138
+ | 크기·우선순위·portal | `size`, `modalPriority`, `portalContainer` | `size`와 RN `Modal` props 일부. `modalPriority`·`portalContainer`는 없다 |
133
139
  | 초점 복귀 | `returnFocusRef`(없으면 trigger) | `returnFocusRef` |
134
140
  | 배치 prop | 없음(`layoutStyle` 제외 대상) | `contentStyle` 배치 key만 |
135
141
 
@@ -9,7 +9,7 @@
9
9
  - 스토리북: `배포/컴포넌트/데이터 표시/이미지·영상 표시` · `배포/컴포넌트/데이터 표시/음성 메모`
10
10
 
11
11
  개발 중인 프로필 지원에서 `rounded`는 가장 가까운 프로필의 `tokens.radius.md`를 따른다.
12
- 프로필이 없으면 기존 12px이고 명시한 square/circle은 그대로다. 이 보강은 아직 미게시이며
12
+ 프로필이 없으면 기존 12px이고 명시한 square/circle은 그대로다. 이 보강은 1.15.0 변경이며
13
13
  1.12.1의 기존 Asset 제공 여부와 구분한다([근거](../../../../../docs/qa/2026-10-07-3dicons-page-review.md)).
14
14
 
15
15
  ## 언제 쓰나
@@ -89,7 +89,7 @@ import { VoiceNote } from "@hjmds/react-native/voice-note";
89
89
  | `children` | `ReactNode` 또는 `(state: { animate: boolean }) => ReactNode` | 필수 | `animate`는 `lottie`·`video`이고 reduced motion이 아닐 때만 `true`다 |
90
90
  | `accessory` | `ReactNode` | — | 액자 바깥 모서리에 붙는 작은 표식(재생 아이콘, 상태 점) |
91
91
  | `layoutStyle` | margin·width·flex·`alignSelf` | — | Asset·AssetGroup 바깥 배치 전용 |
92
- | Native `style`(Asset·AssetGroup) | — | — | deprecated — `layoutStyle` 또는 descriptor `size`/`shape`(개발 모드 1회 경고, 다음 major 제거) |
92
+ | Native `style`(Asset·AssetGroup) | — | — | 제거된 속성 — `layoutStyle` 또는 descriptor `size`/`shape` |
93
93
  | `AssetGroup` `label` · `size` | `string` · `AssetSize` | `label` 필수 · `size` `medium` | 겹침은 크기의 30%로 Avatar와 같다 |
94
94
  | `VoiceNote` `descriptor` | `{ title, state: "paused" \| "playing" \| "loading" \| "error", duration: number \| null, position: number, disabled? }` | 필수 | `duration: null`(메타데이터 미확인)과 `0`이면 재생·탐색이 막힌다 |
95
95
  | `VoiceNote` `labels` | `{ play, pause, seek, loading, error, retry, backward, forward }`(모두 `string`) | 필수 | i18n 문구 |
@@ -101,7 +101,7 @@ import { VoiceNote } from "@hjmds/react-native/voice-note";
101
101
 
102
102
  | 항목 | 값 | 근거 |
103
103
  | --- | --- | --- |
104
- | 크기 | 정사각 액자. 한 변 `small` 32 · `medium` 48 · `large` 72 · `xlarge` 120. 모서리 `square` 0 · `rounded` `radius.md`(무프로필 12, 미게시 보강에서는 프로필 값) · `circle` foundation `radius.full`. 터치 대상이 아니므로 누를 수 있게 하려면 감싸는 버튼·행이 최소 44를 확보한다 | `assetRecipe.sizes`·`shapes`, `.hjm-asset__frame` |
104
+ | 크기 | 정사각 액자. 한 변 `small` 32 · `medium` 48 · `large` 72 · `xlarge` 120. 모서리 `square` 0 · `rounded` `radius.md`(무프로필 12, 프로필이 있으면 프로필 값) · `circle` foundation `radius.full`. 터치 대상이 아니므로 누를 수 있게 하려면 감싸는 버튼·행이 최소 44를 확보한다 | `assetRecipe.sizes`·`shapes`, `.hjm-asset__frame` |
105
105
  | 간격 | `accessory`는 액자 끝·아래 모서리 바깥으로 `spacing.xxs` 4 띄워 붙으므로 옆 요소와 `spacing.xs` 8 이상 띄운다. `AssetGroup` 겹침은 크기의 30%(48이면 −14) | `assetRecipe.accessory`·`overlapRatio`, `react/src/asset.tsx` |
106
106
  | 순서·정렬 | 늘어나지 않는 인라인 요소(Web `inline-flex`, `flex: 0 0 auto`). 행 안에서는 시작 쪽에 둔다. 한 줄에 종류가 다른 그림을 섞을 때 모두 같은 `size`·`shape`를 준다 | `.hjm-asset`, `assetBehavior.scenarios` |
107
107
  | 고정·스크롤 | 고정 영역이 없다 | — |
@@ -112,6 +112,12 @@ import { VoiceNote } from "@hjmds/react-native/voice-note";
112
112
  - 뜻이 있는 그림은 `accessibilityLabel`을, 장식은 `decorative: true`만 준다. 이름 없는 비장식과
113
113
  둘 다 준 경우 모두 렌더 중 `TypeError`가 난다.
114
114
  - reduced motion에서는 숨기지 말고 `animate`로 재생을 멈춘다. 판단을 제품에서 다시 만들지 않는다.
115
+ - `animate`는 허용 값이다. 슬롯의 플레이어가 값 변경에 맞춰 멈추거나 정지 원화로 전환하도록
116
+ 연결한다. autoplay 초기값만 넘기면 실행 중 설정 변경을 반영하지 못할 수 있다.
117
+ - 1회 완료 연출은 플레이어의 반복 옵션을 명시한다(Lottie React Native는 설치 버전 API를
118
+ 확인해 `loop={false}`). 확인된 결과 ID로 재생 여부를 관리해 재진입만으로 다시 축하하지 않는다.
119
+ 재생 완료 콜백으로 작업 성공을 확정하거나 다음 버튼을 늦게 열지 않는다. 자산 실패에도
120
+ 상태·다음 행동을 남긴다([2026-10-12 근거와 선택 기준](../../../../../docs/INTERACTION_QUALITY.md#캐릭터매체-모션의-채택-기준)).
115
121
  - Lottie·비디오·오디오 엔진, 그림 자산은 제품 소유다. HJM은 액자·크기·겹침·표식 위치만 소유한다.
116
122
  - 테마별 그림은 제품의 자산 목록에서 선택해 슬롯에 넣는다. Asset이 URL을 만들거나 재질·각도
117
123
  변형을 자동 생성하지 않는다. 자산마다 지원하는 조합과 실패 대체를 확인하며, 3D 제공자의
@@ -122,7 +128,7 @@ import { VoiceNote } from "@hjmds/react-native/voice-note";
122
128
 
123
129
  | 항목 | Web | Native |
124
130
  | --- | --- | --- |
125
- | 배치 prop | `layoutStyle`(+`className`) | `layoutStyle`(`style`은 deprecated). `VoiceNote`는 Native에 `layoutStyle` 없음 |
131
+ | 배치 prop | `layoutStyle`(+`className`) | `layoutStyle`(`style`은 제거된 속성). `VoiceNote`는 Native에 `layoutStyle` 없음 |
126
132
  | 매체 크기 | CSS가 자식을 액자 안으로 줄인다(`max-*: 100%`) | 가운데 정렬만 한다. 자식 크기를 직접 준다 |
127
133
  | `AssetGroup` 겹침 | CSS로 `.hjm-asset` 형제에 적용 | `children`이 배열일 때만 각 자식을 감싸 적용 |
128
134
  | `VoiceNote` 앞뒤 이동 문구 | `labels.backward/forward`를 받지만 쓰지 않는다 | Slider 접근성 동작 문구로 쓴다 |
@@ -62,7 +62,7 @@ import { AuthProviderButton } from "@hjmds/react-native/provider-button";
62
62
  | `logo` | `ReactNode` | 필수 | 제품이 공급하는 제공자 로고 |
63
63
  | Web `onClick` · Native `onPress` | `(event: MouseEvent<HTMLButtonElement>) => void` · `() => void` | Native 필수 | 로그인 시작 |
64
64
  | `layoutStyle` | margin·width·flex·`alignSelf` | — | 바깥 배치 전용 |
65
- | Native `style` | — | — | deprecated — `layoutStyle`(외형은 `authProviderButtonRecipe` 소유, 개발 모드 1회 경고, 다음 major 제거) |
65
+ | Native `style` | — | — | 제거된 속성 — `layoutStyle`(외형은 `authProviderButtonRecipe` 소유) |
66
66
  | — | — | HJM 소유 | 크기: 높이 `buttonRecipe` medium(Native는 최소 터치 타깃과 큰 쪽), radius `md`, 로고 상자 20, 테두리 1(테두리를 규정한 변형만), 포커스 링은 fill 바깥(offset 2) |
67
67
  | Web `type` | HTML button type | `"button"` | — |
68
68
 
@@ -105,7 +105,7 @@ main 카드(mainCard)
105
105
  | --- | --- | --- |
106
106
  | 이벤트 | `onClick` 등 `<button>` 속성(선택) | `onPress`(필수) |
107
107
  | 비활성 | `descriptor.disabled`(`disabled` 속성은 받지 않음) | `descriptor.disabled` |
108
- | 배치 입력 | `layoutStyle`(+`className`, `style` 없음) | `layoutStyle`(`style`은 deprecated) |
108
+ | 배치 입력 | `layoutStyle`(+`className`, `style` 없음) | `layoutStyle`(`style`은 제거된 속성) |
109
109
  | 접근성 이름 | `aria-label={label}` | `accessibilityLabel={label}` |
110
110
  | `testID`·ref | ref 전달 | 둘 다 없음 |
111
111
 
@@ -76,9 +76,9 @@ import { Avatar } from "@hjmds/react-native/data-display";
76
76
  | Web `alt` | `string` | `name` | `""`이면 보조기기에서 숨긴다 |
77
77
  | Web `imageProps` | `img` 속성(`alt`·`src` 제외) | — | `onError`는 `(event: SyntheticEvent<HTMLImageElement>) => void`이며 대체 표시로 바꾼 뒤 불린다 |
78
78
  | Native `accessibilityLabel` · `decorative` | `{ accessibilityLabel: string }` 또는 `{ decorative: true }` | — | 둘 중 하나가 타입으로 강제된다 |
79
- | Native `renderImage` | `(context: AvatarImageRenderProps) => ReactNode` | 기본 Native Image | 제품의 이미지 캐시·표시 호스트를 연결한다(미게시, 1.13.1 이후). `source`, `size`, HJM `fallback`, 세대가 보호된 `onError` 제공 |
79
+ | Native `renderImage` | `(context: AvatarImageRenderProps) => ReactNode` | 기본 Native Image | 제품의 이미지 캐시·표시 호스트를 연결한다(1.14.0 추가). `source`, `size`, HJM `fallback`, 세대가 보호된 `onError` 제공 |
80
80
  | Native `initials` | `string` | 이름에서 계산 | 앞뒤 공백을 지우고 최대 3자, 대문자 |
81
- | `layoutStyle` | margin·width·flex·`alignSelf` | — | 배치 전용. Native `style`·`imageStyle`은 deprecated — layoutStyle 또는 `size`/`renderFallback` |
81
+ | `layoutStyle` | margin·width·flex·`alignSelf` | — | 배치 전용. Native `style`·`imageStyle`은 제거된 속성 — layoutStyle 또는 `size`/`renderFallback` |
82
82
  | `AvatarGroup`(Web) `label` · `size` · `overflow` | `string` · Avatar 크기 · `ReactNode` | `label` 필수 · `size` `medium` | 빈 `label`은 `TypeError`. `overflow`는 제품이 만든 "+3" 같은 문구이며 `aria-hidden`이다(남은 인원은 `label`에 담는다). 겹침은 크기의 30%다 |
83
83
 
84
84
  ## 배치
@@ -96,13 +96,13 @@ import { Avatar } from "@hjmds/react-native/data-display";
96
96
  - `name`은 빈 문자열이면 `TypeError`다. 접근성 이름은 Avatar가 소유하고, 대체 그림은 장식으로만 그린다(버튼 등 상호작용 금지).
97
97
  - Blobatar `seed`에는 공개 제품 식별자를 쓴다. 이름·이메일·자격증명은 seed로 쓰지 않는다.
98
98
  - 사진·표시 이름·남은 인원 문구는 제품 소유다. `overflow`는 i18n 키로 만든다. `overflow`는 보조기기에서 숨겨지므로 전체 인원은 `label` 문구에 넣는다.
99
- - 배치는 `layoutStyle`로만 한다. Native `style`·`imageStyle`은 deprecated(개발 모드 1회 경고, 다음 major 제거)다.
99
+ - 배치는 `layoutStyle`로만 한다. Native `style`·`imageStyle`은 제거된 속성이다.
100
100
 
101
101
  ## 플랫폼 차이
102
102
 
103
103
  | 항목 | Web | Native |
104
104
  | --- | --- | --- |
105
- | 사진 | `src`(문자열), `imageProps`로 `img` 속성·`onError` 전달 | `source`(`ImageSourcePropType`). `imageStyle`은 deprecated — layoutStyle 또는 `size` |
105
+ | 사진 | `src`(문자열), `imageProps`로 `img` 속성·`onError` 전달 | `source`(`ImageSourcePropType`). `imageStyle`은 제거된 속성 — layoutStyle 또는 `size` |
106
106
  | 접근성 이름 | `alt`(기본 `name`), `alt=""`이면 숨김 | `accessibilityLabel` 필수, 또는 `decorative` |
107
107
  | 대체 표시 | `fallback` 노드 또는 이니셜 | `initials`(최대 3자) 또는 이니셜 |
108
108
  | 크기·모양 축 | 4단 이름 · `circle`/`rounded` | 숫자 · 원만 |
@@ -116,7 +116,7 @@ Stack처럼 내용 폭을 수용하는 영역에 둔다. leading의 overflow/크
116
116
 
117
117
  - `src`/`source`에 `undefined`를 직접 넘기면 `exactOptionalPropertyTypes`에서 타입 오류다. 사진이 없으면 prop을 빼거나 spread로 조건부로 넣는다.
118
118
  - Web `AvatarGroup`은 이제 `style`을 버리지 않고 `layoutStyle`과 합친다. 겹침 변수(`--hjm-avatar-overlap`)는 마지막에 덮이므로 `style`로 겹침을 바꿀 수 없다.
119
- - 이니셜은 두 플랫폼 모두 `resolveAvatarInitials`(`@hjmds/design-contracts/avatar-fallback`)로 첫 단어와 마지막 단어의 첫 글자(code point)다. 1.12.1까지 Web은 앞 두 단어를 써서 "Kim Min Jun"이 Web "KM", Native "KJ"였다(미게시 변경). 1.12.1에서 일치가 필요하면 Native `initials`·Web `fallback`으로 같은 값을 준다.
119
+ - 이니셜은 두 플랫폼 모두 `resolveAvatarInitials`(`@hjmds/design-contracts/avatar-fallback`)로 첫 단어와 마지막 단어의 첫 글자(code point)다. "Kim Min Jun"은 두 플랫폼 모두 "KJ"다. 제품이 다른 이니셜 규칙을 쓰면 Native `initials`·Web `fallback`으로 같은 값을 준다.
120
120
 
121
121
  ### Native 제품 이미지 호스트
122
122
 
@@ -143,4 +143,6 @@ import { Avatar } from "@hjmds/react-native/data-display";
143
143
  실패 시 전달된 `onError`를 호출해야 공통 대체 표시로 바뀐다. 이전 source의 callback은
144
144
  A→B→A로 되돌아와도 무시한다. source를 같은 값으로 재생성하는 것은 재시도가 아니다.
145
145
  슬롯의 자식은 장식 전용이며 공통 wrapper가 접근성 트리에서 숨긴다. 버튼·링크를 넣지 않는다.
146
- 기본 Native Image 경로는 그대로다. Web은 기존 img/imageProps 경로를 사용한다.
146
+ 기본 Native Image 경로는 그대로다. Web은 img/imageProps 경로를 사용하며 src가 바뀌면 요청 img 노드를 교체해 이전 DOM 오류가 새 사진의 대체 표시를 켜지 않도록 한다.
147
+
148
+ Web animated Blobatar는 IntersectionObserver를 사용할 수 없으면 같은 seed·표정의 정적 그림을 유지한다. 장식의 관찰 기능 미지원 때문에 Avatar 본문을 실패시키지 않는다.
@@ -53,7 +53,7 @@ import { Badge } from "@hjmds/react-native/data-display";
53
53
  | `leading` | `ReactNode` | — | 라벨 앞 아이콘 자리이며 보조기기에서 숨겨진다 |
54
54
  | Web `children` · Native `label` | `ReactNode` · `string \| number`(필수) | — | 표시 라벨 |
55
55
  | Native `accessibilityLabel` | `string` | `String(label)` | 숫자 라벨에 단위를 붙여 읽힐 때 쓴다 |
56
- | `layoutStyle` | margin·width·flex·`alignSelf` | — | 배치 전용. Native `style`·`labelStyle`은 deprecated — layoutStyle 또는 tone/토큰 |
56
+ | `layoutStyle` | margin·width·flex·`alignSelf` | — | 배치 전용. Native `style`·`labelStyle`은 제거된 속성 — layoutStyle 또는 tone/토큰 |
57
57
 
58
58
  콜백이 없다. 누를 수 없는 표시다.
59
59
 
@@ -73,7 +73,7 @@ Native 모서리는 Provider token에서 recipe 역할을 읽는다. Badge의 `f
73
73
 
74
74
  - 라벨은 i18n 키로 넣고 짧게 둔다. 상태 뜻을 색에만 싣지 않는다(글자가 뜻을 말해야 한다).
75
75
  - 색은 `tone`으로만 고른다. 브랜드 색을 `style`·`className`·`labelStyle`로 덮지 않는다.
76
- - 배치는 Web·Native 모두 `layoutStyle`로만 한다. Native `style`·`labelStyle`은 deprecated(개발 모드 1회 경고, 다음 major 제거)다.
76
+ - 배치는 Web·Native 모두 `layoutStyle`로만 한다. Native `style`·`labelStyle`은 제거된 속성이다.
77
77
 
78
78
  ## 플랫폼 차이
79
79
 
@@ -81,6 +81,6 @@ Native 모서리는 Provider token에서 recipe 역할을 읽는다. Badge의 `f
81
81
  | --- | --- | --- |
82
82
  | 라벨 | `children` | `label`(문자열·숫자, 필수) |
83
83
  | 접근성 이름 | 라벨 텍스트 | `accessibilityLabel`(기본 `String(label)`) |
84
- | 배치 | `layoutStyle` | `layoutStyle`(`style`은 deprecated) |
85
- | 라벨 스타일 훅 | 없음 | `labelStyle` deprecated — tone/토큰 |
84
+ | 배치 | `layoutStyle` | `layoutStyle`(`style`은 제거된 속성) |
85
+ | 라벨 스타일 훅 | 없음 | `labelStyle` 제거된 속성 — tone/토큰 |
86
86
  | 기본 폭 | inline | `alignSelf: "flex-start"` |
@@ -68,7 +68,7 @@ const insets = useSafeAreaInsets();
68
68
  | `accessibilityLabel` | `string` | — | 바 전체(Web `role="group"`, Native `toolbar`)의 이름 |
69
69
  | `safeAreaBottom` | 0 이상의 유한수 | `0` | 음수·무한대는 `RangeError` |
70
70
  | Web `position` | `flow` · `sticky` | `flow` | fixed는 제공하지 않는다 |
71
- | `layoutStyle` | margin·width·flex·`alignSelf` | — | 배치 전용. Native `style`은 deprecated — layoutStyle 또는 tone/토큰 |
71
+ | `layoutStyle` | margin·width·flex·`alignSelf` | — | 배치 전용. Native `style`은 제거된 속성 — layoutStyle 또는 tone/토큰 |
72
72
  | 큰 글자 | — | — | 두 행동을 세로로 쌓고 **주 행동을 위**에 둔다(`column-reverse`, Native `textScale >= 1.6`, Web `isLargeTextScale`) |
73
73
 
74
74
  ## 배치
@@ -101,7 +101,7 @@ const insets = useSafeAreaInsets();
101
101
  - 폼 제출을 BottomCTA로 옮기면 [Form](form.md)의 제출 버튼과 중복하지 않는다. Web은 Form `actions`에 submit Button을 넣지 않고,
102
102
  Native Form은 내장 제출 버튼(`submitLabel` 필수)을 항상 그리므로 Form 대신 필드 + contracts `createFormSubmitSession`
103
103
  (`@hjmds/design-contracts/components/form`)으로 제출 세션을 잡고 그 상태를 `primaryAction.loading`에 연결한다.
104
- - 배치는 `layoutStyle`로만 한다. Native `style`은 deprecated(개발 모드 1회 경고, 다음 major 제거)다.
104
+ - 배치는 `layoutStyle`로만 한다. Native `style`은 제거된 속성이다.
105
105
  - 하단 inset을 넘긴다. Native는 자동으로 읽지 않으므로 `safeAreaBottom`이 없으면 홈 인디케이터에 붙는다.
106
106
  - 키보드가 열리는 폼 화면(Native)은 `KeyboardAvoiding`(`/keyboard`) 또는 `KeyboardDock`(`/keyboard-controller`,
107
107
  optional peer `react-native-keyboard-controller` 설치 필요)로 감싼다.
@@ -116,7 +116,7 @@ const insets = useSafeAreaInsets();
116
116
  | `loadingLabel` 타입 | `string`(접근성 이름) | `ReactNode` |
117
117
  | `accessibilityHint` | 없음 | 있음 |
118
118
  | root 역할 | `role="group"` | `accessibilityRole="toolbar"` |
119
- | 그 밖 | HTML 속성·`className`·`style` 전달 | `testID`(`style`은 deprecated) |
119
+ | 그 밖 | HTML 속성·`className`·`style` 전달 | `testID`(`style`은 제거된 속성) |
120
120
  | 빈 `label` | `TypeError` | 검사 없음(Button이 빈 이름으로 그려진다) |
121
121
 
122
122
  ## 함정
@@ -124,4 +124,4 @@ const insets = useSafeAreaInsets();
124
124
  - Web `BottomCTA`의 `style`은 recipe CSS 변수 뒤에 펼쳐지므로 `--hjm-bottom-cta-*` 변수를 덮을 수 있다. 외형은 recipe 소유이므로 `style`로 변수를 바꾸지 않는다.
125
125
  - Native는 하단 inset을 스스로 읽지 않는다. `safeAreaBottom`을 빠뜨려도 오류가 없고 홈 인디케이터에 붙어 보인다.
126
126
 
127
- 미게시(1.14.0 이후): 프로필의 `shadow.floating` 색·강도·반경을 상속하되, 위 콘텐츠와 겹치는 footer 역할이라 offsetY는 `-abs(offsetY)`로 위쪽에 표시한다. Native의 0-opacity 프로필은 Android elevation도 0이다. 프로필 없는 Web은 기존 무그림자, Native는 기존 위쪽 recipe 그림자를 유지한다. safe area·큰 글자·로딩/실패와 제품 행동 소유권은 바뀌지 않는다.
127
+ 1.15.0 변경: 프로필의 `shadow.floating` 색·강도·반경을 상속하되, 위 콘텐츠와 겹치는 footer 역할이라 offsetY는 `-abs(offsetY)`로 위쪽에 표시한다. Native의 0-opacity 프로필은 Android elevation도 0이다. 프로필 없는 Web은 기존 무그림자, Native는 기존 위쪽 recipe 그림자를 유지한다. safe area·큰 글자·로딩/실패와 제품 행동 소유권은 바뀌지 않는다.
@@ -54,7 +54,7 @@ import { BottomInfo } from "@hjmds/react-native/bottom-info";
54
54
  | `items` | 비지 않은 문자열 배열 | 필수 | 빈 배열·빈 문자열은 거부한다. 2개 이상이면 목록 표식이 붙는다(`listMarkerFrom: 2`) |
55
55
  | `tone` | `muted` · `emphasis` | `muted` | 법적 고지처럼 놓치면 안 되는 문장만 `emphasis`. danger·success tone은 없다 |
56
56
  | `renderItem` | `(item: string, index: number) => ReactNode` | — | 한 줄을 rich copy로 바꾼다(문장 안 약관 링크 등). `null`·`undefined`를 돌려주면 원래 문장을 그린다 |
57
- | `layoutStyle` | margin·width·flex·`alignSelf` | — | 배치 전용. Native `style`은 deprecated — layoutStyle 또는 tone/토큰 |
57
+ | `layoutStyle` | margin·width·flex·`alignSelf` | — | 배치 전용. Native `style`은 제거된 속성 — layoutStyle 또는 tone/토큰 |
58
58
 
59
59
  상태가 없는 표시 컴포넌트다. 이벤트 콜백이 없다.
60
60
 
@@ -90,7 +90,7 @@ import { BottomInfo } from "@hjmds/react-native/bottom-info";
90
90
  | 항목 | Web | Native |
91
91
  | --- | --- | --- |
92
92
  | root | `<aside>`, 여러 줄이면 `<ul>/<li>` | `View` + 줄마다 `·` 표식 텍스트 |
93
- | 전달 가능 속성 | HTML 속성·`className`·ref·`layoutStyle` | `layoutStyle`(`style`은 deprecated) |
93
+ | 전달 가능 속성 | HTML 속성·`className`·ref·`layoutStyle` | `layoutStyle`(`style`은 제거된 속성) |
94
94
  | `renderItem` 결과 위치 | `<li>`/`<p>` 안 | `Text` 안. 인라인 텍스트(문자열·`Text`·인라인 링크)만 돌려준다 |
95
95
 
96
96
  ## 함정