@hjmds/design-contracts 1.13.1 → 1.15.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 (200) hide show
  1. package/dist/agreement.d.ts +7 -1
  2. package/dist/agreement.d.ts.map +1 -1
  3. package/dist/agreement.js +14 -3
  4. package/dist/agreement.js.map +1 -1
  5. package/dist/behaviors.d.ts +1 -1
  6. package/dist/catalog.d.ts +4 -0
  7. package/dist/catalog.d.ts.map +1 -1
  8. package/dist/content-transition.d.ts +17 -0
  9. package/dist/content-transition.d.ts.map +1 -1
  10. package/dist/content-transition.js +21 -0
  11. package/dist/content-transition.js.map +1 -1
  12. package/dist/date-entry.d.ts +71 -0
  13. package/dist/date-entry.d.ts.map +1 -0
  14. package/dist/date-entry.js +78 -0
  15. package/dist/date-entry.js.map +1 -0
  16. package/dist/design-profile-layout.d.ts +11 -0
  17. package/dist/design-profile-layout.d.ts.map +1 -0
  18. package/dist/design-profile-layout.js +18 -0
  19. package/dist/design-profile-layout.js.map +1 -0
  20. package/dist/design-profile.d.ts +89 -0
  21. package/dist/design-profile.d.ts.map +1 -0
  22. package/dist/design-profile.js +262 -0
  23. package/dist/design-profile.js.map +1 -0
  24. package/dist/design-system-provider.d.ts +6 -0
  25. package/dist/design-system-provider.d.ts.map +1 -1
  26. package/dist/design-system-provider.js +4 -2
  27. package/dist/design-system-provider.js.map +1 -1
  28. package/dist/document-resource.d.ts +104 -0
  29. package/dist/document-resource.d.ts.map +1 -0
  30. package/dist/document-resource.js +77 -0
  31. package/dist/document-resource.js.map +1 -0
  32. package/dist/effect-surface.d.ts +6 -1
  33. package/dist/effect-surface.d.ts.map +1 -1
  34. package/dist/effect-surface.js +5 -2
  35. package/dist/effect-surface.js.map +1 -1
  36. package/dist/field-group.d.ts +47 -0
  37. package/dist/field-group.d.ts.map +1 -0
  38. package/dist/field-group.js +92 -0
  39. package/dist/field-group.js.map +1 -0
  40. package/dist/gooey-navigation.d.ts +19 -1
  41. package/dist/gooey-navigation.d.ts.map +1 -1
  42. package/dist/gooey-navigation.js +37 -2
  43. package/dist/gooey-navigation.js.map +1 -1
  44. package/dist/image.d.ts +17 -0
  45. package/dist/image.d.ts.map +1 -1
  46. package/dist/image.js +19 -0
  47. package/dist/image.js.map +1 -1
  48. package/dist/internal/effect-noise.d.ts +2 -0
  49. package/dist/internal/effect-noise.d.ts.map +1 -0
  50. package/dist/internal/effect-noise.js +4 -0
  51. package/dist/internal/effect-noise.js.map +1 -0
  52. package/dist/palette-contrast.d.ts +6 -0
  53. package/dist/palette-contrast.d.ts.map +1 -1
  54. package/dist/palette-contrast.js +17 -0
  55. package/dist/palette-contrast.js.map +1 -1
  56. package/dist/progressive-blur.d.ts +32 -0
  57. package/dist/progressive-blur.d.ts.map +1 -0
  58. package/dist/progressive-blur.js +28 -0
  59. package/dist/progressive-blur.js.map +1 -0
  60. package/dist/reference-controls.d.ts +32 -0
  61. package/dist/reference-controls.d.ts.map +1 -0
  62. package/dist/reference-controls.js +28 -0
  63. package/dist/reference-controls.js.map +1 -0
  64. package/dist/screen-patterns.d.ts +16 -1
  65. package/dist/screen-patterns.d.ts.map +1 -1
  66. package/dist/screen-patterns.js +4 -0
  67. package/dist/screen-patterns.js.map +1 -1
  68. package/dist/scroll-progress.d.ts +7 -1
  69. package/dist/scroll-progress.d.ts.map +1 -1
  70. package/dist/scroll-progress.js +21 -2
  71. package/dist/scroll-progress.js.map +1 -1
  72. package/dist/text-annotation.d.ts +43 -0
  73. package/dist/text-annotation.d.ts.map +1 -0
  74. package/dist/text-annotation.js +137 -0
  75. package/dist/text-annotation.js.map +1 -0
  76. package/dist/toast-liquid.d.ts +3 -1
  77. package/dist/toast-liquid.d.ts.map +1 -1
  78. package/dist/toast-liquid.js +6 -2
  79. package/dist/toast-liquid.js.map +1 -1
  80. package/dist/version.d.ts +1 -1
  81. package/dist/version.js +1 -1
  82. package/dist/version.js.map +1 -1
  83. package/docs/agreement.md +14 -0
  84. package/docs/asset.md +7 -0
  85. package/docs/brand-boundary.md +14 -5
  86. package/docs/code-block.md +12 -1
  87. package/docs/collapsible.md +6 -0
  88. package/docs/design-profile.md +263 -0
  89. package/docs/design-system-provider.md +7 -0
  90. package/docs/dialog.md +20 -1
  91. package/docs/effect-surface.md +19 -3
  92. package/docs/generated/component-maturity.md +1 -1
  93. package/docs/generated/renderer-evidence.json +3 -3
  94. package/docs/generated/renderer-evidence.md +1 -1
  95. package/docs/generated/showcase-manifest.json +1 -1
  96. package/docs/gooey-navigation.md +37 -6
  97. package/docs/heading.md +15 -0
  98. package/docs/image.md +17 -0
  99. package/docs/optional-adapters.md +25 -0
  100. package/docs/popover.md +15 -0
  101. package/docs/rating.md +6 -1
  102. package/docs/reference-controls.md +40 -0
  103. package/docs/task-list.md +15 -1
  104. package/docs/text-annotation.md +85 -0
  105. package/docs/theming.md +18 -5
  106. package/docs/usage/README.md +23 -1
  107. package/docs/usage/components/activity-heatmap.md +3 -1
  108. package/docs/usage/components/agreement.md +15 -3
  109. package/docs/usage/components/alert-dialog.md +16 -1
  110. package/docs/usage/components/asset.md +9 -2
  111. package/docs/usage/components/avatar.md +33 -1
  112. package/docs/usage/components/badge.md +3 -1
  113. package/docs/usage/components/bottom-cta.md +3 -1
  114. package/docs/usage/components/bottom-navigation.md +5 -1
  115. package/docs/usage/components/button.md +7 -1
  116. package/docs/usage/components/calendar.md +3 -1
  117. package/docs/usage/components/card.md +26 -3
  118. package/docs/usage/components/carousel.md +10 -1
  119. package/docs/usage/components/chat-message.md +11 -1
  120. package/docs/usage/components/chip.md +5 -0
  121. package/docs/usage/components/code-block.md +14 -2
  122. package/docs/usage/components/collapsible.md +10 -2
  123. package/docs/usage/components/combobox.md +12 -1
  124. package/docs/usage/components/command-palette.md +11 -6
  125. package/docs/usage/components/content-transition.md +29 -5
  126. package/docs/usage/components/context-menu.md +7 -1
  127. package/docs/usage/components/date-picker.md +3 -1
  128. package/docs/usage/components/design-system-provider.md +7 -5
  129. package/docs/usage/components/dialog.md +70 -1
  130. package/docs/usage/components/effect-surface.md +4 -2
  131. package/docs/usage/components/empty-state.md +10 -2
  132. package/docs/usage/components/field.md +26 -1
  133. package/docs/usage/components/form.md +13 -3
  134. package/docs/usage/components/heading.md +7 -1
  135. package/docs/usage/components/image-comparison.md +82 -0
  136. package/docs/usage/components/image.md +83 -2
  137. package/docs/usage/components/keyboard-avoiding.md +6 -1
  138. package/docs/usage/components/link.md +3 -1
  139. package/docs/usage/components/list-row.md +5 -1
  140. package/docs/usage/components/list.md +8 -1
  141. package/docs/usage/components/load-more.md +3 -1
  142. package/docs/usage/components/mentions.md +3 -1
  143. package/docs/usage/components/menu.md +3 -1
  144. package/docs/usage/components/menubar.md +7 -1
  145. package/docs/usage/components/message-composer.md +16 -5
  146. package/docs/usage/components/notice.md +9 -0
  147. package/docs/usage/components/number-field.md +3 -1
  148. package/docs/usage/components/onboarding-screen.md +16 -9
  149. package/docs/usage/components/overview-screen.md +73 -0
  150. package/docs/usage/components/password-field.md +5 -1
  151. package/docs/usage/components/popover.md +14 -1
  152. package/docs/usage/components/progress.md +28 -0
  153. package/docs/usage/components/progressive-blur.md +113 -0
  154. package/docs/usage/components/rating.md +74 -0
  155. package/docs/usage/components/saved-items-screen.md +3 -1
  156. package/docs/usage/components/screen-layout.md +9 -1
  157. package/docs/usage/components/search-field.md +13 -1
  158. package/docs/usage/components/search-screen.md +5 -0
  159. package/docs/usage/components/segmented-control.md +17 -0
  160. package/docs/usage/components/select.md +9 -0
  161. package/docs/usage/components/sheet.md +14 -1
  162. package/docs/usage/components/skeleton.md +9 -0
  163. package/docs/usage/components/slider.md +3 -1
  164. package/docs/usage/components/statistic.md +14 -1
  165. package/docs/usage/components/surface.md +8 -1
  166. package/docs/usage/components/tabs.md +11 -2
  167. package/docs/usage/components/tag.md +3 -1
  168. package/docs/usage/components/tags-input.md +11 -1
  169. package/docs/usage/components/text-area.md +3 -1
  170. package/docs/usage/components/text-transition.md +8 -2
  171. package/docs/usage/components/text.md +3 -1
  172. package/docs/usage/components/toast.md +23 -1
  173. package/docs/usage/components/top-bar.md +3 -1
  174. package/docs/usage/components/upload-item.md +3 -1
  175. package/docs/usage/compositions/action-feedback.md +77 -0
  176. package/docs/usage/compositions/adaptive-content.md +81 -0
  177. package/docs/usage/compositions/command-records.md +106 -0
  178. package/docs/usage/compositions/content-transition-comparison.md +108 -0
  179. package/docs/usage/compositions/context-toolbar.md +85 -0
  180. package/docs/usage/compositions/date-entry.md +108 -0
  181. package/docs/usage/compositions/date-time-selection.md +110 -0
  182. package/docs/usage/compositions/design-profile-comparison.md +114 -0
  183. package/docs/usage/compositions/document-resource.md +124 -0
  184. package/docs/usage/compositions/field-group.md +104 -0
  185. package/docs/usage/compositions/illustrated-outcome.md +91 -0
  186. package/docs/usage/compositions/live-list.md +104 -0
  187. package/docs/usage/compositions/optional-adapters.md +1 -1
  188. package/docs/usage/compositions/origin-dialog.md +108 -0
  189. package/docs/usage/compositions/selection-motion.md +73 -0
  190. package/docs/usage/compositions/texture-comparison.md +86 -0
  191. package/docs/usage/compositions/upload-recovery.md +80 -0
  192. package/docs/usage/compositions/video-dialog.md +100 -0
  193. package/docs/usage/screens/flow-onboarding.md +5 -3
  194. package/docs/usage/screens/product-bento.md +117 -0
  195. package/docs/usage/tokens/color.md +9 -2
  196. package/docs/usage/tokens/elevation-opacity.md +8 -1
  197. package/docs/usage/tokens/motion.md +10 -1
  198. package/docs/usage/tokens/radius.md +8 -1
  199. package/docs/usage/tokens/typography.md +15 -1
  200. package/package.json +49 -1
@@ -0,0 +1,40 @@
1
+ # 이미지 비교와 별점 계약
2
+
3
+ 검토일: 2026-10-07 · 상태: 실험 · 변경 계기: 11개 UI 레퍼런스 조사에서 HJM의 전용 대응이 없는 두 기능을 확인했고 사용자가 적용을 요청했다.
4
+
5
+ `reference-controls`는 기존 Image와 Slider가 제공하지 않는 **동일 좌표 이미지 비교**의 조합과,
6
+ 일반 RadioGroup이 구분하지 않는 **미평가·정수 입력·소수 평균**을 정의한다. canonical catalog는
7
+ 늘리지 않으며 두 renderer의 `/image-comparison`, `/rating`에서만 제공한다. root export와 새 peer는 없다.
8
+
9
+ ## Rating
10
+
11
+ - `label`, `value`, `getValueLabel`을 제품이 전달한다. `value`는 controlled이며 자동 저장하지 않는다.
12
+ - `null`은 미평가. 입력은 1~max의 정수, `readOnly` 평균은 0~max의 소수를 허용한다.
13
+ - `max` 기본 5, 범위 1~10. 무한한 별 행은 점수 척도가 아니라 목록이 되므로 거부한다.
14
+ - 입력형은 `onValueChange` 필수. `clearLabel`이 있으면 명시적으로 null로 지운다.
15
+ Web은 초기화 버튼이 비활성화되기 전에 첫 radio로 초점을 옮긴다. 2026-10-07 실제 키보드 검증에서
16
+ 초기화 후 body로 초점이 빠져 재선택 위치를 잃는 문제를 확인했기 때문이다.
17
+ - Web은 실제 radio와 name으로 키보드/폼 제출을 사용한다. Native는 radio role과 checked/disabled
18
+ 상태를 제공한다. 읽기 전용은 하나의 이름 있는 이미지이며 입력처럼 초점을 받지 않는다.
19
+ - 별은 값의 장식이고 현지화된 값 텍스트가 의미를 전달한다. 외부 SVG·폰트·아이콘 패키지를 추가하지 않았다.
20
+ - Web은 기존 styles.css가 필요하다. 큰 글자에서는 별 행을 감아 배치한다.
21
+
22
+ ## ImageComparison
23
+
24
+ - `before`, `after`는 `{src,width,height,label}`이고 같은 aspect ratio가 필요하다. 자동 crop·늘이기는
25
+ 비교 좌표를 바꾸므로 거부한다. 기본 Image가 로딩 오류의 이름과 fallback을 소유한다.
26
+ - controlled `value`는 0~100이며 **왼쪽에 보이는 before의 비율**이다. 0이면 after 전체, 100이면 before 전체.
27
+ 이미지와 전후 라벨의 위치는 RTL에서도 물리 좌표를 유지하고 각 문구의 쓰기 방향은 제품 언어를 따른다.
28
+ 2026-10-07 실제 RTL 화면에서 라벨만 반전되어 이미지와 불일치한 문제를 수정했다.
29
+ - `label`, `getValueText`, `onValueChange`를 전달한다. Native에는 `decrementLabel`·`incrementLabel`도 필수다.
30
+ - 구분선은 장식이며 드래그 핸들이 아니다. 아래의 기존 Slider로 드래그·키보드·Native adjustable
31
+ action을 제공한다. 독자 PanResponder를 만들지 않아 세로 스크롤 판정을 Slider와 공유한다.
32
+ - Native는 프레임 폭을 측정하고 두 이미지 모두 전체 폭으로 렌더링한 후 before를 잘라 표시한다.
33
+ - 서버 업로드·이미지 처리·원본 저장·기록 권한은 제품 소유다. 서로 다른 시점/대상을 비교한다고
34
+ 자동으로 변화율이나 품질 점수를 계산하지 않는다.
35
+
36
+ ## 참조와 검증 경계
37
+
38
+ [조사](../../../docs/plans/ui-reference-full-audit-2026-10-06.md)의 Motion Image Comparison과
39
+ Component Gallery Rating에서 사용자 문제를 확인했다. 원본 코드를 복사하지 않고 HJM Image/Slider,
40
+ semantic token, 플랫폼 입력 의미로 구현했다. Native 호스트 검사는 iOS/Android 실기기 증거가 아니다.
package/docs/task-list.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Task list
2
2
 
3
- Reviewed: 2026-10-01. Web and Native expose TaskList through `/task-list`.
3
+ Reviewed: 2026-10-07. Web and Native expose TaskList through `/task-list`.
4
4
  TaskList composes existing List and Checkbox rather than creating another selection
5
5
  or gesture engine. Each controlled item has unique `id`, localized `label`, boolean
6
6
  `completed`, optional `description` and `disabled`. Invalid identity/completion data
@@ -30,3 +30,17 @@ found that 200% text scaling enlarged Checkbox's decorative mark beyond its fixe
30
30
  Native Checkbox now keeps the checked/mixed artwork at the recipe typography size
31
31
  while its label and description continue scaling. TaskList inherits this correction
32
32
  through Checkbox; it does not provide a second selection indicator.
33
+
34
+ ## Independent item actions
35
+
36
+ `renderItemAction({item, disabled})` optionally supplies a product-localized control below
37
+ that item's checkbox. `disabled` combines the list and item disabled flags; pass it to
38
+ the control. The slot does not save, remove, or confirm anything itself. Keep asynchronous
39
+ storage and rollback in the product. The action is a sibling of Checkbox, never inside its
40
+ label, so it has its own press/focus target. It follows the checkbox with spacing.sm (12)
41
+ and remains part of renderItem when renderCollection supplies sorting.
42
+
43
+ The separate line preserves the full label width at large text sizes. Utilverse checklist
44
+ rows need deletion alongside completion (2026-10-07 consumer audit), which the previous
45
+ completion-only API could not express without rebuilding row layout. Both showcases now
46
+ include independent delete actions; device drag/focus/large-text action QA remains pending.
@@ -0,0 +1,85 @@
1
+ # 문장 주석 — 구현 중
2
+
3
+ 검토일: 2026-10-07. Web 내부 renderer는 구현했고 공개 renderer·Storybook 실험은 아직 없다.
4
+
5
+ 사용자가 제공한 Magic UI Highlighter를 조사한 결과 기존 TextFormat은 Web의
6
+ 단축키·코드·인용 요소를 위한 계약이라 손그림 주석을 직접 대체하지 못했다.
7
+ 조사 근거는 [원본 검토](../../../docs/qa/2026-10-07-highlighter-reference.md)다.
8
+
9
+ ## 공통 구현
10
+
11
+ `src/text-annotation.ts`는 renderer가 측정한 시각적 줄 조각의 x/y/width/height에서
12
+ SVG 경로를 만든다. `@hjmds/design-contracts/text-annotation` subpath로 공개하며 root에는
13
+ 추가하지 않는다. renderer에서 같은 경로 계산을 사용하면서 기본 소비 번들에는 들어가지
14
+ 않도록 한 선택이다. 이 export 추가가 renderer 사용 가능 또는 npm 게시를 뜻하지 않는다.
15
+
16
+ - highlight는 뒤쪽 채움, underline/box/circle/strike-through/crossed-off/bracket은
17
+ 두 번 그린 외곽 경로다. 무작위 값 대신 결정적인 작은 오프셋을 쓴다.
18
+ - 줄 사이 빈 영역을 한 덩어리로 칠하지 않는다. 동일 줄의 bidi 조각도 호스트가 측정한
19
+ 물리 좌표 그대로 받으며 문자열 길이로 위치를 추측하거나 RTL을 한 번 더 뒤집지 않는다.
20
+ - 기본 strokeWidth=1.5, padding=2는 장식 경로 단위다. 문장 레이아웃이나 전역 토큰을
21
+ 바꾸지 않는다. 작은 구두점에는 흔들림을 줄이고 빈 줄에는 경로를 만들지 않는다.
22
+ - 반환 bounds는 경로 제어점·두 번 그린 선·선 굵기를 포함한다. 호스트는 이를 SVG
23
+ 영역에 반영해야 하며 원래 Text의 크기나 줄바꿈 폭을 늘려서는 안 된다.
24
+ - circle의 초기 내접 타원이 32px 글자의 양 끝을 가로질러 바깥으로 휜 루프로 수정했다.
25
+ 여백 0이나 굵은 펜에서도 해당 줄의 글자 영역 밖에 선을 두도록 최소 간격을 계산한다.
26
+ 현재 모양은 타원보다 둥근 테두리에 가깝다. 원본과의 시각적 차이, 조밀한 줄 간격에서
27
+ 이웃 줄과의 충돌 및 팔레트 검증은 남아 있으며 원본 동등 표현으로 주장하지 않는다.
28
+ - 음수 크기, 비유한 좌표와 계산 overflow는 렌더링 전 거부한다. 스크롤·상대 위치 때문에
29
+ 음수 x/y는 유효하며 소수 단위 측정도 유지한다.
30
+ - `mergeTextAnnotationFragments`는 같은 실제 줄(lineIndex)의 맞닿은 글꼴 조각을 합친다.
31
+ Native Skia가 한글·공백 fallback을 23개 조각으로 반환해 marker padding이 겹친 실측을
32
+ 반영한 기능이다. Float32 경계 오차만 흡수하도록 0.01px 허용 오차를 사용하며 선택하지
33
+ 않은 gap이나 다른 줄은 합치지 않는다. 이 함수가 줄 번호를 문자열에서 추측하지 않는다.
34
+
35
+ ## Web 내부 renderer와 Native의 남은 계약
36
+
37
+ `packages/react/src/text-annotation.tsx`는 inline span의 실제 Range 사각형을 측정한다.
38
+ 문장 조각을 inline-block으로 바꾸지 않는다. 절대 위치 기준점과 SVG를 접근성·hit-test에서
39
+ 제외하고, 문구·앞 문장·조상 크기 및 스타일·글꼴 로드 변화에 재측정한다. 새 문구가 이전
40
+ 문구 경로를 받지 않도록 측정 세대를 나눈다. 동일 geometry에는 state 갱신을 보내지 않는다.
41
+ 진입 모션은 motion.slow=320ms, 모션 감소 또는 숨겨진 문서에서는 즉시 표시하며,
42
+ 리사이즈에는 다시 재생하지 않는다. highlight의 20% brand wash는 실험값이다. HJM 기본·Utilverse·BurnTok의
43
+ light/dark × bg/surface × body/muted 24조합에서는 합성 뒤 4.5:1을 통과했으며,
44
+ 다른 표면·제품 색·이미지 배경의 대비를 보장하지 않는다. renderer는 아직 package exports에 넣지 않았다.
45
+
46
+ Native 0.86.2/Expo Go 57.0.9/iOS 26.5에서 진단 fixture를 실행했다. 부모의 onTextLayout은
47
+ 폭 280→184에서 2→3줄을 보고했으나 중첩 Text의 onTextLayout/onLayout은 관찰되지 않았고
48
+ measure는 0×0이었다. 이 경로를 실제 줄별 측정으로 사용할 수 없다. 진단 소스는
49
+ `showcase/native/src/devtools/TextAnnotationMeasurementProbe.tsx`에 보존했다.
50
+
51
+ 후속 `TextAnnotationSkiaProbe.tsx`는 Skia 2.6.2의 Paragraph로 측정하고 **같은 Paragraph**를
52
+ 그린다. getRectsForRange와 실제 line metrics를 이용해 한글 23→3, emoji 9→2, 혼합 RTL
53
+ 21→3개의 표시 영역을 얻었다. RTL 폭 280→184에서 5줄로 다시 배치했다. 이 좌표를 다른
54
+ Native Text 위에 올리는 것은 금지한다. 비교 fixture에서 두 엔진의 줄바꿈이 달랐다.
55
+ 이는 Native renderer 후보의 측정 증거이며 일반 Text 대체가 아니다. Canvas의 읽기 이름만으로
56
+ Native Text 선택·복사 기능이 생기지 않는다. 폰트·스케일·외곽 잘림·선택·접근성·Android를
57
+ 해결하고 공개 API/실험으로 연결하는 일이 남았다.
58
+
59
+ Web은 inline 문장과 Range.getClientRects의 줄 조각, Native는 실제 텍스트 레이아웃에서
60
+ 문장 일부의 줄별 위치를 얻어야 한다. 전체 문단을 주석으로 바꾸는 것으로 이 요구를
61
+ 대체하지 않는다. 글꼴 로드·문구 교체·글자 크기·폭 변경마다 최신 geometry를 공급한다.
62
+
63
+ 본문은 처음부터 읽고 선택할 수 있어야 한다. 주석은 접근성 트리와 pointer hit-testing에서
64
+ 제외하며 highlight는 글자 뒤에 둔다. 색은 제품 팔레트의 의미 색으로 공급하고 대비를
65
+ 검증한다. 취소선이 데이터의 삭제 상태를 대신 전달하지 않도록 제품 문구를 유지한다.
66
+ 모션 감소에서는 즉시 완성된 주석을 보인다. 비활성 화면·unmount 시 animation과 관찰자를
67
+ 정리한다. 리사이즈로 geometry만 바뀔 때 진입 모션을 반복하지 않는다.
68
+
69
+ ## 검증 범위
70
+
71
+ 현재 계약 테스트 15개는 줄 조각 분리/병합, RTL 물리 좌표 유지, 7가지 경로의 결정성, 소수·음수
72
+ 좌표, 빈 상태, 재측정, 비정상 입력을 확인한다. 실제 글자 측정, 글자 대비, 모션, 성능,
73
+ 스크린리더 또는 소비 앱 채택의 증거는 아니다. 별도 Web 브라우저 테스트 7개는 실제
74
+ DOM 측정·줄바꿈·선택·RTL·폭 변경·문구 교체·모션 감소를 검증했다. 전체 스타일·기기·성능
75
+ 검증을 마쳤다는 뜻은 아니다. 추가된 큰 글자 회귀는 선 경로를 0.5px 간격으로 샘플링해
76
+ 선 두께까지 해당 줄의 글자 사각형 바깥에 있는지 검사한다. 이는 이웃 줄까지 포함한
77
+ 모든 배치 조합의 가독성 검증은 아니다.
78
+
79
+ 공개 export·양 renderer·사용 지침·실험 스토리·UI 및 행동 증거가 연결된 다음에 실험 수에
80
+ 포함한다. 안정화·npm 게시·Utilverse 적용은 사용자 요청의 후속 단계로 남아 있다.
81
+
82
+ Web의 같은 top/height 수직 대역에서 맞닿은 bidi run은 기존 fragment 병합 계약으로
83
+ 한 주석을 그린다. 390px 아랍어/영어 혼합 문장에서 내부 세로선이 생긴 실측에 따른
84
+ 수정이다. 수직 대역이 다르거나 실제 수평 gap이 있으면 합치지 않는다. 추가된 회귀·
85
+ 제품 대비·진입 모션 재생 검사까지 Web 브라우저 테스트는 10개다.
package/docs/theming.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # 테마 주입 — 내 브랜드색으로 시작하기
2
2
 
3
- 검토일: 2026-10-06 (`value` 중심 예시를 1.5.0 `brandPalette` prop 경로로 정정)
3
+ 검토일: 2026-10-07 (`value` 중심 예시를 1.5.0 `brandPalette` prop 경로로 정정)
4
4
 
5
5
  HJM은 `theme`(light/dark/system) 같은 **환경**과, 그 환경이 해석된 **값**을 분리해서
6
6
  받는다. 제품 브랜드색은 값 쪽에 넣는다. 이 문서는 새 제품이 처음 부딪히는 그 경로만
@@ -8,7 +8,7 @@ HJM은 `theme`(light/dark/system) 같은 **환경**과, 그 환경이 해석된
8
8
  단일 원본이다.** 팔레트를 어떻게 고를지는 [theme-palette.md](./theme-palette.md), 색의 의미 구분은
9
9
  [identity.md](./identity.md)에 있다.
10
10
 
11
- ## 두 가지 사용 방식
11
+ ## 제품 설정 사용 방식
12
12
 
13
13
  ### 1. 기본 팔레트로 시작 (환경만 넘긴다)
14
14
 
@@ -56,7 +56,7 @@ React Native도 같은 모양이다(`<HjmNativeProvider theme={preference} brand
56
56
 
57
57
  `value`(`resolveDesignSystemProviderValue` 결과 전체)는 1.4까지 브랜드를 넣는 유일한 방법이었고, 지금도
58
58
  타입상 지원한다. 하지만 `value`를 넘기면 Provider가 OS 설정 관찰을 멈추고 `brandPalette` 상속도 끊기므로
59
- 브랜드 경로로 쓰지 않는다([brand-boundary.md §1](./brand-boundary.md#1-지원하는-경로는-brandpalette-하나다)).
59
+ 브랜드 경로로 쓰지 않는다([brand-boundary.md §1](./brand-boundary.md#1-지원하는-제품-설정-경로)).
60
60
  남은 용도는 다음뿐이다.
61
61
 
62
62
  - 테스트·스토리에서 환경을 결정적으로 고정할 때(SSR·테스트만 필요하면 `systemTheme` prop으로도 충분한지 먼저 본다).
@@ -73,10 +73,23 @@ import { checkBrandPaletteContrast } from "@hjmds/design-contracts/palette-contr
73
73
  expect(checkBrandPaletteContrast(PRODUCT_BRAND_PALETTE)).toEqual({ light: [], dark: [] });
74
74
  ```
75
75
 
76
+ ### 3. 표현·구성도 함께 선택 (`designProfile`, 실험·미게시)
77
+
78
+ ```tsx
79
+ import { defineHjmDesignProfile } from "@hjmds/design-contracts/design-profile";
80
+ const productDesign = defineHjmDesignProfile({ extends: "forest", id: "product-forest", compositions: { collection: "rows" } });
81
+ <HjmProvider designProfile={productDesign}><App /></HjmProvider>
82
+ // Native는 동일 데이터를 HjmNativeProvider.designProfile에 넣는다.
83
+ ```
84
+
85
+ 앱이 설정 파일을 소유하고 한 번 주입한다. 기본 팩은 참고용이며 제품 목적에 맞게 선택·수정한다.
86
+ 상태·서버 확정은 테마에 넣지 않는다. 현재 지원 API와 연결 범위는 [프로필 계약](design-profile.md)을 따른다.
87
+
76
88
  ## 덮어도 되는 것과 아닌 것
77
89
 
78
- 규칙은 [brand-boundary.md](./brand-boundary.md)에 있다. 요약하면 `brandPalette`의 17개 semantic key만 바꿀 수 있고,
79
- 상태 강조색과 컴포넌트별 색은 바꿀 수 없으며, `.hjm-*`·`--hjm-*` CSS 재정의는 지원하는 경로가 아니다.
90
+ 규칙은 [brand-boundary.md](./brand-boundary.md)에 있다. 색만 바꿀 때는 `brandPalette`의 17개 semantic key를 쓰고,
91
+ 표현·기본 전환·배치를 바꿀 때는 검증한 `designProfile` 축을 쓴다.
92
+ 상태 강조색과 임의 컴포넌트별 색은 바꿀 수 없으며, `.hjm-*`·`--hjm-*` CSS 재정의는 지원하는 경로가 아니다.
80
93
 
81
94
  ## 어댑터를 두는 이유
82
95
 
@@ -46,7 +46,7 @@
46
46
  | [Breadcrumb](components/breadcrumb.md) | 탐색 | Web의 깊은 계층 화면에서 현재 위치까지의 경로를 보여 주고 상위 계층으로 바로 돌아가게 할 때 쓴다. | 배포 | Web |
47
47
  | [Button](components/button.md) | 동작 | 사용자가 누르면 무언가가 일어나는 텍스트 행동에 쓴다. | 배포 | Web · Native |
48
48
  | [Calendar](components/calendar.md) | 데이터 표시 | 화면에 항상 펼쳐진 한 달 격자에서 날짜 하나를 고를 때 쓴다. | 배포 | Web · Native |
49
- | [Card](components/card.md) | 데이터 표시 | 제목·설명·본문·행동이 한 덩어리로 읽히는 독립된 콘텐츠 단위에 쓴다. | 배포 | Web · Native |
49
+ | [Card](components/card.md) | 데이터 표시 | 문서 metadata·미리보기·내보내기 상태는 실험 구성 DocumentResource를 먼저 대조한다. | 배포 | Web · Native |
50
50
  | [Carousel](components/carousel.md) | 데이터 표시 | 한 번에 카드 하나만 보이고 사용자가 순서대로 넘겨 보는 유한한 묶음에 쓴다. | 배포 | Web · Native |
51
51
  | [Celebration](components/celebration.md) | 구성/직접 조작과 모션 | 목표 달성, 첫 완료처럼 드물게 일어나는 성공 순간에 한 번 터지는 색종이 효과에 쓴다. | 배포 | Web · Native |
52
52
  | [ChatMessage](components/chat-message.md) | 구성/정보 표시 | DM·대화 타임라인의 메시지 한 개에 쓴다. | 배포 | Web · Native |
@@ -83,6 +83,7 @@
83
83
  | [Icon](components/icon.md) | 글자와 아이콘 | HJM semantic 이름(`search`, `back`, `chevronEnd`, `notifications` 등 43개)으로 고르는 그림 기호에 쓴다. | 배포 | Web · Native |
84
84
  | [IconButton](components/icon-button.md) | 동작 | 보이는 글자 없이 아이콘만으로 표시하는 행동에 쓴다. | 배포 | Web · Native |
85
85
  | [Image](components/image.md) | 데이터 표시 | 원본 크기를 아는 사진·차트 이미지를 로드 전에 자리를 잡아 두고, 실패해도 의미를 잃지 않게 보여 줄 때 쓴다. | 배포 | Web · Native |
86
+ | [ImageComparison](components/image-comparison.md) | 데이터 표시 | 같은 좌표와 비율의 두 이미지를 겹쳐 변화량을 비교할 때 쓴다. | 배포 | Web · Native |
86
87
  | [KeyboardAvoiding](components/keyboard-avoiding.md) | — | 추가 native peer 없이 하단 행동(BottomCTA, 채팅 입력창)이 소프트웨어 키보드에 가려지지 않게 할 때 쓴다. | 배포 | Native |
87
88
  | [KeyboardDock](components/keyboard-dock.md) | 구성/직접 조작과 모션 | `react-native-keyboard-controller`를 설치한 앱에서 화면 하단에 고정된 행동(BottomCTA, 채팅 입력창)이 키보드와 함께 위아래로 움직이게 할 때 쓴다(Native 전용, 별도 보조 기능. API 성숙도는 실험적 어댑터). 내부는 `KeyboardStickyView`이며, 여백을 바꾸는 대신 키보드 움직임을 따라 translate 한다. | 배포 | Native |
88
89
  | [KeyboardFormScrollView](components/keyboard-form-scroll-view.md) | 구성/직접 조작과 모션 | 입력 필드가 여러 개인 세로 스크롤 폼(가입, 프로필 수정, 주소 입력)에서 포커스된 필드가 키보드에 가려지지 않게 스크롤해 줄 때 쓴다(Native 전용, 별도 보조 기능. API 성숙도는 실험적 어댑터). 내부는 `react-native-keyboard-controller`의 `KeyboardAwareScrollView`이고, `keyboardShouldPersistTaps="handled"`로 고정돼 키보드가 열린 채 버튼을 눌러도 탭이 전달된다. | 배포 | Native |
@@ -106,6 +107,7 @@
106
107
  | [NumberField](components/number-field.md) | 입력 | 범위가 정해진 **정확한 수 하나**를 입력받을 때 쓴다. | 배포 | Web · Native |
107
108
  | [OnboardingScreen](components/onboarding-screen.md) | 화면/소개 | 첫 실행 소개·초기 설정처럼 **몇 단계를 차례로 넘기는 화면**에 쓴다. | 배포 | Web · Native |
108
109
  | [OtpField](components/otp-field.md) | 입력 | 문자·메일로 받은 **숫자 인증번호**를 칸 모양으로 입력받을 때 쓴다. | 배포 | Web · Native |
110
+ | [OverviewScreen](components/overview-screen.md) | 레이아웃 | 같은 데이터와 기능을 유지하면서 테마별 행·카드·격자와 도구 배치를 선택하는 목록 화면에 쓴다. | 실험 | Web · Native |
109
111
  | [Pagination](components/pagination.md) | 탐색 | 총 개수(또는 총 페이지 수)가 정해진 결과 집합에서 사용자가 **임의의 페이지로 바로 이동**해야 할 때 Web에서 쓴다. | 배포 | Web |
110
112
  | [PasswordField](components/password-field.md) | 입력 | 비밀번호를 입력받고, 필요할 때만 값을 눈으로 확인하게 할 때 쓴다. | 배포 | Web · Native |
111
113
  | [PermissionScreen](components/permission-screen.md) | 화면/소개 | 카메라·위치·알림 같은 권한이 **왜 필요한지 설명하고 다음 행동을 고르게 하는** 화면에 쓴다. | 배포 | Web · Native |
@@ -113,9 +115,11 @@
113
115
  | [Popover](components/popover.md) | 오버레이 | 트리거에 붙어 뜨는 비모달 표면 안에 **포커스를 받는 임의 콘텐츠**를 둘 때 쓴다. | 배포 | Web |
114
116
  | [ProfileScreen](components/profile-screen.md) | 화면/계정 | 내 프로필(또는 계정) 화면 틀에 쓴다. | 배포 | Web · Native |
115
117
  | [Progress](components/progress.md) | 상태와 알림 | 작업이 얼마나 진행됐는지 보여 줄 때 쓴다. | 배포 | Web · Native |
118
+ | [ProgressiveBlur](components/progressive-blur.md) | 시각 효과 | 스크롤 영역의 바깥에 더 내용이 있음을 알리거나 장식 이미지 가장자리를 흐릴 때 쓴다. | 배포 | Web · Native |
116
119
  | [QRCode](components/qr-code.md) | 데이터 표시 | 문자열(초대 링크, 연결 코드, 결제·체크인 URL)을 다른 기기의 카메라로 스캔하게 할 때 쓴다. | 배포 | Web · Native |
117
120
  | [Radio](components/radio.md) | 입력 | 라디오 한 개를 제품이 직접 배치해야 할 때만 쓴다. | 배포 | Web · Native |
118
121
  | [RadioGroup](components/radio-group.md) | 입력 | 한 화면에 펼쳐 둔 선택지 중 정확히 하나를 고를 때 쓴다. | 배포 | Web · Native |
122
+ | [Rating](components/rating.md) | 입력 | 정수 점수를 선택하거나 계산된 소수 평균을 읽기 전용으로 보여 줄 때 쓴다. | 배포 | Web · Native |
119
123
  | [Result](components/result.md) | 상태와 알림 | 사용자 행동 뒤 흐름이 **끝난** 화면에 쓴다. | 배포 | Web · Native |
120
124
  | [SavedItemsScreen](components/saved-items-screen.md) | 화면/콘텐츠 | 저장한 이미지·게시물을 컬렉션 표지 → 사진 격자 → 상세 순서로 탐색할 때 쓴다. | 배포 | Web · Native |
121
125
  | [ScreenLayout](components/screen-layout.md) | 화면/화면 틀과 도구 | 한 라우트 화면의 뼈대가 필요할 때 쓴다. | 배포 | Web · Native |
@@ -168,17 +172,22 @@
168
172
 
169
173
  | 지침 | 분류 | 언제 쓰나 | 상태 | 지원 |
170
174
  | --- | --- | --- | --- | --- |
175
+ | [관련 입력 묶음](compositions/field-group.md) | 입력과 작성 | 주소·연락처처럼 여러 입력이 하나의 질문에 답할 때 쓴다. | 배포 | Web · Native |
176
+ | [날짜 직접 입력](compositions/date-entry.md) | 입력과 작성 | 사용자가 알고 있는 날짜를 직접 입력할 때 쓴다. | 배포 | Web · Native |
171
177
  | [늦은 응답보다 최신 검색 유지](compositions/interaction-flow-search.md) | 입력과 작성 | 검색어를 바꿔 다시 검색했을 때 먼저 보낸 요청이 늦게 도착해도 최신 검색 결과를 덮어쓰지 않게 할 때 쓴다. | 배포 | Web · Native |
172
178
  | [단계별 드로어](compositions/family-drawer.md) | 입력과 작성 | 초대 → 설정 → 확인처럼 짧은 단계 2~5개를 현재 화면을 떠나지 않고 하단 시트 안에서 차례로 진행할 때 쓴다. | 배포 | Web · Native |
173
179
  | [닫았다 열고 초안 이어쓰기](compositions/interaction-flow-draft.md) | 입력과 작성 | 메모·댓글처럼 시트에서 쓰던 글을 저장하지 않고 닫았다가 다시 열었을 때, 쓰던 초안을 그대로 이어 쓰게 할 때 쓴다. | 배포 | Web · Native |
174
180
  | [댓글 작성](compositions/purpose-input-comment.md) | 입력과 작성 | 게시물·기록 아래에서 댓글이나 특정 댓글에 대한 답글을 남기고, 실패하면 글과 답글 대상을 그대로 남겨 다시 등록하게 할 때 쓴다. | 배포 | Web · Native |
175
181
  | [메시지 작성](compositions/purpose-input-message.md) | 입력과 작성 | 대화 화면 하단에서 글과 사진 여러 장을 함께 보내고, 실패하면 글·사진·답장 대상을 그대로 남겨 다시 보내게 할 때 쓴다. | 배포 | Web · Native |
182
+ | [버튼에서 이어지는 편집](compositions/origin-dialog.md) | 입력과 작성 | 현재 화면의 항목을 짧게 편집하고 돌아올 때 출발 위치를 시각적으로 연결한다. | 배포 | Web · Native |
176
183
  | [빠른 메모 작성](compositions/floating-action-button.md) | 입력과 작성 | 스크롤되는 기록 목록 위에 떠 있는 생성 버튼으로 짧은 입력 대화상자를 열고, 저장하면 새 항목을 목록 맨 위에 넣을 때 쓴다. | 배포 | Web · Native |
177
184
  | [선택 내용 검토와 수정](compositions/reference-review.md) | 입력과 작성 | 선택 내용을 검토하고 수정 후 명시적으로 확정 흐름이 필요할 때 쓴다. | 배포 | Web · Native |
178
185
  | [인증번호 확인과 다시 입력](compositions/stea-otp-verify.md) | 입력과 작성 | 문자·메일로 받은 숫자 인증번호를 입력하고 서버 확인을 기다린 뒤, 틀리면 남은 횟수를 보여 주고 다시 받게 하는 흐름에 쓴다. | 배포 | Web · Native |
179
186
  | [입력 시트](compositions/input-sheet.md) | 입력과 작성 | 현재 화면 위에 하단 시트를 띄워 짧은 입력(이름 바꾸기, 메모 한 줄)을 받고, 키보드가 올라와도 본문을 스크롤하며 완료 버튼에 닿게 할 때 쓴다. | 배포 | Web · Native |
187
+ | [입력을 유지하는 도구](compositions/context-toolbar.md) | 입력과 작성 | 작성 중인 입력을 보존한 채 선택적 도구를 펼쳐야 할 때 쓴다. | 배포 | Web · Native |
180
188
  | [첫 작업을 만들고 이어하기](compositions/reference-first.md) | 입력과 작성 | 첫 기록을 단계별 작성하고 중단한 초안 이어가기 흐름이 필요할 때 쓴다. | 배포 | Web · Native |
181
189
  | [날짜 선택과 예정 목록](compositions/stea-schedule-card.md) | 선택과 필터 | 한 주처럼 짧은 날짜 범위에서 날짜 하나를 고르면 같은 카드 안의 일정 목록이 그 날짜로 바뀌는 요약 카드에 쓴다. | 배포 | Web · Native |
190
+ | [날짜와 시각 선택](compositions/date-time-selection.md) | 선택과 필터 | 기록·알림의 날짜 하나와 하루 안의 시각을 함께 고를 때 쓴다. | 실험 | Web · Native |
182
191
  | [대표 항목과 묶음 전체 선택](compositions/selection-scope.md) | 선택과 필터 | 사진 묶음·스레드처럼 대표 항목 하나와 묶음 전체가 같은 모양으로 보일 때, 공유·삭제·이동 전에 대상 범위와 개수를 고르고 문구로 확인한 뒤 적용하게 할 때 쓴다. | 배포 | Web · Native |
183
192
  | [사진 촬영과 앨범 선택](compositions/photo-source.md) | 선택과 필터 | 명시적으로 선택 후 플랫폼 picker 실행 흐름이 필요할 때 쓴다. | 배포 | Web · Native |
184
193
  | [선택 후 적용·취소](compositions/interaction-flow-apply.md) | 선택과 필터 | 표시 방식·정렬·필터처럼 시트에서 여러 번 바꿔 본 뒤 적용을 눌러야 화면에 반영되고, 취소하거나 닫으면 기존 선택을 유지해야 할 때 쓴다. | 배포 | Web · Native |
@@ -186,26 +195,38 @@
186
195
  | [보관함과 페이지 이동](compositions/web-navigation.md) | 탐색과 이동 | Web에서 상위 보관함 → 하위 모음으로 들어가고, 그 모음의 긴 목록을 페이지 단위로 넘겨 보는 탐색에 쓴다. | 배포 | Web |
187
196
  | [펼침과 메뉴](compositions/disclosure.md) | 탐색과 이동 | Web에서 내용을 숨겼다 펼치거나(Collapsible), 대상에 붙은 작업 메뉴를 우클릭·키보드로 열거나(ContextMenu), 데스크톱 앱처럼 상단 메뉴 막대를 두는(Menubar) 세 방식을 각각 보여 주는 모음이다. | 배포 | Web |
188
197
  | [대화 메시지](compositions/common-message.md) | 정보 표시 | 말풍선 하나하나에 반응·답장·원문 이동·전송 실패 후 다시 보내기를 붙일 때 쓴다. | 배포 | Web · Native |
198
+ | [명령 기록 표시](compositions/command-records.md) | 정보 표시 | 명령 원문과 출력 기록을 선택·읽기·복사할 때 쓴다. | 실험 | Web · Native |
199
+ | [문서와 파일](compositions/document-resource.md) | 정보 표시 | 이름·형식·크기와 미리보기·내보내기·별도 메뉴를 함께 제공하는 문서에 쓴다. | 배포 | Web · Native |
189
200
  | [수치와 이전 대비 변화](compositions/stea-stat-summary.md) | 정보 표시 | 매출·주문·반품처럼 몇 개의 핵심 수치를 비교 기간과 함께 보이고, 증감의 방향과 좋고 나쁨을 색 없이도 읽히게 할 때 쓴다. | 배포 | Web · Native |
190
201
  | [알림 항목](compositions/common-notification.md) | 정보 표시 | 알림 한 행을 누르면 바로 읽음으로 바꾸고, 서버가 실패하면 읽지 않음으로 되돌릴 때 쓴다. | 배포 | Web · Native |
191
202
  | [앞면과 상세 정보 전환](compositions/stea-flip-card.md) | 정보 표시 | 모임·상품처럼 한 카드에 요약(앞면)과 상세 항목(뒷면)이 있고, 사용자가 버튼 하나로 두 면을 오가게 할 때 쓴다. | 배포 | Web · Native |
203
+ | [영상 미리보기](compositions/video-dialog.md) | 정보 표시 | 현재 입력을 유지하면서 짧은 영상 설명을 확인할 때 쓴다. | 배포 | Web · Native |
192
204
  | [일정과 식별 정보 티켓](compositions/stea-event-ticket.md) | 정보 표시 | 공연·예약 입장권처럼 일시·장소·좌석 정보와 함께, 현장에서 보여 줄 QR 코드와 사람이 읽을 예매 번호를 한 카드에 담을 때 쓴다. | 배포 | Web · Native |
205
+ | [질감 비교](compositions/texture-comparison.md) | 정보 표시 | 기존 반복 점 grain과 불규칙한 정적 noise를 같은 배경·강도로 비교할 때 쓴다. | 배포 | Web · Native |
206
+ | [추가해도 유지되는 목록](compositions/live-list.md) | 정보 표시 | 입력 중인 목록에 새 데이터가 추가되거나 순서가 바뀌어도 기존 초안과 항목의 정체성을 유지할 때 쓴다. | 배포 | Web · Native |
193
207
  | [카드 묶음과 긴 목록](compositions/data-layouts.md) | 정보 표시 | 많은 항목을 화면에 늘어놓을 방식을 고를 때 쓴다. | 배포 | Web · Native |
208
+ | [그림과 시작 안내](compositions/illustrated-outcome.md) | 피드백과 복구 | 빈 목록에서 시작을 안내하고 짧은 온보딩을 거쳐 결과를 보여 줄 때 쓴다. | 배포 | Web · Native |
209
+ | [버튼 완료 피드백](compositions/action-feedback.md) | 피드백과 복구 | 입력을 유지하며 저장의 진행·성공·재시도 가능 실패를 보여 줄 때 쓴다. | 배포 | Web · Native |
194
210
  | [변경 저장과 이탈 확인](compositions/reference-settings.md) | 피드백과 복구 | 저장값과 편집 초안을 비교해 이탈 확인 흐름이 필요할 때 쓴다. | 배포 | Web · Native |
195
211
  | [보관과 실행 취소](compositions/action-recovery-undo.md) | 피드백과 복구 | 보관·숨기기·목록에서 빼기처럼 제품이 역연산을 제공하는 작업 뒤에, 같은 자리에서 실행 취소를 주고 그 복구 요청이 성공해야 화면을 되돌릴 때 쓴다. | 배포 | Web · Native |
212
+ | [선택과 오류 복구](compositions/upload-recovery.md) | 피드백과 복구 | 파일 선택과 전송 상태의 취소·재시도를 연결할 때 쓴다. | 배포 | Web · Native |
196
213
  | [저장과 재시도](compositions/action-recovery-save.md) | 피드백과 복구 | 입력한 내용을 서버에 저장하는 폼 한 덩어리에서 저장 중 중복 실행을 막고, 실패하면 입력을 지우지 않은 채 제출했던 값 그대로 다시 보낼 때 쓴다. | 배포 | Web · Native |
197
214
  | [중단해도 남는 현재 상태](compositions/expo-interactions.md) | 피드백과 복구 | 버튼으로 상태를 빠르게 바꾸거나 전환 도중 내용을 닫아도 현재 상태가 바로 보이고 남아야 하는 영역에 쓴다. | 배포 | Native |
198
215
  | [즉시 반영과 복구](compositions/action-recovery-optimistic.md) | 피드백과 복구 | 북마크·좋아요·알림 켜기처럼 되돌려도 피해가 없는 저위험 토글을 누르는 즉시 화면에 반영하고, 서버가 실패하면 직전 확인 값으로 되돌릴 때 쓴다. | 배포 | Web · Native |
199
216
  | [처리 단계와 재시도](compositions/stea-order-progress.md) | 피드백과 복구 | 주문·신청처럼 서버가 단계를 하나씩 확정하는 처리 과정을 보여 주고, 확정에 실패하면 같은 단계를 다시 요청하게 할 때 쓴다. | 배포 | Web · Native |
200
217
  | [캐릭터와 시작 행동](compositions/stea-pixel-empty.md) | 피드백과 복구 | 아직 만든 것이 없는 첫 빈 화면에 제품 캐릭터를 움직여 보이고 첫 행동 하나로 이끌 때 쓴다. | 배포 | Web · Native |
201
218
  | [끌기·밀기·화면 전환](compositions/interaction-adapters.md) | 직접 조작과 모션 | 순서 바꾸기·행 작업·내용 전환·카드 넘기기·달성 축하·카드 확대 화면 전환 같은 선택형 상호작용 어댑터를 한 화면에서 함께 쓸 때, 각 어댑터를 어디에 놓고 무엇으로 감싸야 하는지 확인하는 구성이다. | 배포 | Web · Native |
219
+ | [높이가 이어지는 패널](compositions/adaptive-content.md) | 직접 조작과 모션 | 패널의 길이가 달라질 때 아래 행동이 새 높이로 이동해야 하는 작은 내용 영역에 쓴다. | 배포 | Web · Native |
220
+ | [선택 배경 이동](compositions/selection-motion.md) | 직접 조작과 모션 | 짧은 단일 선택의 현재 항목을 이어지는 배경으로 보여 줄 때 쓴다. | 배포 | Web · Native |
202
221
  | [숫자 변화와 메뉴 변형](compositions/optional-motion.md) | 직접 조작과 모션 | 선택 설치 모션(숫자 자리 단위 변화, 메뉴 형태 변환)을 기존 컴포넌트 자리에 끼워 넣을 때 쓴다. | 배포 | Web |
203
222
  | [이미지·시트·키보드 조작](compositions/optional-adapters.md) | 직접 조작과 모션 | Native 앱 한 화면에서 이미지 확대 보기, 끌어서 높이를 바꾸는 시트, OS 길게 누르기 메뉴, 키보드를 따라 올라가는 하단 행동을 함께 쓸 때 provider 중첩 순서와 각 요소의 자리를 확인하는 구성이다. | 배포 | Native |
204
223
  | [내비게이션 바 비교](compositions/navigation-bar-collection.md) | 비교와 검증 | 하단 탭에 목적지 이동과 별개의 행동(작성·전원·기록 추가)을 함께 둘지, 선택한 목적지를 어떻게 보여 줄지 고를 때 이 비교를 본다. | 배포 | Web · Native |
224
+ | [내용 전환 비교](compositions/content-transition-comparison.md) | 비교와 검증 | 동일 내용의 전환 표현을 테마와 비교하거나 단계별 입력·완료·복구를 검토할 때 쓴다. | 실험 | Web · Native |
205
225
  | [네이티브 컴포넌트 기기 확인](compositions/native-renderers.md) | 비교와 검증 | Native 공개 컴포넌트가 실제 기기·시뮬레이터에서 그려지고 눌리는지 범주별로 한 화면에서 확인할 때 쓴다. | 배포 | Native |
206
226
  | [복합 입력 모음](compositions/compound-controls.md) | 비교와 검증 | 기존 컨트롤을 묶은 네 가지 복합 입력(소요 시간, 버튼 자리 확인, 이모지 반응, 알림 종)을 화면 안 한 블록으로 둘 때 쓴다. | 배포 | Web · Native |
207
227
  | [시각 효과 모음](compositions/visual-foundations.md) | 비교와 검증 | 배경 질감, 의미 이름 아이콘, 사진 없는 프로필 얼굴, 문장 전환처럼 화면의 분위기를 더하는 선택 표현을 고를 때 이 모음을 본다. | 배포 | Web · Native |
208
228
  | [웹 전용 보조 컴포넌트](compositions/web-additions.md) | 비교와 검증 | Web에만 있는 보조 컴포넌트 세 개(색 고르기, 문서 워터마크, 스크롤 중 고정되는 실행 영역)를 실제 쓰임 하나씩과 함께 보여 주는 모음이다. | 배포 | Web |
229
+ | [테마 조합](compositions/design-profile-comparison.md) | 비교와 검증 | 같은 기능에 10가지 표현을 적용하고, 앱 소유 테마 설정을 넣었을 때 네 단계의 전파와 상태 유지를 검토할 때 쓴다. | 실험 | Web · Native |
209
230
  | [토스트 배치 비교](compositions/toast-layout.md) | 비교와 검증 | Toast 카드 한 장의 내부 배치(톤 배지·제목·설명·닫기·실행 버튼)와 화면 위 위치를 좁은 폭·큰 글자·긴 문구·톤별로 확인하는 비교 스토리다. | 배포 | Web |
210
231
  | [환경 조합 검증](compositions/environment-matrix.md) | 비교와 검증 | 제품 화면이 테마·쓰기 방향·글자 크기·모션 설정이 달라져도 같은 의미를 유지하는지 확인할 때, 어떤 환경 조합과 검증 항목을 골라 볼지 정하는 기준표로 쓴다. | 배포 | Web |
211
232
 
@@ -216,6 +237,7 @@
216
237
  | [서비스 소개](screens/landing.md) | 소개 | 제품을 처음 보는 사람에게 한 문장 가치 제안을 보여 주고 같은 화면에서 첫 행동(짧은 입력)을 체험하게 하는 소개 화면이다. | 배포 | Web · Native |
217
238
  | [온보딩](screens/flow-onboarding.md) | 소개 | 첫 실행 사용자를 몇 단계(소개 → 관심 주제 → 시작)로 안내하고 마지막 단계에서 완료를 저장하는 화면을 OnboardingScreen 하나로 구성한다. | 배포 | Web · Native |
218
239
  | [권한 안내](screens/flow-permission.md) | 소개 | PermissionScreen을 사용해 권한 안내 흐름을 구성한다. | 배포 | Web · Native |
240
+ | [기능 카드와 주 행동](screens/product-bento.md) | 소개 | 기능을 실제 미리보기로 보여 주고 첫 행동으로 이어지는 소개 화면이다. | 배포 | Web · Native |
219
241
  | [로그인](screens/common-login.md) | 계정 | AuthScreenLayout을 사용해 로그인 흐름을 구성한다. | 배포 | Web · Native |
220
242
  | [프로필](screens/common-profile.md) | 계정 | 내 프로필을 보고(요약·게시물·계정 메뉴) 고치는(사진·이름·소개) 화면을 ProfileScreen과 EditorScreen으로 구성한다. | 배포 | Web · Native |
221
243
  | [알림 설정](screens/notification-settings.md) | 설정 | 알림 종류 몇 개를 스위치로 켜고 끈 뒤 하단 버튼 하나로 저장하는 설정 화면이다. | 배포 | Web · Native |
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-07
8
8
  - 근거: [Activity heatmap](../../activity-heatmap.md), descriptor `resolveActivityHeatmap`(`src/activity-heatmap.ts`)
9
9
  - 스토리북: `배포/컴포넌트/데이터 표시/활동 히트맵`
10
10
 
@@ -74,6 +74,8 @@ import { ActivityHeatmap } from "@hjmds/react-native/activity-heatmap";
74
74
 
75
75
  ## 배치
76
76
 
77
+ Native 데이터 셀 모서리는 Provider의 `tokens.radius.sm / 4`다. Web의 같은 역할과 맞추며 날짜·0·누락 데이터의 의미는 색과 분리한다.
78
+
77
79
  | 항목 | 값 | 근거 |
78
80
  | --- | --- | --- |
79
81
  | 크기 | 칸 16×16(Web `1rem`, Native `spacing.md`), 7행(요일) × 주 수 열. 366일이면 53열 × 20 − 4 = 1056 폭이다. 칸은 터치 대상이 아니다(44 미만) | `react/src/activity-heatmap.tsx`, `react-native/src/activity-heatmap.tsx` |
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-07
8
8
  - 근거: [Agreement contract](../../agreement.md), `agreementRecipe`·`resolveAgreementState`(`src/agreement.ts`)
9
9
  - 스토리북: `배포/컴포넌트/입력/약관 동의`
10
10
 
@@ -68,6 +68,7 @@ import { Agreement } from "@hjmds/react-native/agreement";
68
68
  | prop | 값 | 기본값 | 설명 |
69
69
  | --- | --- | --- | --- |
70
70
  | `descriptor` | `{ accessibilityLabel, allLabel, items }`(`AgreementDescriptor`) | 필수 | 문구는 모두 i18n |
71
+ | 묶음 `descriptor.disabled` | `boolean` | `false` | 제출 중 전체·개별 동의 변경만 잠금. 선택/필수 판정 유지, 전문 읽기는 가능 |
71
72
  | 항목 | `{ id, label, description?, disabled?, required?, detail?: { label, href? } }` | — | 필수이면서 `disabled`인 항목은 throw |
72
73
  | 항목 `required` | `boolean` | `false`(선택) | 필수 항목이 하나라도 비면 `satisfied`가 `false`다 |
73
74
  | 전체 동의 | tri-state(파생) | — | 저장되는 값이 아니라 개별 항목에서 파생한다. 비활성 항목은 분모에서 빠진다 |
@@ -81,11 +82,13 @@ import { Agreement } from "@hjmds/react-native/agreement";
81
82
 
82
83
  ## 배치
83
84
 
85
+ Native의 전체 동의 프레임 `md`와 선택 표시 `sm`은 Provider의 `tokens.radius`를 읽는다. 제품 프로필 교체는 동의 상태를 초기화하지 않는다.
86
+
84
87
  | 항목 | 값 | 근거 |
85
88
  | --- | --- | --- |
86
89
  | 크기 | 폭을 꽉 채운다. 전체 동의 줄 최소 44(`control.minTouchTarget`), 항목 줄 최소 44, [전문 보기] 높이 44, 체크 표시 16×16(`spacing.md`) | `agreementRecipe`, `collectionItemContract`, `.hjm-agreement__mark` |
87
90
  | 간격 | 전체 동의 ↔ 목록 `spacing.xs` 8. 전체 동의 안쪽 위아래 `spacing.sm` 12 · 좌우 `spacing.md` 16, 배경 `canvas`, 모서리 `radius.md` 12. 항목 좌우 `spacing.sm` 12, 체크↔라벨 `spacing.sm` 12, [전문 보기] 좌우 `spacing.xs` 8 | `agreementRecipe`, `.hjm-agreement*` |
88
- | 순서·정렬 | 가입·결제 화면에서 입력 필드 아래, 제출 버튼 바로 위. 안쪽은 [전체 동의] → 항목 목록, 항목은 체크·라벨이 시작 쪽, [전문 보기]가 끝 쪽, 설명은 라벨 아래 줄. 스토리는 Top → TextField → Agreement → 남은 필수 항목 안내(`role="status"`) → Button을 `Stack gap="md"`(16)로 쌓는다 | `showcase/web/src/patterns/Agreement.stories.tsx` |
91
+ | 순서·정렬 | 가입·결제 화면에서 입력 필드 아래, 제출 버튼 바로 위. 안쪽은 [전체 동의] → 항목 목록, 항목은 체크·라벨이 시작 쪽, [전문 보기]가 뒤쪽에 놓인다. 라벨은 행 폭의 70%를 기준으로 확보하고 긴 전문 버튼은 다음 줄로 이동한다. 설명은 라벨 아래 줄. 스토리는 Top → TextField → Agreement → 남은 필수 항목 안내(`role="status"`) → Button을 `Stack gap="md"`(16)로 쌓는다 | `showcase/web/src/patterns/Agreement.stories.tsx` |
89
92
  | 고정·스크롤 | Agreement는 스크롤 본문에 둔다. 하단에 고정할 제출 버튼은 [BottomCTA](bottom-cta.md)로 두고 `satisfied`가 `false`면 비활성 | 같은 스토리 |
90
93
  | 좁은 폭·큰 글자 | 라벨이 줄바꿈되고(`overflow-wrap: anywhere`) [전문 보기]는 끝 쪽에 남는다 | `.hjm-agreement__copy` |
91
94
 
@@ -106,7 +109,8 @@ import { Agreement } from "@hjmds/react-native/agreement";
106
109
 
107
110
  ## 꼭 지킬 것
108
111
 
109
- - 제출 버튼은 `onStateChange`로 받은 `satisfied`만 읽는다. 남은 필수 항목 안내는 `missingRequiredIds`로 제품이 문장을 만든다.
112
+ - 필수 동의 판정은 `resolveAgreementState`의 `satisfied`를 사용하고 제출 가능 여부는 제품의 입력 검증·진행 중 상태와 함께 결정한다. 묶음 잠금은 동의 사실을 바꾸지 않으므로 `satisfied`가 true인 채 잠길 수 있다. 남은 필수 항목 안내는 `missingRequiredIds`로 제품이 문장을 만든다.
113
+ - `onStateChange`는 마운트와 사용자 토글 때 알린다. 제품이 약관 버전이나 제어 `checkedIds`를 직접 바꾸면 같은 렌더에서 `resolveAgreementState(descriptor, checkedIds)`로 판정한다. 문서 버전 변경 시 이전 동의를 초기화하는 책임은 제품에 있다.
110
114
  - `accessibilityLabel`·`allLabel`·항목 `label`·`requiredLabel`·`optionalLabel`은 모두 제품의 i18n 문구다.
111
115
  빈 문자열, 빈 목록, 중복 id, 필수이면서 `disabled`인 항목은 throw다.
112
116
  - 약관 문구·링크 주소·법적 유효성·동의 기록 저장은 제품 소유다. HJM은 판정과 배치만 가진다.
@@ -127,3 +131,11 @@ import { Agreement } from "@hjmds/react-native/agreement";
127
131
  `satisfied`를 받지 못했다. 미게시(1.12.1 이후) 버전은 마운트 때 초기 상태를 한 번 알린다. 1.12.1에서는 제출 버튼의 첫 상태를
128
132
  `resolveAgreementState(descriptor, checkedIds)`(`@hjmds/design-contracts/components/agreement`)로 직접 계산한다.
129
133
  호출 횟수를 세는 코드는 마운트 1회가 늘어난다.
134
+
135
+ 2026-10-07 Utilverse 가입은 요청 중 필수 Checkbox 둘을 잠그는데, 개별 필수 item에 disabled를
136
+ 주면 Agreement 검증이 거절했다. 묶음 `descriptor.disabled`를 추가해 필수 항목을 분모에서
137
+ 빼거나 동의 값을 삭제하지 않고 변경만 막는다. 양 Showcase의 `비활성`에서 전문 읽기와 잠금
138
+ 해제 후 이어서 선택을 확인할 수 있다. Web은 aria-disabled로 초점을 유지하고 키보드 토글을
139
+ 막으며, Native는 disabled/accessibilityState를 함께 적용한다. 별도 전문 행동은 잠그지 않는다.
140
+
141
+ 큰 글자에서도 체크 원은 16×16을 유지한다. 내부 체크는 8×4 도형(테두리 2), 혼합 표시는 10×2 도형이며 글꼴 확대에 따라 원 밖으로 커지지 않는다. Native는 Yoga의 축소 계산 때문에 70% 최소 폭도 함께 적용한다.
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-07
8
8
  - 근거: `AlertDialogRequest`·`createAlertDialogSession`(`src/alert-dialog.ts`), recipe `alertDialogRecipe`, Popover와의 경계 [ConfirmPopover 결정](../../confirm-popover.md), Native 긴 문구 처리 [Dialog](../../dialog.md)
9
9
  - 스토리북: `배포/컴포넌트/오버레이/확인 대화상자`
10
10
 
@@ -83,6 +83,15 @@ import { AlertDialog } from "@hjmds/react-native/overlays";
83
83
  | Web `modalPriority` | `number` | `0` | 높은 우선순위 모달이 뒤에 열린 낮은 모달 위에서 동작한다 |
84
84
  | Native `contentStyle` | 배치 key(margin·width·flex·`alignSelf`)만 | — | 색·radius·padding 등 시각 key는 deprecated(개발 모드 1회 경고, 다음 major에서 배치 key로 좁힘) |
85
85
 
86
+ ### 디자인 프로필 상속
87
+
88
+ 2026-10-07 테마 소비 경로 점검에서 고정 foundation/recipe 값이 남은 곳을 보완했다.
89
+ 모서리의 recipe 역할은 유지하고 값은 가장 가까운 Provider의 `designProfile.tokens.radius`를
90
+ 읽는다. Dialog/AlertDialog/Sheet/일반 Toast의 그림자는 `tokens.shadow.floating`을 읽으며
91
+ 프로필 없는 소비자의 기본값은 유지한다. 상태·초안·선택·Modal teardown은 이 축의 소유가 아니다.
92
+ 플랫폼 근사와 미검증 범위는 [프로필 계약](../../design-profile.md#오버레이선택-입력의-프로필-연결-보완)을 따른다.
93
+
94
+
86
95
  ## 배치
87
96
 
88
97
  | 항목 | 값 | 근거 |
@@ -126,5 +135,11 @@ import { AlertDialog } from "@hjmds/react-native/overlays";
126
135
 
127
136
  ## 함정
128
137
 
138
+ - `onConfirm`은 Promise가 resolve되면 확인 성공으로 처리한다. 제품 command가 실패를 자체
139
+ snapshot에 기록하고 Promise를 resolve하는 경우 그대로 연결하지 않는다. 2026-10-07 Utilverse
140
+ 삭제·신고 조사에서 이 차이를 확인했다. 작업까지 대화상자가 기다리는 경로는 receipt/실패를
141
+ 명시적으로 변환하는 adapter가 필요하다. 사용자 동의만 받는 경로는 `onConfirm` 없이 확인을
142
+ 마치고, Native `onResult`에서 제품 작업을 시작한다. 이때 `confirmed`는 동의 결과이며 서버
143
+ 삭제·Apple 인증·로컬 정리의 성공을 뜻하지 않는다. 처리 결과는 제품 상태 화면이 계속 보여 준다.
129
144
  - Native에서 매우 긴 확인 문구는 본문이 스크롤되도록 바뀌었지만 큰 글자 실기기 검증은 끝나지 않았다([Dialog](../../dialog.md)).
130
145
  문구를 자르거나 글자 크기 상한으로 피하지 않는다.
@@ -4,10 +4,14 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-07
8
8
  - 근거: [Asset contract](../../asset.md), [VoiceNote](../../voice-note.md), recipe `assetRecipe`(`src/asset.ts`)
9
9
  - 스토리북: `배포/컴포넌트/데이터 표시/이미지·영상 표시` · `배포/컴포넌트/데이터 표시/음성 메모`
10
10
 
11
+ 개발 중인 프로필 지원에서 `rounded`는 가장 가까운 프로필의 `tokens.radius.md`를 따른다.
12
+ 프로필이 없으면 기존 12px이고 명시한 square/circle은 그대로다. 이 보강은 아직 미게시이며
13
+ 1.12.1의 기존 Asset 제공 여부와 구분한다([근거](../../../../../docs/qa/2026-10-07-3dicons-page-review.md)).
14
+
11
15
  ## 언제 쓰나
12
16
 
13
17
  아이콘·이미지·Lottie·비디오를 같은 크기·모서리 규칙의 액자에 넣을 때 쓴다. 종류가 다른 그림이
@@ -97,7 +101,7 @@ import { VoiceNote } from "@hjmds/react-native/voice-note";
97
101
 
98
102
  | 항목 | 값 | 근거 |
99
103
  | --- | --- | --- |
100
- | 크기 | 정사각 액자. 한 변 `small` 32 · `medium` 48 · `large` 72 · `xlarge` 120. 모서리 `square` 0 · `rounded` `radius.md` 12 · `circle` `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` |
101
105
  | 간격 | `accessory`는 액자 끝·아래 모서리 바깥으로 `spacing.xxs` 4 띄워 붙으므로 옆 요소와 `spacing.xs` 8 이상 띄운다. `AssetGroup` 겹침은 크기의 30%(48이면 −14) | `assetRecipe.accessory`·`overlapRatio`, `react/src/asset.tsx` |
102
106
  | 순서·정렬 | 늘어나지 않는 인라인 요소(Web `inline-flex`, `flex: 0 0 auto`). 행 안에서는 시작 쪽에 둔다. 한 줄에 종류가 다른 그림을 섞을 때 모두 같은 `size`·`shape`를 준다 | `.hjm-asset`, `assetBehavior.scenarios` |
103
107
  | 고정·스크롤 | 고정 영역이 없다 | — |
@@ -109,6 +113,9 @@ import { VoiceNote } from "@hjmds/react-native/voice-note";
109
113
  둘 다 준 경우 모두 렌더 중 `TypeError`가 난다.
110
114
  - reduced motion에서는 숨기지 말고 `animate`로 재생을 멈춘다. 판단을 제품에서 다시 만들지 않는다.
111
115
  - Lottie·비디오·오디오 엔진, 그림 자산은 제품 소유다. HJM은 액자·크기·겹침·표식 위치만 소유한다.
116
+ - 테마별 그림은 제품의 자산 목록에서 선택해 슬롯에 넣는다. Asset이 URL을 만들거나 재질·각도
117
+ 변형을 자동 생성하지 않는다. 자산마다 지원하는 조합과 실패 대체를 확인하며, 3D 제공자의
118
+ 태그를 의미 있는 대체 텍스트로 그대로 복사하지 않는다. 기능 아이콘은 Icon/Button 계약을 쓴다.
112
119
  - `VoiceNote`의 재생 위치는 실제 플레이어 값을 넘긴다. 내부 타이머로 진행을 꾸미지 않는다.
113
120
 
114
121
  ## 플랫폼 차이
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-07
8
8
  - 근거: [Avatar fallback and Blobatar](../../avatar-fallback.md), recipe `avatarRecipe`(`src/component-recipes.ts`)
9
9
  - 스토리북: `배포/컴포넌트/데이터 표시/아바타` · `배포/컴포넌트/데이터 표시/블로바타 캐릭터` · `배포/컴포넌트/데이터 표시/움직이는 블로바타 캐릭터`
10
10
 
@@ -76,6 +76,7 @@ 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
80
  | Native `initials` | `string` | 이름에서 계산 | 앞뒤 공백을 지우고 최대 3자, 대문자 |
80
81
  | `layoutStyle` | margin·width·flex·`alignSelf` | — | 배치 전용. Native `style`·`imageStyle`은 deprecated — layoutStyle 또는 `size`/`renderFallback` |
81
82
  | `AvatarGroup`(Web) `label` · `size` · `overflow` | `string` · Avatar 크기 · `ReactNode` | `label` 필수 · `size` `medium` | 빈 `label`은 `TypeError`. `overflow`는 제품이 만든 "+3" 같은 문구이며 `aria-hidden`이다(남은 인원은 `label`에 담는다). 겹침은 크기의 30%다 |
@@ -107,8 +108,39 @@ import { Avatar } from "@hjmds/react-native/data-display";
107
108
  | 크기·모양 축 | 4단 이름 · `circle`/`rounded` | 숫자 · 원만 |
108
109
  | 묶음 | `AvatarGroup` | 없음 |
109
110
 
111
+ 2026-10-07 실제 Showcase 비교에서 `AvatarGroup`을 `ListRow.leading`에 넣으면 단일 이미지용
112
+ 40×40 프레임에 그룹과 남은 인원이 잘렸다. 단일 Avatar는 그 슬롯을 사용하고, 묶음은 Card 본문의
113
+ Stack처럼 내용 폭을 수용하는 영역에 둔다. leading의 overflow/크기를 CSS로 우회하지 않는다.
114
+
110
115
  ## 함정
111
116
 
112
117
  - `src`/`source`에 `undefined`를 직접 넘기면 `exactOptionalPropertyTypes`에서 타입 오류다. 사진이 없으면 prop을 빼거나 spread로 조건부로 넣는다.
113
118
  - Web `AvatarGroup`은 이제 `style`을 버리지 않고 `layoutStyle`과 합친다. 겹침 변수(`--hjm-avatar-overlap`)는 마지막에 덮이므로 `style`로 겹침을 바꿀 수 없다.
114
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`으로 같은 값을 준다.
120
+
121
+ ### Native 제품 이미지 호스트
122
+
123
+ Utilverse는 Expo Image의 disk 캐시와 로딩 동안 이니셜을 유지한다. 공통 Avatar로 바꿀 때
124
+ 그 계약을 잃지 않도록 `renderImage`를 제공한다. 새 아바타 컴포넌트나 Expo 의존성을
125
+ HJM에 추가하지 않고 원형 프레임·대체 문자·접근성은 기존 Avatar에 남긴다.
126
+
127
+ ```tsx
128
+ import { Image as ExpoImage } from "expo-image";
129
+ import { Avatar } from "@hjmds/react-native/data-display";
130
+
131
+ <Avatar name={displayName} decorative source={{ uri }} size={28}
132
+ renderImage={({ source, size, fallback, onError }) => (
133
+ <View style={{ width: size, height: size, alignItems: "center", justifyContent: "center" }}>
134
+ {fallback}
135
+ <ExpoImage source={source} cachePolicy="disk" contentFit="cover" accessible={false}
136
+ style={{ position: "absolute", width: size, height: size }} onError={onError} />
137
+ </View>
138
+ )}
139
+ />
140
+ ```
141
+
142
+ 제품이 import하는 `View`·Expo Image는 호스트 구현이며 HJM 색·원형·테두리를 덮지 않는다.
143
+ 실패 시 전달된 `onError`를 호출해야 공통 대체 표시로 바뀐다. 이전 source의 callback은
144
+ A→B→A로 되돌아와도 무시한다. source를 같은 값으로 재생성하는 것은 재시도가 아니다.
145
+ 슬롯의 자식은 장식 전용이며 공통 wrapper가 접근성 트리에서 숨긴다. 버튼·링크를 넣지 않는다.
146
+ 기본 Native Image 경로는 그대로다. Web은 기존 img/imageProps 경로를 사용한다.
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-07
8
8
  - 근거: recipe `badgeRecipe`(`src/component-recipes.ts`)
9
9
  - 스토리북: `배포/컴포넌트/데이터 표시/배지`
10
10
 
@@ -59,6 +59,8 @@ import { Badge } from "@hjmds/react-native/data-display";
59
59
 
60
60
  ## 배치
61
61
 
62
+ Native 모서리는 Provider token에서 recipe 역할을 읽는다. Badge의 `full`은 고정 pill 역할(999)이라 테마가 이를 사각형으로 바꾸지 않는다.
63
+
62
64
  | 항목 | 값 | 근거 |
63
65
  | --- | --- | --- |
64
66
  | 크기 | 내용 폭만 차지한다. 최소 높이 `medium` 24 · `small` 20, 모서리 `radius.full`. 누를 수 없는 표시라 44 터치 영역이 필요 없다(누르게 하려면 [Chip](chip.md)) | `badgeRecipe.sizes`, `.hjm-badge` |
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-07
8
8
  - 근거: [화면 제목과 마지막 행동](../../screen-chrome.md), recipe `bottomCtaRecipe`(`src/component-recipes.ts`)
9
9
  - 스토리북: `배포/컴포넌트/동작/하단 실행 버튼`
10
10
 
@@ -123,3 +123,5 @@ const insets = useSafeAreaInsets();
123
123
 
124
124
  - Web `BottomCTA`의 `style`은 recipe CSS 변수 뒤에 펼쳐지므로 `--hjm-bottom-cta-*` 변수를 덮을 수 있다. 외형은 recipe 소유이므로 `style`로 변수를 바꾸지 않는다.
125
125
  - Native는 하단 inset을 스스로 읽지 않는다. `safeAreaBottom`을 빠뜨려도 오류가 없고 홈 인디케이터에 붙어 보인다.
126
+
127
+ 미게시(1.14.0 이후): 프로필의 `shadow.floating` 색·강도·반경을 상속하되, 위 콘텐츠와 겹치는 footer 역할이라 offsetY는 `-abs(offsetY)`로 위쪽에 표시한다. Native의 0-opacity 프로필은 Android elevation도 0이다. 프로필 없는 Web은 기존 무그림자, Native는 기존 위쪽 recipe 그림자를 유지한다. safe area·큰 글자·로딩/실패와 제품 행동 소유권은 바뀌지 않는다.
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-07
8
8
  - 근거: [BottomNavigation](../../bottom-navigation.md), recipe `bottomNavigationRecipe`
9
9
  - 스토리북: `배포/컴포넌트/탐색/하단 탐색`
10
10
 
@@ -87,6 +87,8 @@ import { BottomNavigation } from "@hjmds/react-native/navigation";
87
87
 
88
88
  ## 배치
89
89
 
90
+ Native floating 프레임·선택 표시·항목 모서리는 각 recipe radius 역할을 Provider token에서 읽는다. full/capsule 원형 역할과 목적지·키보드 동작은 유지한다.
91
+
90
92
  | 항목 | 값 | 근거 |
91
93
  | --- | --- | --- |
92
94
  | 크기 | 항목 최소 `regular` 56×64 · `compact` 52×52. 표면 최대 폭 `bar` 제한 없음(화면 폭) · `floating` 384 · `capsule` 480. 모서리 `floating` `radius.xl` 24 · `capsule` `radius.full`. 아이콘·배지 자리 40×28 | `bottomNavigationRecipe.density`·`presentations`·`indicator` |
@@ -134,3 +136,5 @@ floating·capsule: 바깥 좌우 16 · 위 8 띄운 둥근 표면, 최대 폭 38
134
136
  - `renderLink` 없이 쓰면 일반 `<a>`로 그려 SPA 전환이 일어나지 않는다.
135
137
  - Web `onActivate`는 링크 기본 이동을 막지 않는다. `renderLink`의 라우터 Link가 이동하고 `onActivate`는 계측·맨 위로 스크롤 같은 부수 동작에만 쓴다. 수정키·가운데 클릭에는 불리지 않는다.
136
138
  - 현재 Native 내비게이션 연구 스토리(`showcase/native/src/reference-navigation-bars.tsx`)는 deprecated `style`(`paddingHorizontal: 0`)·`listStyle`(`borderRadius`)로 recipe 여백·모서리를 덮고, 제목을 `Text variant="heading"`으로 그린다. 규칙은 `configuration`·`layoutStyle`과 [Heading](heading.md)이다(스토리 수정 후보).
139
+
140
+ 미게시(1.14.0 이후): Native의 floating/capsule은 프로필 `shadow.floating`을 읽으며 0-opacity는 Android elevation도 제거한다. bar는 원래 무그림자 계약을 유지한다. Web은 기존 CSS token 경로로 같은 프로필을 읽는다. 선택 route와 이동 intent는 제품이 소유한다.