@hjmds/design-contracts 1.17.2 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. package/dist/action-session.d.ts +16 -0
  2. package/dist/action-session.d.ts.map +1 -1
  3. package/dist/action-session.js +18 -0
  4. package/dist/action-session.js.map +1 -1
  5. package/dist/activity-chip.d.ts +127 -0
  6. package/dist/activity-chip.d.ts.map +1 -0
  7. package/dist/activity-chip.js +65 -0
  8. package/dist/activity-chip.js.map +1 -0
  9. package/dist/base-recipes.d.ts +1 -1
  10. package/dist/base-recipes.d.ts.map +1 -1
  11. package/dist/base-recipes.js +2 -1
  12. package/dist/base-recipes.js.map +1 -1
  13. package/dist/behaviors.d.ts +9 -6
  14. package/dist/behaviors.d.ts.map +1 -1
  15. package/dist/behaviors.js +5 -3
  16. package/dist/behaviors.js.map +1 -1
  17. package/dist/bottom-navigation.d.ts +16 -0
  18. package/dist/bottom-navigation.d.ts.map +1 -1
  19. package/dist/bottom-navigation.js +18 -0
  20. package/dist/bottom-navigation.js.map +1 -1
  21. package/dist/card.d.ts +4 -0
  22. package/dist/card.d.ts.map +1 -1
  23. package/dist/card.js +4 -1
  24. package/dist/card.js.map +1 -1
  25. package/dist/carousel.d.ts +3 -0
  26. package/dist/carousel.d.ts.map +1 -1
  27. package/dist/carousel.js.map +1 -1
  28. package/dist/catalog.d.ts +80 -9
  29. package/dist/catalog.d.ts.map +1 -1
  30. package/dist/component-recipes.d.ts +74 -3
  31. package/dist/component-recipes.d.ts.map +1 -1
  32. package/dist/component-recipes.js +38 -6
  33. package/dist/component-recipes.js.map +1 -1
  34. package/dist/content-transition.d.ts +1 -1
  35. package/dist/content-transition.d.ts.map +1 -1
  36. package/dist/content-transition.js +2 -2
  37. package/dist/content-transition.js.map +1 -1
  38. package/dist/field-group.d.ts +14 -0
  39. package/dist/field-group.d.ts.map +1 -1
  40. package/dist/field-group.js +9 -0
  41. package/dist/field-group.js.map +1 -1
  42. package/dist/form.d.ts.map +1 -1
  43. package/dist/form.js +10 -3
  44. package/dist/form.js.map +1 -1
  45. package/dist/icon-button-recipe.d.ts +1 -5
  46. package/dist/icon-button-recipe.d.ts.map +1 -1
  47. package/dist/icon-button-recipe.js +2 -1
  48. package/dist/icon-button-recipe.js.map +1 -1
  49. package/dist/index.d.ts +3 -2
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +2 -0
  52. package/dist/index.js.map +1 -1
  53. package/dist/screen-patterns.d.ts +10 -0
  54. package/dist/screen-patterns.d.ts.map +1 -1
  55. package/dist/screen-patterns.js +17 -3
  56. package/dist/screen-patterns.js.map +1 -1
  57. package/dist/toast-liquid.d.ts +1 -1
  58. package/dist/toast-liquid.d.ts.map +1 -1
  59. package/dist/toast-liquid.js +3 -2
  60. package/dist/toast-liquid.js.map +1 -1
  61. package/dist/toast.d.ts +29 -2
  62. package/dist/toast.d.ts.map +1 -1
  63. package/dist/toast.js +59 -3
  64. package/dist/toast.js.map +1 -1
  65. package/dist/version.d.ts +1 -1
  66. package/dist/version.js +1 -1
  67. package/dist/version.js.map +1 -1
  68. package/docs/action-session.md +2 -0
  69. package/docs/activity-chip.md +34 -0
  70. package/docs/asset.md +7 -0
  71. package/docs/bottom-navigation.md +16 -1
  72. package/docs/brand-boundary.md +3 -3
  73. package/docs/collection-rail.md +4 -0
  74. package/docs/compound-controls.md +8 -3
  75. package/docs/consumer-policy.md +39 -120
  76. package/docs/generated/component-maturity.md +1 -1
  77. package/docs/generated/renderer-evidence.json +3 -3
  78. package/docs/generated/renderer-evidence.md +1 -1
  79. package/docs/generated/showcase-manifest.json +1 -1
  80. package/docs/gravity-letters.md +2 -0
  81. package/docs/grid-reveal.md +2 -0
  82. package/docs/layout.md +2 -2
  83. package/docs/load-more.md +5 -0
  84. package/docs/migration-native-legacy-removal.md +30 -17
  85. package/docs/optional-adapters.md +4 -0
  86. package/docs/screen-patterns.md +23 -1
  87. package/docs/toast.md +64 -2
  88. package/docs/usage/README.md +248 -243
  89. package/docs/usage/SELECTION.md +43 -0
  90. package/docs/usage/STANDARD.md +22 -10
  91. package/docs/usage/components/accordion.md +5 -3
  92. package/docs/usage/components/activity-chip.md +109 -0
  93. package/docs/usage/components/agreement.md +6 -9
  94. package/docs/usage/components/alert-dialog.md +12 -6
  95. package/docs/usage/components/asset.md +10 -4
  96. package/docs/usage/components/auth-provider-button.md +2 -2
  97. package/docs/usage/components/avatar.md +8 -6
  98. package/docs/usage/components/badge.md +4 -4
  99. package/docs/usage/components/bottom-cta.md +4 -4
  100. package/docs/usage/components/bottom-info.md +2 -2
  101. package/docs/usage/components/bottom-navigation.md +18 -6
  102. package/docs/usage/components/button.md +6 -3
  103. package/docs/usage/components/card.md +28 -7
  104. package/docs/usage/components/carousel.md +12 -4
  105. package/docs/usage/components/chat-message.md +10 -2
  106. package/docs/usage/components/chat-screen.md +5 -5
  107. package/docs/usage/components/checkbox-group.md +1 -1
  108. package/docs/usage/components/checkbox.md +2 -2
  109. package/docs/usage/components/chip.md +2 -2
  110. package/docs/usage/components/code-block.md +3 -4
  111. package/docs/usage/components/collapsible.md +6 -4
  112. package/docs/usage/components/combobox.md +2 -2
  113. package/docs/usage/components/command-palette.md +1 -1
  114. package/docs/usage/components/comment-thread-screen.md +7 -4
  115. package/docs/usage/components/container.md +1 -1
  116. package/docs/usage/components/content-transition.md +3 -2
  117. package/docs/usage/components/context-menu.md +2 -2
  118. package/docs/usage/components/counter-badge.md +3 -3
  119. package/docs/usage/components/data-table.md +1 -1
  120. package/docs/usage/components/date-picker.md +4 -4
  121. package/docs/usage/components/description-list.md +3 -3
  122. package/docs/usage/components/design-system-provider.md +2 -2
  123. package/docs/usage/components/dialog.md +14 -3
  124. package/docs/usage/components/divider.md +2 -2
  125. package/docs/usage/components/editor-screen.md +5 -3
  126. package/docs/usage/components/effect-surface.md +1 -1
  127. package/docs/usage/components/empty-state.md +6 -6
  128. package/docs/usage/components/field.md +5 -3
  129. package/docs/usage/components/file-picker.md +6 -3
  130. package/docs/usage/components/form.md +1 -1
  131. package/docs/usage/components/heading.md +3 -3
  132. package/docs/usage/components/icon-button.md +6 -3
  133. package/docs/usage/components/icon.md +1 -1
  134. package/docs/usage/components/image.md +5 -4
  135. package/docs/usage/components/keyboard-dock.md +1 -1
  136. package/docs/usage/components/layout.md +1 -1
  137. package/docs/usage/components/link.md +2 -2
  138. package/docs/usage/components/list-detail-screen.md +6 -6
  139. package/docs/usage/components/list-row.md +13 -4
  140. package/docs/usage/components/list.md +5 -4
  141. package/docs/usage/components/masonry.md +2 -1
  142. package/docs/usage/components/media-selection-screen.md +11 -5
  143. package/docs/usage/components/mentions.md +2 -2
  144. package/docs/usage/components/menu.md +3 -2
  145. package/docs/usage/components/menubar.md +2 -2
  146. package/docs/usage/components/message-composer.md +3 -3
  147. package/docs/usage/components/moderation-screen.md +3 -2
  148. package/docs/usage/components/notice.md +2 -2
  149. package/docs/usage/components/notification-inbox-screen.md +1 -1
  150. package/docs/usage/components/notification-item.md +2 -2
  151. package/docs/usage/components/number-field.md +3 -3
  152. package/docs/usage/components/otp-field.md +1 -1
  153. package/docs/usage/components/overview-screen.md +1 -1
  154. package/docs/usage/components/permission-screen.md +1 -1
  155. package/docs/usage/components/photo-source-sheet.md +1 -1
  156. package/docs/usage/components/popover.md +2 -2
  157. package/docs/usage/components/profile-screen.md +1 -1
  158. package/docs/usage/components/progress.md +2 -2
  159. package/docs/usage/components/qr-code.md +2 -2
  160. package/docs/usage/components/radio-group.md +1 -2
  161. package/docs/usage/components/radio.md +1 -2
  162. package/docs/usage/components/result.md +1 -1
  163. package/docs/usage/components/saved-items-screen.md +4 -2
  164. package/docs/usage/components/screen-layout.md +4 -3
  165. package/docs/usage/components/search-field.md +1 -1
  166. package/docs/usage/components/search-screen.md +4 -4
  167. package/docs/usage/components/section.md +3 -3
  168. package/docs/usage/components/segmented-control.md +4 -4
  169. package/docs/usage/components/select.md +2 -3
  170. package/docs/usage/components/settings-screen.md +18 -56
  171. package/docs/usage/components/sheet.md +10 -5
  172. package/docs/usage/components/sidebar.md +1 -1
  173. package/docs/usage/components/skeleton.md +1 -1
  174. package/docs/usage/components/skip-nav.md +1 -1
  175. package/docs/usage/components/slider.md +2 -2
  176. package/docs/usage/components/spinner.md +2 -2
  177. package/docs/usage/components/splitter.md +1 -1
  178. package/docs/usage/components/statistic.md +5 -4
  179. package/docs/usage/components/steps.md +2 -2
  180. package/docs/usage/components/surface.md +2 -3
  181. package/docs/usage/components/switch.md +1 -1
  182. package/docs/usage/components/tabs.md +4 -4
  183. package/docs/usage/components/tag.md +2 -2
  184. package/docs/usage/components/tags-input.md +4 -4
  185. package/docs/usage/components/text-area.md +5 -3
  186. package/docs/usage/components/text.md +85 -4
  187. package/docs/usage/components/timeline.md +2 -2
  188. package/docs/usage/components/toast.md +41 -13
  189. package/docs/usage/components/toggle-group.md +3 -3
  190. package/docs/usage/components/tooltip.md +1 -1
  191. package/docs/usage/components/top-bar.md +5 -4
  192. package/docs/usage/components/top.md +3 -3
  193. package/docs/usage/components/transfer-list.md +5 -4
  194. package/docs/usage/components/upload-item.md +3 -3
  195. package/docs/usage/components/virtual-list.md +1 -0
  196. package/docs/usage/compositions/action-feedback.md +42 -6
  197. package/docs/usage/compositions/action-recovery-optimistic.md +6 -1
  198. package/docs/usage/compositions/action-recovery-save.md +19 -23
  199. package/docs/usage/compositions/action-recovery-undo.md +6 -1
  200. package/docs/usage/compositions/activity-status.md +168 -0
  201. package/docs/usage/compositions/collection-detail.md +2 -0
  202. package/docs/usage/compositions/command-records.md +15 -5
  203. package/docs/usage/compositions/common-message.md +4 -2
  204. package/docs/usage/compositions/common-notification.md +5 -3
  205. package/docs/usage/compositions/compound-controls.md +13 -5
  206. package/docs/usage/compositions/content-transition-comparison.md +2 -2
  207. package/docs/usage/compositions/date-entry.md +10 -5
  208. package/docs/usage/compositions/date-time-selection.md +3 -3
  209. package/docs/usage/compositions/design-profile-comparison.md +7 -5
  210. package/docs/usage/compositions/disclosure.md +1 -1
  211. package/docs/usage/compositions/document-resource.md +10 -3
  212. package/docs/usage/compositions/expo-interactions.md +3 -2
  213. package/docs/usage/compositions/family-drawer.md +9 -4
  214. package/docs/usage/compositions/field-group.md +7 -1
  215. package/docs/usage/compositions/floating-action-button.md +18 -18
  216. package/docs/usage/compositions/illustrated-outcome.md +3 -2
  217. package/docs/usage/compositions/input-sheet.md +6 -2
  218. package/docs/usage/compositions/interaction-adapters.md +1 -1
  219. package/docs/usage/compositions/interaction-flow-apply.md +6 -3
  220. package/docs/usage/compositions/interaction-flow-draft.md +5 -2
  221. package/docs/usage/compositions/interaction-flow-search.md +5 -2
  222. package/docs/usage/compositions/live-list.md +7 -5
  223. package/docs/usage/compositions/navigation-bar-collection.md +8 -7
  224. package/docs/usage/compositions/optional-adapters.md +1 -1
  225. package/docs/usage/compositions/origin-dialog.md +5 -5
  226. package/docs/usage/compositions/paper-surface.md +1 -1
  227. package/docs/usage/compositions/photo-source.md +1 -1
  228. package/docs/usage/compositions/purpose-input-comment.md +5 -3
  229. package/docs/usage/compositions/purpose-input-message.md +5 -3
  230. package/docs/usage/compositions/reference-first.md +63 -42
  231. package/docs/usage/compositions/reference-review.md +60 -50
  232. package/docs/usage/compositions/reference-settings.md +60 -50
  233. package/docs/usage/compositions/selection-scope.md +48 -35
  234. package/docs/usage/compositions/stea-order-progress.md +1 -0
  235. package/docs/usage/compositions/stea-otp-verify.md +5 -3
  236. package/docs/usage/compositions/stea-pixel-empty.md +3 -3
  237. package/docs/usage/compositions/time-selection.md +6 -5
  238. package/docs/usage/compositions/upload-recovery.md +10 -1
  239. package/docs/usage/compositions/video-dialog.md +10 -3
  240. package/docs/usage/compositions/web-additions.md +5 -5
  241. package/docs/usage/compositions/web-navigation.md +1 -1
  242. package/docs/usage/screens/common-chat.md +21 -8
  243. package/docs/usage/screens/common-comments.md +13 -1
  244. package/docs/usage/screens/common-inbox.md +19 -6
  245. package/docs/usage/screens/common-login.md +19 -7
  246. package/docs/usage/screens/common-profile.md +22 -9
  247. package/docs/usage/screens/common-saved.md +16 -2
  248. package/docs/usage/screens/common-search.md +24 -4
  249. package/docs/usage/screens/common-settings.md +22 -50
  250. package/docs/usage/screens/common-shell.md +14 -2
  251. package/docs/usage/screens/dashboard.md +52 -109
  252. package/docs/usage/screens/discovery-gallery.md +57 -148
  253. package/docs/usage/screens/flow-collection.md +38 -9
  254. package/docs/usage/screens/flow-editor.md +25 -7
  255. package/docs/usage/screens/flow-media.md +23 -7
  256. package/docs/usage/screens/flow-moderation.md +15 -2
  257. package/docs/usage/screens/flow-onboarding.md +3 -3
  258. package/docs/usage/screens/flow-permission.md +17 -3
  259. package/docs/usage/screens/landing.md +37 -20
  260. package/docs/usage/screens/mockup-studio.md +22 -7
  261. package/docs/usage/screens/notification-settings.md +18 -4
  262. package/docs/usage/screens/product-bento.md +18 -9
  263. package/docs/usage/screens/reference-comparison.md +29 -10
  264. package/docs/usage/tokens/color.md +6 -3
  265. package/docs/usage/tokens/elevation-opacity.md +4 -2
  266. package/docs/usage/tokens/font-roles.md +2 -2
  267. package/docs/usage/tokens/layers.md +3 -3
  268. package/docs/usage/tokens/motion.md +2 -2
  269. package/docs/usage/tokens/radius.md +4 -2
  270. package/docs/usage/tokens/spacing.md +3 -3
  271. package/docs/usage/tokens/theme-studio.md +2 -2
  272. package/docs/usage/tokens/typography-studio.md +14 -8
  273. package/docs/usage/tokens/typography.md +15 -5
  274. package/package.json +7 -1
@@ -4,8 +4,8 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
8
- - 근거: `showcase/web/src/patterns/Landing.stories.tsx`·`Landing.previews.tsx`, `showcase/native/src/Landing.stories.tsx`·`introduction-preview.tsx`, 변형 `showcase/*/reference-flow-previews.tsx`(`ServiceIntroduction`), `showcase/shared/landing-pattern.ts`, `showcase/native/src/pattern-status.tsx`, `packages/react/src/effect-surface.tsx`, `packages/react-native/src/effect-surface.tsx`. 2026-10-06 사용자 승인으로 실험 `서비스 소개/제품 체험 중심`(설명과 사례 중심 포함)을 배포하면서 같은 소개 목적의 배포 `화면/랜딩 화면`과 한 항목으로 합쳤다(Web id `patterns-landing` 보존, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
7
+ - 검토일: 2026-10-13
8
+ - 근거: `showcase/web/src/patterns/Landing.stories.tsx`·`Landing.previews.tsx`(진입점)·`showcase/web/src/patterns/landing-preview.tsx`(기본 본체), `showcase/native/src/Landing.stories.tsx`·`introduction-preview.tsx`(진입점)·`showcase/native/src/landing-preview.tsx`(기본 본체), 변형 `showcase/*/reference-flow-previews.tsx`(`ServiceIntroduction`), `showcase/shared/landing-pattern.ts`, `showcase/native/src/pattern-status.tsx`, `packages/react/src/effect-surface.tsx`, `packages/react-native/src/effect-surface.tsx`. 2026-10-06 사용자 승인으로 실험 `서비스 소개/제품 체험 중심`(설명과 사례 중심 포함)을 배포하면서 같은 소개 목적의 배포 `화면/랜딩 화면`과 한 항목으로 합쳤다(Web id `patterns-landing` 보존, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
9
9
  - 스토리북: `배포/화면/소개/서비스 소개`
10
10
 
11
11
  ## 목적
@@ -63,7 +63,7 @@
63
63
  | 영역 | 컴포넌트 | 위치 | 크기·간격 |
64
64
  | --- | --- | --- | --- |
65
65
  | 바깥 틀 | Web `main` > `Container size="content" gutter={gutter}` > `Stack gap="xl"`(문서 스크롤) · Native `ScrollView`(`contentContainerStyle={{ paddingVertical: spacing.lg }}`) > `Container size="content" gutter={gutter}` > `Stack gap="xl"` | 화면 전체, 스크롤. 위 안전 영역은 내비게이션 헤더(또는 [TopBar](../components/top-bar.md) `safeAreaTop`)가 맡고 헤더 없이 띄울 때만 제품이 감싼다. 아래 고정 영역은 없다. 바깥에는 입력이 없어 키보드 처리는 ⑥ 시트가 맡는다 | 좌우 gutter: 폭 600 미만 `compact` 16(`layout.pagePadding.compact`), 이상 `regular` 20([화면 여백](../tokens/layout.md)). Native 위아래 `spacing.lg` 20. 직계 요소 사이 `spacing.xl` 24(`layout.sectionGap`) |
66
- | ① 히어로 | EffectSurface `descriptor={{ layers: ["mesh","grain"], active: false }}` > Stack `gap="lg"` > Text·Heading·Text·Button | 맨 위 | 요소 사이 `spacing.lg` 20. EffectSurface는 안쪽 여백이 없어 제품이 컨테이너에 `spacing.xl` 24를 준다(Web `className`, Native `style`) |
66
+ | ① 히어로 | EffectSurface `descriptor={{ layers: ["mesh","grain"], active: false }}` > Surface `padding="xl"` > Stack `gap="lg"` > Text·Heading·Text·Button | 맨 위 | 요소 사이 `spacing.lg` 20. EffectSurface 안에 Surface `padding="xl"`을 두어 spacing.xl 24를 준다. 실제 양 플랫폼 예제와 같은 공개 구성이다 |
67
67
  | ② 미리보기 | Surface `padding="lg"` > Stack `gap="md"` > Heading·상태 Text·List | 히어로 아래 | 안쪽 20, 요소 사이 `spacing.md` 16, 한 줄 행 최소 56(`layout.rowHeight.singleLine`). 새 기록이 맨 위 |
68
68
  | ③ 기능 | Heading level2 + Surface `padding="lg"` ×3 > Stack `gap="sm"` | 미리보기 아래 | 카드 안 `spacing.sm` 12, 카드 사이 24 |
69
69
  | ④ FAQ | Heading level2 + Collapsible × n | 기능 아래 | 트리거 최소 44(`control.minTouchTarget`), 트리거 위아래 `spacing.xs` 8(Web) |
@@ -76,7 +76,7 @@
76
76
  ScreenLayout(제목 = 소개 문장, 최대 720, 바깥 padding spacing.md 16) — 본문 스크롤
77
77
  ├─ 제품 체험 중심: [히어로·행동 | 제품 미리보기] → 특징 3열 → FAQ → 끝 행동 카드
78
78
  └─ 설명과 사례 중심: 히어로·행동 → [설명 | 제품 미리보기] → 특징 3열 → FAQ → 끝 행동 카드
79
- [a | b] = Grid 두 열(열 최소 240), 좁은 폭·큰 글자에서 한 열로 접힌다
79
+ [a | b] = Grid 두 열(열 최소 240), 좁은 폭에서 한 열로 접힌다
80
80
  입력: Sheet(아래 고정 footer에 저장)
81
81
  ```
82
82
 
@@ -92,10 +92,10 @@ ScreenLayout(제목 = 소개 문장, 최대 720, 바깥 padding spacing.md 16)
92
92
  | 행동 | 컴포넌트·tone | 위치 | 개수·순서 |
93
93
  | --- | --- | --- | --- |
94
94
  | 주 행동(체험 시작) | Button 기본 primary, `medium` | ① 히어로 소개문 바로 아래 | 1 |
95
- | 같은 행동 반복 | Button `secondary`, 같은 라벨·같은 동작 | ⑤ FAQ 다음 | 1. 큰 글자에서 히어로 행동이 첫 화면 밖으로 밀리므로 끝에 한 번 더 둔다 |
95
+ | 같은 행동 반복 | Button `secondary`, 같은 라벨·같은 동작 | ⑤ FAQ 다음 | 1. 기능·FAQ를 읽은 뒤 위로 돌아가지 않고 체험할 수 있도록 끝에 한 번 더 둔다 |
96
96
  | FAQ 펼치기 | Collapsible 트리거 | ④ 각 질문 행 전체 | 질문마다 1 |
97
97
  | 입력 제출 | Button 기본 primary | ⑥ Sheet `footer`(Web 끝 정렬, Native 꽉 찬 폭) | 1. 시트 안에서 따로 센다([Button](../components/button.md#꼭-지킬-것)) |
98
- | 시트 닫기 | Sheet `closeLabel` | 시트 머리 끝 | 1. 저장 중에는 Sheet `busy`로 막는다 |
98
+ | 시트 닫기 | Sheet `closeLabel` | 시트 머리 끝 | 1. 기본 예제는 닫아도 초안을 유지한다. 제품이 비동기 저장을 연결하면 저장 중에는 Sheet `busy`로 막는다 |
99
99
  | 파괴 행동 | — | — | 없음 |
100
100
 
101
101
  화면의 primary는 히어로 행동 하나다. 시트 `footer`의 제출은 시트 안에서 따로 센다.
@@ -104,6 +104,14 @@ ScreenLayout(제목 = 소개 문장, 최대 720, 바깥 padding spacing.md 16)
104
104
  키보드: Native 시트는 `keyboardAvoidance`로 키보드 위에 붙고 `footer`가 키보드 위에 고정돼 제출 버튼이 가려지지 않는다.
105
105
  바깥 ScrollView에는 입력이 없다.
106
106
 
107
+ 기본 예제는 시트를 열면 입력칸에 초점을 둔다(Web `initialFocusRef`, Native `autoFocus`). Web Enter와 Native Done 키는
108
+ footer의 저장과 같은 동작을 실행한다. Web에서 IME 조합 확정 중인 Enter는 저장으로 처리하지 않는다.
109
+ 빈 입력을 제출하면 시트를 유지하고 오류가 있는 입력칸에 초점을 돌린다. 닫기·Escape 또는 저장으로 시트가 끝나면
110
+ `returnFocusRef`로 실제 누른 위·아래 CTA에 복귀한다(Native는 접근성 초점).
111
+
112
+ 2026-10-13 실제 예제 검수에서 닫은 뒤 초안 소실과 연속 제출 시 중복 행을 재현해 수정했다.
113
+ 기본 예제는 닫을 때 초안을 보존하고, 성공한 제출만 한 번 추가한 뒤 초안을 비운다. 다시 열면 새로운 기록을 시작한다.
114
+
107
115
  ## 상태
108
116
 
109
117
  | 상태 | 화면 모습 | 행동 |
@@ -112,7 +120,8 @@ ScreenLayout(제목 = 소개 문장, 최대 720, 바깥 padding spacing.md 16)
112
120
  | 로딩 | 스토리에 없음. 저장이 비동기면 `footer` 제출 Button `loading`, Sheet `busy`로 닫기를 막는다. 입력 값은 그대로 둔다 | 기다림 |
113
121
  | 빈 | ②에 기록이 0개인 경우는 스토리에 없다. 예시 한 줄을 처음부터 넣어 미리보기가 비지 않게 한다 | — |
114
122
  | 오류 | 입력 오류: ⑥ TextField `error`(`landing.error.empty`). 입력을 바꾸면 지워지고 시트는 닫히지 않는다. 저장 실패는 스토리에 없음. 시트 본문 맨 위에 [Notice](../components/notice.md) `tone="danger"`(Native `announcement="assertive"` — 기본 `none`), 문구는 원인별 키: 네트워크 `landing.error.offline`, 서버 `landing.error.server`. 입력 값과 ②의 이전 기록은 유지하고 시트를 닫지 않는다. 다시 시도는 같은 `footer` 제출 버튼이며, 또 실패하면 Notice를 같은 자리에 두고 `loading`·`busy`만 푼다 | 다시 입력 · 다시 제출 |
115
- | 성공 | 시트가 닫히고 ② 맨 위에 새 행, "추가했어요" 상태 문구가 읽힌다 | — |
123
+ | 성공 | 기본 예제는 시트가 닫히고 ② 맨 위에 새 행을 한 번 추가하며, "추가했어요" 상태 문구가 읽힌다. 입력 초안을 비워 다음 기록을 준비한다 | CTA로 초점 복귀 · 다시 기록 |
124
+ | 저장 없이 닫음 | 시트만 닫히고 이전 미리보기는 유지한다. 초안은 위·아래 어느 CTA로 다시 열어도 남는다 | 같은 초안 계속 입력 |
116
125
  | FAQ 펼침 | 질문 아래에 답이 열린다(같은 자리 enter/exit 모션) | 다시 눌러 접기 |
117
126
  | 제품 체험 중심 | `제품 체험 중심` 스토리: 히어로·행동과 제품 미리보기 두 열 → 특징 3열 → FAQ → 끝 행동 카드. 저장하면 미리보기가 방금 쓴 장면으로 바뀐다 | 히어로·끝 행동 = 같은 Sheet |
118
127
  | 설명과 사례 중심 | `설명과 사례 중심` 스토리: 히어로·행동 한 열 → 설명과 미리보기 두 열 → 이하 같음 | 같은 Sheet |
@@ -137,6 +146,13 @@ ScreenLayout(제목 = 소개 문장, 최대 720, 바깥 padding spacing.md 16)
137
146
  | [Stack](../components/stack.md) | 모든 세로 리듬 |
138
147
  | [간격](../tokens/spacing.md) | `spacing.sm`·`md`·`lg`·`xl` |
139
148
 
149
+ ### 제품의 비동기 저장 연결
150
+
151
+ 기본과 두 변형 스토리는 메모리 저장이다. 공개 `LandingScreen`을 제공하는 것으로 해석하지 않는다.
152
+ 실제 서버에 연결할 때는 제출 시 draft snapshot을 잡고 같은 요청의 중복 실행을 막는다.
153
+ 성공 응답 뒤에만 미리보기와 저장 문구를 갱신하고 Sheet를 닫는다. 실패하면 초안·이전 미리보기·현재 Sheet를 유지하며
154
+ 오류는 입력 영역 Notice, 재시도는 기존 footer 제출 하나로 둔다. 소개 자료를 재조회해도 열린 입력 Sheet를 remount하지 않는다.
155
+
140
156
  ## 코드 골격
141
157
 
142
158
  카피·기능 목록·FAQ·장식 seed는 제품 소유다. 오류 문구는 상태→키 상수 표로 고른다.
@@ -158,15 +174,15 @@ const failureKey = { offline: "landing.error.offline", server: "landing.error.se
158
174
  const gutter = resolveWindowClass(window.innerWidth) === "compact" ? "compact" : "regular";
159
175
 
160
176
  <main><Container size="content" gutter={gutter}><Stack gap="xl">
161
- {/* landing-hero: padding: var(--hjm-space-xl) — EffectSurface는 안쪽 여백이 없다 */}
162
- <EffectSurface className="landing-hero" descriptor={{ layers: ["mesh", "grain"], seed: "landing", active: false }}>
177
+ {/* 장식과 콘텐츠 여백은 기존 EffectSurface + Surface로 분리한다. */}
178
+ <EffectSurface descriptor={{ layers: ["mesh", "grain"], seed: "landing", active: false }}><Surface padding="xl">
163
179
  <Stack gap="lg">
164
180
  <Text tone="muted">{t("landing.eyebrow")}</Text>
165
181
  <Heading level="level1">{t("landing.title")}</Heading>
166
182
  <Text as="p">{t("landing.intro")}</Text>
167
183
  <Button onClick={start}>{t("landing.cta")}</Button>
168
184
  </Stack>
169
- </EffectSurface>
185
+ </Surface></EffectSurface>
170
186
  <Surface padding="lg"><Stack gap="md">
171
187
  <Heading level="level2">{t("landing.preview")}</Heading>
172
188
  {saved ? <Text role="status">{t("landing.saved")}</Text> : null}
@@ -196,7 +212,7 @@ const gutter = resolveWindowClass(window.innerWidth) === "compact" ? "compact" :
196
212
  import { ScrollView, useWindowDimensions } from "react-native";
197
213
  import { spacing } from "@hjmds/design-contracts/foundations";
198
214
  import { resolveWindowClass } from "@hjmds/design-contracts/responsive";
199
- import { Container, Stack, Text } from "@hjmds/react-native/primitives";
215
+ import { Container, Stack, Surface, Text } from "@hjmds/react-native/primitives";
200
216
  import { Heading } from "@hjmds/react-native/heading";
201
217
  import { Button } from "@hjmds/react-native/actions";
202
218
  import { TextField } from "@hjmds/react-native/inputs";
@@ -210,13 +226,13 @@ const gutter = resolveWindowClass(width) === "compact" ? "compact" : "regular";
210
226
 
211
227
  <ScrollView contentContainerStyle={{ paddingVertical: spacing.lg }}>
212
228
  <Container size="content" gutter={gutter}><Stack gap="xl">
213
- <EffectSurface style={{ padding: spacing.xl }} descriptor={{ layers: ["mesh", "grain"], seed: "landing", active: false }}>
229
+ <EffectSurface descriptor={{ layers: ["mesh", "grain"], seed: "landing", active: false }}><Surface padding="xl">
214
230
  <Stack gap="lg">
215
231
  <Heading level="level1">{t("landing.title")}</Heading>
216
232
  <Text>{t("landing.intro")}</Text>
217
233
  <Button onPress={start}>{t("landing.cta")}</Button>
218
234
  </Stack>
219
- </EffectSurface>
235
+ </Surface></EffectSurface>
220
236
  {/* 미리보기·기능·FAQ·반복 행동(secondary)·범위 안내: Web과 같은 순서, onPress */}
221
237
  </Stack></Container>
222
238
  <Sheet keyboardAvoidance scrollable busy={saving} title={t("landing.sheetTitle")} closeLabel={t("common.close")}
@@ -315,16 +331,15 @@ import { ScreenLayout } from "@hjmds/react-native/screens";
315
331
  </>
316
332
  ```
317
333
 
318
- ## 큰 글자·다크·좁은 폭
334
+ ## 다크·좁은 폭
319
335
 
320
336
  | 조건 | 바뀌는 것 |
321
337
  | --- | --- |
322
- | 큰 글자(`textScale` 2) | 제목·카드가 세로로 길어질 뿐 순서는 같다. 히어로 주 행동이 첫 화면 밖으로 밀릴 수 있어 ⑤의 반복 행동이 필요하다. 시트 본문이 길어져도 `footer` 제출은 고정이다 |
323
338
  | 다크 | EffectSurface 배경은 테마 팔레트의 `bg`를 쓰고 장식 층이 그 위에 그려진다. 나머지는 테마 토큰 |
324
339
  | 좁은 폭 | 한 열 그대로, 폭 600 미만은 gutter `compact` 16. 시트는 아래에 붙고 Native는 키보드 위로 올라간다 |
325
340
  | 넓은 폭 Web | 한 열 유지, Container `content` 1200으로 폭을 묶고 gutter `regular` 20 |
326
341
  | 모션 줄이기 | 스토리는 `active: false`로 장식 움직임을 끈다. Native는 `visible={false}`로 화면 밖 장식을 멈춘다 |
327
- | 변형의 두 열 | 열 최소 240을 못 채우면(좁은 폭·큰 글자) Grid가 한 열로 접어 히어로 → 미리보기 순서로 읽힌다. Sheet `footer` 저장은 고정이다 |
342
+ | 변형의 두 열 | 열 최소 240을 못 채우면(좁은 폭) Grid가 한 열로 접어 히어로 → 미리보기 순서로 읽힌다. Sheet `footer` 저장은 고정이다 |
328
343
 
329
344
  ## 플랫폼 차이
330
345
 
@@ -334,14 +349,16 @@ import { ScreenLayout } from "@hjmds/react-native/screens";
334
349
  | 시트 키보드 처리 | 브라우저가 처리 | `keyboardAvoidance` 명시 |
335
350
  | 시트 본문 스크롤 | 자동 | `scrollable` 명시 |
336
351
  | 시트 `footer` 버튼 | 끝 정렬 가로 줄 | 꽉 찬 폭 세로 열(`fullWidth`) |
337
- | 저장 알림 | `role="status"` | 라이브 영역(Android) + iOS는 제품이 직접 알림. 문구가 새로 나타날 때 한 번 읽히게 한다(스토리 `PatternStatus announceOnMount`) |
352
+ | 저장 알림 | `role="status"` | 공개 StatusText. 조건부 저장 결과는 `announceOnMount`로 iOS 첫 결과도 알림 |
338
353
  | 실패 알림 | Notice가 자기 역할로 읽힌다 | Notice `announcement="assertive"` 명시(기본 `none`) |
339
- | 히어로 안쪽 여백 | `className`으로 `var(--hjm-space-xl)` | `style`의 `padding: spacing.xl` |
354
+ | 히어로 안쪽 여백 | EffectSurface 내부 Surface `padding="xl"` | 같은 Surface 공개 prop |
340
355
 
341
356
  ## 함정
342
357
 
343
358
  - 히어로는 Surface padding="xl"로 내부 여백을 갖고, 바깥 폭·gutter는 Container가 소유한다. 시트 저장은 footer에 둔다(2026-10-06 지침과 예제 정합성 수정).
344
- - 위·아래 CTA는 동일 행동이다. 긴 스크롤 구간의 접근성을 위한 반복 primary 예외이며 서로 다른 주 행동을 허용하지 않는다.
359
+ - 기본 예제의 위·아래 CTA는 같은 시트를 연다. 히어로는 primary, FAQ 뒤의 반복 행동은 secondary다.
345
360
  - 미리보기 행은 번역 문구나 배열 위치 대신 생성 시 부여한 id로 유지한다.
346
- - 변형 예제의 저장은 메모리에만 남고 Sheet를 닫아도 입력 초안이 남는다. 운영 저장·실패 처리는 위 오류 행 규칙을 따른다.
361
+ - 기본·변형 예제의 저장은 메모리에만 남는다. 기본 예제는 저장 없이 닫으면 초안을 보존하고 성공하면 비운다. 변형은 Sheet를 닫아도 입력 초안이 남는다. 운영 저장·실패 처리는 위 오류 행 규칙을 따른다.
347
362
  - 현재 변형 스토리는 FAQ를 Collapsible이 아니라 `selected` 토글 Button 하나로 그린다. 질문이 여럿이면 기본처럼 Collapsible을 쓴다.
363
+
364
+ 제품 체험·편집형 ServiceIntroduction 변형도 Web에서 CTA 클릭의 `event.currentTarget`을 복귀 대상으로 기록한다. Safari의 포인터 클릭은 버튼에 초점을 주지 않을 수 있어, 열기 직전 `document.activeElement`만으로 복귀 대상을 추정하지 않는다. 이 변형은 저장한 한 장면을 다시 편집하는 예제이므로 닫기뿐 아니라 저장 뒤 재열기에도 입력값을 유지한다. 새 기록을 계속 추가하는 기본 랜딩의 성공 후 입력 초기화와 구분한다.
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-13
8
8
  - 근거: `showcase/web/src/foundations/MockupStudio.stories.tsx`, `src/responsive.ts`(`resolveWindowClass`), `showcase/web/src/studio/MockupStudio.tsx`, `showcase/web/src/studio/SceneTimeline.tsx`, `showcase/shared/mockup-scene.ts`, `showcase/shared/scene-timeline.ts`
9
9
  - 스토리북: `배포/화면/화면 틀과 도구/목업 편집`
10
10
 
@@ -74,7 +74,7 @@ Native 스토리는 없다(장면 작성은 Web showcase에 둔다는 통합 계
74
74
  | 장면 설정 저장 | Button `secondary` | ⑩ 둘째 | 1. JSON 파일 내려받기 |
75
75
  | 초기화 | Button `ghost` | ⑩ 셋째(끝) | 1. 확인 없이 모든 설정·이미지를 버린다 |
76
76
  | 재생·일시 정지 | Button `secondary` | ⑥ 슬라이더 아래 첫째 | 1. 이미지 없음·모션 줄이기·영상 출력 중이면 `disabled` |
77
- | 영상 내보내기 | Button `secondary` + 진행 중 `출력 취소` ghost | ⑥ 아래쪽 | 1(+1). 진행률을 라벨에 % 로 표시(`t("editor.videoProgress", { percent })`) |
77
+ | 영상 내보내기 | Button `secondary` + 진행 중 `출력 취소` ghost | ⑥ 아래쪽 | 1(+1). 영상 버튼 아래 `role="progressbar"`에 실제 진행률 0–100% 표시 |
78
78
  | 실패 후 다시 시도 | Notice `action` > Button `tone="secondary" size="small"` | ⑤ 실패 Notice 안 | 내보내기 실패일 때만 1. 파일 읽기 실패는 다른 파일을 고르는 것이 복구라 버튼을 두지 않는다 |
79
79
  | 설정 고르기 | Button `secondary`(형태·프레임·배경·움직임) · `ghost`(각도·여백·길이·fps) + `selected` | ⑥⑧ 각 줄 | 줄마다 2~5 |
80
80
  | 파일 고르기 | `<input type="file">`(PNG·JPEG·WebP / JSON) | ④ | 2 |
@@ -90,9 +90,9 @@ Native 스토리는 없다(장면 작성은 Web showcase에 둔다는 통합 계
90
90
  | 기본 | 빈 프레임 미리보기, 상태 "스크린샷을 선택해 시작하세요.", PNG·재생·영상 비활성 | 화면 캡처 선택 |
91
91
  | 로딩 | 상태 "스크린샷을 여는 중이에요.", PNG 비활성 | 기다림 |
92
92
  | 빈 | 기본과 같다(이미지 없음). 장면 JSON만 불러오면 이미지가 비워지고 "파일을 다시 선택해 주세요" | 같은 캡처 다시 선택 |
93
- | 오류 | 네트워크·서버 요청이 없다(브라우저 안에서만 처리). ⑤ 자리에 [Notice](../components/notice.md) `tone="danger"`. 이미지 읽기 실패(형식 PNG·JPEG·WebP 밖·20MB·16384px 초과): 이전 이미지·설정 유지. 장면 JSON 실패(64KB 초과·형식 오류): 현재 장면 유지. PNG·포스터 내보내기 실패: `action` 다시 시도, `loading` 해제. 영상: 인코더 미지원·실패면 PNG 대안 안내, 사용자가 취소하면 오류가 아니라 상태 문구. 다시 시도가 또 실패하면 같은 Notice가 같은 자리에 남는다 | 다른 파일 선택 / 다시 시도 / PNG 저장 |
93
+ | 오류 | 아래는 제품 편집기의 복구 규칙이며 현재 도구의 영상·포스터는 SceneTimeline 상태 표시를 따른다. 네트워크·서버 요청이 없다(브라우저 안에서만 처리). ⑤ 자리에 [Notice](../components/notice.md) `tone="danger"`. 이미지 읽기 실패(형식 PNG·JPEG·WebP 밖·20MB·16384px 초과): 이전 이미지·설정 유지. 장면 JSON 실패(64KB 초과·형식 오류): 현재 장면 유지. PNG 내보내기 실패: Notice `action` 다시 시도. 포스터 실패: 타임라인 상태 문구와 기존 포스터 버튼으로 재시도, `loading` 해제. 영상: 인코더 미지원·실패면 PNG 대안 안내, 사용자가 취소하면 오류가 아니라 상태 문구. 다시 시도가 또 실패하면 같은 Notice가 같은 자리에 남는다 | 다른 파일 선택 / 다시 시도 / PNG 저장 |
94
94
  | 불러옴 | 상태 "파일명 · 가로 × 세로 화면을 불러왔어요.", 미리보기에 그려짐 | 편집·내보내기 |
95
- | 영상 출력 중 | 버튼 라벨 "영상 출력 N%"(보간 키), [출력 취소] 등장. 다른 탭으로 가면 취소 | 취소 |
95
+ | 영상 출력 중 | 진행률(0–100%)과 [출력 취소] 등장. 다른 탭으로 가거나 장면·이미지가 바뀌면 이전 출력 취소 | 취소 |
96
96
  | 모션 줄이기 | 재생 비활성, 안내 문구. 슬라이더로 정지 장면 확인 | 슬라이더 |
97
97
 
98
98
  ## 사용하는 지침
@@ -113,6 +113,13 @@ Native 스토리는 없다(장면 작성은 Web showcase에 둔다는 통합 계
113
113
  | [화면 여백과 너비](../tokens/layout.md) | gutter·`layout.contentMaxWidth` |
114
114
  | [간격](../tokens/spacing.md) | `spacing.sm`·`md`·`xl` |
115
115
 
116
+ ### 도구와 제품 편집기의 상태 경계
117
+
118
+ 이미지·장면을 다시 불러올 때 현재 미리보기와 편집 입력을 지우지 않는다. 처음부터 이미지가 없는 빈 상태만
119
+ 미리보기 영역 중앙에 표시하고, 파일 교체 실패는 이전 이미지·장면을 유지한 채 그 영역의 Notice로 설명한다.
120
+ 내보내기는 클릭 시점의 scene/image snapshot을 사용한다. 내보낸 파일과 이후 편집 상태를 구분하고
121
+ 완료 전 다운로드 성공을 표시하지 않는다. 장면 JSON에는 원본 이미지를 포함하지 않는다.
122
+
116
123
  ## 코드 골격
117
124
 
118
125
  도구를 다시 만들 필요는 없다. 아래는 같은 편집기 배치를 제품에서 짤 때의 뼈대다. 상태 문구는 상태→키 표로 고르고 파일 이름·크기는 보간 값으로 넘긴다.
@@ -172,11 +179,10 @@ const gutter = resolveWindowClass(window.innerWidth) === "compact" ? "compact" :
172
179
  // 없음. 목업 편집은 Web showcase 전용이다.
173
180
  ```
174
181
 
175
- ## 큰 글자·다크·좁은 폭
182
+ ## 다크·좁은 폭
176
183
 
177
184
  | 조건 | 바뀌는 것 |
178
185
  | --- | --- |
179
- | 큰 글자(`textScale` 2) | 모든 버튼 줄이 감긴다. canvas 안 글자는 출력 이미지의 일부라 글자 배율을 따르지 않는다 |
180
186
  | 다크 | 화면 UI는 테마 토큰. 출력 이미지 배경은 장면 설정(새벽·잉크·민트)이 정하고 테마와 무관하다 |
181
187
  | 좁은 폭 | 한 열 그대로. 폭 600 미만이면 Container gutter `compact` 16. 미리보기는 폭 100%·최대 70vh로 줄고 파일 입력도 폭 100% |
182
188
  | 넓은 폭 | 한 열 그대로, Container `content`로 최대 1200. 미리보기 옆에 설정을 두는 두 열 배치는 확인되지 않았다 |
@@ -187,4 +193,13 @@ const gutter = resolveWindowClass(window.innerWidth) === "compact" ? "compact" :
187
193
  - PNG는 화면에 보이는 canvas가 아니라 내보낼 때 새로 그린 canvas에서 만든다. 인코딩 중 설정을 바꿔도 결과가 바뀌지 않는다.
188
194
  - 초기화는 확인 없이 지운다. 제품 편집기에서 되돌리기 어려운 초기화에는 [InlineConfirm](../components/button.md)이나 [AlertDialog](../components/alert-dialog.md)를 둔다.
189
195
  - 구획 제목은 Heading으로 표시한다. 이전 Text heading 예제는 2026-10-06 제목 의미 구조를 맞추면서 수정했다.
190
- - 현재 스토리는 읽기·내보내기 실패를 상태 Text 문구로만 알리고(Notice·다시 시도 없음), 상태 문구를 템플릿 문자열로 조립한다. PNG 내보내기 중에는 `loading` 대신 `disabled` + 라벨 교체를 쓴다.
196
+ - 현재 스토리는 이미지·장면 읽기와 PNG 내보내기 실패를 Notice로 표시하고 PNG 실패에는 같은 내보내기 함수를 다시 실행하는 행동을 둔다. PNG 버튼은 `loading`을 사용한다. 상태 문구는 Showcase 소유이며 제품은 위 보간 키 예제로 지역화한다. 타임라인의 영상·포스터 실패 표현은 SceneTimeline이 별도로 소유한다.
197
+
198
+
199
+ ### 출력 작업과 편집 수명
200
+
201
+ 2026-10-13 실행 검수에서 같은 렌더의 연속 클릭이 출력을 중복 시작하고, 초기화·장면 교체 뒤 이전 출력이 새 상태에 결과를 쓰는 경합을 확인했다. PNG·포스터·영상은 각각 작업 식별자와 즉시 잠금을 사용한다. 초기화·파일 선택·언마운트 뒤 늦은 PNG 결과를 버리며, 타임라인의 장면·이미지 변경/언마운트는 진행 중 영상과 포스터 결과를 무효화한다. 사용자가 영상 취소 후 바로 다시 시작해도 이전 진행률·완료가 새 작업에 반영되지 않는다.
202
+
203
+ 주 PNG 내보내기는 시작 시 오프스크린 Canvas에 그린 복사본을 인코딩하므로 제목·비율 편집 중에도 그 출력의 픽셀은 바뀌지 않는다. 재시도 Notice가 사라진 뒤 초점은 완료 시 주 PNG 행동으로 돌아오되, 사용자가 다른 입력이나 빈 영역으로 옮긴 초점은 가져오지 않는다. 타임라인 재생은 부모 callback의 참조가 바뀌어도 기존 시계를 유지한다. 다운로드 안내는 브라우저에 파일 전송을 시작했다는 뜻이며 사용자의 디스크 저장 완료를 판정하지 않는다.
204
+
205
+ 최대 글자와 이를 모사하는 확대 조건은 공통 사용자 지침에 따라 설계·검증에서 제외한다. PNG 픽셀·제어 인코더 회귀를 실제 영상 코덱 품질이나 OS 다운로드 완료 근거로 확대하지 않는다.
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-13
8
8
  - 근거: `showcase/web/src/patterns/NotificationSettings.stories.tsx`, `showcase/native/src/NotificationSettings.stories.tsx`, `src/component-recipes.ts`(`topBarRecipe`·`bottomCtaRecipe`·`sectionRecipe`·`switchRecipe`), `src/container.ts`, `src/responsive.ts`(`resolveWindowClass`), `packages/react/src/styles.css`(`.hjm-top-bar`·`.hjm-bottom-cta`)
9
9
  - 스토리북: `배포/화면/설정/알림 설정`
10
10
 
@@ -78,12 +78,26 @@ Native 고정 배치
78
78
 
79
79
  | 상태 | 화면 모습 | 행동 |
80
80
  | --- | --- | --- |
81
- | 기본 | 스위치가 현재 값, 저장 버튼 활성 | 스위치 바꾸기, 저장 |
81
+ | 기본 | 스위치가 메모리 예제의 초기 저장값. 변경이 없으면 저장 버튼 비활성 | 스위치를 바꾸면 저장 가능. 저장값으로 되돌리면 다시 비활성 |
82
82
  | 로딩 | 스토리에 없음. 첫 값을 불러오는 동안 Section 안 스위치 자리에 [Skeleton](../components/skeleton.md) `shape="text"`, 저장 버튼 `disabled`. 저장 중이면 `primaryAction`에 `loading`(누름을 막고 같은 자리에 스피너) | 기다림 |
83
83
  | 빈 | 해당 없음(설정 항목은 항상 있다). 항목이 조건부면 Section 아래 안내 Text | — |
84
- | 오류 | 스토리에 없음. **불러오기 실패**: 스위치 대신 [Notice](../components/notice.md) `tone="danger"` + `action` 다시 시도, 저장 버튼 `disabled`. **저장 실패**: Section 끝에 Notice `tone="danger"`(action 없음 — 다시 시도는 저장 버튼이 맡는다), 사용자가 고른 스위치 값을 그대로 두고 `loading`만 푼다. 문구는 네트워크 실패 `settings.error.offline`·서버 실패 `settings.error.server`로 나눈다. 다시 시도가 또 실패하면 같은 Notice가 같은 자리에 남고 문구만 갱신한다. Native는 `announcement="assertive"`(기본 `none`) | 다시 시도 / 다시 저장 |
84
+ | 오류 | **최초 불러오기 실패**(스토리에는 없음): 스위치 대신 [Notice](../components/notice.md) `tone="danger"` + `action` 다시 시도, 저장 버튼 `disabled`. **저장 실패**(스토리에서 `다음 저장 실패시키기`로 실행): Section 끝에 Notice `tone="danger"`(action 없음 — 다시 시도는 저장 버튼이 맡는다), 사용자가 고른 스위치 값을 그대로 두고 `loading`만 푼다. 문구는 네트워크 실패 `settings.error.offline`·서버 실패 `settings.error.server`로 나눈다. 다시 시도가 또 실패하면 같은 Notice가 같은 자리에 남고 문구만 갱신한다. Native는 `announcement="assertive"`(기본 `none`) | 다시 시도 / 다시 저장 |
85
85
  | 저장됨 | Section 끝에 Notice `tone="success"`(제목 + 켜짐/꺼짐 요약). 버튼은 "저장했어요"로 바뀌고 비활성. Native Notice `announcement="polite"` | 스위치를 다시 바꾸면 저장 상태가 풀린다 |
86
86
 
87
+
88
+ ### 설정 초안의 저장 경계
89
+
90
+ 두 스토리는 `showcase/shared/notification-settings.ts`에서 기존 `createActionSession`을 사용한다. 마지막 저장값과 현재 초안을 따로 표시하며, 저장 중에도 스위치를 수정할 수 있다. 성공은 제출 snapshot만 확정하고 새 초안은 남긴다. 저장 실패 후 하단의 같은 버튼으로 **현재 초안**을 다시 제출한다. 중복 저장은 잠그며 값을 저장값으로 되돌리면 불필요한 저장을 막는다. `예제 실행`의 실패 스위치는 다음 저장 한 번에만 적용한다. 실제 조회·권한·푸시·계정 API 없이 화면이 열려 있는 동안만 유지되는 메모리 데모다.
91
+
92
+ 2026-10-13 전수 점검에서 최초 조회와 변경 요청의 상태를 같은 골격으로 설명하던 부분을 구체화했다.
93
+
94
+ | 상황 | 연결 | 유지·복구 |
95
+ | --- | --- | --- |
96
+ | 최초 조회 실패 | 설정값 대신 오류·재시도 | 기본 false를 실제 서버 설정처럼 저장하지 않는다 |
97
+ | 사용자 수정 | draft와 마지막 saved 분리 | 값을 바꾸면 저장됨 표시를 해제한다 |
98
+ | 저장 중 재수정 | 제출 snapshot 보관 | 성공은 제출한 값에만 적용한다. 현재 draft가 다르면 다시 저장 가능 상태 |
99
+ | 저장 실패 | 현재 draft 유지 | 하단 저장 버튼으로 재시도하고 별도의 경쟁 저장 버튼을 만들지 않는다 |
100
+
87
101
  ## 사용하는 지침
88
102
 
89
103
  | 지침 | 쓰는 곳 |
@@ -198,7 +212,7 @@ const gutter = resolveWindowClass(width) === "compact" ? "compact" : "regular";
198
212
  | 하단 바 고정 | `position="sticky"` | prop 없음 — `ScrollView` 바깥에 둔다 |
199
213
  | 안전 영역 | CSS `env(safe-area-inset-*)`와 `safeAreaTop`/`safeAreaBottom` 중 큰 값 | `safeAreaTop`/`safeAreaBottom`만(기본 0) |
200
214
  | 저장·오류 알림 | Notice가 `role="status"` | Notice `announcement` 명시(기본 `none`): 저장됨 `polite`, 오류 `assertive` |
201
- | 스토리 상태 | 기본·큰 글자(어두운 테마 스토리 없음) | 기본만 |
215
+ | 스토리 상태 | 기본·다크·큰 글자, 기본 화면에서 저장 실패와 재시도 실행 | 기본·다크·큰 글자, 동일 메모리 저장 흐름 |
202
216
 
203
217
  ## 함정
204
218
 
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.14.0
7
- - 검토일: 2026-10-07
7
+ - 검토일: 2026-10-13
8
8
  - 근거: `reference-adoption-previews.tsx`, `reference-adoption.css`
9
9
  - 스토리북: `배포/화면/소개/기능 카드와 주 행동`
10
10
 
@@ -51,11 +51,11 @@
51
51
  | 상태 | 화면 모습 | 행동 |
52
52
  | --- | --- | --- |
53
53
  | 기본 | 설명과 비교·주/보조 행동·조건 안내 | 첫 입력 또는 사용 방법으로 이동 |
54
- | 로딩 | 실제 이미지 로드 상태 | 주 행동은 제품 가용성에 맞춤 |
54
+ | 로딩 | 현재 예제는 로컬 이미지 비교. 제품 네트워크 이미지가 있으면 미리보기 내부에 상태 배치 | 주 행동은 제품 가용성에 맞춤 |
55
55
  | 빈 | 제품 자료가 없으면 비교 영역 생략 | 문구와 첫 행동 유지 |
56
56
  | 오류 | 이미지 실패 대체 또는 저장 실패 문구·제목 유지 | 현재 제목으로 다시 저장 |
57
- | 진행 중 | 주 행동 spinner, 보조 행동 비활성 | 중복 실행 방지, 입력 내용은 제품 상태에 유지 |
58
- | 완료 | 확정한 제목과 성공 문구 | 다음 입력 가능 |
57
+ | 진행 중 | 주 행동 spinner, 보조 행동 비활성 | 중복 실행과 늦게 도착한 소개 복귀 콜백을 막는다. 제목은 계속 수정할 수 있으며 제출 snapshot과 구분한다 |
58
+ | 완료 | 확정한 제목을 별도로 표시. 현재 초안이 같으면 성공 문구와 비활성 `저장했어요` 버튼, 다르면 현재 제목의 미저장 안내 | 제목을 바꾸면 다시 저장 가능 |
59
59
 
60
60
  ## 사용하는 지침
61
61
 
@@ -65,12 +65,23 @@
65
65
  | [Stack](../components/stack.md) | 읽기 순서와 간격 |
66
66
  | [BottomCTA](../components/bottom-cta.md) · [BottomInfo](../components/bottom-info.md) | 주·보조 행동과 항상 보이는 조건 |
67
67
 
68
+ ### 공개 API와 저장 범위
69
+
70
+ `ProductBentoPreview`는 양 Showcase의 구성 예제이며 제품에서 import하는 공개 화면 API가 아니다.
71
+ 제품은 기존 ScreenLayout·Grid·Surface·Heading·BottomCTA·BottomInfo를 사용한다.
72
+ 현재 예제의 그림은 로컬 예시이므로 이미지의 네트워크 로딩·실패를 검증한 상태가 아니다.
73
+ 제품은 미리보기 영역 안에만 이미지 상태를 두고 제목 입력·CTA를 화면 전체 로딩으로 교체하지 않는다.
74
+ 저장 pending은 BottomCTA 주 행동에만 연결하고 서버 확정 전 저장된 제목을 바꾸지 않는다.
75
+ 실패 시 현재 초안과 마지막 확정 제목을 따로 유지한다. 재시도는 현재 초안을 새 snapshot으로 제출한다.
76
+ 저장 중 수정한 제목은 이전 요청의 성공으로 확정하지 않는다. 현재 초안과 확정 제목이 다르면 미저장 안내를 유지하며, 같으면 중복 저장을 막는다. 소개로 돌아가는 행동은 현재 저장 세션이 pending이면 지연된 콜백도 무시한다.
77
+
68
78
  ## 코드 골격
69
79
 
70
80
  ```tsx
71
81
  // Web
82
+ import { Heading } from "@hjmds/react/heading";
72
83
  <Stack gap="lg">
73
- <Text variant="heading">{title}</Text>
84
+ <Heading level="level3" semanticLevel={1}>{title}</Heading>
74
85
  <Surface padding="lg"><ImageComparison {...comparison} /></Surface>
75
86
  <BottomCTA primaryAction={{ label: startLabel, onClick: start }} secondaryAction={{ label: helpLabel, onClick: showHelp }} />
76
87
  <BottomInfo items={conditions} />
@@ -93,11 +104,10 @@
93
104
  Native는 같은 public component entry를 `@hjmds/react-native`로 바꾸고 Button에 `onPress`를 쓴다.
94
105
  Native ImageComparison에는 증감 행동 label을 추가한다. 제품의 계정/저장 안내와 데이터는 제품이 공급한다.
95
106
 
96
- ## 큰 글자·다크·좁은 폭
107
+ ## 다크·좁은 폭
97
108
 
98
109
  | 조건 | 바뀌는 것 |
99
110
  | --- | --- |
100
- | 큰 글자 | 제목·본문·버튼이 자연스럽게 줄바꿈 |
101
111
  | 다크 | provider semantic palette 사용 |
102
112
  | 좁은 폭 | 예제 Web 48rem 이하에서 1열. Native는 항상 단일 순서 |
103
113
 
@@ -113,5 +123,4 @@ Ente 캡처에서 플랫폼 조건에 맞춘 행동 묶음을 확인했다. HJM
113
123
  중복 요청·pending·실패 후 재시도를 검증하며 350ms 지연은 데모 전용이다. 실패 시 현재 제목은
114
124
  유지하고 다시 저장은 현재 입력 snapshot을 제출한다. 저장된 제목은 마지막으로 확정한 값이다.
115
125
 
116
- Web 큰 글자에서는 BottomCTA 계약대로 주 행동이 위, 보조 행동이 아래로 쌓인다. 모바일 Native도
117
- 같은 계약을 따르며 화면 host가 스크롤·키보드·safe area를 제공한다. Native 예제에는 기존 ScreenLayout과 KeyboardAvoiding을 연결한다. 스크롤 없는 Canvas에서 CTA가 화면 밖으로 잘렸던 2026-10-07 실제 확인에 따른 수정이다. 제품은 host safe area/keyboard offset을 실제 내비게이션에 맞춘다.
126
+ 화면 host가 스크롤·키보드·safe area를 제공한다. Native 예제에는 기존 ScreenLayout과 KeyboardAvoiding을 연결한다. 스크롤 없는 Canvas에서 CTA가 화면 밖으로 잘렸던 2026-10-07 실제 확인에 따른 수정이다. 제품은 host safe area/keyboard offset을 실제 내비게이션에 맞춘다.
@@ -3,8 +3,8 @@
3
3
  - 단계: 화면
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
- - 적용: 미게시(1.12.1 이후)
7
- - 검토일: 2026-10-06
6
+ - 적용: 구성 API 지침 참조
7
+ - 검토일: 2026-10-13
8
8
  - 근거: [반복 화면 조합](../../screen-patterns.md); 공통 API와 실제 Web·Native 예제의 슬롯·상태를 대조해 중복 조립 방지. 2026-10-06 사용자 승인으로 스토리북 배포(이전 `실험/화면/표현 비교/같은 기록의 세 가지 구성`, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
9
9
  - 스토리북: `배포/화면/화면 틀과 도구/기록 표현 비교`
10
10
 
@@ -39,9 +39,9 @@ host: 남은 높이·safe area·키보드
39
39
  | 상태 | 화면 모습 | 행동 |
40
40
  | --- | --- | --- |
41
41
  | 기본 | 표현 방식 선택 → 검색 → 같은 데이터 목록·읽기·이미지 표현 | 각 공개 콜백을 제품 상태에 연결 |
42
- | 로딩 | 최초 조회는 본문 상태, 저장은 해당 행동 pending | 중복 제출 차단; 성공을 먼저 표시하지 않음 |
42
+ | 로딩 | 동기 메모리 예제에는 없음. 제품 최초 조회는 본문 중앙 상태, 재조회는 기존 기록을 유지하고 저장은 해당 행동 pending | 중복 제출 차단; 성공을 먼저 표시하지 않음 |
43
43
  | 빈 | 검색 결과 0건: EmptyState `compact`(상태 문구는 0개로 남는다) | `검색 초기화` |
44
- | 오류 | 데모 저장은 메모리에서만 유지; 표현 전환은 검색·저장 상태를 초기화하지 않음 | 실패 원인과 재시도 경로 제공 |
44
+ | 오류 | 동기 예제는 서버 실패를 제공하지 않음. 제품 최초 실패는 중앙 state/error + stateAction, 재조회 실패는 notice와 기존 목록, 저장 실패는 해당 항목 옆에서 복구 | 검색·보기·저장 상태와 열린 상세 유지 |
45
45
 
46
46
  ## 사용하는 지침
47
47
 
@@ -55,6 +55,16 @@ host: 남은 높이·safe area·키보드
55
55
  | [Grid](../components/grid.md) | 이미지 중심 두 열 |
56
56
  | [EmptyState](../components/empty-state.md) | 결과 없음 |
57
57
 
58
+ ### 현재 예제와 제품 비동기 상태
59
+
60
+ 실행 원본은 `showcase/web/src/patterns/reference-flow-previews.tsx`와
61
+ `showcase/native/src/reference-flow-previews.tsx`의 RecordComparison이다. 공개 RecordComparison 화면 export는 없다.
62
+ 현재 예제는 같은 메모리 목록·즉시 저장 토글·세 표현을 비교한다. 아래 코드는 제품 상태를 연결하는 골격이다.
63
+ `hasLoaded`는 0건을 포함해 성공 응답을 받은 이력이다. 재조회 때 false로 되돌리지 않는다.
64
+ 최초 실패는 ScreenLayout stateAction에 재시도를 주고, 재조회 실패는 notice에 Notice와 재시도를 둔다.
65
+ 검색어·보기 방식은 제품 상태에 유지하며 제품의 전체 빈 결과는 ScreenLayout 본문 중앙에 둔다.
66
+ 예제의 compact EmptyState는 카드 구성 비교용 인라인 표시이며 전체 빈 화면의 배치 기준이 아니다.
67
+
58
68
  ## 코드 골격
59
69
 
60
70
  ```tsx
@@ -74,7 +84,10 @@ type Mode = keyof typeof modeKey;
74
84
  <ScreenLayout
75
85
  title={t("records.compare.title")}
76
86
  description={t("records.compare.intro")}
77
- state={loading ? { kind: "loading", title: t("records.loading") } : { kind: "ready" }}
87
+ stateAction={error && !hasLoaded ? retryAction : null}
88
+ notice={hasLoaded && error ? refreshNotice : null}
89
+ state={loading && !hasLoaded ? { kind: "loading", title: t("records.loading") }
90
+ : error && !hasLoaded ? { kind: "error", title: t("records.error") } : { kind: "ready" }}
78
91
  >
79
92
  <Stack gap="xl">
80
93
  <SegmentedControl label={t("records.mode.label")} value={mode} onValueChange={(value) => setMode(value as Mode)}
@@ -115,7 +128,10 @@ type Mode = keyof typeof modeKey;
115
128
  <ScreenLayout
116
129
  title={t("records.compare.title")}
117
130
  description={t("records.compare.intro")}
118
- state={loading ? { kind: "loading", title: t("records.loading") } : { kind: "ready" }}
131
+ stateAction={error && !hasLoaded ? retryAction : null}
132
+ notice={hasLoaded && error ? refreshNotice : null}
133
+ state={loading && !hasLoaded ? { kind: "loading", title: t("records.loading") }
134
+ : error && !hasLoaded ? { kind: "error", title: t("records.error") } : { kind: "ready" }}
119
135
  >
120
136
  <Stack gap="xl">
121
137
  <SegmentedControl label={t("records.mode.label")} value={mode} onValueChange={setMode}
@@ -143,11 +159,10 @@ type Mode = keyof typeof modeKey;
143
159
 
144
160
  기록·검색·저장은 제품 상태다. 표현 전환은 같은 `results`를 다시 그리기만 하고 검색어·저장 상태를 초기화하지 않는다. 콜백·데이터·지역화 함수는 제품에서 공급한다. Web·Native import와 필수 props는 위 API 지침에서 확인한다.
145
161
 
146
- ## 큰 글자·다크·좁은 폭
162
+ ## 다크·좁은 폭
147
163
 
148
164
  | 조건 | 바뀌는 것 |
149
165
  | --- | --- |
150
- | 큰 글자 | 2배 글자에서 제목·행은 내용 높이로 증가. footer·닫기·입력 필드가 겹치지 않는지 확인 |
151
166
  | 다크 | semantic 색으로 내용과 표면을 함께 전환; 예제 브랜드 색을 제품 기본값으로 복사하지 않음 |
152
167
  | 좁은 폭 | 320px부터 한 열로 읽기 순서 유지. 가상화 본문은 scroll=content, 중첩 스크롤 금지 |
153
168
  | 키보드 | Native host가 safe area와 키보드를 한 번 처리; Web은 포커스된 입력과 footer 가림 확인 |
@@ -155,5 +170,9 @@ type Mode = keyof typeof modeKey;
155
170
  ## 함정
156
171
 
157
172
  - 데모 저장은 메모리에서만 유지; 표현 전환은 검색·저장 상태를 초기화하지 않음.
158
- - 결과 개수 문구는 Web `Text role="status"`, Native는 iOS 알림을 보내는 live region(예제의 `PatternStatus`)으로 둔다. Native `accessibilityLiveRegion`만으로는 iOS에서 읽히지 않는다.
159
- - Storybook은 실제 서버·OS 권한·라우터 연동 증거가 아니다. 기본·다크·큰 글자와 실패/복구를 각각 확인한다.
173
+ - 결과 개수 문구는 Web `Text role="status"`, Native는 iOS 알림을 포함한 공개 `StatusText`으로 둔다. Native `accessibilityLiveRegion`만으로는 iOS에서 읽히지 않는다.
174
+ - Storybook은 실제 서버·OS 권한·라우터 연동 증거가 아니다. 기본·다크와 실패/복구를 각각 확인한다.
175
+
176
+ ### 검색 복구와 상세 복귀
177
+
178
+ Web 예제의 검색 초기화는 사라질 빈 상태 버튼에서 기존 검색 입력으로 초점을 옮긴다. 보기 방식과 저장 목록은 그대로 유지한다. 상세를 닫으면 열었던 읽기 행동으로 돌아간다. Safari 포인터 클릭은 버튼을 focus하지 않을 수 있어 실제 클릭의 `event.currentTarget`을 Sheet `returnFocusRef`에 기록한다. Native 예제는 열 때의 host ref를 고정해 상세 안의 완료 행동이 복귀 대상을 덮지 않게 한다. host ref 검사는 실제 OS 접근성 초점 검증과 구분한다.
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-07
7
+ - 검토일: 2026-10-13
8
8
  - 근거: [브랜드 경계](../../brand-boundary.md), [테마 팔레트](../../theme-palette.md), [테마 사용법](../../theming.md), `src/colors.ts`, `src/semantic-colors.ts`, `src/component-recipes.ts`(`textRecipe`·`iconRecipe`), `src/base-recipes.ts`(`buttonRecipe`·`surfaceRecipe`), `packages/react/src/theme.ts`, `packages/react-native/src/provider.tsx`
9
9
  - 스토리북: `배포/토큰/색과 글자/색상`
10
10
 
@@ -69,7 +69,8 @@
69
69
  | `accentTint` | `weak` 0.1 · `base` 0.15 · `strong` 0.2 · `border` 0.3 | — | `accentTint` | 피드백 색을 옅게 깔 때 쓰는 alpha |
70
70
 
71
71
  도메인 상태(예: "배송 지연")는 제품 어댑터에서 `info`·`success`·`warning`·`attention`·`danger` 중 하나로 매핑한다.
72
- 피드백 색은 `brandPalette`로 바꿀 수 없다(브랜드와 오류·성공이 같은 색이 되는 것을 막는다).
72
+ `info`·`success`·`warning`·`attention`의 accent 색은 `brandPalette` 변경 대상이 아니다.
73
+ `danger`·`dangerFill`·`onDanger`는 팔레트 역할이므로 변경할 수 있지만 오류 의미와 지정 대비 쌍을 유지해야 한다.
73
74
 
74
75
  ### 테두리·상호작용
75
76
 
@@ -141,7 +142,9 @@ import { HjmProvider } from "@hjmds/react/provider";
141
142
  | 피드백 색 | `--hjm-accent-<tone>`, `--hjm-accent-fill-<tone>` | `theme.palette.statusAccents` · `statusAccentFills` |
142
143
  | alpha 섞기 | CSS `color-mix` | `resolveColorReference` 또는 `withAlpha` |
143
144
 
144
- ### 디자인 프로필(실험·미게시)
145
+ ### 디자인 프로필
146
+
147
+ 기본 프로필은 1.15.0, 후속 글자·토큰 소비 개선은 1.16.0부터 제공한다.
145
148
 
146
149
  위 표는 프로필 없는 기본값이다. 앱이 [디자인 프로필](../../design-profile.md)을 선택하면 연결된
147
150
  컴포넌트는 Web CSS 변수 또는 Native `theme.tokens`/semantic palette를 읽는다.
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-07
7
+ - 검토일: 2026-10-13
8
8
  - 근거: `src/foundations.ts`(`shadow`·`opacity`·`stateLayer`·`overlay`·`backdrop`·`scrim`), `src/component-contracts.ts`(`floatingSurfaceContract`), `src/component-recipes.ts`(`dialogRecipe`·`sheetRecipe`·`toastRecipe`·`bottomCtaRecipe`), `packages/react/src/theme.ts`, `packages/react/src/styles.css`, `packages/react-native/src/primitives.tsx`
9
9
  - 스토리북: `배포/토큰/표면과 움직임/그림자와 투명도`
10
10
 
@@ -85,7 +85,9 @@ const s = shadow.floating;
85
85
 
86
86
  2026-10-06 후속 검수에서 정의만 있던 Web 그림자 변수를 실제 떠 있는 표면에 연결했다. CommandPalette·Tour 같은 큰 강조 표면은 `shadow.overlay`, 일반 popup·Surface는 `shadow.floating`을 쓴다. Native Surface의 별도 blur 6도 floating token으로 맞췄다. 포커스 링·선택 테두리·스위치 손잡이의 inset 표현은 높이 그림자가 아니므로 각 컴포넌트 상태 계약을 유지한다.
87
87
 
88
- ### 디자인 프로필(실험·미게시)
88
+ ### 디자인 프로필
89
+
90
+ 기본 프로필은 1.15.0, 후속 글자·토큰 소비 개선은 1.16.0부터 제공한다.
89
91
 
90
92
  위 표는 프로필 없는 기본값이다. 앱이 [디자인 프로필](../../design-profile.md)을 선택하면 연결된
91
93
  컴포넌트는 Web CSS 변수 또는 Native `theme.tokens`/semantic palette를 읽는다.
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.16.0
7
- - 검토일: 2026-10-08
7
+ - 검토일: 2026-10-13
8
8
  - 근거: [서체 역할 계약](../../font-roles.md), `src/foundations.ts`, `src/design-profile.ts`, 양 renderer Text/Heading/Provider
9
9
  - 스토리북: `배포/토큰/색과 글자/표시·읽기·기술 글자`
10
10
 
@@ -73,7 +73,7 @@ const design = defineHjmDesignProfile({ extends: "paper", tokens: { fontFamily:
73
73
  - 같은 역할을 앱마다 또 다른 Provider/전역 CSS 엔진으로 구현하지 않는다. 설정·자산·로딩은 앱이 관리한다.
74
74
  - 글꼴 전환 시 입력 또는 열린 상세를 key로 다시 마운트하지 않는다. 상태는 기존 구성/제품이 소유한다.
75
75
 
76
- ### 조작 라벨과 읽기 본문의 구분 — 미게시
76
+ ### 조작 라벨과 읽기 본문의 구분
77
77
 
78
78
  2026-10-07 소비 감사에서 Native 조작 라벨 24곳이 body/bodyLarge 크기를 사용하면서 읽기 서체로 해석됐다.
79
79
  Button의 내부 라벨·제공자 버튼·ToggleGroup·선택 컨트롤·Switch·SegmentedControl·Chip·Tabs·Menu·Select·
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-13
8
8
  - 근거: [오버레이 스택](../../overlay-stack.md), `src/foundations.ts`(`layer`), `src/component-recipes.ts`(`toastRecipe.viewport.layer`·`tooltipRecipe.layer`), `packages/react/src/styles.css`, `packages/react-native/src/feedback.tsx`
9
9
  - 스토리북: `배포/토큰/표면과 움직임/겹침 순서`
10
10
 
@@ -69,8 +69,8 @@ import { layer } from "@hjmds/design-contracts/foundations";
69
69
 
70
70
  ### Web 표면별 실제 값
71
71
 
72
- 1.12.1까지 Web은 토큰과 다른 500–1400 숫자를 직접 썼다. 아래 값은 미게시(1.12.1 이후) 변경이며
73
- `.changeset/shared-layer-elevation-consumption.md`가 이전 값을 함께 적는다. HJM 층끼리의 상대 순서는 Popover를 빼면 그대로다.
72
+ 아래 표는 현재 소스의 Web 소비 값과 1.12.1의 이전 값을 비교한다. 과거 값은 기존 제품의 층 충돌을
73
+ 이관할 때만 참고하고 새 코드에는 토큰을 사용한다. HJM 층끼리의 상대 순서는 Popover를 빼면 그대로다.
74
74
 
75
75
  | Web 표면 | 토큰 | 값 | 1.12.1 값 |
76
76
  | --- | --- | --- | --- |
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-07
7
+ - 검토일: 2026-10-13
8
8
  - 근거: `src/foundations.ts`(`motion`·`easing`·`motionPreset`·`spring`), `packages/react/src/theme.ts`, `packages/react/src/provider.tsx`, `packages/react-native/src/provider.tsx`
9
9
  - 스토리북: `배포/토큰/표면과 움직임/모션`
10
10
 
@@ -87,7 +87,7 @@ Web에서 JS로 직접 움직일 때는 `useHjmTheme().environment.reducedMotion
87
87
  | 곡선 | `cubic-bezier(...)` | `Easing.bezier(...)` |
88
88
  | 스프링 | 쓰지 않음 | `spring.*` |
89
89
 
90
- 프로필 선택 이동 (미게시, 1.14.0 이후):
90
+ 프로필 선택 이동(기본 프로필 1.15.0부터 제공):
91
91
 
92
92
  `designProfile.interactions.selectionMotion`은 SegmentedControl과 appearance를 생략한
93
93
  [Tabs](../components/tabs.md)에 연결된다. Tabs의 `slide`는 `motion.normal` 200ms·
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-07
7
+ - 검토일: 2026-10-13
8
8
  - 근거: `src/foundations.ts`(`radius`), `src/base-recipes.ts`(`buttonRecipe.shapes`·`fieldRecipe.shapes`·`surfaceDefaults`), `src/component-recipes.ts`, `packages/react/src/theme.ts`
9
9
  - 스토리북: `배포/토큰/표면과 움직임/둥글기`
10
10
 
@@ -52,7 +52,9 @@ const styles = StyleSheet.create({ thumb: { borderRadius: radius.md, overflow: "
52
52
  - HJM 컴포넌트의 radius를 `style`·`className`으로 덮지 않는다. Button·필드는 `shape`, Surface는 `radius` prop으로만 고른다.
53
53
  - 원형을 만들려고 `50%`나 폭의 절반을 계산하지 않는다. `radius.full`을 쓴다.
54
54
 
55
- ### 디자인 프로필(실험·미게시)
55
+ ### 디자인 프로필
56
+
57
+ 기본 프로필은 1.15.0, 후속 글자·토큰 소비 개선은 1.16.0부터 제공한다.
56
58
 
57
59
  위 표는 프로필 없는 기본값이다. 앱이 [디자인 프로필](../../design-profile.md)을 선택하면 연결된
58
60
  컴포넌트는 Web CSS 변수 또는 Native `theme.tokens`/semantic palette를 읽는다.