@hjmds/design-contracts 1.14.0 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/dist/design-profile-layout.d.ts +11 -0
  2. package/dist/design-profile-layout.d.ts.map +1 -0
  3. package/dist/design-profile-layout.js +18 -0
  4. package/dist/design-profile-layout.js.map +1 -0
  5. package/dist/design-profile.d.ts +89 -0
  6. package/dist/design-profile.d.ts.map +1 -0
  7. package/dist/design-profile.js +262 -0
  8. package/dist/design-profile.js.map +1 -0
  9. package/dist/design-system-provider.d.ts +6 -0
  10. package/dist/design-system-provider.d.ts.map +1 -1
  11. package/dist/design-system-provider.js +4 -2
  12. package/dist/design-system-provider.js.map +1 -1
  13. package/dist/gooey-navigation.d.ts +19 -1
  14. package/dist/gooey-navigation.d.ts.map +1 -1
  15. package/dist/gooey-navigation.js +37 -2
  16. package/dist/gooey-navigation.js.map +1 -1
  17. package/dist/palette-contrast.d.ts +6 -0
  18. package/dist/palette-contrast.d.ts.map +1 -1
  19. package/dist/palette-contrast.js +17 -0
  20. package/dist/palette-contrast.js.map +1 -1
  21. package/dist/screen-patterns.d.ts +16 -1
  22. package/dist/screen-patterns.d.ts.map +1 -1
  23. package/dist/screen-patterns.js +4 -0
  24. package/dist/screen-patterns.js.map +1 -1
  25. package/dist/toast-liquid.d.ts +3 -1
  26. package/dist/toast-liquid.d.ts.map +1 -1
  27. package/dist/toast-liquid.js +6 -2
  28. package/dist/toast-liquid.js.map +1 -1
  29. package/dist/version.d.ts +1 -1
  30. package/dist/version.js +1 -1
  31. package/dist/version.js.map +1 -1
  32. package/docs/asset.md +7 -0
  33. package/docs/brand-boundary.md +14 -5
  34. package/docs/code-block.md +12 -1
  35. package/docs/collapsible.md +6 -0
  36. package/docs/design-profile.md +263 -0
  37. package/docs/design-system-provider.md +7 -0
  38. package/docs/dialog.md +5 -1
  39. package/docs/generated/component-maturity.md +1 -1
  40. package/docs/generated/renderer-evidence.json +3 -3
  41. package/docs/generated/renderer-evidence.md +1 -1
  42. package/docs/generated/showcase-manifest.json +1 -1
  43. package/docs/gooey-navigation.md +37 -6
  44. package/docs/heading.md +15 -0
  45. package/docs/theming.md +18 -5
  46. package/docs/usage/README.md +5 -0
  47. package/docs/usage/components/activity-heatmap.md +3 -1
  48. package/docs/usage/components/agreement.md +2 -0
  49. package/docs/usage/components/alert-dialog.md +9 -0
  50. package/docs/usage/components/asset.md +9 -2
  51. package/docs/usage/components/badge.md +3 -1
  52. package/docs/usage/components/bottom-cta.md +3 -1
  53. package/docs/usage/components/bottom-navigation.md +5 -1
  54. package/docs/usage/components/button.md +7 -1
  55. package/docs/usage/components/calendar.md +3 -1
  56. package/docs/usage/components/card.md +19 -2
  57. package/docs/usage/components/carousel.md +5 -0
  58. package/docs/usage/components/code-block.md +14 -2
  59. package/docs/usage/components/collapsible.md +10 -2
  60. package/docs/usage/components/combobox.md +12 -1
  61. package/docs/usage/components/command-palette.md +11 -6
  62. package/docs/usage/components/content-transition.md +1 -1
  63. package/docs/usage/components/context-menu.md +7 -1
  64. package/docs/usage/components/date-picker.md +3 -1
  65. package/docs/usage/components/design-system-provider.md +7 -5
  66. package/docs/usage/components/dialog.md +53 -1
  67. package/docs/usage/components/field.md +3 -1
  68. package/docs/usage/components/form.md +7 -2
  69. package/docs/usage/components/heading.md +7 -1
  70. package/docs/usage/components/image.md +2 -0
  71. package/docs/usage/components/list-row.md +2 -0
  72. package/docs/usage/components/list.md +2 -0
  73. package/docs/usage/components/load-more.md +3 -1
  74. package/docs/usage/components/mentions.md +3 -1
  75. package/docs/usage/components/menu.md +3 -1
  76. package/docs/usage/components/menubar.md +7 -1
  77. package/docs/usage/components/message-composer.md +11 -5
  78. package/docs/usage/components/notice.md +9 -0
  79. package/docs/usage/components/number-field.md +3 -1
  80. package/docs/usage/components/onboarding-screen.md +16 -9
  81. package/docs/usage/components/overview-screen.md +73 -0
  82. package/docs/usage/components/password-field.md +5 -1
  83. package/docs/usage/components/popover.md +7 -0
  84. package/docs/usage/components/saved-items-screen.md +3 -1
  85. package/docs/usage/components/screen-layout.md +9 -1
  86. package/docs/usage/components/search-field.md +3 -1
  87. package/docs/usage/components/select.md +9 -0
  88. package/docs/usage/components/sheet.md +14 -1
  89. package/docs/usage/components/skeleton.md +9 -0
  90. package/docs/usage/components/slider.md +3 -1
  91. package/docs/usage/components/statistic.md +3 -1
  92. package/docs/usage/components/surface.md +8 -1
  93. package/docs/usage/components/tabs.md +11 -2
  94. package/docs/usage/components/tag.md +3 -1
  95. package/docs/usage/components/tags-input.md +6 -1
  96. package/docs/usage/components/text-area.md +3 -1
  97. package/docs/usage/components/text-transition.md +8 -2
  98. package/docs/usage/components/text.md +3 -1
  99. package/docs/usage/components/toast.md +18 -1
  100. package/docs/usage/components/top-bar.md +3 -1
  101. package/docs/usage/compositions/command-records.md +106 -0
  102. package/docs/usage/compositions/content-transition-comparison.md +108 -0
  103. package/docs/usage/compositions/date-time-selection.md +110 -0
  104. package/docs/usage/compositions/design-profile-comparison.md +114 -0
  105. package/docs/usage/tokens/color.md +9 -2
  106. package/docs/usage/tokens/elevation-opacity.md +8 -1
  107. package/docs/usage/tokens/motion.md +10 -1
  108. package/docs/usage/tokens/radius.md +8 -1
  109. package/docs/usage/tokens/typography.md +15 -1
  110. package/package.json +13 -1
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-07
8
8
  - 근거: `src/base-recipes.ts`(`fieldRecipe`)
9
9
  - 스토리북: `배포/컴포넌트/입력/여러 줄 입력`
10
10
 
@@ -79,6 +79,8 @@ import { TextArea } from "@hjmds/react-native/inputs";
79
79
 
80
80
  ## 배치
81
81
 
82
+ Native는 제품 프로필의 `tokens.fontFamily.ui`를 실제 텍스트/입력 host에 연결한다. 기본 UI stack은 OS 서체를 유지하고, 제품이 지정한 첫 named font의 등록·글리프 확인은 제품이 맡는다.
83
+
82
84
  | 항목 | 값 | 근거 |
83
85
  | --- | --- | --- |
84
86
  | 크기 | 최소 높이 80(`fieldRecipe.multilineMinHeight`)에서 시작해 입력에 따라 자란다. 좌우 안쪽 여백 `spacing.md` 16, 모서리 기본 `radius.md` 12 | `fieldRecipe`, `.hjm-field__control--multiline` |
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-07
8
8
  - 근거: `src/content-transition.ts`(`resolveContentTransition`). 독립 계약 문서 없음(supplemental)
9
9
  - 스토리북: `배포/컴포넌트/시각 효과/내용 전환`, `배포/구성/직접 조작과 모션/끌기·밀기·화면 전환`
10
10
 
@@ -70,7 +70,7 @@ const uploadStatusKey = {
70
70
  | prop | 값 | 기본값 | 설명 |
71
71
  | --- | --- | --- | --- |
72
72
  | `text`(필수) | 문자열 | — | 바뀔 때마다 전환한다. 같은 문자열이면 아무것도 하지 않는다 |
73
- | `preset` | `fade` · `rise`(아래 12에서) · `slide`(가로 16, RTL이면 반대) · `scale`(0.96에서) | `fade` | — |
73
+ | `preset` | `fade` · `rise`(아래 12에서) · `slide`(가로 16, RTL이면 반대) · `scale`(0.96에서) | provider `designProfile.interactions.contentTransition`, 프로필 없으면 `fade` | 명시한 prop이 프로필보다 우선 |
74
74
  | `motion` | `system` · `none` | `system` | `system`이어도 provider의 reduced motion이면 즉시 바뀐다 |
75
75
  | `layoutStyle`(Web) | `HjmCompositionStyleProp` | — | 바깥 `<div>` 배치. Native에는 없다 |
76
76
  | `ContentTransition` `stateKey`·`children` | `string`·`ReactNode` | — (필수) | `stateKey`가 바뀔 때 하위 트리를 전환한다 |
@@ -93,6 +93,12 @@ const uploadStatusKey = {
93
93
  - 문자열은 i18n 키로 만든 최종 문구를 넘긴다. 글자 수 변화를 노린 연출용으로 쓰지 않는다.
94
94
  - 글자 모양 prop은 없다. 글자 모양은 감싸는 쪽이 정한다(아래 플랫폼 차이). Web은 `layoutStyle`로 배치만 하고, Native는 배치 prop이 없어 감싸는 쪽이 배치한다.
95
95
  - Web에서 이 subpath를 쓰려면 앱에 `framer-motion`을 설치한다. 없으면 import 시점에 실패한다.
96
+ - 테마별 전환을 따르려면 `preset`을 생략한다. `motion="none"`과 환경의 모션 감소는 프로필보다 우선한다.
97
+ - [Motion Text Loop 실제 검토](../../../../../docs/qa/2026-10-07-motion-reference-page-review.md)에서
98
+ 모션 감소 중에도 순환·공간 이동, exit/enter 두 문구가 동시에 읽히는 경로를 확인했다.
99
+ 현재 TextTransition은 단일 값의 전환만 맡으며 timer/배열/자동 순환 API를 제공하지 않는다.
100
+ 소개용 문구를 순환하는 구성에는 정지·읽기 시간·정적 대체·locale별 줄바꿈의 별도 계약이 필요하다.
101
+ 실제 처리 상태는 제품 상태에서 받은 `text`로 표시하고 타이머로 성공/진행을 만들지 않는다.
96
102
 
97
103
  ## 플랫폼 차이
98
104
 
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-07
8
8
  - 근거: `src/component-recipes.ts`(`textRecipe`), [Gravity Letters](../../gravity-letters.md)
9
9
  - 스토리북: `배포/컴포넌트/글자와 아이콘/본문 글자`, `배포/컴포넌트/시각 효과/중력 글자`
10
10
 
@@ -68,6 +68,8 @@ import { Text } from "@hjmds/react-native/primitives";
68
68
 
69
69
  ## 배치
70
70
 
71
+ Native는 제품 프로필의 `tokens.fontFamily.ui`를 실제 텍스트/입력 host에 연결한다. 기본 UI stack은 OS 서체를 유지하고, 제품이 지정한 첫 named font의 등록·글리프 확인은 제품이 맡는다.
72
+
71
73
  | 항목 | 값 | 근거 |
72
74
  | --- | --- | --- |
73
75
  | 크기 | 폭은 부모를 따른다. 긴 읽기 글은 부모 폭을 `layout.readingMaxWidth` 720 이하로 둔다 | `foundations.ts`(`layout`) |
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-07
8
8
  - 근거: [Toast](../../toast.md), [Liquid Toast](../../../../react-native/docs/liquid-toast.md), `src/component-recipes.ts`(`toastRecipe`)
9
9
  - 스토리북: `배포/컴포넌트/상태와 알림/토스트`, `배포/컴포넌트/상태와 알림/리퀴드 토스트`
10
10
 
@@ -93,6 +93,23 @@ function useProfileSavedToast() {
93
93
  | `layoutStyle`(Native `Toast`·`ToastRegion`) | `HjmCompositionStyleProp` | — | Web Toast·ToastProvider에는 없다(떠 있는 층이라 배치 대상이 아님) |
94
94
  | `style`·`toastStyle`(Native) | `StyleProp<ViewStyle>` | — | deprecated — `layoutStyle` 또는 `placement`·`safeAreaInsets`. 개발 모드에서 한 번 경고하고 다음 major에서 제거된다 |
95
95
 
96
+ ### 디자인 프로필 상속
97
+
98
+ 2026-10-07 테마 소비 경로 점검에서 고정 foundation/recipe 값이 남은 곳을 보완했다.
99
+ 모서리의 recipe 역할은 유지하고 값은 가장 가까운 Provider의 `designProfile.tokens.radius`를
100
+ 읽는다. Dialog/AlertDialog/Sheet/일반 Toast의 그림자는 `tokens.shadow.floating`을 읽으며
101
+ 프로필 없는 소비자의 기본값은 유지한다. 상태·초안·선택·Modal teardown은 이 축의 소유가 아니다.
102
+ 플랫폼 근사와 미검증 범위는 [프로필 계약](../../design-profile.md#오버레이선택-입력의-프로필-연결-보완)을 따른다.
103
+
104
+ Native의 선택형 `/toast-liquid`도 가장 가까운 프로필의 `radius.lg`와 `shadow.raised`를
105
+ 읽는다. 일반 Toast의 `floating` 대신 `raised`를 유지하는 이유는 리퀴드 카드의 얕은 깊이를
106
+ 정한 기존 검토다. RN 내용 clip과 Skia 카드 모서리를 함께 바꾸고 그림자의 위·아래·좌·우
107
+ 여유를 확보한다. 프로필이 없으면 기존 모서리 12와 foundation `raised`를 유지한다.
108
+ 원형 시작점·큐·행동은 유지하며 Web은 일반 Toast fallback이다. 기존
109
+ `실험/구성/비교와 검증/테마 조합`의 Native ‘물방울 알림의 테마 비교’에서 같은 알림을
110
+ 유지한 채 다음 테마로 바꿀 수 있다. 기기 시각·음성·제스처 검증은 별도로 남는다.
111
+
112
+
96
113
  ## 배치
97
114
 
98
115
  | 항목 | 값 | 근거 |
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-07
8
8
  - 근거: [화면 제목과 마지막 행동](../../screen-chrome.md), [NavigationBar](../../navigation-bar.md), `src/component-recipes.ts`(`topBarRecipe`)
9
9
  - 스토리북: `배포/컴포넌트/탐색/상단 탐색 막대`, `배포/컴포넌트/탐색/검색·메뉴가 있는 상단 바`, `배포/컴포넌트/탐색/내비게이션 바`
10
10
 
@@ -83,6 +83,8 @@ import { TopBar, TopBarAction } from "@hjmds/react-native/top-bar";
83
83
 
84
84
  ## 배치
85
85
 
86
+ NavigationBar의 Native 프레임 모서리는 Provider의 `tokens.radius.xl`을 읽는다. 기존 불투명 semantic 표면과 목적지·행동 슬롯을 유지한다.
87
+
86
88
  | 항목 | 값 | 근거 |
87
89
  | --- | --- | --- |
88
90
  | 크기 | 최소 높이 `control.buttonHeight.large` 52 + 위쪽 안전 영역. Web은 `max(env(safe-area-inset-top), safeAreaTop)`을 위 여백으로, Native는 `safeAreaTop`을 `paddingTop`과 `minHeight`(52 + `safeAreaTop`)에 더한다. 좌우 열 최소 폭 `control.minTouchTarget` 44, Native `TopBarAction` 최소 44×44 | `topBarRecipe.minHeight`·`sideMinWidth`·`action`, `.hjm-top-bar` |
@@ -0,0 +1,106 @@
1
+ # 명령 기록 표시
2
+
3
+ - 단계: 구성
4
+ - 상태: 실험
5
+ - 지원: Web · Native
6
+ - 적용: 미게시(1.14.0 이후)
7
+ - 검토일: 2026-10-07
8
+ - 근거: [후보 조사](../../../../../docs/qa/2026-10-07-reference-parallel-b.md), 양 Showcase `command-records-preview.tsx`
9
+ - 스토리북: `실험/구성/정보 표시/명령 기록 표시`
10
+
11
+ ## 언제 쓰나
12
+
13
+ 명령 원문과 출력 기록을 선택·읽기·복사할 때 쓴다. [Magic Terminal](https://magicui.design/docs/components/terminal)의
14
+ 명령/출력 구분과 [Aceternity 코드 블록](https://ui.aceternity.com/components/code-block)의 탭/복사 표현을
15
+ 기존 API에 합성했다. 터미널 에뮬레이터·명령 실행기·구문 분석기·서버 스트리밍 엔진이 아니다.
16
+ 자동 타이핑은 정확한 전체 원문과 복사 계약을 대신하지 않는다. 해당 기능은 별도 lifecycle/읽기 검토 대상이다.
17
+
18
+ ## 구성 요소
19
+
20
+ | 컴포넌트 | 역할 | 지침 |
21
+ | --- | --- | --- |
22
+ | Provider | 10개 테마 표현 상속 | [프로필](../../design-profile.md) |
23
+ | Container·Section·Stack | 제목·세로 흐름·폭 | [컨테이너](../components/container.md) · [구역](../components/section.md) · [스택](../components/stack.md) |
24
+ | SegmentedControl | 줄바꿈/가로 스크롤 중 하나 선택 | [선택 입력](../components/segmented-control.md) |
25
+ | Tabs·TabPanel | 명령/출력 두 패널 중 하나 | [탭](../components/tabs.md) |
26
+ | CodeBlock | 선택 가능한 LTR 원문 | [코드 블록](../components/code-block.md) |
27
+ | ClipboardButton | Web 원문 복사와 성공/오류 callback | [코드 블록의 복사 슬롯](../components/code-block.md#최소-사용-예) |
28
+ | Button·Notice·Text | 갱신·진행·실패·원문 선택 안내 | [버튼](../components/button.md) · [알림](../components/notice.md) |
29
+ | Collapsible | 결정적인 Storybook 응답 도구 | [접기](../components/collapsible.md) |
30
+
31
+ ## 배치
32
+
33
+ ```text
34
+ Section 제목/설명 → 현재 테마/다음 테마
35
+ 긴 줄 표시: 줄바꿈 / 가로 스크롤
36
+ 명령 / 출력 탭 → 언어·복사 보조 행동 → 선택 가능한 원문
37
+ 복사 실패 안내 / 닫기
38
+ 갱신 진행 또는 원문을 유지한 실패 알림
39
+ 표시 새로고침(주 행동) → 예제 응답 안내
40
+ 검증 도구: 미리보기 응답 받기 / 다음 갱신 실패
41
+ ```
42
+
43
+ | 영역 | 컴포넌트 | 위치 | 크기·간격 |
44
+ | --- | --- | --- | --- |
45
+ | 바깥 틀 | Container·Section·Stack | 세로 문서 흐름 | Container `gutter="compact"` 16, Stack `spacing.md` 16. Native ScrollView 위아래 `spacing.lg` 20 |
46
+ | 보기 선택 | SegmentedControl | 탭 목록 위 | `presentation="pills"`, 큰 글자에서 줄바꿈 |
47
+ | 패널 | TabPanel·CodeBlock | 탭 목록 아래 | dynamic 단일 패널. CodeBlock의 `radius.lg`·`spacing.md`·프로필 code font, 원문 LTR |
48
+ | 복사 | ClipboardButton | Web CodeBlock 머리 줄 끝 | `tone="secondary" size="small"`. Native는 원문 시스템 선택; 제품 host action이 있으면 같은 슬롯 |
49
+ | 갱신·복구 | Button·Notice | 원문 아래 | 기본 Button 높이 `control.buttonHeight.medium` 44; 진행 중 잠금, 이전 원문 유지 |
50
+
51
+ ## 흐름과 상태
52
+
53
+ 1. 명령 또는 출력을 고른다. manual Tabs의 화살표는 포커스만 움직이고 Enter/Space로 선택한다.
54
+ 2. 긴 줄 표시를 바꾼다. 줄바꿈은 원문을 바꾸지 않고 가로 스크롤은 코드 영역에 한정한다.
55
+ 3. 테마를 바꿔도 현재 패널·보기·원문·갱신 상태를 유지한다. CodeBlock이나 복사 행동을 key로 다시 만들지 않는다.
56
+ 4. Web 복사는 현재 원문 전체를 전달하며 권한 거부를 Notice로 알린다. Native는 시스템 선택 또는 제품이 공급한 복사 host를 쓴다.
57
+ 5. 표시 새로고침 동안 이전 원문을 읽을 수 있다. fixture 응답 실패도 원문을 유지하며 재시도할 수 있다.
58
+
59
+ | 상태 | 모습 | 포커스·알림 |
60
+ | --- | --- | --- |
61
+ | 기본 | 정확한 전체 원문, 두 패널·두 보기 방식 | 탭 manual focus, Web pre focus/Native selectable Text |
62
+ | 진행 중 | 갱신 버튼 loading, 기존 원문·선택 유지 | 응답 도구만 갱신 상태 확정, Web status/Native PatternStatus |
63
+ | 실패 | danger Notice·같은 갱신 재시도, 이전 원문 유지 | Web alert/Native Notice, Web 응답 뒤 갱신 버튼으로 복귀 |
64
+ | 복사 실패 | danger Notice·직접 원문 선택 안내 | 닫기는 안내만 제거하며 성공 아님. Web 닫기 후 복사 버튼으로 복귀 |
65
+ | 복사 성공 | Web ClipboardButton copied | OS 요청 뒤 live status, 진행 중 포커스 유지 |
66
+
67
+ ## 코드 골격
68
+
69
+ ```tsx
70
+ // Web: 원문·출력·갱신 상태는 제품 데이터이며 실행하지 않는다.
71
+ import { Tabs, TabPanel } from "@hjmds/react/navigation";
72
+ import { CodeBlock } from "@hjmds/react/code-block";
73
+ import { ClipboardButton } from "@hjmds/react/clipboard";
74
+ <Tabs id={id} label={tabsLabel} items={items} value={selected} onValueChange={setSelected}
75
+ renderPanels={false} panelMode="dynamic" activationMode="manual" />
76
+ <TabPanel tabsId={id} activeValue={selected} mode="dynamic">
77
+ <CodeBlock code={code} label={sourceLabel} wrap={wrap} copyAction={
78
+ <ClipboardButton value={code} labels={copyLabels} tone="secondary" size="small"
79
+ onCopy={onCopy} onCopyError={onCopyError} />
80
+ } />
81
+ </TabPanel>
82
+ ```
83
+
84
+ ```tsx
85
+ // Native: 선택 가능한 원문은 기존 CodeBlock, OS 복사는 제품 host 경계다.
86
+ import { CodeBlock } from "@hjmds/react-native/code-block";
87
+ <CodeBlock code={code} label={sourceLabel} wrap={wrap} copyAction={productCopyAction} />
88
+ ```
89
+
90
+ ## 플랫폼 차이
91
+
92
+ | 항목 | Web | Native |
93
+ | --- | --- | --- |
94
+ | 복사 | ClipboardButton·OS Promise·live status | 원문 길게 누르기와 시스템 선택. ClipboardButton API 없음 |
95
+ | 원문 | focus 가능한 pre, 긴 줄 자체 scroll | selectable Text, 긴 줄 horizontal ScrollView |
96
+ | 갱신 응답 뒤 포커스 | fixture는 주 갱신 행동으로 복귀 | 기기/AT 검증 별도 |
97
+ | 진행 알림 | Text role=status | Showcase PatternStatus iOS announce/Android live region. 제품은 같은 정책 연결 |
98
+
99
+ ## 함정
100
+
101
+ Showcase fixture를 제품에서 import하지 않는다. 제품은 i18n 문구·원문·허용 데이터·조회/스트리밍
102
+ 정책을 공급한다. 출력 문자열은 실제 CI·배포 성공 증거가 아니다. 비밀값은 제품이 표시 전에 제거한다.
103
+ token.text는 공백까지 원문과 같아야 하며 HTML로 삽입하지 않는다.
104
+ 미게시 Web ClipboardButton 후속 수정은 중복 OS 쓰기를 막고, 원문 변경/언마운트 뒤 이전 결과 callback과
105
+ 성공 표시를 무시한다. 취소할 수 없는 OS 쓰기가 끝날 때까지 loading 잠금을 유지해 다음 쓰기와 순서가
106
+ 뒤집히지 않게 한다. Native 복사 실패 스토리는 제품 host 오류의 표시 fixture이며 실제 OS 거부 재현이 아니다.
@@ -0,0 +1,108 @@
1
+ # 내용 전환 비교
2
+
3
+ - 단계: 구성
4
+ - 상태: 실험
5
+ - 지원: Web · Native
6
+ - 적용: 미게시(1.14.0 이후)
7
+ - 검토일: 2026-10-07
8
+ - 근거: [Motion 검토](../../../../../docs/qa/2026-10-07-motion-reference-page-review.md), 양 Showcase `content-transition-comparison-preview.tsx`
9
+ - 스토리북: `실험/구성/비교와 검증/내용 전환 비교`
10
+
11
+ ## 언제 쓰나
12
+
13
+ 동일 내용의 전환 표현을 테마와 비교하거나 단계별 입력·완료·복구를 검토할 때 쓴다.
14
+ Motion Primitives Transition Panel의 두 제공 예제를 실제 검토한 후보 단위 등록이다.
15
+ 11개 사이트 전체 조사 완료나 모든 환경 검증을 뜻하지 않는다. 제품은 Showcase를 import하지 않고
16
+ 아래 공개 API에 자기 문구·데이터·완료 callback을 공급한다.
17
+
18
+ ## 구성 요소
19
+
20
+ | 컴포넌트 | 역할 | 지침 |
21
+ | --- | --- | --- |
22
+ | Heading | 비교 구역의 의미 있는 제목 | [제목](../components/heading.md) |
23
+ | Provider | 10개 테마의 내용 전환 기본값 상속 | [프로필 계약](../../design-profile.md) |
24
+ | Tabs·TabPanel | 수동 활성화와 선택 패널의 의미 | [탭](../components/tabs.md) |
25
+ | ContentTransition | 현재 본문 하나의 등장과 높이 전환 | [내용 전환](../components/content-transition.md) |
26
+ | OnboardingScreen | 이전·다음·마지막 완료와 진행 표시 | [안내 화면](../components/onboarding-screen.md) |
27
+ | TextField | 제품 상태에 보관한 같은 초안 | [텍스트 입력](../components/field.md) |
28
+ | SegmentedControl | 한 번에 하나의 전환 표현 선택 | [선택 입력](../components/segmented-control.md) |
29
+ | Button·Notice | 표현 선택·실패 예약·복구 결과 | [버튼](../components/button.md) · [알림](../components/notice.md) |
30
+
31
+ ## 배치
32
+
33
+ ```text
34
+ 설명 → 현재 테마/다음 테마 → 전환 표현(줄바꿈)
35
+ 탭 비교: 탭 목록 → 현재 패널 → 제목/내용/같은 초안
36
+ 단계 비교: 안내 제목/설명/진행 → 본문 전환/초안 → 고정 이전·다음·완료
37
+ 마지막 단계: 실패 재현 예약 → 실패 알림 → 같은 완료로 재시도
38
+ 완료: 확인 알림 → 초안 → 다시 비교
39
+ ```
40
+
41
+ | 영역 | 컴포넌트 | 위치 | 크기·간격 |
42
+ | --- | --- | --- | --- |
43
+ | 바깥 틀 | Stack | 세로 | `spacing.lg` 20 |
44
+ | 표현 선택 | SegmentedControl | 안내 아래 | 공개 `presentation="pills"`, 큰 글자·좁은 화면 줄바꿈 |
45
+ | 탭 본문 | TabPanel·ContentTransition·Stack | 탭 목록 아래 | `spacing.md` 16, 높이는 현재 내용 측정 |
46
+ | 단계 틀 | OnboardingScreen | 표현 선택 아래 | Web preview 70dvh/minHeight 360, Native preview 720. 이는 fixture viewport이며 제품은 실제 가용 높이 사용 |
47
+ | 주 행동 | OnboardingScreen footer | 본문 scroll 바깥 | [기존 안내 배치](../components/onboarding-screen.md#배치) |
48
+
49
+ ## 흐름과 상태
50
+
51
+ 1. 초안을 입력하고 탭을 바꾼다. Tabs는 수동 활성화여서 Web 화살표는 포커스만 이동하고 Enter/Space로 선택한다.
52
+ 2. 테마를 순회하거나 나타남·떠오름·옆으로·확대·즉시를 선택한다. `preset` 생략은 테마 기본값, 명시 값은 우선한다.
53
+ 3. 단계별 완료 스토리에서 다음·이전으로 이동한다. 화면 전체를 key로 다시 마운트하지 않고 본문만 전환한다.
54
+ 4. 마지막 단계에서 완료 실패 재현을 예약하고 기록 확인을 누른다. 초안을 유지한 실패 알림 뒤 같은 완료를 다시 누른다.
55
+ 5. 확인 결과와 초안을 보고 다시 비교한다. 완료는 로컬 fixture이며 서버 저장·라우팅 완료로 안내하지 않는다.
56
+
57
+ | 상태 | 모습 | 포커스·알림 |
58
+ | --- | --- | --- |
59
+ | 기본 | 선택한 내용 하나·같은 초안 | 탭/본문 이름 연결, 이전 내용을 복제하지 않음 |
60
+ | 진행 중 | 현재 내용 등장/높이 전환 | 화면 제목·탭 목록·footer는 전환 바깥. 모션 감소·즉시는 즉시 반영 |
61
+ | 실패 | 마지막 단계의 danger Notice | 초안 보존, Web alert·Native assertive; 완료 버튼으로 재시도. Web 성공 뒤 다시 비교에 포커스 |
62
+ | 성공 | success Notice·확인한 초안 | 다시 비교 가능; 서버 성공을 의미하지 않음 |
63
+
64
+ ## 코드 골격
65
+
66
+ ```tsx
67
+ // Web: 선택 상태와 초안은 전환 바깥의 제품 상태다.
68
+ import { Tabs, TabPanel } from "@hjmds/react/navigation";
69
+ import { ContentTransition } from "@hjmds/react/content-transition";
70
+ <Tabs id={id} label={label} items={items} value={value} onValueChange={setValue}
71
+ activationMode="manual" panelMode="dynamic" renderPanels={false} />
72
+ <TabPanel tabsId={id} activeValue={value} mode="dynamic">
73
+ <ContentTransition stateKey={value} animateHeight>{body}</ContentTransition>
74
+ </TabPanel>
75
+ ```
76
+
77
+ ```tsx
78
+ // Native: label로 현재 패널 이름도 공급한다. 완료/오류는 제품이 소유한다.
79
+ import { TabPanel } from "@hjmds/react-native/navigation";
80
+ import { ContentTransition } from "@hjmds/react-native/content-transition";
81
+ import { OnboardingScreen } from "@hjmds/react-native/screen-flows";
82
+ <TabPanel tabsId={id} activeValue={value} label={currentLabel} mode="dynamic">
83
+ <ContentTransition stateKey={value} animateHeight>{body}</ContentTransition>
84
+ </TabPanel>
85
+ <OnboardingScreen steps={steps} index={index} onIndexChange={setIndex}
86
+ nextLabel={nextLabel} backLabel={backLabel} complete={complete}
87
+ progressLabel={progressLabel} />
88
+ ```
89
+
90
+ ## 플랫폼 차이
91
+
92
+ | 항목 | Web | Native |
93
+ | --- | --- | --- |
94
+ | 표현 엔진 | optional framer-motion·높이 측정 | core Animated·높이는 opt-in JS driver |
95
+ | 전환 본문 | 키가 바뀌면 내부 subtree remount | 현재 children 하나 유지. 실기기 포커스/AT 확인 별도 |
96
+ | 안내 배치 | 제목·설명·진행은 body scroll 위 | 안내도 body 안 scroll, footer 고정 |
97
+ | 포커스 | 탭 동작 유지. 본문 내부 행동이 본문을 교체하면 공개 focusTarget 검토 | focusTarget 없음; 같은 동작이라고 추정하지 않음 |
98
+
99
+ ## 함정
100
+
101
+ 동적 패널의 입력은 controlled 제품 상태로 보존한다. DOM identity가 필요한 입력은 전환 바깥에
102
+ 두거나 keyed/visited Tabs를 쓴다. 원본의 exit 복제·이름 없는 탭 버튼·마지막 Close의 빈 callback은
103
+ 복사하지 않는다. 무거운 내용과 실제 키보드·스크린리더·Native 기기의 높이 전환 검증은 별도다.
104
+
105
+ 2026-10-07 로컬 토큰 감사에서 Web preview 최소 높이360이 raw-length로 검출됐다.
106
+ 이는 짧은 viewport에서도 body scroll과 footer를 함께 검증하려는 fixture 한계값이며
107
+ 제품의 spacing/width 토큰을 높이로 전용하지 않는다. token-boundary-exceptions.json에
108
+ 해당 selector·minHeight·360만 한정해 등록했다. 제품 host는 실제 가용 높이를 공급한다.
@@ -0,0 +1,110 @@
1
+ # 날짜와 시각 선택
2
+
3
+ - 단계: 구성
4
+ - 상태: 실험
5
+ - 지원: Web · Native
6
+ - 적용: 미게시(1.14.0 이후)
7
+ - 검토일: 2026-10-07
8
+ - 근거: [Magic 조사](../../../../../docs/qa/2026-10-07-reference-parallel-b.md), 양 Showcase `date-time-selection-preview.tsx`, shared `date-time-selection.ts`
9
+ - 스토리북: `실험/구성/선택과 필터/날짜와 시각 선택`
10
+
11
+ ## 언제 쓰나
12
+
13
+ 기록·알림의 날짜 하나와 하루 안의 시각을 함께 고를 때 쓴다. [기존 시간 선택](time-selection.md)에
14
+ 날짜 선택을 붙이는 구성이다. 서버 예약·시간대 변환을 담당하는 새 DateTimePicker API가 아니다.
15
+ [Magic 글](https://magicui.design/blog/time-and-date-picker)은 공개 interface와 설명을 제공하며
16
+ 동작하는 picker 구현은 제공하지 않는다. 설명의 구성 아이디어만 기존 API에 연결했다.
17
+
18
+ ## 구성 요소
19
+
20
+ | 컴포넌트 | 역할 | 지침 |
21
+ | --- | --- | --- |
22
+ | Provider | 10개 테마 순회와 필드 표현 상속 | [프로필 계약](../../design-profile.md) |
23
+ | DatePicker | ISO 날짜 하나와 표시 달, 접근 가능한 달력 표면 | [날짜 선택](../components/date-picker.md) |
24
+ | Select ×2 | 시·분 선택 | [선택 목록](../components/select.md) |
25
+ | Section·Container·Stack | 제목·전체 흐름·폭 | [구역](../components/section.md) · [컨테이너](../components/container.md) · [스택](../components/stack.md) |
26
+ | Button | 확인·다시 선택 | [버튼](../components/button.md) |
27
+ | Text·Notice | 선택값·진행·실패·확인 결과 | [텍스트](../components/text.md) · [알림](../components/notice.md) |
28
+ | Collapsible | Storybook의 결정적 응답/실패 검증 도구 | [접기](../components/collapsible.md) |
29
+
30
+ ## 배치
31
+
32
+ ```text
33
+ Section 제목/설명 → 선택 테마/다음 테마
34
+ 날짜 트리거 → 달력(선택/지우기/이전·다음 달/닫기)
35
+ 시 Select → 분 Select
36
+ 같은 선택값 → 시간대/서버는 제품 소유 안내
37
+ 확인(주 행동) → 다시 고르기(보조)
38
+ 실패/확인 결과
39
+ 검증 도구: 미리보기 응답 받기 → 다음 확인 실패
40
+ ```
41
+
42
+ | 영역 | 컴포넌트 | 위치 | 크기·간격 |
43
+ | --- | --- | --- | --- |
44
+ | 바깥 틀 | Container·Section·Stack | 세로 문서 흐름; Native ScrollView | `gutter="compact"` 16, `spacing.md` 16. Native 위아래 `spacing.lg` 20 |
45
+ | 날짜 | DatePicker | 제목 아래 | 기존 [날짜 필드 배치](../components/date-picker.md#배치); Web popover/Native Sheet |
46
+ | 시각 | Select | 날짜 아래, 시→분 | `control.fieldHeight` 44, 사이 `spacing.md` 16 |
47
+ | 주·보조 행동 | Button | 선택 상태 아래 | 확인→다시 고르기, `control.buttonHeight.medium` 44 기본. 고정하지 않음 |
48
+ | 결과 | Notice | 행동 아래 | 실패는 danger, 확인은 success. 위 `spacing.md` 16 |
49
+
50
+ ## 흐름과 상태
51
+
52
+ 1. 날짜를 고른다. 테마를 순회해도 같은 값·표시 달·진행 요청을 유지한다. 달 이동은 표시 달만 바꾸며 이미 고른 날짜·시각을 지우지 않는다.
53
+ 2. 시0–23와 분0–59를 고른다. 셋 중 하나라도 없으면 확인은 비활성이다. 어느 값을 바꾸면 이전 결과를 지운다.
54
+ 3. 선택 확인 후 진행 상태 동안 날짜·시·분·재설정·실패 예약을 잠근다. 진행 상태는 제품 mutation이 소유한다.
55
+ 4. 미리보기에서는 응답 받기로 현재 요청의 선택값을 확정한다. 실패를 예약했다면 값이 남은 실패 상태가 된다.
56
+ 5. 실패 후 다시 확인하고 응답 받기로 복구한다. 다시 고르기는 모든 선택과 결과를 비운다.
57
+
58
+ | 상태 | 모습 | 포커스·알림 |
59
+ | --- | --- | --- |
60
+ | 기본 | 세 선택값 없음·확인 비활성 | 날짜·시·분 모두 필수 이름. 셀 disabled는 API 계약으로 제어 |
61
+ | 진행 중 | 확인 버튼 loading·입력/재설정 잠김 | placeholder를 서버 성공으로 바꾸지 않음; 이미 고른 값 유지 |
62
+ | 실패 | danger Notice·다시 확인 | 같은 날짜·시·분 보존; Web alert·Native assertive; Web 응답 뒤 확인 버튼으로 포커스 복귀 |
63
+ | 성공 | success Notice·확정한 날짜와 시각 | 값 변경 시 이전 결과 제거. 실제 예약·서버 저장을 뜻하지 않음 |
64
+ | 비활성 | 선택 트리거/행동 비활성 | CSS pointer-events로만 차단하지 않음 |
65
+
66
+ ## 코드 골격
67
+
68
+ ```tsx
69
+ // Web: 날짜·시각·시간대 정책과 서버 응답은 제품 소유다.
70
+ import { DatePicker } from "@hjmds/react/date-picker";
71
+ import { Select } from "@hjmds/react/forms";
72
+ <DatePicker descriptor={{ grid, label: dateLabel, placeholder, displayValue: date,
73
+ selectedDate: date, onSelectionChange: setDate, focusedMonth: month,
74
+ onFocusedMonthChange: setMonth, disabled: busy }} monthLabel={monthLabel}
75
+ composeAccessibleName={composeAccessibleName} clearLabel={clearLabel} closeLabel={closeLabel} />
76
+ <Select label={hourLabel} placeholder={hourPlaceholder} emptySelectionLabel={hourClearLabel}
77
+ items={hours} selectedKey={hour} onSelectionChange={setHour} disabled={busy} />
78
+ <Select label={minuteLabel} placeholder={minutePlaceholder} emptySelectionLabel={minuteClearLabel}
79
+ items={minutes} selectedKey={minute} onSelectionChange={setMinute} disabled={busy} />
80
+ ```
81
+
82
+ ```tsx
83
+ // Native: 같은 날짜 descriptor, 시간 목록은 닫기 문구도 공급한다.
84
+ import { DatePicker } from "@hjmds/react-native/date-picker";
85
+ import { Select } from "@hjmds/react-native/forms";
86
+ <DatePicker descriptor={descriptor} monthLabel={monthLabel} composeAccessibleName={composeAccessibleName}
87
+ clearLabel={clearLabel} closeLabel={closeLabel} />
88
+ <Select label={hourLabel} placeholder={hourPlaceholder} dismissLabel={hourDismissLabel}
89
+ items={hours} selectedKey={hour} onSelectionChange={setHour} disabled={busy} />
90
+ ```
91
+
92
+ ## 플랫폼 차이
93
+
94
+ | 항목 | Web | Native |
95
+ | --- | --- | --- |
96
+ | 날짜 표면 | 필드에 붙은 popover | Sheet |
97
+ | 시각 표면 | listbox popover | modal sheet |
98
+ | 상태 | Text role=status | Showcase PatternStatus의 Android live region/iOS announce 보완. 제품은 공개Text/Notice와 같은 정책을 연결 |
99
+ | 날짜 키보드 | Calendar roving focus, 달 경계 callback은 제품 설정 | touch/AT, 실기기 확인 별도 |
100
+
101
+ ## 함정
102
+
103
+ 제품 문구는 제품의 i18n으로 공급한다.
104
+ Showcase shared fixture/검증 도구를 제품에서 import하지 않는다. 이 화면은 날짜를 기기 시간대의
105
+ Date timestamp로 암묵 변환하지 않는다. 민간 날짜·시간대·DST·예약 허용 범위·로캘·전송 값은
106
+ 제품 계약에서 결정한다. 직접 CSS 색/모서리나 새 날짜 라이브러리를 이 구성에 추가하지 않는다.
107
+
108
+ ISO 날짜와 시각을 한 줄에 표시할 때는 제품 display 문자열에 LTR isolate를 적용해 RTL에서도
109
+ 날짜→시각 순서를 유지한다. 표시용 Unicode 제어 문자를 원래 civil/전송 값에 넣지 않는다.
110
+ 이는 이 실험의 ISO 표시 선택이며 실제 제품은 자신의 locale format을 공급한다.
@@ -0,0 +1,114 @@
1
+ # 테마 조합
2
+
3
+ - 단계: 구성
4
+ - 상태: 실험
5
+ - 지원: Web · Native
6
+ - 적용: 미게시(1.14.0 이후)
7
+ - 검토일: 2026-10-07
8
+ - 근거: [프로필 계약](../../design-profile.md), [조사와 QA](../../../../../docs/qa/2026-10-07-design-profile-research.md), 두 Showcase `design-profile-preview.tsx`
9
+ - 스토리북: `실험/구성/비교와 검증/테마 조합`
10
+
11
+ ## 언제 쓰나
12
+
13
+ 같은 기능에 10가지 표현을 적용하고, 앱 소유 테마 설정을 넣었을 때 네 단계의 전파와 상태 유지를 검토할 때 쓴다.
14
+
15
+ ## 구성 요소
16
+
17
+ | 컴포넌트 | 역할 | 지침 |
18
+ | --- | --- | --- |
19
+ | Provider | 한 번 선택한 프로필 상속 | [프로필 계약](../../design-profile.md) |
20
+ | SegmentedControl | 테마/기간 선택 | [선택 입력](../components/segmented-control.md) |
21
+ | OverviewScreen | 도구·목록·주 행동 | [목록 화면](../components/overview-screen.md) |
22
+ | Tabs | 프로필 상속/명시 표시와 방문한 패널의 초안 유지 | [탭](../components/tabs.md) |
23
+ | Card | 무늬 위의 표면 질감과 초안 | [카드](../components/card.md) |
24
+ | Asset | 같은 그림의 프로필 둥근 액자와 명시한 정사각·원형 비교 | [자산](../components/asset.md) |
25
+ | Heading | 선택 테마의 5단계 제목 크기 | [제목](../components/heading.md) |
26
+ | BottomCTA | 저장·실패 재현과 위쪽 그림자 | [하단 행동](../components/bottom-cta.md) |
27
+ | Popover(Web) | 비모달 초안과 같은 프로필 순회 | [팝오버](../components/popover.md) |
28
+ | Dialog·Sheet | 열린 초안과 같은 프로필 순회 | [대화상자](../components/dialog.md) · [패널](../components/sheet.md) |
29
+ | Notice·Skeleton·Toast | 알림·로딩·확정 후 피드백의 모서리/그림자 | [알림](../components/notice.md) · [로딩](../components/skeleton.md) · [토스트](../components/toast.md) |
30
+ | ContentTransition | 실제 저장 상태 전환 | [내용 전환](../components/content-transition.md) |
31
+ | Collapsible | 전체 비교 접기 | [접기](../components/collapsible.md) |
32
+
33
+ ## 배치
34
+
35
+ ```text
36
+ 설명 → 앱 테마 적용(참고 테마/산책 노트/문장 모음) → 표현 선택(기존 10종)
37
+ 선택한 프로필: 헤더 → 저장 상태 → 이름/기간 → 같은 기록 3개 → 저장/실패 재현
38
+ 선택한 프로필: 제목 크기 비교(5단계, 문서 단계 h3 유지)
39
+ 표면 질감 비교: 장식 무늬 → Card 제목/설명 → 같은 초안 → 다음 테마
40
+ 탭 선택 표시 비교: 테마 따르기/밑줄/이동/늘어남 → 기록/보관함 → 같은 초안 → 다음 테마
41
+ 자산 액자 비교(기본 접힘): 같은 기존 Tick 그림 → 둥근/정사각/원형 → 다음 테마
42
+ 입력·알림·오버레이 비교: Notice → Skeleton → Toast → Dialog/Sheet 열기
43
+ 오버레이: 제목/닫기 → 같은 초안 → 다음 테마(현재 10종 순환)
44
+ 10종 비교: 각 이름 → 같은 화면(현재 앱 설정도 함께 적용)
45
+ ```
46
+
47
+ | 영역 | 컴포넌트 | 위치 | 크기·간격 |
48
+ | --- | --- | --- | --- |
49
+ | 바깥 틀 | Stack | 세로 | `spacing.xl` 24 |
50
+ | 앱/참고 테마 | SegmentedControl | 선택 화면 위 | 앱 설정 3개 후 참고 테마 10개; `presentation="pills"`, 큰 선택 목록의 좁은 폭 배치는 공개 선택 계약을 따른다 |
51
+ | 이름/기간 | TextField·SegmentedControl | 도구 | `spacing.md` 16 |
52
+ | 목록 | OverviewScreen | 본문 | [목록 배치](../components/overview-screen.md#배치) |
53
+ | 저장/실패 | BottomCTA | footer | `spacing.sm` 12; 주 행동 후 ghost 실패 재현 |
54
+ | 자산 비교 | Asset·Stack | 탭 비교 아래 | `xlarge` 120px, `spacing.md` 16, 좁으면 줄바꿈. rounded는 프로필 radius.md, square 0, circle foundation full 유지 |
55
+
56
+ ## 흐름과 상태
57
+
58
+ 1. 기록 이름·기간을 바꾼다. 앱 설정과 표현을 바꿔도 같은 선택 화면의 초안/선택을 유지한다.
59
+ 산책 노트는 녹색 잉크·cards/collapsible·landscape·slide/rise를, 문장 모음은 보라 잉크·rows/inline·editorial·none/fade를 지정한다.
60
+ 나머지 표면/모서리/글자/질감은 고른 참고 테마를 상속한다. 두 설정은 제품 소유 설정 파일을 보여 주는 fixture이며 새 HJM 프리셋이 아니다.
61
+ 2. 도구 접기/펼치기를 확인한다. 항상 펼친 테마로 가면 내용이 보인다.
62
+ 3. 무늬 배경의 카드에 입력하고 다음 테마를 누른다. 유리·클레이 질감과 같은 초안 유지를 확인한다. Native 지원/접근성 설정에 따라 불투명 대체 경로도 확인한다.
63
+ 4. 대화상자/패널을 열고 초안을 바꾼 뒤 다음 테마를 누른다. 열린 오버레이 안에서 프로필을 바꾸며 초안/문서 역할을 유지한다. 닫고 다시 열어도 제어 초안은 남는다.
64
+ 5. 미리보기 저장 또는 실패 재현을 누른다. 실패 후 같은 입력을 재시도한다.
65
+ 6. 자산 액자 비교를 펼쳐 다음 테마를 누른다. 같은 기존 CC0 그림과 120px 액자를 유지하며
66
+ rounded만 프로필을 따른다. 이 예제는 그림 재질·각도 자동 선택이나 Native 이미지 decode 검증의 완료 근거가 아니다.
67
+
68
+ | 상태 | 모습 | 포커스·알림 |
69
+ | --- | --- | --- |
70
+ | 기본 | 저장 전 | 입력·선택 가능 |
71
+ | 진행 중 | 저장 버튼 pending | 입력을 제거하지 않음 |
72
+ | 실패 | 실패 문구·다시 저장 | 초안 유지·상태 알림 |
73
+ | 성공 | 미리보기 저장 문구 | 서버 저장으로 안내하지 않음 |
74
+
75
+ ## 코드 골격
76
+
77
+ ```tsx
78
+ // Web
79
+ import { HjmProvider } from "@hjmds/react/provider";
80
+ import { OverviewScreen } from "@hjmds/react/design-profile";
81
+ import { defineHjmDesignProfile } from "@hjmds/design-contracts/design-profile";
82
+ const design = defineHjmDesignProfile({ extends: "paper", id: "my-app",
83
+ compositions: { collection: "cards", toolbar: "collapsible" }, screens: { overview: "landscape" } });
84
+ <HjmProvider designProfile={design}><OverviewScreen title={title} toolbarLabel={toolsLabel} toolbar={tools} items={items} footer={save} /></HjmProvider>
85
+ ```
86
+
87
+ ```tsx
88
+ // Native
89
+ import { HjmNativeProvider } from "@hjmds/react-native/provider";
90
+ import { OverviewScreen } from "@hjmds/react-native/design-profile";
91
+ import { defineHjmDesignProfile } from "@hjmds/design-contracts/design-profile";
92
+ const design = defineHjmDesignProfile({ extends: "paper", id: "my-app",
93
+ compositions: { collection: "cards", toolbar: "collapsible" }, screens: { overview: "landscape" } });
94
+ <HjmNativeProvider designProfile={design}><OverviewScreen title={title} toolbarLabel={toolsLabel} toolbar={tools} items={items} footer={save} /></HjmNativeProvider>
95
+ ```
96
+
97
+ 제품은 Showcase를 import하지 않고 공개 API에 제품 문구/데이터를 넣는다. 유리 blur·클레이 inset shadow의 플랫폼 조건과 기기 미확인 범위는 [QA](../../../../../docs/qa/2026-10-07-design-profile-research.md)에 남긴다.
98
+
99
+ `showcase/shared/product-design.ts`는 순수 설정과 fixture 조합만 공유한다. 양 renderer가 자신의 workspace에서
100
+ 공개 `defineHjmDesignProfile`을 주입한다. 루트에 renderer peer를 설치하거나 TypeScript alias로 소비 경계를 우회하지 않는다.
101
+ 실제 앱은 이 fixture를 import하지 않고 자신의 `theme.ts`에서 같은 공개 helper를 사용한다. 테마 저장/URL/계정 동기화와 폰트/자산 로딩은 앱이 소유한다.
102
+
103
+ 두 앱 변형은 같은 항목의 `ProductNotes`(앱 테마 · 산책)·`ProductReading`(앱 테마 · 문장) 스토리다.
104
+ 새 테마마다 폴더나 상태 엔진을 만들지 않으며 Provider/RecordSample/입력/탭/오버레이에 product/preset key를 달아 교체하지 않는다.
105
+ OS 최대 글자와 최대값을 모사한 확대는 이번 추가의 설계·검증·후속·완료/릴리스 조건에서 제외한다.
106
+
107
+ 코드 비교는 양 플랫폼의 공개 `CodeBlock`을 사용한다. 같은 원문에 프로필 code font·body metrics를 적용하며 RTL에서도 코드 본문은 LTR로 읽는다.
108
+
109
+ 탭 비교는 공개 `Tabs`·`TextField`와 `mountPolicy="visited"`를 사용한다. 테마/표시 방식 변경은
110
+ 선택한 탭과 같은 입력 호스트를 유지한다. forest·glass·aurora·clay는 생략한 appearance가
111
+ slide로 해석되고 나머지 6종은 standard다. 명시 값은 프로필보다 우선한다. 실제 제품은
112
+ 이 샘플의 패널 수명을 기본값으로 복사하지 않고 초안과 탭의 사용 목적에 맞춰 선택한다.
113
+
114
+ Web의 팝오버는 비모달 편집 초안과 테마 순회를 추가 비교한다. Native의 같은 용도는 기존 Sheet 경로다. 저장/실패 샘플은 두 플랫폼 공개 BottomCTA이며 브랜드가 바뀌어도 같은 저장 상태·초안·재시도 callback을 유지한다.
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-07
8
8
  - 근거: [브랜드 경계](../../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
 
@@ -127,7 +127,7 @@ import { HjmProvider } from "@hjmds/react/provider";
127
127
 
128
128
  - `#0369a1`, `"gray"` 같은 색 값을 직접 쓰지 않는다. `THEMES.light.primary`를 import해 고정하는 것도 다크 모드와 브랜드를 깨뜨린다.
129
129
  현재 palette(`useHjmTheme()`·`useHjmNativeTheme()`)를 읽는다.
130
- - 제품 브랜드를 `--hjm-color-*` 재정의나 `.hjm-*` 덮어쓰기로 넣지 않는다. 경로는 `brandPalette` 하나다([브랜드 경계](../../brand-boundary.md)).
130
+ - 제품 브랜드를 `--hjm-color-*` 재정의나 `.hjm-*` 덮어쓰기로 넣지 않는다. 색만 바꾸면 `brandPalette`, 여러 표현 축을 함께 선택하면 검증한 `designProfile`을 쓴다([브랜드 경계](../../brand-boundary.md)).
131
131
  - 성공·오류 색을 브랜드 색으로, 브랜드 색을 상태 색으로 쓰지 않는다.
132
132
  - `textWeak`로 읽혀야 하는 문구를 쓰지 않는다. 보조 문구는 `textMuted`·`textSub`다.
133
133
  - 채운 `primary`·`dangerFill`·`accentFill` 위에 `text`를 올리지 않는다. 짝(`onPrimary`·`onDanger`·`onAccentFill`)을 쓴다.
@@ -140,3 +140,10 @@ import { HjmProvider } from "@hjmds/react/provider";
140
140
  | 테마 색 읽기 | `--hjm-color-<kebab key>` CSS 변수 또는 `useHjmTheme().palette.theme` | `useHjmNativeTheme().colors` |
141
141
  | 피드백 색 | `--hjm-accent-<tone>`, `--hjm-accent-fill-<tone>` | `theme.palette.statusAccents` · `statusAccentFills` |
142
142
  | alpha 섞기 | CSS `color-mix` | `resolveColorReference` 또는 `withAlpha` |
143
+
144
+ ### 디자인 프로필(실험·미게시)
145
+
146
+ 위 표는 프로필 없는 기본값이다. 앱이 [디자인 프로필](../../design-profile.md)을 선택하면 연결된
147
+ 컴포넌트는 Web CSS 변수 또는 Native `theme.tokens`/semantic palette를 읽는다.
148
+ 직접 foundations를 import한 값은 기본 상수이므로 프로필 변경을 따라가지 않는다. 현재 연결 API의 범위는
149
+ 프로필 계약에서 확인하고 앱 CSS로 내부 값을 덮지 않는다.
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-07
8
8
  - 근거: `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
 
@@ -84,3 +84,10 @@ const s = shadow.floating;
84
84
  | HJM 목록 팝업 그림자 | `--hjm-shadow-floating`(Select·Menubar·DatePicker·Menu·Popover) | Modal 기반 메뉴는 해당 플랫폼 표면 계약 |
85
85
 
86
86
  2026-10-06 후속 검수에서 정의만 있던 Web 그림자 변수를 실제 떠 있는 표면에 연결했다. CommandPalette·Tour 같은 큰 강조 표면은 `shadow.overlay`, 일반 popup·Surface는 `shadow.floating`을 쓴다. Native Surface의 별도 blur 6도 floating token으로 맞췄다. 포커스 링·선택 테두리·스위치 손잡이의 inset 표현은 높이 그림자가 아니므로 각 컴포넌트 상태 계약을 유지한다.
87
+
88
+ ### 디자인 프로필(실험·미게시)
89
+
90
+ 위 표는 프로필 없는 기본값이다. 앱이 [디자인 프로필](../../design-profile.md)을 선택하면 연결된
91
+ 컴포넌트는 Web CSS 변수 또는 Native `theme.tokens`/semantic palette를 읽는다.
92
+ 직접 foundations를 import한 값은 기본 상수이므로 프로필 변경을 따라가지 않는다. 현재 연결 API의 범위는
93
+ 프로필 계약에서 확인하고 앱 CSS로 내부 값을 덮지 않는다.
@@ -4,7 +4,7 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-07
8
8
  - 근거: `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
 
@@ -86,3 +86,12 @@ Web에서 JS로 직접 움직일 때는 `useHjmTheme().environment.reducedMotion
86
86
  | 동작 줄이기 | `prefers-reduced-motion`을 Provider가 읽어 `--hjm-motion-*`를 `0ms`로, 루트에 `data-motion="reduced"` | `AccessibilityInfo`를 Provider가 읽어 `environment.reducedMotion`. 첫 프레임은 줄이기로 가정 |
87
87
  | 곡선 | `cubic-bezier(...)` | `Easing.bezier(...)` |
88
88
  | 스프링 | 쓰지 않음 | `spring.*` |
89
+
90
+ 프로필 선택 이동 (미게시, 1.14.0 이후):
91
+
92
+ `designProfile.interactions.selectionMotion`은 SegmentedControl과 appearance를 생략한
93
+ [Tabs](../components/tabs.md)에 연결된다. Tabs의 `slide`는 `motion.normal` 200ms·
94
+ `easing.standard`로 측정한 표시선만 이동한다. 명시 `gooey`의 기존 320ms 늘어남과 구분한다.
95
+ 프로필은 선택 값·패널 수명·제품 초안을 결정하지 않는다. 동작 줄이기와 배경 상태에서는
96
+ 선택 위치로 즉시 정리한다. 원본 대조에서 발견한 누락과 플랫폼 검증 범위는
97
+ [채택 판단](../../../../../docs/plans/aceternity-interaction-adoption-2026-10-07.md)에 남겼다.