@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
@@ -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
  - 근거: [버튼 라벨 줄바꿈](../../button-label.md), `src/base-recipes.ts`(`buttonRecipe`), `src/foundations.ts`(`control.buttonHeight`)
9
9
  - 스토리북: `배포/컴포넌트/동작/버튼`, `배포/컴포넌트/동작/버튼 안에서 확인`, `배포/컴포넌트/동작/반응 선택`
10
10
 
@@ -95,6 +95,12 @@ import { Button } from "@hjmds/react-native/actions";
95
95
 
96
96
  ## 꼭 지킬 것
97
97
 
98
+ - 2026-10-07 [Stateful Button 대조](../../../../../docs/plans/aceternity-interaction-adoption-2026-10-07.md)를
99
+ 기존 [실행과 복구](../../action-session.md)와 비교했다. Button은 callback의 Promise를 자동으로
100
+ 기다려 성공을 판정하는 엔진이 아니다. 제품 mutation 또는 기존 action-session의 pending을
101
+ `loading`에 연결하고 실제 확정 뒤 성공 문구를 보여 준다. 실패하면 초안을 보존하고 재시도를
102
+ 제공한다. Promise가 resolve됐다는 이유만으로 도메인 응답의 실패를 성공으로 표시하지 않는다.
103
+
98
104
  - 라벨은 i18n 키로 넣는다. 자르지 말고 두 줄을 넘으면 카피를 고친다([라벨 정책](../../button-label.md)).
99
105
  - 배치는 `layoutStyle`로만 한다. 색·radius·높이를 `style`/`className`으로 덮지 않는다.
100
106
  Native에서 `style`·`labelStyle`을 넘기면 실행 중 `TypeError`가 난다.
@@ -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
  - 근거: [Calendar](../../calendar.md), recipe `calendarRecipe`(`src/calendar.ts`)
9
9
  - 스토리북: `배포/컴포넌트/데이터 표시/달력`
10
10
 
@@ -80,6 +80,8 @@ import { Calendar } from "@hjmds/react-native/calendar";
80
80
 
81
81
  ## 배치
82
82
 
83
+ Native `renderCellContent` 최소 높이는 Provider의 `tokens.typography.label.lineHeight × textScale`다. 날짜의 원형 geometry와 선택 계약은 별도다.
84
+
83
85
  | 항목 | 값 | 근거 |
84
86
  | --- | --- | --- |
85
87
  | 크기 | 날짜 칸 44(`medium` `control.minTouchTarget`, `large` `glyph.xxl` 44 — large는 글자만 `bodyLarge`로 커진다). 격자 최소 폭 7 × 44 = 308. 월 이동 버튼 44 원(`control.buttonHeight.medium`). 셀 `content`는 날짜 아래 label 한 줄 높이 | `calendarRecipe.sizes`·`header.navButton`, `.hjm-calendar__grid` |
@@ -4,12 +4,17 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-07
8
8
  - 근거: recipe `cardRecipe`(`src/card.ts`), 바탕 `surfaceRecipe`(`src/base-recipes.ts`). 별도 계약 문서는 없다
9
9
  - 스토리북: `배포/컴포넌트/데이터 표시/카드`
10
10
 
11
11
  ## 언제 쓰나
12
12
 
13
+ 문서 metadata·미리보기·내보내기 상태는 실험 구성 [DocumentResource](../compositions/document-resource.md)를
14
+ 먼저 대조한다. 2026-10-07 파일 사례에서 일반 카드만으로 다운로드/저장 상태까지 동등하다고
15
+ 판단할 수 없음을 확인해 별도 구성으로 연결했다.
16
+
17
+
13
18
  제목·설명·본문·행동이 한 덩어리로 읽히는 독립된 콘텐츠 단위에 쓴다. 주문 요약, 설정 묶음,
14
19
  미리보기처럼 화면 안에서 경계가 보여야 하는 블록이 여기에 속한다. 위에서부터
15
20
  `media` → `leading`+`title`+`description` → `children` → `actions` 순서가 고정이다.
@@ -30,6 +35,7 @@
30
35
  | 이름 | 역할 | Web | Native |
31
36
  | --- | --- | --- | --- |
32
37
  | `Card` | 기본 | `@hjmds/react`, `/display` | `@hjmds/react-native`, `/data-display` |
38
+ | `DocumentResource` | 실험 문서 구성 | `@hjmds/react/document-resource` | `@hjmds/react-native/document-resource` |
33
39
 
34
40
  ## 최소 사용 예
35
41
 
@@ -68,7 +74,7 @@ import { Button } from "@hjmds/react-native/actions";
68
74
  | `tone` | `default` · `raised` · `accent` · `sunken` · `subtle`(Surface tone) | `default` | — |
69
75
  | `bordered` | `boolean` | `true` | Surface 기본 `false`와 다르다 |
70
76
  | `padding` | spacing 이름 | `md`(16) | — |
71
- | `radius` | radius 이름 | `lg`(16) | — |
77
+ | `radius` | radius 이름 | `lg`(foundation 16) | 선택한 designProfile의 같은 radius 역할로 frame과 media clip을 함께 변경 |
72
78
  | `selected` | `boolean` | `false` | `true`면 tone이 `accent`로 바뀐다(`cardRecipe.selectedTone`) |
73
79
  | `headingLevel`(Web만) | `2` · `3` · `4` | `3` | 제목이 `h3`로 렌더되므로 문서 위계에 맞춰 고른다 |
74
80
  | `title` · `description` · `leading` · `media` · `actions` · `children` | `ReactNode` | — | 슬롯. 순서는 HJM이 고정한다 |
@@ -76,6 +82,19 @@ import { Button } from "@hjmds/react-native/actions";
76
82
 
77
83
  Card 자체에는 콜백이 없다. 누름 행동은 `actions`의 Button(Web `onClick: (event: MouseEvent<HTMLButtonElement>) => void`, Native `onPress`)이 갖는다.
78
84
 
85
+ 2026-10-07 [확장 카드·그리드 대조](../../../../../docs/plans/aceternity-interaction-adoption-2026-10-07.md)에서
86
+ 카드와 상세 표시를 별도 새 카드 엔진으로 복제할 필요가 없는 경우를 확인했다. 상세는 `actions`의
87
+ Button으로 [Dialog](dialog.md)를 열고, 위치 연결이 필요하면 Dialog의 `motionOrigin`을 사용한다.
88
+ 같은 흐름 안에서 내용을 늘릴 때는 [Collapsible](collapsible.md)를 고른다. 이미지·제목이 각각 이동하는
89
+ shared-element 효과와 일반 상세 Dialog는 다르며, 전자를 제공한다고 안내하지 않는다.
90
+
91
+ ### 프로필 표면 질감(미게시)
92
+
93
+ 선택한 `designProfile.material.surface`를 자동으로 읽는다. glass는 지원하는 Web에서 실제 배경 흐림,
94
+ clay는 안쪽 그림자를 사용한다. Native는 루트 Provider의 선택형 `surfaceEffects` host와 inset capability를
95
+ 한 번 등록한다. 사용할 수 없거나 투명도 줄이기 설정이면 불투명 표면을 유지한다. 효과와 입력은 서로 다른
96
+ subtree라 질감을 바꾸거나 host가 실패해도 본문/초안은 유지한다. [범위·등록·대비 계약](../../design-profile.md#surfacecard의-유리클레이-질감)을 따른다.
97
+
79
98
  ## 배치
80
99
 
81
100
  | 항목 | 값 | 근거 |
@@ -102,9 +121,13 @@ Card 자체에는 콜백이 없다. 누름 행동은 `actions`의 Button(Web `on
102
121
  | root | `<article>` | `View`(Surface) |
103
122
  | 제목 | `h{headingLevel}` | `Text` + `accessibilityRole="header"`, 수준 지정 없음 |
104
123
  | 배치 | `layoutStyle` | `layoutStyle` |
105
- | 내용 clip | tone의 `clipsContent`(`raised`는 그림자 때문에 clip 안 함) | 내부 `View`가 항상 clip, 그림자는 바깥에 남는다 |
124
+ | 내용 clip | tone의 `clipsContent`(`raised`는 그림자 때문에 clip 안 함) | 내부 `View`가 항상 clip, 그림자는 바깥에 남는다. frame과 clip이 같은 Provider radius token을 사용 |
106
125
 
107
126
  ## 함정
108
127
 
128
+ - `radius="lg"`는 모든 테마에서 16px이라는 뜻이 아니다. foundation에서는 16이고, designProfile이
129
+ 등록되면 그 테마의 `tokens.radius.lg`를 쓴다. 2026-10-07 카드 갤러리 비교에서 Native의 바깥
130
+ Surface만 테마를 따르고 내부 media clip은 foundation 값을 쓰는 누락을 발견해 같은 token으로 연결했다.
131
+
109
132
  - `selected`는 tone만 바꾼다. Web은 `data-state="selected"` 속성뿐이고 Native는 접근성 state를
110
133
  알리지 않는다. 선택 가능한 카드 목록이라면 선택 상태를 문구나 다른 컨트롤로도 전달한다.
@@ -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
  - 근거: [Carousel](../../carousel.md), `CarouselMotion`은 [선택형 어댑터](../../optional-adapters.md), contract `src/carousel.ts`
9
9
  - 스토리북: `배포/컴포넌트/데이터 표시/캐러셀`
10
10
 
@@ -99,6 +99,15 @@ import { Carousel } from "@hjmds/react-native/carousel";
99
99
  - 슬라이드 id는 유일하고 앞뒤 공백이 없어야 한다. 빈 배열은 던지므로 로딩·빈 상태는 마운트 전에 제품이 처리한다.
100
100
  - `composeAccessibleName`의 어순·조사는 제품 문구다. HJM은 위치 정보만 넘긴다.
101
101
  - 슬라이드 안의 시각 콘텐츠(카드·이미지)는 제품 소유다. 컨트롤·점·접근성 구조는 HJM 소유라 다시 만들지 않는다.
102
+ - 2026-10-07 [Motion 네 변형 검토](../../../../../docs/qa/2026-10-07-motion-reference-page-review.md)와
103
+ [Cedar Filmstrip 대조](../../../../../docs/qa/2026-10-07-reference-parallel-c.md)에서 여러 카드가
104
+ 동시에 보이는 strip은 단일 active panel과 다른 계약임을 확인했다. 현재 Carousel에 임의
105
+ `basis-1/3`/translate 스타일을 덮어 strip을 제공하지 않는다. 선택 버튼의 초점·현재 위치 의미,
106
+ 끝 정렬·폭 변경·부분 노출 항목의 초점/읽기 순서를 함께 갖춘 명시적 구성 확장이 필요한 후보다.
107
+ - 두 renderer는 숨겨진 슬라이드에도 `renderSlide`를 호출한다. 숨김은 네트워크 요청 취소나
108
+ 자식 unmount가 아니다. 권한 확인이 필요한 사진을 현재 페이지만 읽는 제품은 제어된
109
+ `currentKey`와 비교해 선택되지 않은 콘텐츠를 `null`로 반환한다. 2026-10-07 Utilverse의
110
+ 선택 페이지 단독 조회·로그아웃 후 캐시 제거 계약을 대조하며 확인한 경계다.
102
111
  - 배치는 `layoutStyle`로만 한다. Native `style`은 deprecated(개발 모드 1회 경고, 다음 major 제거)다.
103
112
 
104
113
  ## 플랫폼 차이
@@ -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-patterns.md), Web·Native `src/screens.tsx`·`src/screen-flows.tsx`; 기존 개별 지침을 새 규격으로 통합. 예제 스토리는 2026-10-06 사용자 승인으로 스토리북 배포([승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정)). 스토리북 배포는 API 게시가 아니다(`적용` 참고)
9
9
  - 스토리북: `배포/구성/정보 표시/대화 메시지`, `배포/화면/소통/채팅`
10
10
 
@@ -103,6 +103,11 @@ import { Text } from "@hjmds/react-native/primitives";
103
103
  답장·반응은 접근성 동작(`reply`·`activate`)으로 남는다. 2026-10-06 리뷰에서 행 이름(`author`)과 반응 이름(`picker.label`)이
104
104
  본문을 가려 화면 낭독기가 메시지를 읽지 못했다.
105
105
  - 타임라인 전체를 live region으로 감싸지 않는다(과거 메시지 로딩 때 읽기 순서를 가로챈다).
106
+ - 반응 **선택 메뉴**와 현재 반응 **집계 버튼**은 다른 행동이다. 집계를 눌러 메시지 메뉴를 열 때는
107
+ `actions` 슬롯에 Button을 공급할 수 있다. 본문 안에 집계 버튼을 넣는다면 사진·링크가 없어도
108
+ `interactiveContent=true`가 필요하다. 2026-10-07 Utilverse 소스 대조에서 사진/도구만 검사해
109
+ 집계 버튼이 부모 접근성 그룹에 들어갈 수 있는 경로를 발견했다. `actions`에 둘 때도 긴 집계와
110
+ 시각이 좁은 폭·큰 글자에서 함께 읽히고 조작 가능한지 확인한다.
106
111
 
107
112
  ## 플랫폼 차이
108
113
 
@@ -120,3 +125,8 @@ import { Text } from "@hjmds/react-native/primitives";
120
125
  (부작용·고유 ref를 두지 않는다).
121
126
  - `reactions`의 `options`와 `more.options`는 id가 합쳐서 유일하고 emoji·label이 비어 있지 않아야 한다. `value`가
122
127
  목록에 없는 id면 렌더 중 `TypeError`가 난다(`validateReactions`). 서버가 모르는 반응을 돌려줄 때를 대비한다.
128
+
129
+
130
+ ### 고정 아이콘과 큰 글자
131
+
132
+ 2026-10-06 최근 검색 삭제 기호가 큰 글자에서 잘린 재현에 따라 Native 내장 삭제·메뉴 기호는 고정 아이콘 틀의 크기를 유지한다. 주변 제목·라벨은 계속 확대한다. Chip의 체크와 Toast 닫기는 기존 비확대 경로를 유지하며 회귀 검사에 포함한다. 제품이 전달한 아이콘 슬롯은 제품이 같은 조건을 검증한다.
@@ -105,3 +105,8 @@ import { Chip } from "@hjmds/react-native/inputs";
105
105
  타입 오류가 나고, Native의 `onPress(next, event)`를 Web에 넘기면 첫 인자가 이벤트다.
106
106
  - 1.13.0 이하 Native Chip은 높이가 고정(`height` 36)이라 큰 글자에서 라벨 아래가 잘렸다(2026-10-06 utilverse 적용, SearchScreen
107
107
  `필터` 칩·추천 검색어 칩, accessibility-large). 1.13.1부터 Button처럼 최소 높이다. 제품이 `layoutStyle`로 높이를 다시 고정하지 않는다.
108
+
109
+
110
+ ### 고정 아이콘과 큰 글자
111
+
112
+ 2026-10-06 최근 검색 삭제 기호가 큰 글자에서 잘린 재현에 따라 Native 내장 삭제·메뉴 기호는 고정 아이콘 틀의 크기를 유지한다. 주변 제목·라벨은 계속 확대한다. Chip의 체크와 Toast 닫기는 기존 비확대 경로를 유지하며 회귀 검사에 포함한다. 제품이 전달한 아이콘 슬롯은 제품이 같은 조건을 검증한다.
@@ -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
  - 근거: [Code block](../../code-block.md), contract `src/code-block.ts`
9
9
  - 스토리북: `배포/컴포넌트/데이터 표시/코드 블록`
10
10
 
@@ -78,6 +78,10 @@ import { CodeBlock } from "@hjmds/react-native/code-block";
78
78
 
79
79
  ## 배치
80
80
 
81
+ Native 코드 제목은 `tokens.fontFamily.ui`, 선택 가능한 원문과 그 token span은 `tokens.fontFamily.code`를 읽는다. 주변 UI와 코드 원문의 서체 역할을 구분하고 코드 본문의 LTR 순서는 유지한다.
82
+
83
+ 양 플랫폼의 코드 본문은 프로필 `fontFamily.code`와 `typography.body`를 읽고 구문 span은 상속한다. Native의 기본 monospace 의도는 iOS Menlo/Android monospace로 번역하고 custom font 등록은 제품이 한다. 코드 본문은 RTL 화면에서도 LTR 읽기 순서를 유지한다.
84
+
81
85
  | 항목 | 값 | 근거 |
82
86
  | --- | --- | --- |
83
87
  | 크기 | 부모 폭을 채우고 높이는 코드 줄 수가 정한다. 모서리 `radius.lg` 16, 배경 `surface-alt` | `react/src/code-block.tsx`, `react-native/src/code-block.tsx` |
@@ -107,5 +111,13 @@ import { CodeBlock } from "@hjmds/react-native/code-block";
107
111
  ## 함정
108
112
 
109
113
  - 복사는 화면의 주 행동과 경쟁하지 않도록 `ClipboardButton tone="secondary" size="small"`을 쓴다. Web 예제도 이 구성을 따른다.
110
- - `ClipboardButton`은 `navigator.clipboard`가 거부되면 상태를 바꾸지 않고 `onCopyError`만 부른다. 이 콜백을 비워 두면
114
+ - `ClipboardButton`은 `navigator.clipboard`가 거부되면 성공 상태로 바꾸지 않고 `onCopyError`만 부른다. 이 콜백을 비워 두면
111
115
  사용자는 실패를 알 수 없다.
116
+
117
+ 2026-10-07 명령 기록 후보를 구현하며 지연 OS 응답을 검증한 결과, 이전 원문의 복사 성공/오류가
118
+ 새 원문과 언마운트 뒤에도 callback을 호출하고 중복 쓰기를 허용했다. 미게시(1.14.0 이후) 후속
119
+ 수정은 OS 쓰기 동안 Button loading/aria-busy/aria-disabled로 잠그고 키보드 포커스를 유지한다.
120
+ 원문이 바뀌어도 진행 중 OS 쓰기는 취소할 수 없으므로 완료까지 새 쓰기를 막는다. 이전 결과의
121
+ 성공 표시·callback은 무시하고 완료 뒤 현재 원문을 다시 복사할 수 있다. value가 바뀔 때마다
122
+ 세대를 나누므로 A→B→A 변경도 이전 요청의 성공으로 오인하지 않는다. 제품은 이미 시작된
123
+ OS 쓰기 자체가 취소됐다고 안내하지 않는다. [검증 기록](../../../../../docs/qa/2026-10-07-command-records.md).
@@ -4,14 +4,14 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-07
8
8
  - 근거: [Collapsible](../../collapsible.md), `FolderPreview`는 [Folder preview](../../folder-preview.md), recipe `collapsibleRecipe`(`src/collapsible.ts`)
9
9
  - 스토리북: `배포/컴포넌트/데이터 표시/접기와 펼치기` · `배포/컴포넌트/데이터 표시/폴더 미리보기`
10
10
 
11
11
  ## 언제 쓰나
12
12
 
13
13
  이웃 없이 혼자 접었다 펴는 한 덩어리에 쓴다. "더 보기", 필터 패널, 접히는 본문이 여기에 속한다.
14
- 닫히면 내용은 트리에서 빠진다.
14
+ 기본값에서는 닫히면 내용은 트리에서 빠진다. `keepMounted`는 로컬 입력을 보존하고 hidden/display:none 접근성 경계로 내용을 숨긴다.
15
15
 
16
16
  ## 쓰지 않을 때
17
17
 
@@ -81,6 +81,8 @@ import { FolderPreview } from "@hjmds/react-native/folder-preview";
81
81
 
82
82
  ## 배치
83
83
 
84
+ FolderPreview의 Native preview `md`와 앞면 `lg`는 Provider radius를 읽는다. 기존 Collapsible·전환·장식 접근성 제외를 유지한다.
85
+
84
86
  | 항목 | 값 | 근거 |
85
87
  | --- | --- | --- |
86
88
  | 크기 | 트리거는 폭을 꽉 채운다. 트리거 최소 높이 44(`control.minTouchTarget`, 두 플랫폼. Native는 미게시(1.12.1 이후)), Web 위아래 `spacing.xs` 8. `FolderPreview` 표지는 최대 260×160 고정 그림 | `.hjm-collapsible__trigger`, `react-native/src/collapsible.tsx`, `react/src/folder-preview.tsx` |
@@ -110,3 +112,9 @@ import { FolderPreview } from "@hjmds/react-native/folder-preview";
110
112
 
111
113
  - 현재 랜딩 스토리(Web·Native `Landing.stories.tsx`)는 FAQ 여러 항목을 `Collapsible` 반복으로 그린다. 서로 연결된
112
114
  여러 항목은 [Accordion](accordion.md)이 규칙이다(한 번에 하나 펼침·키보드 이동·heading 위계를 Accordion이 소유한다).
115
+
116
+ ### 프로필 도구의 표현
117
+
118
+ 2026-10-07 테마 전환 시 입력이 사라지는 문제를 피하려고 `presentation="inline"`과 `keepMounted`를 추가했다.
119
+ inline은 트리거를 숨기고 항상 내용을 펼친다. 기본 disclosure는 기존 open/defaultOpen 규칙을 유지한다.
120
+ keepMounted가 있어도 닫힌 내용은 포커스·스크린리더·터치 대상에서 제외한다. 단순 read-only 내용은 기존 기본값을 쓴다.
@@ -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 `comboboxRecipe`(`src/component-recipes.ts`), behavior `combobox`
9
9
  - 스토리북: `배포/컴포넌트/입력/검색형 선택`
10
10
 
@@ -81,8 +81,19 @@ import { Combobox } from "@hjmds/react-native/forms";
81
81
  | Native `asyncState` | `{ status: "idle" }` · `{ status: "loading" \| "loadingMore" \| "empty" \| "error"; message: string }` | — | 결과 시트의 상태 문구. `error`면 `onRetry: () => void`·`retryLabel`로 다시 시도 버튼이 생긴다 |
82
82
  | `layoutStyle` | margin·width·flex·`alignSelf` | — | 필드 바깥 배치 |
83
83
 
84
+ ### 디자인 프로필 상속
85
+
86
+ 2026-10-07 테마 소비 경로 점검에서 고정 foundation/recipe 값이 남은 곳을 보완했다.
87
+ 모서리의 recipe 역할은 유지하고 값은 가장 가까운 Provider의 `designProfile.tokens.radius`를
88
+ 읽는다. Dialog/AlertDialog/Sheet/일반 Toast의 그림자는 `tokens.shadow.floating`을 읽으며
89
+ 프로필 없는 소비자의 기본값은 유지한다. 상태·초안·선택·Modal teardown은 이 축의 소유가 아니다.
90
+ 플랫폼 근사와 미검증 범위는 [프로필 계약](../../design-profile.md#오버레이선택-입력의-프로필-연결-보완)을 따른다.
91
+
92
+
84
93
  ## 배치
85
94
 
95
+ Native는 제품 프로필의 `tokens.fontFamily.ui`를 실제 텍스트/입력 host에 연결한다. 기본 UI stack은 OS 서체를 유지하고, 제품이 지정한 첫 named font의 등록·글리프 확인은 제품이 맡는다.
96
+
86
97
  | 항목 | 값 | 근거 |
87
98
  | --- | --- | --- |
88
99
  | 크기 | 입력 높이 `medium` 44(`fieldFrameContract`) · `large` 52(`control.buttonHeight.large`), 폭은 폼 열을 채운다. Web 목록 최대 높이 22.5rem(360, recipe `popover.maxHeight`), 선택지 최소 높이 `compact` 44 · `comfortable` 56(3.5rem). Native 시트 최대 높이 화면의 75% | `selectRecipe.sizes`·`popover`, `.hjm-combobox__listbox`·`__option`, `react-native/src/forms.tsx` |
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-07
8
8
  - 근거: [CommandPalette contract](../../command-palette.md), recipe `commandPaletteRecipe`(`src/command-palette.ts`)
9
9
  - 스토리북: `배포/컴포넌트/오버레이/명령 검색`
10
10
 
@@ -54,8 +54,8 @@ import { CommandPalette } from "@hjmds/react/command-palette";
54
54
  />
55
55
  ```
56
56
 
57
- `paletteSource`는 `{ sections: [{ id: "recent", label: t("palette.recent"), items: recentItems }, …] }`처럼 만들고
58
- `useMemo`로 고정한다. 기본(`queryState` 없음)은 renderer가 `query`로 항목의 `label`·`textValue`를 부분 일치로 거른다.
57
+ `paletteSource`는 `{ sections: [{ id: "recent", label: t("palette.recent"), items: recentItems }, …] }`처럼 만든다.
58
+ 기본(`queryState` 없음)은 renderer가 `query`로 항목의 `label`·`textValue`를 부분 일치로 거른다.
59
59
  항목은 `{ id, label, textValue, description?, shortcut?, disabled?, tone? }`이다. 항목의 label·description·shortcut과
60
60
  섹션 label은 제품 i18n에서 만든다.
61
61
 
@@ -116,10 +116,15 @@ Native: 없음.
116
116
 
117
117
  ## 함정
118
118
 
119
- - 현재 renderer는 `query`·`source`·`queryState`의 참조가 바뀔 때마다 활성 행을 첫 활성 항목으로 되돌린다(`useEffect` 의존성).
120
- `source`나 `queryState`를 JSX 안에서 객체 리터럴로 만들면 렌더마다 참조가 바뀌어 화살표 키·마우스로 옮긴 활성 행이
121
- 바로 첫 행으로 돌아간다. 둘 다 `useMemo`로 고정한다.
119
+ - 현재 renderer는 `query`가 바뀔 때 활성 행을 첫 활성 항목으로 되돌린다. `source`·`queryState`의
120
+ 새 참조만으로는 키보드 선택을 초기화하지 않는다. 선택한 ID가 결과에서 없어지면 첫 활성 행으로
121
+ 돌아간다. 2026-10-07 실제 source의 의존성과 부모 재렌더 회귀를 대조해 예전 참조 경고를 정정했다.
122
122
  - 닫을 때 `query`를 지우는 것은 제품 몫이다. 비우지 않으면 다음에 열 때 이전 검색어가 남는다.
123
123
  - `filtering: "external"`인데 `resultQuery`를 갱신하지 않으면 결과가 보이기만 하고 Enter·클릭이 먹지 않는다(`aria-disabled`).
124
124
  - 현재 Web 스토리는 제품 쪽에서 `label.includes(query)`로 직접 거르고 결과가 없을 때 `asyncState` `empty`로 안내하며
125
125
  `closeLabel`이 없다. 새 코드는 로컬 필터링 기본값과 `emptyMessage`·`closeLabel`을 쓴다.
126
+
127
+ 2026-10-07 동일한 메뉴 입력 패턴을 검사해 늦은 mouseenter가 키보드로 선택한 명령을 되돌리는
128
+ 문제를 재현했다. 미게시(1.14.0 이후) 수정은 실제 마우스 이동으로만 활성 행을 바꾸므로 팝업
129
+ 배치의 경계 이벤트는 키보드 선택을 덮지 않는다. 검색 초기화·비활성/오래된 결과 잠금·클릭 실행은
130
+ 유지한다. [검증 기록](../../../../../docs/qa/2026-10-07-command-records.md).
@@ -4,14 +4,14 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-07
8
8
  - 근거: contract `src/content-transition.ts`(`resolveContentTransition`)
9
9
  - 스토리북: `배포/컴포넌트/시각 효과/내용 전환`
10
10
 
11
11
  ## 언제 쓰나
12
12
 
13
13
  같은 자리의 내용이 상태에 따라 바뀔 때(필터 결과 패널, 단계별 본문) 새 내용이 짧게 나타나도록 감싼다.
14
- `stateKey`가 바뀔 때만 움직이고, 화면에는 현재 내용 하나만 남는다.
14
+ 기본적으로 `stateKey`가 바뀔 때 움직이고, 화면에는 현재 내용 하나만 남는다.
15
15
 
16
16
  ## 쓰지 않을 때
17
17
 
@@ -66,21 +66,23 @@ import { ContentTransition } from "@hjmds/react-native/content-transition";
66
66
 
67
67
  | prop | 값 | 기본값 | 설명 |
68
68
  | --- | --- | --- | --- |
69
- | `preset` | `fade` · `rise` · `slide` · `scale` | `fade` | `rise`는 아래 12에서, `slide`는 가로 16(RTL이면 반대), `scale`은 0.96에서 시작 |
69
+ | `preset` | `fade` · `rise` · `slide` · `scale` | provider `designProfile.interactions.contentTransition`, 프로필 없으면 `fade` | 명시한 prop이 프로필보다 우선. `rise`는 아래 12에서, `slide`는 가로 16(RTL이면 반대), `scale`은 0.96에서 시작 |
70
70
  | `motion` | `system` · `none` | `system` | `system`은 reduced motion을 따르고 `none`은 항상 즉시 교체 |
71
71
  | `stateKey` | `string` | — (필수) | 바뀔 때만 새 내용이 나타난다 |
72
+ | `enterOnMount` | boolean | false | 미게시: 새 데이터 항목이 처음 추가될 때도 등장 전환. 이미 표시한 항목은 stable key를 유지한다 |
73
+ | `animateHeight` | boolean | false | 내용의 측정 높이가 바뀔 때 주변 틀 높이를 전환한다. 모션 감소에서는 즉시 반영 |
72
74
  | Web `focusTarget` | `RefObject<HTMLElement \| null>` | — | 바뀌기 전 포커스가 안에 있었으면 전환 뒤 이 요소로 옮긴다 |
73
75
  | Web `layoutStyle` | 배치 전용 style | — | 바깥 고정 wrapper에 붙는다(키가 바뀌는 안쪽 패널이 아님) |
74
76
 
75
77
  콜백 prop은 없다. `TextTransition`은 `text: string`을 `stateKey`로 쓴다.
76
78
 
77
- - 첫 렌더는 움직이지 않는다. 시간은 `motion.normal`, 곡선은 `easing.enter` 토큰이다.
79
+ - 기본 첫 렌더는 움직이지 않는다. `enterOnMount`는 새로 추가한 항목에만 선택하며 초기 서버 렌더 목록을 지연시키는 용도로 쓰지 않는다. 시간은 `motion.normal`, 곡선은 `easing.enter` 토큰이다.
78
80
 
79
81
  ## 배치
80
82
 
81
83
  | 항목 | 값 | 근거 |
82
84
  | --- | --- | --- |
83
- | 크기 | 자체 크기·여백이 없다. Web은 `div` 두 겹(블록), Native는 `Animated.View` 하나로 감싼다 | `packages/react/src/content-transition.tsx`, `packages/react-native/src/content-transition.tsx` |
85
+ | 크기 | 자체 크기·여백이 없다. Web은 배치 wrapper·높이 틀·측정 flow·keyed panel, Native는 바깥 Animated.View·측정 View·표현 Animated.View로 감싼다 | `packages/react/src/content-transition.tsx`, `packages/react-native/src/content-transition.tsx` |
84
86
  | 간격 | 자체 간격이 없다. 위아래 간격은 감싸는 [Stack](stack.md) 등이 정한다. 움직임 폭(세로 12·가로 16·0.96배)만큼 래퍼 밖으로 잠깐 밀려 나오므로 바로 옆 요소와 간격을 둔다 | `src/content-transition.ts` |
85
87
  | 순서·정렬 | 바뀌는 영역 하나만 감싼다(결과 패널, 단계 본문). 필터 막대·탭·제목처럼 그대로 남는 부분은 바깥에 둔다 | — |
86
88
  | 고정·스크롤 | Native 래퍼에는 `flex`가 없어 남은 높이를 채우지 않는다. 화면 높이를 채워야 하는 내용이면 바깥 View가 높이를 정한다 | `packages/react-native/src/content-transition.tsx` |
@@ -99,3 +101,25 @@ import { ContentTransition } from "@hjmds/react-native/content-transition";
99
101
  | 포커스 복원 | `focusTarget`: 바뀌기 전 포커스가 안에 있었으면 그 요소로 옮긴다 | 없음 |
100
102
  | 앱이 백그라운드로 감 | 해당 없음 | 진행 중 전환을 멈추고 바로 표시 |
101
103
  | 배치 prop | `layoutStyle`(바깥 wrapper) | 없음 |
104
+
105
+ ### 측정 기반 overlay 전환 준비 (미게시)
106
+
107
+ `@hjmds/design-contracts/content-transition`의 `resolveOriginTransition(origin, destination, reducedMotion)`은
108
+ 같은 물리 viewport 좌표계의 `TransitionRect { x, y, width, height }` 두 개를 받는다.
109
+ 도착 경계의 중심 기준 translateX/Y와 scaleX/Y를 반환한다. RTL 좌표를 다시 뒤집지 않는다.
110
+ 미측정·0 크기·비유한 값·계산 overflow·모션 감소에서는 null로 일반 overlay 표현을 유지한다.
111
+
112
+ 이 함수 자체는 renderer나 상태 엔진이 아니다. Dialog의 선택적 motionOrigin 표현이
113
+ 이 계산을 사용한다([Dialog 지침](dialog.md)). 기존 ContentTransition의
114
+ 단일 subtree 전환과 Native SharedTransitionElement의 라우터 전환을 대체하지 않는다.
115
+ trigger 측정 시점과 같은 좌표계 보장은 제품이, 취소·재열기·exit presence·초점 복귀는
116
+ 해당 renderer가 소유한다. Motion Primitives의 원본에서 닫기 후 초점 손실과 작성 예제의 초안 소실을
117
+ 확인했으므로 geometry만 흡수하고 기존 HJM overlay 상태 엔진을 유지한다.
118
+
119
+
120
+ ### 실제 목록 데이터의 등장
121
+
122
+ [추가해도 유지되는 목록](../compositions/live-list.md)은 stable id를 React key와 stateKey로
123
+ 쓰고 새 항목에만 enterOnMount를 켠다. 목록 전체의 stateKey를 배열 직렬화 값으로 바꾸면 Web의
124
+ 입력 subtree가 교체되므로 하지 않는다. 데이터는 즉시 전달하며 stagger 타이머로 감추지 않는다.
125
+ 삭제된 항목은 즉시 제거한다. 퇴장 사본을 남기지 않으므로 키보드·터치 대상도 함께 사라진다.
@@ -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
  - 근거: [ContextMenu](../../context-menu.md), [선택 어댑터](../../optional-adapters.md), recipe `menuRecipe`
9
9
  - 스토리북: `배포/컴포넌트/탐색/상황별 메뉴`
10
10
 
@@ -134,3 +134,9 @@ import { Pressable } from "react-native";
134
134
  | 항목 `textValue`·`shortcut` | 있음 | 없음 |
135
135
  | 열림 알림 | 없음 | `onOpenChange(open)` |
136
136
  | 성숙도 | stable | 실험적 어댑터(기기 증거 전까지 canonical unsupported) |
137
+
138
+ 2026-10-07 Web 키보드 회귀에서 팝업이 정지한 커서 아래에 배치된 뒤 mouseenter가 활성 항목을
139
+ 첫 항목으로 되돌리는 문제를 재현했다. 미게시(1.14.0 이후) 수정은 실제 마우스 이동으로 항목을
140
+ 바꾸며, 늦은 경계 이벤트만으로 키보드 선택을 덮지 않는다. 클릭·키보드 실행·포커스 복귀와
141
+ Native OS 어댑터는 기존 계약을 유지한다. 고정 대기 시간을 추가하지 않고 입력 의도를 구분했다.
142
+ [검증 기록](../../../../../docs/qa/2026-10-07-command-records.md).
@@ -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
  - 근거: [DatePicker](../../date-picker.md), 격자는 [Calendar](../../calendar.md), recipe `datePickerRecipe`(`src/date-picker.ts`)
9
9
  - 스토리북: `배포/컴포넌트/입력/날짜 선택`
10
10
 
@@ -95,6 +95,8 @@ import { DatePicker } from "@hjmds/react-native/date-picker";
95
95
 
96
96
  ## 배치
97
97
 
98
+ Native trigger 모서리는 `datePickerRecipe.frame.radius` 역할을 Provider의 `tokens.radius`에서 읽는다. 날짜·열림·선택 계약은 유지한다.
99
+
98
100
  | 항목 | 값 | 근거 |
99
101
  | --- | --- | --- |
100
102
  | 크기 | 트리거 높이: `medium` 44 · `large` 52(`datePickerRecipe.sizes`, 두 플랫폼), 좌우 여백 16 · 20. 2026-10-06까지 렌더 값이 Web 44·56, Native 48·56이었다(1.12.1 이후 미게시). 지우기 버튼 44×44. Web 팝오버 폭 `min(22.5rem, 100vw − 2rem)`(최대 360). 날짜 셀 44, 7열 | `datePickerRecipe.sizes`, `packages/react/src/styles.css` `.hjm-date-picker*`, `packages/react-native/src/date-picker.tsx`, `src/calendar.ts` |
@@ -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
  - 근거: [DesignSystemProvider](../../design-system-provider.md), [브랜드 경계](../../brand-boundary.md)(브랜드 규칙 단일 원본), [테마 주입](../../theming.md), [팔레트 결정](../../theme-palette.md)
9
9
  - 스토리북: `배포/컴포넌트/기반 기능/디자인 시스템 설정`
10
10
 
@@ -77,10 +77,12 @@ expect(checkBrandPaletteContrast(PRODUCT_BRAND_PALETTE)).toEqual({ light: [], da
77
77
  | `reducedMotion` | `true` · `false` | `false` | — |
78
78
  | `minimumVisualTarget` | `true` · `false` | `false` | — |
79
79
  | `brandPalette` | `{ light?, dark? }` | — | 각각 `ThemeColors` 17개 key 중 필요한 것만 넘긴다(부분 병합). 상태 강조색은 덮을 수 없다. 중첩 Provider는 가장 가까운 상위의 값을 물려받는다 |
80
- | `value` | `DesignSystemProviderValue`(`resolveDesignSystemProviderValue` 결과) | — | 테스트·스토리·임베딩용. 환경 prop·`brandPalette`와 함께 쓸 수 없고(타입이 막는다), 주면 OS theme·모션 관찰과 상위 `brandPalette` 상속이 멈춘다 |
80
+ | `designProfile` | `HjmDesignProfile` | 가장 가까운 상위 프로필 또는 없음 | 미게시 실험. `hjmDesignPresets` 또는 `defineHjmDesignProfile` 결과만 넣는다. [프로필 계약](../../design-profile.md)의 토큰·질감·전환·구성·화면 기본값을 상속한다 |
81
+ | `value` | `DesignSystemProviderValue`(`resolveDesignSystemProviderValue` 결과) | — | 테스트·스토리·임베딩용. 환경 prop·`brandPalette`·`designProfile`과 함께 쓸 수 없고(타입이 막는다), 주면 OS theme·모션 관찰과 상위 `brandPalette` 상속이 멈춘다 |
82
+ | `surfaceEffects`(Native) | `{ renderBackdrop?, insetShadows? }` | 상위 host 또는 없음 | 미게시 실험. 실제 blur host·inset capability를 한 번 등록하며 `{}`로 하위 영역을 해제. [Surface 질감 계약](../../design-profile.md#surfacecard의-유리클레이-질감) |
81
83
  | `safeAreaInsets`(Native) | `{ top?, right?, bottom?, left? }`(pt) | `{}` | 보통 `useSafeAreaInsets()` 결과. 중첩 Provider는 가장 가까운 상위 값을 물려받는다 |
82
84
 
83
- - 이벤트·콜백 prop은 없다. 해석된 값은 Web `useHjmTheme()`, Native `useHjmNativeTheme()`로 읽는다.
85
+ - 제품 상태를 바꾸는 이벤트 prop은 없다. Native의 선택형 `surfaceEffects.renderBackdrop`은 장식 host만 공급한다. 해석된 값은 Web `useHjmTheme()`, Native `useHjmNativeTheme()`로 읽는다.
84
86
  - `layoutStyle`을 받지 않는다(Web `HjmProvider`는 `layoutStyle` 제외 15개 중 하나). Web 루트 `div`의 표면 처리는 `host`로 정한다.
85
87
 
86
88
  - 주지 않은 축은 상위 Provider → OS 신호 → 기본값 순이다.
@@ -97,8 +99,8 @@ expect(checkBrandPaletteContrast(PRODUCT_BRAND_PALETTE)).toEqual({ light: [], da
97
99
 
98
100
  ## 꼭 지킬 것
99
101
 
100
- - 제품 브랜드는 `brandPalette` prop으로만 넣는다. 전체 `value`를 손으로 조립하는 것은 테스트·임베딩용이다
101
- ([브랜드 경계 §1](../../brand-boundary.md#1-지원하는-경로는-brandpalette-하나다)).
102
+ - 색만 바꾸면 `brandPalette`, 표현·구성도 바꾸면 검증한 `designProfile`을 쓴다. 전체 `value`를 손으로 조립하는 것은 테스트·임베딩용이다
103
+ ([브랜드 경계 §1](../../brand-boundary.md#1-지원하는-제품-설정-경로)).
102
104
  - **Showcase·Theme Studio의 예시 색·자산·테마를 제품 기본값으로 복사하지 않는다**(2026-10-05 규칙). 색은 제품 목적과
103
105
  기존 디자인에서 정해 `brandPalette`의 semantic key로 연결하고, 로고·이미지·문구는 각 컴포넌트의 공개 슬롯으로 넘긴다.
104
106
  - 모든 브랜드 팔레트는 `checkBrandPaletteContrast` 결과가 빈 배열이어야 한다(MUST).
@@ -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
  - 근거: [Dialog](../../dialog.md), [명령형 오버레이](../../overlay-stack.md), recipe `dialogRecipe`(`src/component-recipes.ts`)
9
9
  - 스토리북: `배포/컴포넌트/오버레이/대화상자`
10
10
 
@@ -105,6 +105,7 @@ await handle.closed; // portal 제거·초점 복구 뒤. 다음 오버레이는
105
105
  | `size` | `small` · `medium` · `large` | `medium` | Native recipe 최대 폭 320 · 420 · 640. Web 최대 폭 28rem · 36rem · 48rem(아래 배치 표) |
106
106
  | `dismissible` | `true` · `false` | `true` | `false`면 닫기 버튼과 바깥·Escape/back 닫기가 사라진다 |
107
107
  | `busy` | `true` · `false` | `false` | 비동기 작업 중에는 `open`을 유지하고 `busy`로 반복 행동과 닫기를 막는다 |
108
+ | `motionOrigin` | `{ x, y, width, height }`(`TransitionRect`) | 없음 | 열기 직전 측정한 출발 영역. Web viewport/Native window의 물리 좌표를 쓴다. 1.14.0 양 renderer의 게시 타입에 포함. 선택적 실험 표현이며 기기·성능 검증 완료와 구분한다 |
108
109
  | `closeLabel` | 현지화 문자열 | — | 양쪽 필수. 닫기 버튼의 접근성 이름 |
109
110
  | `open`·`defaultOpen` | `boolean` | `false` | 제어형은 `open`+`onOpenChange`. Web 비제어형은 `trigger`가 필수다 |
110
111
  | `onOpenChange` | `(open: boolean, detail: { reason }) => void` | — | Web `reason`: `"trigger"`·`"close-action"`·`"escape"`·`"outside"`. Native: `"close-action"`·`"back"`·`"outside"`. `busy`이거나 `dismissible={false}`면 닫기 요청을 보내지 않는다 |
@@ -118,6 +119,15 @@ await handle.closed; // portal 제거·초점 복구 뒤. 다음 오버레이는
118
119
 
119
120
  - Dialog는 `layoutStyle`을 받지 않는다(Web 제외 15개 중 하나 — 포털로 가운데 그려져 배치할 흐름 안 루트가 없다).
120
121
 
122
+ ### 디자인 프로필 상속
123
+
124
+ 2026-10-07 테마 소비 경로 점검에서 고정 foundation/recipe 값이 남은 곳을 보완했다.
125
+ 모서리의 recipe 역할은 유지하고 값은 가장 가까운 Provider의 `designProfile.tokens.radius`를
126
+ 읽는다. Dialog/AlertDialog/Sheet/일반 Toast의 그림자는 `tokens.shadow.floating`을 읽으며
127
+ 프로필 없는 소비자의 기본값은 유지한다. 상태·초안·선택·Modal teardown은 이 축의 소유가 아니다.
128
+ 플랫폼 근사와 미검증 범위는 [프로필 계약](../../design-profile.md#오버레이선택-입력의-프로필-연결-보완)을 따른다.
129
+
130
+
121
131
  ## 배치
122
132
 
123
133
  | 항목 | 값 | 근거 |
@@ -174,3 +184,62 @@ Native, 큰 글자 또는 폭 < 480: [ 취소 ]
174
184
  - Native `DialogProps`는 RN `Modal` props(`style` 포함)를 그대로 받는다. 배치는 `contentStyle`로만 준다.
175
185
  - Native는 제어형과 비제어형을 렌더 중에 바꾸면 예외가 난다.
176
186
  - Native 기본 렌더러 예제는 실제 저장 서버가 없는 동기 완료 예시다. 제품에서는 저장 Promise를 반환하며, 예제의 영문 고정 문구는 제품 i18n 키로 치환한다. 닫기는 action이 요청하는 `close-action`이 맡는다.
187
+
188
+ ### 선택적인 트리거 형태 전환 (게시 API·실험 표현)
189
+
190
+ 2026-10-07 [확장 카드·그리드 대조](../../../../../docs/plans/aceternity-interaction-adoption-2026-10-07.md) 중
191
+ 실제 npm 1.14.0 양 renderer tarball의 `dist/overlays.d.ts`에서 이 옵션을 확인했다. 기존 미게시 표기를
192
+ 수정한다. 테마 변경은 모서리·서체·그림자를 상속하며, 이 옵션의 출발 위치는 제품이 측정한다.
193
+ `designProfile.interactions.contentTransition`이 Dialog의 출발 위치나 shared-element 전환을
194
+ 자동 선택하는 것은 아니다. 카드 안 상세 행동은 [Card](card.md)의 `actions` Button으로 제공한다.
195
+
196
+ 양 renderer의 `motionOrigin?: TransitionRect`는 열기 직전에 측정한 트리거의
197
+ `{ x, y, width, height }`를 받는다. Web은 getBoundingClientRect, Native는 measureInWindow로
198
+ 얻은 물리 viewport/window 좌표를 전달한다. 별도 HJM morph wrapper를 만들지 않고 기존
199
+ Dialog의 제목·닫기·busy·초점 복귀·초안 소유 계약을 그대로 사용한다.
200
+
201
+ Web은 닫기 전환이 끝나야 portal을 제거하고 onDismissComplete와 초점 복귀를 실행한다.
202
+ 닫는 동안 내용은 inert이며 빠른 재열기는 같은 subtree를 유지하고 오래된 완료를 취소한다.
203
+ Native는 실제 Modal 콘텐츠를 측정하며 콜백이 오지 않으면 기존 enter 시간 내 일반 표시로
204
+ 복귀한다. 뒤늦은 측정으로 닫힌 세션을 되살리지 않는다. 측정이 없거나 모션 감소이면
205
+ 공간 전환 없이 기존 표현을 쓴다. Native에서 진입 후 크기가 달라지면 이전 측정으로
206
+ 닫지 않고 일반 fade로 복귀한다. 다음 open에서 다시 측정한다. 이 옵션의 실험은 `버튼에서 이어지는 편집` 구성이다.
207
+
208
+ 초안은 제품 상태에 두고 닫을 때 삭제하지 않는다. 내용·화면 회전·키보드로 목적지 크기가
209
+ 달라지는 흐름은 실제 기기 검증 후 채택한다. 원점이 다른 좌표계를 혼합하지 않는다.
210
+
211
+ Web에서 버튼 위치와 상세 대화상자를 연결하는 최소 골격은 다음과 같다. 카드·이미지 전체에서
212
+ 이어지는 효과가 필요하면 동일 좌표계의 그 영역을 측정한다. 아래는 버튼 영역을 쓰는 예이며
213
+ 원본 이미지/제목을 복제해 이동시키는 shared-element 애니메이션은 아니다.
214
+
215
+ ```tsx
216
+ import { useRef, useState } from "react";
217
+ import type { TransitionRect } from "@hjmds/design-contracts/content-transition";
218
+ import { Button } from "@hjmds/react/actions";
219
+ import { Card } from "@hjmds/react/display";
220
+ import { Dialog } from "@hjmds/react/overlays";
221
+
222
+ function RecordDetail() {
223
+ const [open, setOpen] = useState(false);
224
+ const [origin, setOrigin] = useState<TransitionRect>();
225
+ const returnFocus = useRef<HTMLButtonElement>(null);
226
+ return <>
227
+ <Card title={t("record.title")} actions={<Button ref={returnFocus} onClick={event => {
228
+ // Measure on activation: cached mount-time bounds become stale after scroll/layout.
229
+ const { x, y, width, height } = event.currentTarget.getBoundingClientRect();
230
+ setOrigin({ x, y, width, height });
231
+ setOpen(true);
232
+ }}>{t("record.openDetail")}</Button>} />
233
+ <Dialog open={open} onOpenChange={setOpen} title={t("record.title")}
234
+ closeLabel={t("common.close")} returnFocusRef={returnFocus}
235
+ {...(origin ? { motionOrigin: origin } : {})}>
236
+ <RecordFields />
237
+ </Dialog>
238
+ </>;
239
+ }
240
+ ```
241
+
242
+ `RecordFields`의 초안은 제품의 편집 수명에 맞춰 Dialog 밖 상태에 둔다. Native에서는 측정 가능한
243
+ 실제 View의 `measureInWindow`로 활성화 시점의 영역을 얻고, 좌표를 얻지 못해도 일반 Dialog를 연다.
244
+ 측정 callback만 무기한 기다리면서 열기 행동을 막지 않는다. 버튼의 접근성 이름과 초점 복귀 대상을
245
+ 유지하고, 카드 전체를 누름 가능한 비의미적 View/div로 바꾸지 않는다.
@@ -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
  - 근거: [Composable decorative surfaces](../../effect-surface.md), 별도 보조 기능(supplemental)
9
9
  - 스토리북: `배포/컴포넌트/시각 효과/배경 시각 효과`
10
10
 
@@ -62,7 +62,7 @@ import { EffectSurface } from "@hjmds/react-native/effect-surface";
62
62
 
63
63
  | prop | 값 | 기본값 | 설명 |
64
64
  | --- | --- | --- | --- |
65
- | `descriptor.layers` | `mesh` · `glow` · `grain` | `["mesh"]` | 서로 다른 1~3개 |
65
+ | `descriptor.layers` | `mesh` · `glow` · `grain` · `noise` | `["mesh"]` | 서로 다른 1~4개. noise는 미게시 실험 |
66
66
  | `descriptor.intensity` | 0~1 | `0.22` | — |
67
67
  | `descriptor.period` | 2~120초 | `12` | — |
68
68
  | `descriptor.seed` | 문자열 | `"hjm"` | 빈 문자열 금지 |
@@ -118,3 +118,5 @@ import { EffectSurface } from "@hjmds/react-native/effect-surface";
118
118
  - Native의 장식 실패 대비(error boundary)는 **렌더 오류**만 잡는다. peer가 없어 모듈 해석이 실패하면 앱 번들 자체가
119
119
  깨진다. `tsc` 통과를 설치 확인으로 보지 않는다.
120
120
  - descriptor 검증 오류는 대비 밖에 있어 그대로 던져진다. 값 범위를 지킨다.
121
+
122
+ `noise`의 구현 차이·실험 조건은 [질감 비교](../compositions/texture-comparison.md)를 따른다. 기존 grain을 교체하지 않는다.
@@ -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
  - 근거: [Result와의 경계](../../result.md#emptystate와의-경계), [ContentState 범위 축](../../content-state.md), recipe `emptyStateRecipe`(`src/component-recipes.ts`)
9
9
  - 스토리북: `배포/컴포넌트/상태와 알림/빈 상태`
10
10
 
@@ -69,7 +69,9 @@ import { EmptyState } from "@hjmds/react-native/feedback";
69
69
  | Web `icon` · Native `illustration` | `ReactNode` | — | 장식, 접근성에서 숨김 |
70
70
  | `layoutStyle` | 배치 전용 style | — | 루트 배치. Native `style`·`illustrationStyle`·`titleStyle`·`descriptionStyle`·`actionStyle`은 deprecated |
71
71
 
72
- EmptyState 자체에는 콜백 prop이 없다.
72
+ EmptyState 자체에는 콜백 prop이 없다. 2026-10-07 기존 예제의 생성 버튼에 동작이 없음을 확인해
73
+ 양 기본 스토리를 예시 생성 → 검색 0건 → 검색어 지우기 → 목록 복구로 연결했다. 이 데이터는
74
+ 메모리에만 존재한다. 검색·필터 화면 전체가 필요하면 [SearchScreen](../screens/common-search.md)을 쓴다.
73
75
 
74
76
  ## 배치
75
77
 
@@ -96,6 +98,12 @@ EmptyState 자체에는 콜백 prop이 없다.
96
98
 
97
99
  ## 꼭 지킬 것
98
100
 
101
+ - 외부 라이브러리가 권한·실패·성공까지 EmptyState라고 불러도 HJM의 의미 경계를 유지한다.
102
+ 부분 실패는 Notice, 끝난 결과는 Result다. 기기 권한과 서비스 접근 권한을 같은 복구 행동으로 연결하지 않는다.
103
+ 2026-10-07 Primer/PatternFly 대조에서 이름보다 사용자 다음 행동을 기준으로 흡수하기로 했다.
104
+ - 첫 표시부터 있던 빈 화면은 조용히 둔다(Web `aria-live="off"`, Native `announcement="none"`).
105
+ 검색 후 0건으로 바뀔 때는 Web 기본 status, Native `announcement="polite"`로 알리며 입력 초점을 빼앗지 않는다.
106
+
99
107
  - 제목·설명·행동 문구는 i18n 키로 넣는다. "검색 0건"과 "아직 만든 것 없음"은 다른 문구로 구분한다.
100
108
  - 다음 행동이 있으면 `action`에 Button 하나를 둔다. 아이콘·일러스트는 장식이라 접근성에서 숨겨진다.
101
109
  - Native는 `title`·`description`·`accessibilityLabel` 중 하나는 있어야 한다. 모두 없으면 `TypeError`를 던진다.