@hjmds/design-contracts 1.12.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. package/dist/avatar-fallback.d.ts +11 -0
  2. package/dist/avatar-fallback.d.ts.map +1 -1
  3. package/dist/avatar-fallback.js +21 -0
  4. package/dist/avatar-fallback.js.map +1 -1
  5. package/dist/base-recipes.d.ts +17 -0
  6. package/dist/base-recipes.d.ts.map +1 -1
  7. package/dist/base-recipes.js +17 -0
  8. package/dist/base-recipes.js.map +1 -1
  9. package/dist/catalog.d.ts +25 -0
  10. package/dist/catalog.d.ts.map +1 -1
  11. package/dist/command-palette.d.ts +14 -9
  12. package/dist/command-palette.d.ts.map +1 -1
  13. package/dist/command-palette.js +8 -9
  14. package/dist/command-palette.js.map +1 -1
  15. package/dist/component-recipes.d.ts +17 -0
  16. package/dist/component-recipes.d.ts.map +1 -1
  17. package/dist/component-recipes.js +5 -0
  18. package/dist/component-recipes.js.map +1 -1
  19. package/dist/provider-button.d.ts.map +1 -1
  20. package/dist/provider-button.js +3 -0
  21. package/dist/provider-button.js.map +1 -1
  22. package/dist/reactions.d.ts +10 -0
  23. package/dist/reactions.d.ts.map +1 -1
  24. package/dist/reactions.js +7 -0
  25. package/dist/reactions.js.map +1 -1
  26. package/dist/screen-patterns.d.ts +147 -0
  27. package/dist/screen-patterns.d.ts.map +1 -0
  28. package/dist/screen-patterns.js +149 -0
  29. package/dist/screen-patterns.js.map +1 -0
  30. package/dist/slider.d.ts +8 -0
  31. package/dist/slider.d.ts.map +1 -1
  32. package/dist/slider.js +6 -1
  33. package/dist/slider.js.map +1 -1
  34. package/dist/upload-item.d.ts +5 -0
  35. package/dist/upload-item.d.ts.map +1 -1
  36. package/dist/upload-item.js +5 -0
  37. package/dist/upload-item.js.map +1 -1
  38. package/dist/version.d.ts +1 -1
  39. package/dist/version.js +1 -1
  40. package/dist/version.js.map +1 -1
  41. package/docs/action-session.md +3 -3
  42. package/docs/agreement.md +5 -0
  43. package/docs/avatar-fallback.md +7 -0
  44. package/docs/bottom-navigation.md +6 -0
  45. package/docs/brand-boundary.md +1 -1
  46. package/docs/button-label.md +6 -0
  47. package/docs/clipboard.md +3 -0
  48. package/docs/command-palette.md +45 -2
  49. package/docs/consumer-policy.md +5 -1
  50. package/docs/data-table.md +6 -4
  51. package/docs/dialog.md +8 -2
  52. package/docs/form.md +51 -0
  53. package/docs/generated/component-maturity.md +1 -1
  54. package/docs/generated/renderer-evidence.json +3 -3
  55. package/docs/generated/renderer-evidence.md +1 -1
  56. package/docs/generated/showcase-manifest.json +1 -1
  57. package/docs/link.md +8 -0
  58. package/docs/migration-native-legacy-removal.md +45 -1
  59. package/docs/optional-adapters.md +1 -1
  60. package/docs/password-field.md +5 -0
  61. package/docs/product-composition-adoption.md +40 -0
  62. package/docs/progress.md +19 -1
  63. package/docs/provider-button.md +13 -0
  64. package/docs/result.md +3 -0
  65. package/docs/screen-chrome.md +10 -0
  66. package/docs/screen-patterns.md +376 -0
  67. package/docs/sheet.md +12 -0
  68. package/docs/splitter.md +8 -2
  69. package/docs/theming.md +36 -29
  70. package/docs/toggle-group.md +7 -0
  71. package/docs/tour.md +7 -1
  72. package/docs/tree.md +5 -2
  73. package/docs/upload-item.md +7 -0
  74. package/docs/usage/README.md +236 -0
  75. package/docs/usage/STANDARD.md +108 -0
  76. package/docs/usage/components/accordion.md +107 -0
  77. package/docs/usage/components/activity-heatmap.md +104 -0
  78. package/docs/usage/components/affix.md +86 -0
  79. package/docs/usage/components/agreement.md +129 -0
  80. package/docs/usage/components/alert-dialog.md +130 -0
  81. package/docs/usage/components/anchor.md +96 -0
  82. package/docs/usage/components/aspect-ratio.md +89 -0
  83. package/docs/usage/components/asset.md +126 -0
  84. package/docs/usage/components/auth-provider-button.md +116 -0
  85. package/docs/usage/components/auth-screen-layout.md +129 -0
  86. package/docs/usage/components/avatar.md +114 -0
  87. package/docs/usage/components/badge.md +84 -0
  88. package/docs/usage/components/bottom-cta.md +125 -0
  89. package/docs/usage/components/bottom-info.md +99 -0
  90. package/docs/usage/components/bottom-navigation.md +136 -0
  91. package/docs/usage/components/breadcrumb.md +81 -0
  92. package/docs/usage/components/button.md +118 -0
  93. package/docs/usage/components/calendar.md +122 -0
  94. package/docs/usage/components/card.md +110 -0
  95. package/docs/usage/components/carousel.md +113 -0
  96. package/docs/usage/components/celebration.md +96 -0
  97. package/docs/usage/components/chat-message.md +122 -0
  98. package/docs/usage/components/chat-screen.md +112 -0
  99. package/docs/usage/components/checkbox-group.md +104 -0
  100. package/docs/usage/components/checkbox.md +103 -0
  101. package/docs/usage/components/chip.md +104 -0
  102. package/docs/usage/components/code-block.md +111 -0
  103. package/docs/usage/components/collapsible.md +112 -0
  104. package/docs/usage/components/color-picker.md +86 -0
  105. package/docs/usage/components/combobox.md +137 -0
  106. package/docs/usage/components/command-palette.md +125 -0
  107. package/docs/usage/components/comment-thread-screen.md +125 -0
  108. package/docs/usage/components/container.md +98 -0
  109. package/docs/usage/components/content-transition.md +101 -0
  110. package/docs/usage/components/context-menu.md +136 -0
  111. package/docs/usage/components/counter-badge.md +107 -0
  112. package/docs/usage/components/data-table.md +122 -0
  113. package/docs/usage/components/date-picker.md +142 -0
  114. package/docs/usage/components/date-range-picker.md +111 -0
  115. package/docs/usage/components/description-list.md +103 -0
  116. package/docs/usage/components/design-system-provider.md +124 -0
  117. package/docs/usage/components/dialog.md +176 -0
  118. package/docs/usage/components/divider.md +89 -0
  119. package/docs/usage/components/editor-screen.md +126 -0
  120. package/docs/usage/components/effect-surface.md +120 -0
  121. package/docs/usage/components/empty-state.md +114 -0
  122. package/docs/usage/components/field.md +129 -0
  123. package/docs/usage/components/file-picker.md +114 -0
  124. package/docs/usage/components/floating-action-button.md +138 -0
  125. package/docs/usage/components/form.md +162 -0
  126. package/docs/usage/components/grid.md +99 -0
  127. package/docs/usage/components/heading.md +87 -0
  128. package/docs/usage/components/icon-button.md +126 -0
  129. package/docs/usage/components/icon.md +105 -0
  130. package/docs/usage/components/image.md +122 -0
  131. package/docs/usage/components/keyboard-avoiding.md +93 -0
  132. package/docs/usage/components/keyboard-dock.md +110 -0
  133. package/docs/usage/components/keyboard-form-scroll-view.md +95 -0
  134. package/docs/usage/components/keyboard-motion-provider.md +86 -0
  135. package/docs/usage/components/layout.md +117 -0
  136. package/docs/usage/components/link.md +121 -0
  137. package/docs/usage/components/list-detail-screen.md +103 -0
  138. package/docs/usage/components/list-row.md +124 -0
  139. package/docs/usage/components/list.md +119 -0
  140. package/docs/usage/components/load-more.md +115 -0
  141. package/docs/usage/components/masonry.md +109 -0
  142. package/docs/usage/components/media-selection-screen.md +119 -0
  143. package/docs/usage/components/mentions.md +119 -0
  144. package/docs/usage/components/menu.md +129 -0
  145. package/docs/usage/components/menubar.md +93 -0
  146. package/docs/usage/components/message-composer.md +124 -0
  147. package/docs/usage/components/moderation-screen.md +113 -0
  148. package/docs/usage/components/notice.md +106 -0
  149. package/docs/usage/components/notification-inbox-screen.md +97 -0
  150. package/docs/usage/components/notification-item.md +98 -0
  151. package/docs/usage/components/number-field.md +131 -0
  152. package/docs/usage/components/onboarding-screen.md +106 -0
  153. package/docs/usage/components/otp-field.md +101 -0
  154. package/docs/usage/components/pagination.md +82 -0
  155. package/docs/usage/components/password-field.md +137 -0
  156. package/docs/usage/components/permission-screen.md +107 -0
  157. package/docs/usage/components/photo-source-sheet.md +119 -0
  158. package/docs/usage/components/popover.md +108 -0
  159. package/docs/usage/components/profile-screen.md +89 -0
  160. package/docs/usage/components/progress.md +122 -0
  161. package/docs/usage/components/qr-code.md +122 -0
  162. package/docs/usage/components/radio-group.md +124 -0
  163. package/docs/usage/components/radio.md +104 -0
  164. package/docs/usage/components/result.md +116 -0
  165. package/docs/usage/components/saved-items-screen.md +126 -0
  166. package/docs/usage/components/screen-layout.md +119 -0
  167. package/docs/usage/components/search-field.md +120 -0
  168. package/docs/usage/components/search-screen.md +215 -0
  169. package/docs/usage/components/section.md +111 -0
  170. package/docs/usage/components/segmented-control.md +138 -0
  171. package/docs/usage/components/select.md +142 -0
  172. package/docs/usage/components/settings-screen.md +126 -0
  173. package/docs/usage/components/shared-transition-element.md +111 -0
  174. package/docs/usage/components/shared-transition-screen.md +86 -0
  175. package/docs/usage/components/sheet.md +151 -0
  176. package/docs/usage/components/side-panel.md +104 -0
  177. package/docs/usage/components/sidebar.md +107 -0
  178. package/docs/usage/components/skeleton.md +105 -0
  179. package/docs/usage/components/skip-nav.md +76 -0
  180. package/docs/usage/components/slider.md +121 -0
  181. package/docs/usage/components/sortable-collection.md +127 -0
  182. package/docs/usage/components/spinner.md +86 -0
  183. package/docs/usage/components/splitter.md +103 -0
  184. package/docs/usage/components/stack.md +93 -0
  185. package/docs/usage/components/statistic.md +123 -0
  186. package/docs/usage/components/steps.md +110 -0
  187. package/docs/usage/components/surface.md +91 -0
  188. package/docs/usage/components/swipe-actions.md +124 -0
  189. package/docs/usage/components/switch.md +120 -0
  190. package/docs/usage/components/tabs.md +134 -0
  191. package/docs/usage/components/tag.md +84 -0
  192. package/docs/usage/components/tags-input.md +111 -0
  193. package/docs/usage/components/text-area.md +112 -0
  194. package/docs/usage/components/text-format.md +75 -0
  195. package/docs/usage/components/text-transition.md +104 -0
  196. package/docs/usage/components/text.md +101 -0
  197. package/docs/usage/components/thinking-orb.md +105 -0
  198. package/docs/usage/components/timeline.md +105 -0
  199. package/docs/usage/components/toast.md +145 -0
  200. package/docs/usage/components/toggle-group.md +95 -0
  201. package/docs/usage/components/tooltip.md +103 -0
  202. package/docs/usage/components/top-bar.md +124 -0
  203. package/docs/usage/components/top.md +89 -0
  204. package/docs/usage/components/tour.md +118 -0
  205. package/docs/usage/components/transfer-list.md +115 -0
  206. package/docs/usage/components/tree.md +91 -0
  207. package/docs/usage/components/upload-item.md +99 -0
  208. package/docs/usage/components/virtual-list.md +105 -0
  209. package/docs/usage/components/visually-hidden.md +72 -0
  210. package/docs/usage/components/watermark.md +78 -0
  211. package/docs/usage/compositions/action-recovery-optimistic.md +180 -0
  212. package/docs/usage/compositions/action-recovery-save.md +235 -0
  213. package/docs/usage/compositions/action-recovery-undo.md +193 -0
  214. package/docs/usage/compositions/common-message.md +132 -0
  215. package/docs/usage/compositions/common-notification.md +101 -0
  216. package/docs/usage/compositions/compound-controls.md +186 -0
  217. package/docs/usage/compositions/data-layouts.md +157 -0
  218. package/docs/usage/compositions/disclosure.md +144 -0
  219. package/docs/usage/compositions/environment-matrix.md +139 -0
  220. package/docs/usage/compositions/expo-interactions.md +149 -0
  221. package/docs/usage/compositions/family-drawer.md +201 -0
  222. package/docs/usage/compositions/floating-action-button.md +197 -0
  223. package/docs/usage/compositions/input-sheet.md +148 -0
  224. package/docs/usage/compositions/interaction-adapters.md +190 -0
  225. package/docs/usage/compositions/interaction-flow-apply.md +205 -0
  226. package/docs/usage/compositions/interaction-flow-draft.md +188 -0
  227. package/docs/usage/compositions/interaction-flow-search.md +171 -0
  228. package/docs/usage/compositions/native-renderers.md +106 -0
  229. package/docs/usage/compositions/navigation-bar-collection.md +164 -0
  230. package/docs/usage/compositions/optional-adapters.md +169 -0
  231. package/docs/usage/compositions/optional-motion.md +109 -0
  232. package/docs/usage/compositions/photo-source.md +104 -0
  233. package/docs/usage/compositions/purpose-input-comment.md +110 -0
  234. package/docs/usage/compositions/purpose-input-message.md +119 -0
  235. package/docs/usage/compositions/reference-first.md +96 -0
  236. package/docs/usage/compositions/reference-review.md +107 -0
  237. package/docs/usage/compositions/reference-settings.md +107 -0
  238. package/docs/usage/compositions/selection-scope.md +174 -0
  239. package/docs/usage/compositions/stea-event-ticket.md +166 -0
  240. package/docs/usage/compositions/stea-flip-card.md +162 -0
  241. package/docs/usage/compositions/stea-order-progress.md +184 -0
  242. package/docs/usage/compositions/stea-otp-verify.md +215 -0
  243. package/docs/usage/compositions/stea-pixel-empty.md +140 -0
  244. package/docs/usage/compositions/stea-schedule-card.md +169 -0
  245. package/docs/usage/compositions/stea-stat-summary.md +154 -0
  246. package/docs/usage/compositions/time-selection.md +174 -0
  247. package/docs/usage/compositions/toast-layout.md +128 -0
  248. package/docs/usage/compositions/visual-foundations.md +185 -0
  249. package/docs/usage/compositions/web-additions.md +146 -0
  250. package/docs/usage/compositions/web-navigation.md +143 -0
  251. package/docs/usage/screens/common-chat.md +127 -0
  252. package/docs/usage/screens/common-comments.md +108 -0
  253. package/docs/usage/screens/common-inbox.md +110 -0
  254. package/docs/usage/screens/common-login.md +98 -0
  255. package/docs/usage/screens/common-profile.md +221 -0
  256. package/docs/usage/screens/common-saved.md +127 -0
  257. package/docs/usage/screens/common-search.md +274 -0
  258. package/docs/usage/screens/common-settings.md +126 -0
  259. package/docs/usage/screens/common-shell.md +108 -0
  260. package/docs/usage/screens/dashboard.md +245 -0
  261. package/docs/usage/screens/discovery-gallery.md +306 -0
  262. package/docs/usage/screens/flow-collection.md +96 -0
  263. package/docs/usage/screens/flow-editor.md +120 -0
  264. package/docs/usage/screens/flow-media.md +111 -0
  265. package/docs/usage/screens/flow-moderation.md +120 -0
  266. package/docs/usage/screens/flow-onboarding.md +193 -0
  267. package/docs/usage/screens/flow-permission.md +103 -0
  268. package/docs/usage/screens/landing.md +347 -0
  269. package/docs/usage/screens/mockup-studio.md +190 -0
  270. package/docs/usage/screens/notification-settings.md +206 -0
  271. package/docs/usage/screens/reference-comparison.md +159 -0
  272. package/docs/usage/templates/component.md +61 -0
  273. package/docs/usage/templates/composition.md +47 -0
  274. package/docs/usage/templates/screen.md +56 -0
  275. package/docs/usage/templates/token.md +32 -0
  276. package/docs/usage/tokens/color.md +142 -0
  277. package/docs/usage/tokens/elevation-opacity.md +86 -0
  278. package/docs/usage/tokens/layers.md +98 -0
  279. package/docs/usage/tokens/layout.md +114 -0
  280. package/docs/usage/tokens/motion.md +88 -0
  281. package/docs/usage/tokens/radius.md +53 -0
  282. package/docs/usage/tokens/size.md +74 -0
  283. package/docs/usage/tokens/spacing.md +73 -0
  284. package/docs/usage/tokens/stroke.md +50 -0
  285. package/docs/usage/tokens/theme-studio.md +70 -0
  286. package/docs/usage/tokens/typography-studio.md +70 -0
  287. package/docs/usage/tokens/typography.md +89 -0
  288. package/package.json +7 -1
@@ -0,0 +1,142 @@
1
+ # 색상
2
+
3
+ - 단계: 토큰
4
+ - 상태: 배포
5
+ - 지원: Web · Native
6
+ - 적용: 1.12.1
7
+ - 검토일: 2026-10-06
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
+ - 스토리북: `배포/토큰/색과 글자/색상`
10
+
11
+ ## 언제 쓰나
12
+
13
+ 색은 팔레트 이름(파랑·회색)이 아니라 배경·글자·브랜드·피드백·테두리 **역할**로 고른다. 두 테마(light·dark)는 정확히
14
+ 같은 key를 가지므로 역할로 고르면 다크 모드가 따라온다. 대부분은 컴포넌트의 `tone` prop이 역할을 대신 고르므로,
15
+ 값을 직접 읽는 것은 제품 고유 영역(그래프·일러스트 틀·제품 전용 카드)일 때뿐이다.
16
+
17
+ 고르는 순서:
18
+
19
+ 1. 컴포넌트에 `tone`이 있으면 그것으로 고른다([Text](../components/text.md), [Icon](../components/icon.md),
20
+ [Badge](../components/badge.md), [Notice](../components/notice.md), [Button](../components/button.md)).
21
+ 2. 없으면 아래 표에서 **칠할 대상**(용도 열)으로 역할을 찾는다. 같은 대상에 후보가 둘이면 위쪽(더 강한) 것을 먼저 본다.
22
+ 3. 채운 바탕 위의 글자는 반드시 짝(`onPrimary`·`onDanger`·`onAccentFill`)을 쓴다.
23
+
24
+ ## 값
25
+
26
+ 값 열은 `light / dark`. Native 경로의 `theme`은 `useHjmNativeTheme()`, 역할 이름은 `semanticColors`(`@hjmds/design-contracts/tokens`)다.
27
+
28
+ ### 배경
29
+
30
+ | 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
31
+ | --- | --- | --- | --- | --- |
32
+ | `canvas` = `bg` | `#ffffff` / `#0d1116` | `--hjm-color-bg` | `theme.colors.bg` | 화면 바탕. 카드·패널(Surface·Card)도 모든 tone이 `bg`를 칠하고 층은 테두리·그림자로 나눈다 |
33
+ | `surface.default` = `surface` | `#f2f4f6` / `#161b22` | `--hjm-color-surface` | `theme.colors.surface` | 바탕과 구분되는 옅은 면 |
34
+ | `surface.sunken` = `surfaceAlt` | `#e5e8eb` / `#1e232a` | `--hjm-color-surface-alt` | `theme.colors.surfaceAlt` | 한 단계 더 들어간 면(CodeBlock 바탕) |
35
+ | `surface.brand` = `surfaceAccent` | `#c9e2ff` / `#224159` | `--hjm-color-surface-accent` | `theme.colors.surfaceAccent` | 브랜드가 드러나는 옅은 면 |
36
+
37
+ ### 글자
38
+
39
+ | 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
40
+ | --- | --- | --- | --- | --- |
41
+ | `content.primary` = `text` | `#191f28` / `#f3f5f7` | `--hjm-color-text` | `theme.colors.text` | 제목·강조 글자. Text `tone="primary"`(기본) |
42
+ | `content.body` = `textBody` | `#333d4b` / `#e1e5ea` | `--hjm-color-text-body` | `theme.colors.textBody` | 긴 본문. Text `tone="body"` |
43
+ | `content.secondary` = `textMuted` | `#4e5968` / `#cad0d8` | `--hjm-color-text-muted` | `theme.colors.textMuted` | 보조 설명. Text `tone="muted"` |
44
+ | `content.tertiary` = `textSub` | `#65707d` / `#9ba5b0` | `--hjm-color-text-sub` | `theme.colors.textSub` | 시간·출처 같은 약한 메타 정보. Text `tone="subtle"` |
45
+ | `content.decorative` = `textWeak` | `#8b95a1` / `#8b96a2` | `--hjm-color-text-weak` | `theme.colors.textWeak` | 비활성·placeholder·장식 표지. Text `tone="weak"`. 읽혀야 하는 글자에는 쓰지 않는다 |
46
+
47
+ ### 브랜드
48
+
49
+ | 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
50
+ | --- | --- | --- | --- | --- |
51
+ | `action.brand.background` = `primary` | `#0369a1` / `#0476b4` | `--hjm-color-primary` | `theme.colors.primary` | 채운 주 행동 바탕. Button `tone="primary"` |
52
+ | `action.brand.content` = `onPrimary` | `#ffffff` / `#ffffff` | `--hjm-color-on-primary` | `theme.colors.onPrimary` | `primary` 위 글자·아이콘. Text·Icon `tone="inverse"` |
53
+ | `content.brand` = `contentBrand` | `#075985` / `#51bff6` | `--hjm-color-content-brand` | `theme.colors.contentBrand` | 바탕 위 브랜드 색 글자(링크·현재 위치·강조 숫자). Text `tone="brand"`. 포커스 링(`border.focus`)도 이 색 |
54
+ | `brandGradient` | `#0369a1` → `#155dfc` | — | `brandGradient` | HJM 조직 표면 서명. 제품 기본 브랜드가 아니다 |
55
+
56
+ ### 피드백
57
+
58
+ | 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
59
+ | --- | --- | --- | --- | --- |
60
+ | `content.danger` = `danger` | `#b71919` / `#f87171` | `--hjm-color-danger` | `theme.colors.danger` | 오류 글자·아이콘. Text `tone="danger"`, 필드 오류 |
61
+ | `action.danger.background` = `dangerFill` | `#b91c1c` / `#b91c1c` | `--hjm-color-danger-fill` | `theme.colors.dangerFill` | 파괴 행동 바탕. Button `tone="danger"` |
62
+ | `action.danger.content` = `onDanger` | `#ffffff` / `#ffffff` | `--hjm-color-on-danger` | `theme.colors.onDanger` | `dangerFill` 위 글자 |
63
+ | `feedback.info.foreground` | `#6d28d9` / `#a78bfa` | `--hjm-accent-info` | `theme.palette.statusAccents.info` | 안내 |
64
+ | `feedback.success.foreground` | `#065f46` / `#34d399` | `--hjm-accent-success` | `theme.palette.statusAccents.success` | 완료 |
65
+ | `feedback.warning.foreground` | `#92400e` / `#fbbf24` | `--hjm-accent-warning` | `theme.palette.statusAccents.warning` | 주의가 필요한 상태 |
66
+ | `feedback.attention.foreground` | `#9a3412` / `#fb923c` | `--hjm-accent-attention` | `theme.palette.statusAccents.attention` | 눈길을 끌 새 소식 |
67
+ | `feedback.<tone>.background` · `.border` | 위 색 alpha 0.1 · 0.3 | — (`color-mix`) | `resolveColorReference(…, theme.palette)` | 피드백 영역의 옅은 바탕·테두리(Notice·Badge가 그린다) |
68
+ | `accentFill.<tone>` | info `#6d28d9` · success `#065f46` · warning `#92400e` · attention `#9a3412`(두 테마 공통) | `--hjm-accent-fill-<tone>` | `theme.palette.statusAccentFills.<tone>` | 꽉 찬 상태 표지 바탕. 글자는 `onAccentFill` `#ffffff` |
69
+ | `accentTint` | `weak` 0.1 · `base` 0.15 · `strong` 0.2 · `border` 0.3 | — | `accentTint` | 피드백 색을 옅게 깔 때 쓰는 alpha |
70
+
71
+ 도메인 상태(예: "배송 지연")는 제품 어댑터에서 `info`·`success`·`warning`·`attention`·`danger` 중 하나로 매핑한다.
72
+ 피드백 색은 `brandPalette`로 바꿀 수 없다(브랜드와 오류·성공이 같은 색이 되는 것을 막는다).
73
+
74
+ ### 테두리·상호작용
75
+
76
+ | 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
77
+ | --- | --- | --- | --- | --- |
78
+ | `border.default` = `border` | `#e5e8eb` / `#6a788a` | `--hjm-color-border` | `theme.colors.border` | 영역 경계 hairline. Surface `bordered`, Divider. `border.subtle`은 alpha 0.7 |
79
+ | `border.control` = `borderControl` | `#6b7684` / `#929faf` | `--hjm-color-border-control` | `theme.colors.borderControl` | 쉬고 있는 컨트롤 윤곽(Button `secondary`, 필드) |
80
+ | `border.strong` = `textWeak` | `#8b95a1` / `#8b96a2` | `--hjm-color-text-weak` | `theme.colors.textWeak` | 강한 경계 |
81
+ | `border.focus` | `contentBrand` | `--hjm-color-focus` | `theme.colors.contentBrand` | 포커스 링. 컴포넌트가 그린다 |
82
+ | `interaction.hover` · `focus` · `pressed` · `selected` | `text` 0.06 · `contentBrand` 0.08 · `text` 0.1 · `primary` 0.1 | — | `resolveColorReference(…, theme.palette)` | 상태 덧칠. 제품 고유 누름 영역에만 직접 쓴다 |
83
+
84
+ ### 함께 쓰는 쌍과 최소 대비
85
+
86
+ `checkBrandPaletteContrast`가 검사하는 쌍이다. 이 밖의 조합(예: `contentBrand` 글자를 `surfaceAccent` 위에)은 HJM이 대비를 보장하지 않는다.
87
+
88
+ - 4.5 이상: `text`·`textBody`·`textMuted`·`textSub`·`contentBrand`·`danger` on `bg`·`surface`, `onPrimary` on `primary`, `onDanger` on `dangerFill`.
89
+ - 3 이상: `primary`·`borderControl` vs `bg`·`surface`(형태), `textWeak` vs `bg`.
90
+
91
+ ## 쓰는 법
92
+
93
+ ```tsx
94
+ // Web
95
+ import { useHjmTheme } from "@hjmds/react/provider";
96
+ import { semanticColors } from "@hjmds/design-contracts/tokens";
97
+ import { resolveColorReference } from "@hjmds/design-contracts/color-references";
98
+
99
+ // 제품 CSS: .product-chart-axis { color: var(--hjm-color-text-sub); border-color: var(--hjm-color-border); }
100
+ const { palette } = useHjmTheme();
101
+ const good = resolveColorReference(semanticColors.feedback.success.foreground, palette);
102
+ ```
103
+
104
+ ```tsx
105
+ // Native
106
+ import { View } from "react-native";
107
+ import { useHjmNativeTheme } from "@hjmds/react-native/provider";
108
+ import { semanticColors } from "@hjmds/design-contracts/tokens";
109
+ import { resolveColorReference } from "@hjmds/design-contracts/color-references";
110
+
111
+ const theme = useHjmNativeTheme();
112
+ <View style={{ backgroundColor: theme.colors.bg, borderColor: theme.colors.border, borderWidth: 1 }} />;
113
+ const tint = resolveColorReference(semanticColors.feedback.warning.background, theme.palette);
114
+ ```
115
+
116
+ 제품 브랜드는 Provider의 `brandPalette`로 17개 key 중 필요한 것만 바꾼다. 값은 [테마 편집](theme-studio.md)으로 고르고 대비를 확인한다.
117
+
118
+ ```tsx
119
+ // Web
120
+ import { HjmProvider } from "@hjmds/react/provider";
121
+ <HjmProvider brandPalette={{ light: { primary: brand.light.fill, contentBrand: brand.light.text }, dark: { primary: brand.dark.fill, contentBrand: brand.dark.text } }}>
122
+ {app}
123
+ </HjmProvider>
124
+ ```
125
+
126
+ ## 하지 말 것
127
+
128
+ - `#0369a1`, `"gray"` 같은 색 값을 직접 쓰지 않는다. `THEMES.light.primary`를 import해 고정하는 것도 다크 모드와 브랜드를 깨뜨린다.
129
+ 현재 palette(`useHjmTheme()`·`useHjmNativeTheme()`)를 읽는다.
130
+ - 제품 브랜드를 `--hjm-color-*` 재정의나 `.hjm-*` 덮어쓰기로 넣지 않는다. 경로는 `brandPalette` 하나다([브랜드 경계](../../brand-boundary.md)).
131
+ - 성공·오류 색을 브랜드 색으로, 브랜드 색을 상태 색으로 쓰지 않는다.
132
+ - `textWeak`로 읽혀야 하는 문구를 쓰지 않는다. 보조 문구는 `textMuted`·`textSub`다.
133
+ - 채운 `primary`·`dangerFill`·`accentFill` 위에 `text`를 올리지 않는다. 짝(`onPrimary`·`onDanger`·`onAccentFill`)을 쓴다.
134
+ - Showcase의 예시 색을 제품 기본값으로 복사하지 않는다.
135
+
136
+ ## 플랫폼 차이
137
+
138
+ | 항목 | Web | Native |
139
+ | --- | --- | --- |
140
+ | 테마 색 읽기 | `--hjm-color-<kebab key>` CSS 변수 또는 `useHjmTheme().palette.theme` | `useHjmNativeTheme().colors` |
141
+ | 피드백 색 | `--hjm-accent-<tone>`, `--hjm-accent-fill-<tone>` | `theme.palette.statusAccents` · `statusAccentFills` |
142
+ | alpha 섞기 | CSS `color-mix` | `resolveColorReference` 또는 `withAlpha` |
@@ -0,0 +1,86 @@
1
+ # 그림자와 투명도
2
+
3
+ - 단계: 토큰
4
+ - 상태: 배포
5
+ - 지원: Web · Native
6
+ - 적용: 1.12.1
7
+ - 검토일: 2026-10-06
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
+ - 스토리북: `배포/토큰/표면과 움직임/그림자와 투명도`
10
+
11
+ ## 언제 쓰나
12
+
13
+ 면이 다른 면 위에 떠 있음을 보일 때(그림자), 비활성·누름·끌기 상태를 흐리게 할 때(투명도), 상태 덧칠의 세기와
14
+ 모달 뒤 배경막을 정할 때 쓴다. HJM은 층을 색 단계보다 테두리와 그림자로 나누므로(모든 Surface tone이 `bg`를 칠한다)
15
+ 떠 있는 면이 필요하면 그림자 토큰을 고른다.
16
+
17
+ ## 값
18
+
19
+ ### 그림자
20
+
21
+ | 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
22
+ | --- | --- | --- | --- | --- |
23
+ | `shadow.raised` | `#000000` 0.08 · radius 4 · offsetY 1 | `--hjm-shadow-raised` | `shadow.raised` | 바탕에서 살짝 뜬 면 |
24
+ | `shadow.floating` | `#000000` 0.12 · radius 12 · offsetY 4 | `--hjm-shadow-floating` | `shadow.floating` | 떠 있는 면: Dialog·Sheet·Toast·팝오버(`floatingSurfaceContract`), Surface `tone="raised"`(Web) |
25
+ | `shadow.overlay` | `#000000` 0.16 · radius 24 · offsetY 8 | `--hjm-shadow-overlay` | `shadow.overlay` | 화면 위에 가장 높이 뜬 면 |
26
+
27
+ Web 변수는 `0 <offsetY>px <radius>px rgb(0 0 0 / <opacity>%)` box-shadow 문자열이다(blur = radius).
28
+
29
+ ### 투명도
30
+
31
+ | 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
32
+ | --- | --- | --- | --- | --- |
33
+ | `opacity.disabled` | 0.5 | `--hjm-button-disabled-opacity`(Button) | `opacity.disabled` | 비활성 컨트롤 |
34
+ | `opacity.muted` | 0.72 | — | `opacity.muted` | 덜 중요한 내용(Calendar의 다른 달 날짜) |
35
+ | `opacity.pressed` | 0.86 | `--hjm-button-pressed-opacity`(Button) | `opacity.pressed` | 누르는 동안 |
36
+ | `opacity.dragged` | 0.64 | — | `opacity.dragged` | 끄는 중인 요소(Slider·Carousel·Toast 스와이프) |
37
+
38
+ ### 상태 덧칠·배경막
39
+
40
+ | 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
41
+ | --- | --- | --- | --- | --- |
42
+ | `stateLayer.hover` | 0.06 | — | `stateLayer.hover` | 마우스 올림 덧칠 세기(`interaction.hover`) |
43
+ | `stateLayer.focus` | 0.08 | — | `stateLayer.focus` | 포커스 덧칠 세기 |
44
+ | `stateLayer.pressed` | 0.1 | — | `stateLayer.pressed` | 누름 덧칠 세기 |
45
+ | `stateLayer.selected` | 0.1 | — | `stateLayer.selected` | 선택 덧칠 세기 |
46
+ | `overlay.scrim` · `backdrop.modal` | `#000000` 0.6 | `--hjm-backdrop-modal` | `backdrop.modal` · `scrim`(`rgba(0, 0, 0, 0.6)`) | Dialog·Sheet·Tour 뒤 배경막 |
47
+ | `overlay.veil` · `backdrop.veil` | `#000000` 0.25 | — | `backdrop.veil` | 화면을 가볍게 덮는 막 |
48
+
49
+ Native 경로의 이름은 `@hjmds/design-contracts/foundations` import다.
50
+
51
+ ## 쓰는 법
52
+
53
+ ```tsx
54
+ // Web
55
+ // 제품 고유 떠 있는 카드: .product-floating { box-shadow: var(--hjm-shadow-floating); }
56
+ import { Surface } from "@hjmds/react/layout";
57
+
58
+ <Surface tone="raised" padding="md">{children}</Surface>
59
+ ```
60
+
61
+ ```tsx
62
+ // Native
63
+ import { Pressable, View } from "react-native";
64
+ import { opacity, shadow } from "@hjmds/design-contracts/foundations";
65
+
66
+ const s = shadow.floating;
67
+ <View style={{ shadowColor: s.color, shadowOpacity: s.opacity, shadowRadius: s.radius, shadowOffset: { width: 0, height: s.offsetY }, elevation: 4 }} />;
68
+ <Pressable style={({ pressed }) => ({ opacity: pressed ? opacity.pressed : 1 })} onPress={open}>{tile}</Pressable>
69
+ ```
70
+
71
+ ## 하지 말 것
72
+
73
+ - 새 그림자(`0 2px 6px rgba(0,0,0,.2)`)를 만들지 않는다. 세 단계 중 고른다.
74
+ - 그림자를 겹쳐 층을 표현하지 않는다. 한 면에는 하나만 쓴다.
75
+ - 비활성을 투명도만으로 알리지 않는다. 컴포넌트 `disabled` prop을 써서 보조기기에도 알린다.
76
+ - 배경막 색·세기를 제품이 바꾸지 않는다. 모달은 HJM Dialog·Sheet가 배경막을 그린다.
77
+
78
+ ## 플랫폼 차이
79
+
80
+ | 항목 | Web | Native |
81
+ | --- | --- | --- |
82
+ | 그림자 표현 | box-shadow 문자열(`--hjm-shadow-*`) | iOS `shadow*` 속성 + Android `elevation` |
83
+ | Surface `tone="raised"` | `--hjm-shadow-floating` | `shadow.floating` + `elevation` 4 |
84
+ | HJM 목록 팝업 그림자 | `--hjm-shadow-floating`(Select·Menubar·DatePicker·Menu·Popover) | Modal 기반 메뉴는 해당 플랫폼 표면 계약 |
85
+
86
+ 2026-10-06 후속 검수에서 정의만 있던 Web 그림자 변수를 실제 떠 있는 표면에 연결했다. CommandPalette·Tour 같은 큰 강조 표면은 `shadow.overlay`, 일반 popup·Surface는 `shadow.floating`을 쓴다. Native Surface의 별도 blur 6도 floating token으로 맞췄다. 포커스 링·선택 테두리·스위치 손잡이의 inset 표현은 높이 그림자가 아니므로 각 컴포넌트 상태 계약을 유지한다.
@@ -0,0 +1,98 @@
1
+ # 겹침 순서
2
+
3
+ - 단계: 토큰
4
+ - 상태: 배포
5
+ - 지원: Web · Native
6
+ - 적용: 1.12.1
7
+ - 검토일: 2026-10-06
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
+ - 스토리북: `배포/토큰/표면과 움직임/겹침 순서`
10
+
11
+ ## 언제 쓰나
12
+
13
+ 화면 위에 겹쳐 뜨는 것(고정 헤더·드롭다운·대화상자·툴팁·토스트)의 위아래 순서를 정할 때 쓴다. 값은 사이를 비워 둔
14
+ 순서표다. 제품 고유 층은 두 토큰 사이 숫자를 쓰지 말고 같은 의미의 토큰을 고른다.
15
+
16
+ ## 값
17
+
18
+ | 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
19
+ | --- | --- | --- | --- | --- |
20
+ | `layer.base` | 0 | `--hjm-layer-base` | `layer.base` | 일반 문서 흐름 |
21
+ | `layer.sticky` | 100 | `--hjm-layer-sticky` | `layer.sticky` | 스크롤해도 붙어 있는 헤더·탭·하단 바 |
22
+ | `layer.dropdown` | 400 | `--hjm-layer-dropdown` | `layer.dropdown` | 입력에 붙어 열리는 목록·메뉴 |
23
+ | `layer.overlay` | 800 | `--hjm-layer-overlay` | `layer.overlay` | 화면을 덮는 배경막(scrim)·패널 |
24
+ | `layer.modal` | 900 | `--hjm-layer-modal` | `layer.modal` | 대화상자·시트 본체 |
25
+ | `layer.tooltip` | 950 | `--hjm-layer-tooltip` | `layer.tooltip` | 툴팁(`tooltipRecipe.layer`) |
26
+ | `layer.toast` | 1000 | `--hjm-layer-toast` | `layer.toast` | 토스트. 기본 modal·tooltip 위(`toastRecipe.viewport.layer`, Native는 `zIndex`·`elevation`으로 쓴다) |
27
+
28
+ 순서는 아래→위로 `base < sticky < dropdown < overlay < modal < tooltip < toast`다. Native 경로의 `layer`는
29
+ `@hjmds/design-contracts/foundations` import다.
30
+
31
+ ## 쓰는 법
32
+
33
+ ```tsx
34
+ // Web
35
+ import { layer } from "@hjmds/design-contracts/foundations";
36
+
37
+ <header style={{ position: "sticky", insetBlockStart: 0, zIndex: layer.sticky }}>{topBar}</header>
38
+ ```
39
+
40
+ ```tsx
41
+ // Native
42
+ import { View } from "react-native";
43
+ import { layer } from "@hjmds/design-contracts/foundations";
44
+
45
+ <View style={{ position: "absolute", top: 0, left: 0, right: 0, zIndex: layer.sticky, elevation: layer.sticky }}>{banner}</View>
46
+ ```
47
+
48
+ 대화상자·시트·토스트는 HJM 컴포넌트가 층을 정하므로 제품이 zIndex를 주지 않는다([Dialog](../components/dialog.md),
49
+ [Sheet](../components/sheet.md), [Toast](../components/toast.md)).
50
+
51
+ ## 하지 말 것
52
+
53
+ - `z-index: 9999`처럼 순서표 밖 값을 쓰지 않는다. 가장 위가 필요하면 그것이 토스트인지부터 확인한다.
54
+ - 제품 헤더를 `layer.modal` 이상으로 올리지 않는다. 대화상자 배경막 위로 헤더가 떠서 닫기 전 조작이 가능해진다.
55
+ - 겹침 순서로 포커스 순서를 대신하지 않는다. 모달의 포커스 가두기는 컴포넌트가 맡는다.
56
+ - 1.12.1의 옛 숫자(700·800·1000 등)를 기준으로 맞춘 제품 `z-index`를 그대로 두지 않는다. 예를 들어 500인 제품 헤더는 예전엔
57
+ 메뉴(800·900) 아래였지만 이제 메뉴(400)를 덮고, 950인 배너는 대화상자(1000) 아래였지만 이제 대화상자(900)를 덮는다.
58
+ 제품 층은 숫자를 베끼지 말고 `var(--hjm-layer-sticky)`(메뉴가 덮어야 하는 제품 chrome), `var(--hjm-layer-dropdown)`보다 낮은 값(본문 위 떠 있는 내용),
59
+ `var(--hjm-layer-toast)`보다 높은 값(모든 HJM 층을 덮어야 하는 것만)처럼 토큰 기준으로 적는다.
60
+
61
+ ## 플랫폼 차이
62
+
63
+ | 항목 | Web | Native |
64
+ | --- | --- | --- |
65
+ | HJM 컴포넌트가 쓰는 값 | FAB·BottomNavigation·sticky CTA는 `layer.sticky`, 목록·메뉴·Popover는 `layer.dropdown`, Dialog·Sheet는 `layer.modal + modalPriority`, Tooltip은 `layer.tooltip`, Toast는 `layer.toast`. 모달 내부 popup은 소유 모달 + 1 | Toast는 `layer.toast` 1000. Dialog·Sheet·Select는 RN `Modal`로 창 위에 뜬다 |
66
+ | 제품 고정 헤더 | `layer.sticky` 100이면 HJM 오버레이 아래에 있다 | `layer.sticky`, Android는 `elevation`도 같은 값 |
67
+
68
+ 2026-10-06 후속 검수에서 Web의 별도 500–1400 순서표가 공통 토큰과 어긋나는 것을 확인해 토큰을 직접 소비하도록 바꿨다. 모달 내부 메뉴는 소유 모달보다 한 단계 위에 있어야 입력할 수 있으므로 +1 관계를 유지한다. 건너뛰기 링크는 키보드 초점 시 토스트에 가려지지 않게 `layer.toast + 1`을 쓴다. 이들은 새 독립 토큰이 아닌 소유·접근성 관계다.
69
+
70
+ ### Web 표면별 실제 값
71
+
72
+ 1.12.1까지 Web은 토큰과 다른 500–1400 숫자를 직접 썼다. 아래 값은 미게시(1.12.1 이후) 변경이며
73
+ `.changeset/shared-layer-elevation-consumption.md`가 이전 값을 함께 적는다. HJM 층끼리의 상대 순서는 Popover를 빼면 그대로다.
74
+
75
+ | Web 표면 | 토큰 | 값 | 1.12.1 값 |
76
+ | --- | --- | --- | --- |
77
+ | BottomCTA(`data-position="sticky"`), FloatingActionButton, BottomNavigation | `layer.sticky` | 100 | 1 · 500 · 700 |
78
+ | Select·Combobox 목록, DatePicker 팝오버, `useAnchoredPopup` 기본값, Menu, Menubar 패널, Mentions 목록, Popover | `layer.dropdown` | 400 | 800 · 900 · 950(Popover) |
79
+ | Dialog·AlertDialog·Sheet·SidePanel 배경막, ContextMenu, CommandPalette, Tour 배경막 | `layer.modal + modalPriority`(`getModalLayer`) | 900 + priority | 1000 + priority |
80
+ | Tour 팝업, 모달 안에서 열린 popup | 소유 모달 + 1 | 901 | 1001 |
81
+ | Tooltip | `layer.tooltip` | 950 | 1100 |
82
+ | Toast 영역 | `layer.toast` | 1000 | 1200 |
83
+ | SkipNav, Layout 건너뛰기 링크 | `layer.toast + 1` | 1001 | 1300 · 1400 |
84
+
85
+ Popover는 이제 메뉴와 같은 `dropdown` 층이다. 메뉴에서 연 Popover는 나중에 portal되므로 메뉴 위에 그려진다.
86
+
87
+ ### `modalPriority` 기준
88
+
89
+ `getModalLayer(priority) = layer.modal + priority`다. 기준이 1000에서 900으로 내려와 문턱도 함께 내려왔다.
90
+
91
+ | 이 우선순위부터 | 모달이 덮는 것 | 1.12.1 문턱 |
92
+ | --- | --- | --- |
93
+ | 51 | Tooltip(950) | 101 |
94
+ | 101 | Toast(1000) | 201 |
95
+ | 102 | 건너뛰기 링크(1001) | 401 |
96
+
97
+ `modalPriority`는 모달 스택 내부 순서 조정 용도이며 제품이 전역 층을 바꾸는 우회로로 쓰지 않는다. 50을 넘는 값을 쓰는
98
+ 제품은 위 문턱으로 다시 확인한다. 컨트롤 내부 장식의 0/1/2는 로컬 쌓임이며 전역 층 토큰과 구분한다.
@@ -0,0 +1,114 @@
1
+ # 화면 여백과 너비
2
+
3
+ - 단계: 토큰
4
+ - 상태: 배포
5
+ - 지원: Web · Native
6
+ - 적용: 1.12.1
7
+ - 검토일: 2026-10-06
8
+ - 근거: [Container](../../container.md), [반응형 Grid](../../responsive-grid.md), `src/foundations.ts`(`layout`·`breakpoint`), `src/container.ts`, `src/responsive.ts`, `src/component-recipes.ts`(`listRowRecipe`)
9
+ - 스토리북: `배포/토큰/공간과 크기/화면 여백과 너비`
10
+
11
+ ## 언제 쓰나
12
+
13
+ 화면 좌우 여백·본문 최대 폭·구획 간격·행 높이·breakpoint를 정하는 화면 배치의 기준값이다. 숫자는 Web CSS px,
14
+ Native dp(pt)로 같다. 화면 지침의 영역 구조는 이 값을 전제로 한다.
15
+
16
+ ## 값
17
+
18
+ ### 화면 좌우 여백·구획 간격
19
+
20
+ | 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
21
+ | --- | --- | --- | --- | --- |
22
+ | `layout.pagePadding.compact` | 16 (`spacing.md`) | — (Container `gutter="compact"`) | `layout.pagePadding.compact` | 폭 600 미만 화면 좌우 여백, AuthScreen compact |
23
+ | `layout.pagePadding.regular` | 20 (`spacing.lg`) | — (Container `gutter="regular"`, 기본) | `layout.pagePadding.regular` | 일반 화면 좌우 여백, AuthScreen regular |
24
+ | `layout.pagePadding.spacious` | 24 (`spacing.xl`) | — (Container `gutter="spacious"`) | `layout.pagePadding.spacious` | 여유 있는 넓은 화면 |
25
+ | — | 0 | — (Container `gutter="none"`) | — | 지도·갤러리처럼 가장자리까지 채우는 영역 |
26
+ | `layout.sectionGap` | 24 (`spacing.xl`) | `--hjm-space-xl` | `layout.sectionGap` | 화면 안 구획 사이(Stack `gap="xl"`) |
27
+ | `layout.contentGap` | 16 (`spacing.md`) | `--hjm-space-md` | `layout.contentGap` | 한 구획 안 요소 사이(Stack `gap="md"`) |
28
+
29
+ ### 최대 폭
30
+
31
+ | 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
32
+ | --- | --- | --- | --- | --- |
33
+ | `layout.readingMaxWidth` | 720 | — (Container `size="reading"`) | `layout.readingMaxWidth` | 글 읽기·설정·폼처럼 한 줄 길이를 제한할 화면 |
34
+ | `layout.contentMaxWidth` | 1200 | — (Container `size="content"`, 기본) | `layout.contentMaxWidth` | 일반 제품 화면 |
35
+ | — | 제한 없음 | — (Container `size="full"`) | — | 의도적으로 가득 채우는 영역 |
36
+
37
+ ### 행 높이
38
+
39
+ | 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
40
+ | --- | --- | --- | --- | --- |
41
+ | `layout.rowHeight.singleLine` | 56 | `--hjm-list-row-comfortable-one-line` | `layout.rowHeight.singleLine` | ListRow 한 줄 최소 높이(`comfortable` 기본). `relaxed` 64 · `spacious` 72 · `compact` 44(`control.minTouchTarget`) |
42
+ | `layout.rowHeight.twoLine` | 68 | `--hjm-list-row-comfortable-two-line` | `layout.rowHeight.twoLine` | ListRow 두 줄 최소 높이(`comfortable`). `relaxed` 76 · `spacious` 84 · `compact` 60. UploadItem 최소 높이 |
43
+
44
+ ### Breakpoint(폭 구간)
45
+
46
+ | 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
47
+ | --- | --- | --- | --- | --- |
48
+ | `breakpoint.compact` | 0 | — | `breakpoint.compact` | `compact` 구간: 0 이상 600 미만. 휴대폰 세로 |
49
+ | `breakpoint.medium` | 600 | — | `breakpoint.medium` | `medium` 구간: 600 이상 960 미만. AlertDialog 버튼이 가로로 바뀌는 경계 |
50
+ | `breakpoint.expanded` | 960 | — | `breakpoint.expanded` | `expanded` 구간: 960 이상 1280 미만 |
51
+ | `breakpoint.wide` | 1280 | — | `breakpoint.wide` | `wide` 구간: 1280 이상 |
52
+
53
+ - 경계값은 포함한다. 폭 600은 `medium`이다(`resolveWindowClass`).
54
+ - 구간 이름은 기기 종류가 아니라 폭이다. 태블릿 세로·작은 브라우저 창도 폭으로 판단한다.
55
+ - `ResponsiveValue`는 `compact` 값이 필수이고, 빠진 큰 구간은 가장 가까운 좁은 구간 값을 물려받는다.
56
+ - Native 경로의 `layout`·`breakpoint`는 `@hjmds/design-contracts/foundations` import다. CSS 변수가 없는 값은 Container·Grid prop으로 쓴다.
57
+
58
+ ## 쓰는 법
59
+
60
+ 화면 본문은 Container로 감싸 최대 폭과 좌우 여백을 한 번에 정한다. 구획은 Stack `gap="xl"`(`layout.sectionGap`)로 띄운다.
61
+
62
+ ```text
63
+ 폭 < 600 (compact) 폭 ≥ 960 (expanded) — Container size="content"
64
+ ┌────────────────────────┐ ┌──────────────────────────────────────────────┐
65
+ │←16→ 본문 ←16→│ │ ┌──── 최대 1200 ────┐ │
66
+ │ 구획 A │ │ ←20→ │ 구획 A │ ←20→ │
67
+ │ ↕ 24 (sectionGap) │ │ │ ↕ 24 │ │
68
+ │ 구획 B │ │ │ 구획 B │ │
69
+ └────────────────────────┘ └──────────────────────────────────────────────┘
70
+ gutter="compact"(16) gutter="regular"(20), inline 축 가운데 정렬
71
+ ```
72
+
73
+ ```tsx
74
+ // Web
75
+ import { Container, Stack } from "@hjmds/react/layout";
76
+ import { resolveWindowClass } from "@hjmds/design-contracts/responsive";
77
+
78
+ const gutter = resolveWindowClass(window.innerWidth) === "compact" ? "compact" : "regular";
79
+
80
+ <Container size="content" gutter={gutter}>
81
+ <Stack gap="xl">{sections}</Stack>
82
+ </Container>
83
+ ```
84
+
85
+ ```tsx
86
+ // Native
87
+ import { useWindowDimensions } from "react-native";
88
+ import { Container, Stack } from "@hjmds/react-native/primitives";
89
+ import { resolveWindowClass } from "@hjmds/design-contracts/responsive";
90
+
91
+ const { width } = useWindowDimensions();
92
+ const gutter = resolveWindowClass(width) === "compact" ? "compact" : "regular";
93
+
94
+ <Container size="reading" gutter={gutter}>
95
+ <Stack gap="xl">{sections}</Stack>
96
+ </Container>
97
+ ```
98
+
99
+ 폭에 따라 열 수가 바뀌는 배치는 [Grid](../components/grid.md)의 `columns`(ResponsiveValue)로, 화면 틀은 [Layout](../components/layout.md)으로 정한다.
100
+
101
+ ## 하지 말 것
102
+
103
+ - `max-width: 1100px`, `@media (min-width: 768px)`처럼 토큰에 없는 폭을 만들지 않는다. 경계는 600·960·1280만 쓴다.
104
+ - 화면마다 `paddingHorizontal: 20`을 직접 적지 않는다. Container `gutter`나 `layout.pagePadding`을 쓴다.
105
+ - `left`/`right` 물리 방향 여백을 쓰지 않는다. Container는 inline 축 기준이라 RTL에서도 맞다.
106
+ - 기기 이름(phone·tablet)으로 분기하지 않는다. 폭 구간으로 분기한다.
107
+
108
+ ## 플랫폼 차이
109
+
110
+ | 항목 | Web | Native |
111
+ | --- | --- | --- |
112
+ | 폭 읽기 | `window.innerWidth` 또는 컨테이너 폭 | `useWindowDimensions().width` |
113
+ | Container 폭·여백 | `max-inline-size` · `padding-inline` | `maxWidth` + `width: "100%"` · `paddingHorizontal` |
114
+ | CSS media query | 변수를 못 읽으므로 경계 숫자를 쓸 때는 600·960·1280만 | 해당 없음 |
@@ -0,0 +1,88 @@
1
+ # 모션
2
+
3
+ - 단계: 토큰
4
+ - 상태: 배포
5
+ - 지원: Web · Native
6
+ - 적용: 1.12.1
7
+ - 검토일: 2026-10-06
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
+ - 스토리북: `배포/토큰/표면과 움직임/모션`
10
+
11
+ ## 언제 쓰나
12
+
13
+ 전환·나타남·사라짐의 길이와 곡선을 정할 때 쓴다. 먼저 의도(`motionPreset`)로 고르고, 길이와 곡선을 따로 고르는 것은 그 다음이다.
14
+ 모든 프리셋은 OS의 "동작 줄이기"에서 어떻게 바뀌는지(`reducedMotion`)를 함께 정한다.
15
+
16
+ ## 값
17
+
18
+ ### 프리셋(먼저 고른다)
19
+
20
+ | 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
21
+ | --- | --- | --- | --- | --- |
22
+ | `motionPreset.micro` | 120ms · `standard` · 줄이기 `instant` | `--hjm-motion-fast` | `motionPreset.micro` | 눌림·토글·색 바뀜 같은 작은 상태 변화 |
23
+ | `motionPreset.enter` | 200ms · `enter` · 줄이기 `opacity` | `--hjm-motion-normal` | `motionPreset.enter` | 시트·토스트·팝오버가 나타남 |
24
+ | `motionPreset.exit` | 120ms · `exit` · 줄이기 `instant` | `--hjm-motion-fast` | `motionPreset.exit` | 사라짐(나타남보다 짧게) |
25
+ | `motionPreset.context` | 320ms · `emphasized` · 줄이기 `opacity` | `--hjm-motion-slow` | `motionPreset.context` | 화면 맥락이 바뀌는 큰 전환 |
26
+
27
+ 줄이기 값: `instant` 즉시 바뀜, `opacity` 이동 없이 투명도만, `static` 움직임 없음.
28
+
29
+ ### 길이·곡선·스프링
30
+
31
+ | 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
32
+ | --- | --- | --- | --- | --- |
33
+ | `motion.fast` | 120ms | `--hjm-motion-fast` | `motion.fast` | 작은 상태 변화 |
34
+ | `motion.normal` | 200ms | `--hjm-motion-normal` | `motion.normal` | 나타남 |
35
+ | `motion.slow` | 320ms | `--hjm-motion-slow` | `motion.slow` | 큰 전환 |
36
+ | `easing.standard` | `cubic-bezier(0.2, 0, 0, 1)` | — | `easing.standard` | 화면 안 이동 |
37
+ | `easing.enter` | `cubic-bezier(0, 0, 0, 1)` | — | `easing.enter` | 들어옴(감속) |
38
+ | `easing.exit` | `cubic-bezier(0.3, 0, 1, 1)` | — | `easing.exit` | 나감(가속) |
39
+ | `easing.emphasized` | `cubic-bezier(0.2, 0, 0, 1)` | — | `easing.emphasized` | 강조 전환 |
40
+ | `spring.responsive` | stiffness 760 · damping 52 · mass 1 | — | `spring.responsive` | 손을 따라가는 빠른 스프링(Native) |
41
+ | `spring.expressive` | stiffness 520 · damping 38 · mass 1 | — | `spring.expressive` | 튀는 느낌의 스프링(Native) |
42
+
43
+ Web의 `--hjm-motion-*`는 동작 줄이기가 켜지면 `0ms`가 된다. Native 경로의 이름은 `@hjmds/design-contracts/foundations` import다.
44
+
45
+ ## 쓰는 법
46
+
47
+ ```tsx
48
+ // Web
49
+ import { easing, motionPreset } from "@hjmds/design-contracts/foundations";
50
+
51
+ // 제품 CSS: .product-chip { transition: background-color var(--hjm-motion-fast); }
52
+ const enter = motionPreset.enter;
53
+ const transition = `opacity ${enter.duration}ms cubic-bezier(${easing[enter.easing].join(", ")})`;
54
+ ```
55
+
56
+ ```tsx
57
+ // Native
58
+ import { Animated } from "react-native";
59
+ import { Easing } from "react-native";
60
+ import { easing, motionPreset } from "@hjmds/design-contracts/foundations";
61
+ import { useHjmNativeTheme } from "@hjmds/react-native/provider";
62
+
63
+ const { environment } = useHjmNativeTheme();
64
+ const enter = motionPreset.enter;
65
+ Animated.timing(value, {
66
+ toValue: 1,
67
+ duration: environment.reducedMotion ? 0 : enter.duration,
68
+ easing: Easing.bezier(...easing[enter.easing]),
69
+ useNativeDriver: true,
70
+ }).start();
71
+ ```
72
+
73
+ Web에서 JS로 직접 움직일 때는 `useHjmTheme().environment.reducedMotion`을 확인한다.
74
+
75
+ ## 하지 말 것
76
+
77
+ - 동작 줄이기를 무시하지 않는다. `reducedMotion`이 켜지면 프리셋의 줄이기 값대로 바꾼다.
78
+ - 300ms, `ease-in-out`처럼 토큰에 없는 길이·곡선을 쓰지 않는다.
79
+ - 사라짐을 나타남보다 길게 만들지 않는다.
80
+ - 반복 애니메이션으로 주의를 끌지 않는다. 로딩 표시(Spinner·Skeleton)는 컴포넌트가 맡는다.
81
+
82
+ ## 플랫폼 차이
83
+
84
+ | 항목 | Web | Native |
85
+ | --- | --- | --- |
86
+ | 동작 줄이기 | `prefers-reduced-motion`을 Provider가 읽어 `--hjm-motion-*`를 `0ms`로, 루트에 `data-motion="reduced"` | `AccessibilityInfo`를 Provider가 읽어 `environment.reducedMotion`. 첫 프레임은 줄이기로 가정 |
87
+ | 곡선 | `cubic-bezier(...)` | `Easing.bezier(...)` |
88
+ | 스프링 | 쓰지 않음 | `spring.*` |
@@ -0,0 +1,53 @@
1
+ # 둥글기
2
+
3
+ - 단계: 토큰
4
+ - 상태: 배포
5
+ - 지원: Web · Native
6
+ - 적용: 1.12.1
7
+ - 검토일: 2026-10-06
8
+ - 근거: `src/foundations.ts`(`radius`), `src/base-recipes.ts`(`buttonRecipe.shapes`·`fieldRecipe.shapes`·`surfaceDefaults`), `src/component-recipes.ts`, `packages/react/src/theme.ts`
9
+ - 스토리북: `배포/토큰/표면과 움직임/둥글기`
10
+
11
+ ## 언제 쓰나
12
+
13
+ 모서리 반경을 정할 때 쓴다. 컴포넌트는 자기 radius를 recipe로 이미 갖고 있으므로, 제품이 직접 고르는 것은 제품 고유 틀
14
+ (이미지 틀·제품 카드 안쪽 영역)일 때다. 고를 때는 아래 용도 열에서 같은 크기의 HJM 컴포넌트를 찾아 맞춘다.
15
+
16
+ ## 값
17
+
18
+ | 토큰 | 값 | Web CSS 변수 | Native 경로 | 용도 |
19
+ | --- | --- | --- | --- | --- |
20
+ | `radius.sm` | 8 | `--hjm-radius-sm` | `radius.sm` · `theme.tokens.radius.sm` | Tooltip, Skeleton 글자 줄 |
21
+ | `radius.md` | 12 | `--hjm-radius-md` | `radius.md` · `theme.tokens.radius.md` | Button `shape="rounded"`(기본), 필드 `shape="medium"`, Notice, Skeleton 블록, Avatar `shape="rounded"`, 목록 팝업 |
22
+ | `radius.lg` | 16 | `--hjm-radius-lg` | `radius.lg` · `theme.tokens.radius.lg` | Surface·Card 기본, Dialog, Toast, SegmentedControl, 필드 `shape="large"` |
23
+ | `radius.xl` | 24 | `--hjm-radius-xl` | `radius.xl` · `theme.tokens.radius.xl` | Sheet 위쪽 모서리 |
24
+ | `radius.full` | 999 | `--hjm-radius-full` | `radius.full` · `theme.tokens.radius.full` | 원·캡슐: Button `shape="pill"`, Badge, Chip, SearchField, Avatar `shape="circle"`, BottomNavigation 캡슐 |
25
+
26
+ - 안쪽 요소의 radius는 바깥보다 크지 않게 고른다. 예: `radius.lg` Surface 안의 이미지 틀은 `radius.md`.
27
+ - Native 경로의 `radius`는 `@hjmds/design-contracts/foundations` import다.
28
+
29
+ ## 쓰는 법
30
+
31
+ ```tsx
32
+ // Web
33
+ import { Surface } from "@hjmds/react/layout";
34
+
35
+ <Surface radius="lg" padding="md">{children}</Surface>
36
+ // 제품 고유 틀: .product-thumb { border-radius: var(--hjm-radius-md); overflow: hidden; }
37
+ ```
38
+
39
+ ```tsx
40
+ // Native
41
+ import { StyleSheet } from "react-native";
42
+ import { radius } from "@hjmds/design-contracts/foundations";
43
+ import { Surface } from "@hjmds/react-native/primitives";
44
+
45
+ <Surface radius="lg" padding="md">{children}</Surface>
46
+ const styles = StyleSheet.create({ thumb: { borderRadius: radius.md, overflow: "hidden" } });
47
+ ```
48
+
49
+ ## 하지 말 것
50
+
51
+ - `border-radius: 10px`처럼 토큰에 없는 값을 쓰지 않는다.
52
+ - HJM 컴포넌트의 radius를 `style`·`className`으로 덮지 않는다. Button·필드는 `shape`, Surface는 `radius` prop으로만 고른다.
53
+ - 원형을 만들려고 `50%`나 폭의 절반을 계산하지 않는다. `radius.full`을 쓴다.