@hjmds/design-contracts 1.36.1 → 1.37.1

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 (53) hide show
  1. package/dist/catalog.d.ts +46 -0
  2. package/dist/catalog.d.ts.map +1 -1
  3. package/dist/chart.d.ts +10 -0
  4. package/dist/chart.d.ts.map +1 -1
  5. package/dist/component-recipes.d.ts +36 -0
  6. package/dist/component-recipes.d.ts.map +1 -1
  7. package/dist/component-recipes.js +19 -3
  8. package/dist/component-recipes.js.map +1 -1
  9. package/dist/dataviz.d.ts +10 -0
  10. package/dist/dataviz.d.ts.map +1 -1
  11. package/dist/dataviz.js +3 -0
  12. package/dist/dataviz.js.map +1 -1
  13. package/dist/design-system-provider.d.ts +3 -0
  14. package/dist/design-system-provider.d.ts.map +1 -1
  15. package/dist/design-system-provider.js +7 -0
  16. package/dist/design-system-provider.js.map +1 -1
  17. package/dist/palette-contrast.d.ts +4 -2
  18. package/dist/palette-contrast.d.ts.map +1 -1
  19. package/dist/palette-contrast.js +9 -2
  20. package/dist/palette-contrast.js.map +1 -1
  21. package/dist/semantic-colors.d.ts.map +1 -1
  22. package/dist/semantic-colors.js +21 -19
  23. package/dist/semantic-colors.js.map +1 -1
  24. package/dist/theme-studio.d.ts +1 -1
  25. package/dist/theme-studio.js +2 -2
  26. package/dist/theme-studio.js.map +1 -1
  27. package/dist/version.d.ts +1 -1
  28. package/dist/version.js +1 -1
  29. package/dist/version.js.map +1 -1
  30. package/docs/brand-boundary.md +6 -3
  31. package/docs/design-profile.md +1 -1
  32. package/docs/generated/component-maturity.md +1 -1
  33. package/docs/generated/renderer-evidence.json +3 -3
  34. package/docs/generated/renderer-evidence.md +1 -1
  35. package/docs/generated/showcase-manifest.json +1 -1
  36. package/docs/toast.md +9 -0
  37. package/docs/usage/README.md +5 -4
  38. package/docs/usage/components/bottom-cta.md +2 -0
  39. package/docs/usage/components/card.md +1 -1
  40. package/docs/usage/components/chart.md +4 -0
  41. package/docs/usage/components/combobox.md +7 -5
  42. package/docs/usage/components/composition-layout.md +2 -1
  43. package/docs/usage/components/design-system-provider.md +2 -2
  44. package/docs/usage/components/empty-state.md +2 -0
  45. package/docs/usage/components/notice.md +4 -0
  46. package/docs/usage/components/segmented-control.md +2 -0
  47. package/docs/usage/components/select.md +9 -7
  48. package/docs/usage/components/sheet.md +5 -1
  49. package/docs/usage/components/time-picker.md +1 -1
  50. package/docs/usage/components/toast.md +15 -3
  51. package/docs/usage/compositions/save-feedback.md +64 -0
  52. package/docs/usage/tokens/color.md +11 -5
  53. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  # Renderer evidence coverage
2
2
 
3
- > Generated for HJM 1.36.1. Do not edit directly; run `pnpm evidence:sync`.
3
+ > Generated for HJM 1.37.1. Do not edit directly; run `pnpm evidence:sync`.
4
4
 
5
5
  This projection joins the canonical surface maturity manifest with first-party renderer claims. Missing scenarios are explicit beta promotion debt; stable surfaces are blocked by CI until none remain.
6
6
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
- "designSystemVersion": "1.36.1",
3
+ "designSystemVersion": "1.37.1",
4
4
  "packageName": "@hjmds/design-contracts",
5
5
  "components": [
6
6
  {
package/docs/toast.md CHANGED
@@ -249,3 +249,12 @@ overflow와 update를 검증합니다.
249
249
  일괄 기본 행간 변경은 기존 알림 레이아웃까지 바꾸므로 채택하지 않았다. Native 본문 행간은 이미 body
250
250
  토큰을 소비하며, 이번 Native 변경은 action 라벨의 ui 서체 역할만 명시한다. 알림 문구·큐·시간·닫기·
251
251
  행동·초점 계약은 바꾸지 않는다.
252
+
253
+ ## 2026-10-20 저장 알림 시안 반영 (1.37.1)
254
+
255
+ 사용자가 카드형 저장 피드백(이전 3번)을 제거하고 한 줄·요약 토스트의 라이트·다크 시안을 선택했다.
256
+ 일반 토스트의 최신 표면 정본은 `toastRecipe.material`이다. 이전 절의 tinted badge·강한 경계·프로필 모서리는 일반 표면의 현재 규격이 아니다.
257
+ 배지 색 면을 없애고 요약형의 장식 아이콘은 생략한다. 한 줄 본문과 제목은 primary, 제목 아래 설명은 secondary다.
258
+ 24 모서리·얇은 경계·floating 그림자를 공유하고, 내용 구성·타이머·큐·press/action/닫기 계약은 유지한다.
259
+ 웹은 지원 환경에서 실제 backdrop blur, 투명도 감소에서는 불투명이다. Native 기본 renderer는 같은 색의 불투명 표면이다.
260
+ 선택형 liquid 어댑터의 host·움직임 계약은 유지한다. 자세한 선택은 [저장 결과](usage/compositions/save-feedback.md)를 따른다.
@@ -6,7 +6,7 @@
6
6
  버튼 위치에서 시작해 구성·컴포넌트 지침으로 내려가고, 값은 토큰 지침에서 고른다.
7
7
  [선택 기준](SELECTION.md)으로 그대로 사용·공개 슬롯 확장·공통 API 보완·제품 고유 기능 연결을 고른다. 설치 버전의 지침을 본다: `node_modules/@hjmds/design-contracts/docs/usage/`.
8
8
  제공 기준은 확인한 기본 API 버전이며 최초 출시 버전이 아니다. 구성·화면은 각 API 지침의 제공 버전을 확인한다.
9
- API 안정성은 1.35.1의 생성 manifest에서 읽은 정규(canonical) API 기준이다. 동반·선택형 확장은 개별 계약을 확인한다.
9
+ API 안정성은 1.36.1의 생성 manifest에서 읽은 정규(canonical) API 기준이다. 동반·선택형 확장은 개별 계약을 확인한다.
10
10
  Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품 반영을 의미하지 않는다.
11
11
  `분류`는 Storybook 제목 `<배포|실험>/<단계>/<분류>/<항목>`의 셋째 마디이고, 토큰·구성·화면은 Storybook 메뉴와 같은 순서다.
12
12
 
@@ -127,7 +127,7 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
127
127
  | [Menubar](components/menubar.md) | 탐색 | 데스크톱 Web 앱 상단에 항상 같은 자리에 있는 가로 메뉴 막대(파일·편집·보기)에 쓴다. | 1.12.1 | Web | Web stable / Native unsupported | 배포 |
128
128
  | [MessageComposer](components/message-composer.md) | 구성/입력과 작성 | 채팅·DM·댓글 입력창에 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
129
129
  | [ModerationScreen](components/moderation-screen.md) | 화면/소통 | 게시물·댓글·사용자 **신고** 화면에 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
130
- | [Notice](components/notice.md) | 상태와 알림 | 화면 흐름 안 **제자리에 남아 있는** 상태 알림에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
130
+ | [Notice](components/notice.md) | 상태와 알림 | 저장된 항목·폼 안의 작은 성공 상태는 `appearance="status"`(1.37.0)를 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
131
131
  | [NotificationInboxScreen](components/notification-inbox-screen.md) | 화면/소통 | 알림함 화면 전체 틀에 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
132
132
  | [NotificationItem](components/notification-item.md) | 구성/정보 표시 | 알림함의 알림 한 행에 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
133
133
  | [NotificationRow](components/notification-row.md) | 데이터 표시 | 알림 목록에서 아바타, 유형 배지, 미리보기, 시간, 읽지 않음 표시, 선택적 썸네일을 나란히 배치한다. | 1.26.0 | Web · Native | Web stable / Native stable | 배포 |
@@ -196,7 +196,7 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
196
196
  | [ThinkingOrb](components/thinking-orb.md) | 상태와 알림 | AI 에이전트가 실제로 검색·생성·듣기 같은 작업을 하는 동안 그 단계를 보여 줄 때만 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
197
197
  | [Timeline](components/timeline.md) | 데이터 표시 | 이미 일어난 일을 시간 순서대로 보여 줄 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
198
198
  | [TimePicker](components/time-picker.md) | 입력 | 하루 중 시각 하나를 시·분 두 열에서 고를 때 쓴다. | 미게시(1.34.0 이후) | Web · Native | Web stable / Native stable | 배포 |
199
- | [Toast](components/toast.md) | 상태와 알림 | 방금 한 행동의 결과처럼 **무시해도 안전한 짧은 알림**에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
199
+ | [Toast](components/toast.md) | 상태와 알림 | 저장 완료의 한 줄형·대상 요약형 선택은 저장 결과 구성을 따른다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
200
200
  | [ToggleFeedbackButton](components/toggle-feedback-button.md) | 구성/직접 조작과 모션 | 좋아요·저장·알림 받기처럼 켜고 끄는 선택에 반응 모션과 개수를 붙인다. | 1.21.0 | Web · Native | 개별 API 계약 참조 | 배포 |
201
201
  | [ToggleGroup](components/toggle-group.md) | 입력 | 여러 개를 동시에 켜고 끄는 짧은 버튼 묶음에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
202
202
  | [Tooltip](components/tooltip.md) | 오버레이 | Web에서 이미 이름과 focus를 가진 컨트롤(대개 IconButton)에 **짧은 보충 설명 한 문장**을 붙일 때 쓴다. | 1.12.1 | Web | Web stable / Native unsupported | 배포 |
@@ -242,6 +242,7 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
242
242
  | [펼치는 검색과 실제 메시지 답장](compositions/communication.md) | 선택과 필터 | 검색 아이콘 버튼에서 검색 영역을 펼치거나 채팅의 실제 메시지를 선택해 답장을 전송할 때 쓴다. | 1.21.0 | Web · Native | 구성 API별 | 배포 |
243
243
  | [보관함과 페이지 이동](compositions/web-navigation.md) | 탐색과 이동 | Web에서 상위 보관함 → 하위 모음으로 들어가고, 그 모음의 긴 목록을 페이지 단위로 넘겨 보는 탐색에 쓴다. | 1.12.1 | Web | 구성 API별 | 배포 |
244
244
  | [펼침과 메뉴](compositions/disclosure.md) | 탐색과 이동 | 내용을 숨겼다 펼치거나(Collapsible), Web에서 대상에 붙은 작업 메뉴를 우클릭·키보드로 열거나(ContextMenu), 데스크톱 앱처럼 상단 메뉴 막대를 두는(Menubar) 세 방식을 각각 보여 주는 모음이다. | 1.12.1 | Web · Native | 구성 API별 | 배포 |
245
+ | [ShortcutRail](compositions/shortcut-rail.md) | 탐색과 이동 | 최근 장소·최근 도구처럼 누르면 바로 열리는 유한 목록이다. | 1.36.1 | Web · Native | 구성 API별 | 배포 |
245
246
  | [대화 메시지](compositions/common-message.md) | 정보 표시 | 말풍선 하나하나에 반응·답장·원문 이동·전송 실패 후 다시 보내기를 붙일 때 쓴다. | 구성 API 지침 참조 | Web · Native | 구성 API별 | 배포 |
246
247
  | [명령 기록 표시](compositions/command-records.md) | 정보 표시 | 명령 원문과 출력 기록을 선택·읽기·복사할 때 쓴다. | 1.16.0 | Web · Native | 구성 API별 | 배포 |
247
248
  | [문서와 파일](compositions/document-resource.md) | 정보 표시 | 이름·형식·크기와 미리보기·내보내기·별도 메뉴를 함께 제공하는 문서에 쓴다. | 1.14.0 | Web · Native | 구성 API별 | 배포 |
@@ -261,6 +262,7 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
261
262
  | [보관과 실행 취소](compositions/action-recovery-undo.md) | 피드백과 복구 | 보관·숨기기·목록에서 빼기처럼 제품이 역연산을 제공하는 작업 뒤에, 같은 자리에서 실행 취소를 주고 그 복구 요청이 성공해야 화면을 되돌릴 때 쓴다. | 1.12.1 | Web · Native | 구성 API별 | 배포 |
262
263
  | [선택과 오류 복구](compositions/upload-recovery.md) | 피드백과 복구 | 파일 선택과 전송 상태의 취소·재시도를 연결할 때 쓴다. | 1.14.0 | Web · Native | 구성 API별 | 배포 |
263
264
  | [스트림 응답과 중지](compositions/streaming-answer.md) | 피드백과 복구 | 실제 provider가 보내는 응답 delta를 표시하고 중지·재생성·오류를 같은 영역에서 제공할 때 쓴다. | 1.21.0 | Web · Native | 구성 API별 | 배포 |
265
+ | [저장 결과](compositions/save-feedback.md) | 피드백과 복구 | 사용자 저장 요청의 성공이 확인된 뒤, 대상과 현재 화면에 맞는 피드백을 선택할 때 쓴다. | 1.37.0 | Web · Native | 구성 API별 | 실험 |
264
266
  | [저장과 재시도](compositions/action-recovery-save.md) | 피드백과 복구 | 입력한 내용을 서버에 저장하는 폼 한 덩어리에서 저장 중 중복 실행을 막고, 실패하면 입력을 지우지 않은 채 제출했던 값 그대로 다시 보낼 때 쓴다. | 1.12.1 | Web · Native | 구성 API별 | 배포 |
265
267
  | [전송 진행과 취소](compositions/transfer-task.md) | 피드백과 복구 | 실제 다운로드·업로드·작업의 시작/대기/진행·취소·실패 복구를 한 구성으로 제공할 때 쓴다. | 1.21.0 | Web · Native | 구성 API별 | 배포 |
266
268
  | [중단해도 남는 현재 상태](compositions/expo-interactions.md) | 피드백과 복구 | 버튼으로 상태를 빠르게 바꾸거나 전환 도중 내용을 닫아도 현재 상태가 바로 보이고 남아야 하는 영역에 쓴다. | 1.12.1 | Native | 구성 API별 | 배포 |
@@ -286,7 +288,6 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
286
288
  | [테마 조합](compositions/design-profile-comparison.md) | 비교와 검증 | 같은 기능에 10가지 표현을 적용하고, 앱 소유 테마 설정을 넣었을 때 네 단계의 전파와 상태 유지를 검토할 때 쓴다. | 1.16.0 | Web · Native | 구성 API별 | 배포 |
287
289
  | [토스트 배치 비교](compositions/toast-layout.md) | 비교와 검증 | Toast 카드 한 장의 내부 배치(톤 배지·제목·설명·닫기·실행 버튼)와 화면 위 위치를 좁은 폭·큰 글자·긴 문구·톤별로 확인하는 비교 스토리다. | 1.12.1 | Web · Native | 구성 API별 | 배포 |
288
290
  | [환경 조합 검증](compositions/environment-matrix.md) | 비교와 검증 | 제품 화면이 테마·쓰기 방향·글자 크기·모션 설정이 달라져도 같은 의미를 유지하는지 확인할 때, 어떤 환경 조합과 검증 항목을 골라 볼지 정하는 기준표로 쓴다. | 1.12.1 | Web | 구성 API별 | 배포 |
289
- | [ShortcutRail](compositions/shortcut-rail.md) | 콘텐츠 | 최근 장소·최근 도구처럼 누르면 바로 열리는 유한 목록이다. | 미게시(1.34.0 이후) | Web · Native | 구성 API별 | 실험 |
290
291
 
291
292
  ## 화면
292
293
 
@@ -75,6 +75,8 @@ const insets = useSafeAreaInsets();
75
75
 
76
76
  ## 배치
77
77
 
78
+ 2026-10-20 선택의창 사용자 지적: 버튼의 보이는 면과 터치 영역은 홈 인디케이터·하단 제스처 안전 영역 위에 배치한다. 바탕만 화면 끝까지 이어진다. Native Sheet footer는 앱이 `HjmNativeProvider safeAreaInsets`로 공급한 실제 창 inset을 사용한다. 시트 크기를 줄여도 이 여백은 보존하며, inset 미전달을 제품별 고정 padding으로 덮지 않는다.
79
+
78
80
  2026-10-20 번뚝 작성 화면은 BottomCTA에 HTML 제출 연결이 없어 버튼 묶음을 직접 유지했다.
79
81
  Web은 기존 form의 onSubmit/IME·검증을 유지하고 아래처럼 footer의 버튼을 form ID로 연결한다.
80
82
  onClick에서 requestSubmit을 흉내 내거나 빈 콜백을 넣을 필요가 없다. 여러 편집기가 함께 있으면 React useId로 ID를 만든다.
@@ -79,7 +79,7 @@ Web·Native 모두 기존 `raised` Surface의 테두리·그림자를 재사용
79
79
 
80
80
  | prop | 값 | 기본값 | 설명 |
81
81
  | --- | --- | --- | --- |
82
- | `tone` | `default` · `raised` · `accent` · `sunken` · `subtle`(Surface tone) | `raised` | 테두리와 공통 floating shadow로 입체감 표시. 평면 카드는 `tone="default"` 지정 |
82
+ | `tone` | `default` · `raised` · `accent` · `sunken` · `subtle`(Surface tone) | `raised` | 테두리와 공통 raised shadow로 입체감 표시. 평면 카드는 `tone="default"` 지정 |
83
83
  | `bordered` | `boolean` | `true` | Surface 기본 `false`와 다르다 |
84
84
  | `padding` | spacing 이름 | `md`(16) | — |
85
85
  | `radius` | radius 이름 | `lg`(foundation 16) | 선택한 designProfile의 같은 radius 역할로 frame과 media clip을 함께 변경 |
@@ -276,3 +276,7 @@ Native centerContent에는 Text 등 Native 노드를 쓴다. 모두 제외하거
276
276
  accessibilityLabel={t("comparison")} formatValue={formatValue} missingLabel={t("missing")}
277
277
  animateChanges showAxis />
278
278
  ```
279
+
280
+ ### 테마와 글자
281
+
282
+ 원형 차트의 비율 라벨은 `chartRecipe.chrome.labelSurface`(`bg`) 판 위에 `labelContent`(`text`)로 표시한다. 제품이 계열색을 바꾸거나 선택하지 않은 조각을 흐리게 해도 라벨 대비가 유지된다. Native SVG 라벨은 프로필의 `caption` 크기와 `ui` 글꼴, Provider의 글자 배율을 따르며 범례 값과 커서 안내도 공통 Text를 사용한다.
@@ -92,13 +92,15 @@ import { Combobox } from "@hjmds/react-native/forms";
92
92
 
93
93
  ## 배치
94
94
 
95
+ 2026-10-20 언어 선택 화면에서 자체 모달에 옛 ×·여백이 남은 문제를 해결하기 위해 Native는 공통 Sheet를 직접 사용한다. 닫기 제스처·안전 영역·높이·키보드 대응·teardown은 Sheet가 소유하고, 선택 목록과 확정 콜백은 입력 컴포넌트가 소유한다.
96
+
95
97
  Native는 제품 프로필의 `tokens.fontFamily.ui`를 실제 텍스트/입력 host에 연결한다. 기본 UI stack은 OS 서체를 유지하고, 제품이 지정한 첫 named font의 등록·글리프 확인은 제품이 맡는다.
96
98
 
97
99
  | 항목 | 값 | 근거 |
98
100
  | --- | --- | --- |
99
- | 크기 | 입력 높이 `medium` 44(`fieldFrameContract`) · `large` 52(`control.buttonHeight.large`), 폭은 폼 열을 채운다. Web 목록 최대 높이 22.5rem(360, recipe `popover.maxHeight`), 선택지 최소 높이 `compact` 44 · `comfortable` 56(3.5rem). Native 시트 최대 높이 화면의 75% | `selectRecipe.sizes`·`popover`, `.hjm-combobox__listbox`·`__option`, `react-native/src/forms.tsx` |
100
- | 간격 | 라벨·설명·오류는 [Field](field.md) 규칙(`formSupportContract.gap` `spacing.xs` 8). Web 목록 안쪽 `spacing.xs` 8(`comboboxRecipe.popover.padding` = Select와 같은 표면. 2026-10-06까지 4, 1.13.0 변경), 선택지 안쪽 위아래 `spacing.xs` 8 · 좌우 `spacing.sm` 12. Native 시트 안쪽 `spacing.md` 16, 아래는 `spacing.md` + 하단 안전 영역, 요소 사이 `spacing.sm` 12 | `.hjm-combobox__option`, `react-native/src/forms.tsx` |
101
- | 순서·정렬 | 폼 안에서 다른 입력과 같은 열에 둔다. Web은 입력 바로 아래(공간이 없으면 위, `data-placement`)에 목록이 붙는다. Native는 화면 아래에서 시트가 올라오고 [제목·닫기] → [상태 문구 또는 선택지 목록] 순서다 | `react/src/advanced-forms.tsx`(`AnchoredPortal`), `CollectionSheetHeader` |
101
+ | 크기 | 입력 높이 `medium` 44(`fieldFrameContract`) · `large` 52(`control.buttonHeight.large`), 폭은 폼 열을 채운다. Web 목록 최대 높이 22.5rem(360, recipe `popover.maxHeight`), 선택지 최소 높이 `compact` 44 · `comfortable` 56(3.5rem). Native는 공통 Sheet의 `auto` 높이·가용 영역 상한을 따른다 | `selectRecipe.sizes`·`popover`, `.hjm-combobox__listbox`·`__option`, `react-native/src/forms.tsx` |
102
+ | 간격 | 라벨·설명·오류는 [Field](field.md) 규칙(`formSupportContract.gap` `spacing.xs` 8). Web 목록 안쪽 `spacing.xs` 8(`comboboxRecipe.popover.padding` = Select와 같은 표면. 2026-10-06까지 4, 1.13.0 변경), 선택지 안쪽 위아래 `spacing.xs` 8 · 좌우 `spacing.sm` 12. Native 시트 여백·안전 영역·손잡이와 본문 간격은 [Sheet](sheet.md)의 공통 recipe를 따른다 | `.hjm-combobox__option`, `react-native/src/forms.tsx` |
103
+ | 순서·정렬 | 폼 안에서 다른 입력과 같은 열에 둔다. Web은 입력 바로 아래(공간이 없으면 위, `data-placement`)에 목록이 붙는다. Native는 공통 Sheet의 손잡이 → 상태 문구 또는 선택지 목록 순서다. 제목은 접근성 이름으로만 남기고 ×는 기본으로 숨긴다 | `react/src/advanced-forms.tsx`(`AnchoredPortal`), Native `Sheet` |
102
104
  | 고정·스크롤 | 목록은 그 안에서 스크롤한다. Web 목록은 `position: fixed`(z-index `layer.dropdown` 400)로 떠서 화면 스크롤과 무관하다. Native 시트는 배경막(`backdrop.modal`)과 함께 모달로 뜬다 | `.hjm-combobox__listbox`, `react-native/src/forms.tsx` |
103
105
  | 좁은 폭·큰 글자 | 선택지 문구는 줄바꿈되고(`overflow-wrap: anywhere`) 높이가 늘어난다. 좁은 폭에서도 Web 목록은 입력에 붙는다 | `.hjm-combobox__option` |
104
106
 
@@ -109,8 +111,8 @@ Web Native
109
111
  │ [ 입력어 _____________ ▾ ]│ │ [ 입력어 _____________ ▾ ]│
110
112
  │ ┌──────────────────────┐ │ │ ▒▒▒▒ 배경막 ▒▒▒▒▒▒▒▒▒▒▒▒ │
111
113
  │ │ 선택지 1 (44/56) │ │ ← fixed │ ┌──────────────────────┐ │
112
- │ │ 선택지 2 │ │ 스크롤 │ │ 제목 [닫기]│ │
113
- │ └──────────────────────┘ │ │ │ 선택지 목록(스크롤) │ │ ← 최대 75%
114
+ │ │ 선택지 2 │ │ 스크롤 │ │ ━━━ │ │
115
+ │ └──────────────────────┘ │ │ │ 선택지 목록(스크롤) │ │ ← Sheet 가용 높이
114
116
  │ 설명·오류 │ │ │ ░ 하단 안전 영역 ░ │ │
115
117
  └──────────────────────────┘ └─┴──────────────────────┴─┘
116
118
  ```
@@ -51,6 +51,7 @@ import { CompositionLayout } from "@hjmds/react-native/screens";
51
51
 
52
52
  | prop | 값 | 기본값 | 설명 |
53
53
  | --- | --- | --- | --- |
54
+ | titleSize | default · compact | default | 1.37.0. compact는 body(14)·500으로 표시하고 제목 의미를 유지한다. 2026-10-20 Spint 시트의 TOP 제목 축소 요청 |
54
55
  | title / description | string | 없음 | 제품 지역화 문구 |
55
56
  | leading / badge / actions | ReactNode | 없음 | 앞 콘텐츠·제목 옆 배지·끝 행동. 자리와 간격은 HJM |
56
57
  | notice | ReactNode | 없음 | 초안을 유지하는 비차단 안내 |
@@ -93,7 +94,7 @@ footer
93
94
  | 의미 | Heading·alert/status | header·accessibilityLiveRegion |
94
95
  | 환경 | 부모 화면의 높이 | 부모 화면의 safe area·키보드 |
95
96
 
96
- 제목과 설명 묶음은 자연 너비를 기준으로 늘어나며 부모 너비를 넘지 않는다. 제목과 actions가 한 줄에 들어가지 않으면 actions가 다음 줄로 이동한다. Spint 셀에서 제목이 정렬 버튼 옆의 남은 폭으로 압축되던 문제를 양 renderer에서 수정했다(미게시).
97
+ 제목과 설명 묶음은 자연 너비를 기준으로 늘어나며 부모 너비를 넘지 않는다. 제목과 actions가 한 줄에 들어가지 않으면 actions가 다음 줄로 이동한다. Spint 셀에서 제목이 정렬 버튼 옆의 남은 폭으로 압축되던 문제를 양 renderer에서 수정했다(1.37.0).
97
98
 
98
99
  2026-10-19 사용자 지시: 앱·모바일 웹뷰의 큰 글씨를 지양하므로 구획 제목은 두 renderer 모두 18(level5)을 기본으로 쓴다.
99
100
  headingLevel은 문서 구조만 결정한다. 기존 24는 상한이며 일반 카드·선택 영역 제목의 기본값이 아니다.
@@ -85,7 +85,7 @@ expect(checkBrandPaletteContrast(PRODUCT_BRAND_PALETTE)).toEqual({ light: [], da
85
85
  | `textScale` | 연속값 | `1` | — |
86
86
  | `reducedMotion` | `true` · `false` | `false` | — |
87
87
  | `minimumVisualTarget` | `true` · `false` | `false` | — |
88
- | `brandPalette` | `{ light?, dark? }` | — | 각각 `ThemeColors` 17개 key 중 필요한 것만 넘긴다(부분 병합). 상태 강조색은 덮을 수 없다. 중첩 Provider는 가장 가까운 상위의 값을 물려받는다 |
88
+ | `brandPalette` | `{ light?, dark? }` | — | 각각 `ThemeColors` 20개 key 중 필요한 것만 넘긴다(부분 병합). 상태 강조색은 덮을 수 없다. 중첩 Provider는 가장 가까운 상위의 값을 물려받는다 |
89
89
  | `designProfile` | `HjmDesignProfile` | 가장 가까운 상위 프로필 또는 없음 | 기본 프로필·표면 질감은 1.15.0 추가. `hjmDesignPresets` 또는 `defineHjmDesignProfile` 결과만 넣는다. 글자 역할·관련 구성 개선은 1.16.0 변경. [프로필 계약](../../design-profile.md)의 토큰·질감·전환·구성·화면 기본값을 상속한다 |
90
90
  | `value` | `DesignSystemProviderValue`(`resolveDesignSystemProviderValue` 결과) | — | 테스트·스토리·임베딩용. 환경 prop·`brandPalette`·`designProfile`과 함께 쓸 수 없고(타입이 막는다), 주면 OS theme·모션 관찰과 상위 `brandPalette` 상속이 멈춘다 |
91
91
  | `surfaceEffects`(Native) | `{ renderBackdrop?, insetShadows? }` | 상위 host 또는 없음 | 1.15.0 실험 API. 실제 blur host·inset capability를 한 번 등록하며 `{}`로 하위 영역을 해제. [Surface 질감 계약](../../design-profile.md#surfacecard의-유리클레이-질감) |
@@ -120,7 +120,7 @@ expect(checkBrandPaletteContrast(PRODUCT_BRAND_PALETTE)).toEqual({ light: [], da
120
120
 
121
121
  | 항목 | Web | Native |
122
122
  | --- | --- | --- |
123
- | `density`(`comfortable` 기본 · `compact`) | 있음 | 없음 |
123
+ | `density`(`comfortable` 기본 · `compact`) | 있음 | 있음. ListRow·Menu·DataTable의 생략된 density에 적용 |
124
124
  | `host`(`surface` 기본 · `contents`) | 있음. 문서 루트가 이미 표면을 칠하면 `contents` | 없음 |
125
125
  | `systemTheme` 고정(SSR·테스트) | 있음 | 없음(`useColorScheme`) |
126
126
  | `safeAreaInsets` | 없음 | 있음. Sheet·DatePicker·Select·Combobox가 기본 여백으로 쓴다 |
@@ -122,3 +122,5 @@ EmptyState 자체에는 콜백 prop이 없다. 2026-10-07 기존 예제의 생
122
122
  | 배치 | `layoutStyle`(그 밖에 `className`과 div 속성) | `layoutStyle`(슬롯 style 다섯 개는 제거된 속성) |
123
123
 
124
124
  2026-10-18 사용자 결정: 제목과 보조 설명 모두 `body`(기본 14, 줄 높이 20)를 사용한다. 화면·부분 영역의 크기나 Web 부모의 큰 글자 스타일에 따라 빈 상태 문구를 키우지 않는다. 공통 화면·구성의 `empty` 상태도 같은 기준이다. Spint의 부분 빈 상태가 제목 크기로 커진 문제와 Web의 부모 글자 상속·Native 설명 12의 차이를 함께 정리했다.
125
+
126
+ 2026-10-20 Native 행동 영역은 본문 폭을 확보하고 일반 버튼은 가운데에 둔다. `action`에 세로 버튼 묶음을 넣으면 묶음과 버튼에 `width: "100%"`/`fullWidth`를 사용한다. 비행중 빈 화면에서 내용 기반 부모와 백분율 자식의 순환 폭 계산으로 버튼 폭과 중앙 정렬이 달라진 문제를 수정했다(개발 소스, 미게시).
@@ -10,6 +10,10 @@
10
10
 
11
11
  ## 언제 쓰나
12
12
 
13
+ 저장된 항목·폼 안의 작은 성공 상태는 `appearance="status"`(1.37.0)를 쓴다. 기존 Badge에 제목을 표시하고,
14
+ 바깥 배경·테두리·padding을 없앤다. 설명·행동·낭독은 그대로 유지한다. 기본 `appearance="panel"`은 변경하지 않는다.
15
+ 세 가지 저장 결과 선택은 [저장 결과 구성](../compositions/save-feedback.md)을 따른다.
16
+
13
17
  화면 흐름 안 **제자리에 남아 있는** 상태 알림에 쓴다. 저장 실패, 오프라인, 권한 제한,
14
18
  결제 수단 만료처럼 사용자가 읽고 필요하면 행동(`action`)할 때까지 사라지면 안 되는 내용이 여기에 속한다.
15
19
 
@@ -184,3 +184,5 @@ Spint 스팟 필터가 제품 ScrollView와 고정 추가 행을 재조립하던
184
184
 
185
185
  <!-- usage-example: Web ../../../../../showcase/web/src/components/SegmentedControl.stories.tsx -->
186
186
  <!-- usage-example: Native ../../../../../showcase/native/src/components/SegmentedControl.stories.tsx -->
187
+
188
+ 2026-10-20 개발 소스·미게시: Spint 카테고리 재검토에서 사용자가 여백은 이전처럼 유지하고 글씨를 줄이도록 요청했다. pills 좌우 여백은 small 12, medium 16으로 복원하고 글씨는 label(12/18)로 통일한다. 높이와 터치 영역은 유지한다.
@@ -104,12 +104,14 @@ import { NativeSelect } from "@hjmds/react/forms";
104
104
 
105
105
  ## 배치
106
106
 
107
+ 2026-10-20 언어 선택 화면에서 자체 모달에 옛 ×·여백이 남은 문제를 해결하기 위해 Native는 공통 Sheet를 직접 사용한다. 닫기 제스처·안전 영역·높이·키보드 대응·teardown은 Sheet가 소유하고, 선택 목록과 확정 콜백은 입력 컴포넌트가 소유한다.
108
+
107
109
  | 항목 | 값 | 근거 |
108
110
  | --- | --- | --- |
109
- | 크기 | 트리거 최소 높이 `medium` 44 · `large` 52, 폼 열 폭을 채운다(Web `inline-size: 100%`). 선택지 행 `comfortable` 56 · `compact` 44. Web 목록 최대 폭 420(`min(26.25rem, 100vw − 2×spacing.md)`)·최대 높이 360. Native sheet 최대 높이 75% | `fieldFrameContract.minHeight`, `control.buttonHeight.large`, `selectRecipe.density`·`popover`, `.hjm-select__listbox`, Native `forms.tsx` |
110
- | 간격 | 트리거 좌우 `medium` `spacing.md` 16 · `large` `spacing.lg` 20(recipe), 안쪽 요소 사이 `spacing.sm` 12. 라벨·설명·오류와 `spacing.xs` 8. Web 목록은 트리거에서 8 떨어지고 안쪽 여백 `spacing.xs` 8(`selectRecipe.popover.padding`; 2026-10-06까지 4, 1.13.0 변경), 화면 가장자리에서 8(`selectRecipe.popover.collisionPadding`). Native sheet 안쪽 `spacing.md` 16, 머리와 목록 사이 `spacing.sm` 12 | `selectRecipe.sizes`·`value.gap`, `formSupportContract.gap`, `selectRecipe.popover`(sideOffset 8, collisionPadding 8, padding 8), Native `forms.tsx` |
111
- | 순서·정렬 | 트리거: 앞 아이콘 → 값(넘치면 말줄임) → 펼침 표시. Web 목록은 트리거 아래·트리거와 같은 폭, 공간이 없으면 위로 뒤집힌다. Native sheet 머리는 제목 + 끝쪽 닫기 ×(44). 확인 버튼은 없고 항목을 누르면 선택하고 닫힌다 | `.hjm-select__trigger`, `select.tsx`(`matchAnchorWidth`), `CollectionSheetHeader` |
112
- | 고정·스크롤 | Web 목록은 `position: fixed` popover(z-index `layer.dropdown` 400), 넘치면 목록 안에서 스크롤. Native는 화면 아래에서 올라오는 modal sheet로 목록만 스크롤되고, 아래 여백에 안전 영역(`safeArea.bottom`)을 더한다. 배경(scrim)을 누르면 닫힌다 | `.hjm-select__listbox`, Native `Select`(Modal·ScrollView) |
111
+ | 크기 | 트리거 최소 높이 `medium` 44 · `large` 52, 폼 열 폭을 채운다(Web `inline-size: 100%`). 선택지 행 `comfortable` 56 · `compact` 44. Web 목록 최대 폭 420(`min(26.25rem, 100vw − 2×spacing.md)`)·최대 높이 360. Native는 공통 Sheet의 `auto` 높이·가용 영역 상한을 따른다 | `fieldFrameContract.minHeight`, `control.buttonHeight.large`, `selectRecipe.density`·`popover`, `.hjm-select__listbox`, Native `forms.tsx` |
112
+ | 간격 | 트리거 좌우 `medium` `spacing.md` 16 · `large` `spacing.lg` 20(recipe), 안쪽 요소 사이 `spacing.sm` 12. 라벨·설명·오류와 `spacing.xs` 8. Web 목록은 트리거에서 8 떨어지고 안쪽 여백 `spacing.xs` 8(`selectRecipe.popover.padding`; 2026-10-06까지 4, 1.13.0 변경), 화면 가장자리에서 8(`selectRecipe.popover.collisionPadding`). Native 시트 여백·안전 영역·손잡이와 본문 간격은 [Sheet](sheet.md)의 공통 recipe를 따른다 | `selectRecipe.sizes`·`value.gap`, `formSupportContract.gap`, `selectRecipe.popover`(sideOffset 8, collisionPadding 8, padding 8), Native `forms.tsx` |
113
+ | 순서·정렬 | 트리거: 앞 아이콘 → 값(넘치면 말줄임) → 펼침 표시. Web 목록은 트리거 아래·트리거와 같은 폭, 공간이 없으면 위로 뒤집힌다. Native는 공통 Sheet의 상단 손잡이를 쓰며 제목은 접근성 이름으로만 남기고 ×는 기본으로 숨긴다. 확인 버튼은 없고 항목을 누르면 선택하고 닫힌다 | `.hjm-select__trigger`, `select.tsx`(`matchAnchorWidth`), Native `Sheet` |
114
+ | 고정·스크롤 | Web 목록은 `position: fixed` popover(z-index `layer.dropdown` 400), 넘치면 목록 안에서 스크롤. Native는 화면 아래에서 올라오는 modal sheet로 목록만 스크롤되고, 아래 여백에 안전 영역(`safeArea.bottom`)을 더한다. 배경(scrim)을 누르면 닫힌다 | `.hjm-select__listbox`, Native `Select`(Sheet·ScrollView) |
113
115
  | 좁은 폭·큰 글자 | 트리거 높이는 최소값이라 큰 글자에서 늘어나고 값은 한 줄 말줄임이다. Web 목록 폭은 화면 폭 − 32를 넘지 않는다 | `.hjm-select__value`, `.hjm-select__listbox` |
114
116
 
115
117
  ```text
@@ -117,13 +119,13 @@ Web popover Native sheet
117
119
  라벨 ┌──────── scrim (누르면 닫힘) ────────┐
118
120
  ┌──────────────────────┐ │ │
119
121
  │ 선택된 값 ⌄ │ 44 ├─────────────────────────────────────┤ radius.lg 16
120
- └──────────────────────┘ │ 정렬 기준 (×) │ ← 머리, × 44
121
- ↓ 8 │ gap spacing.sm 12 │
122
+ └──────────────────────┘ │ ━━━ │ ← 손잡이
123
+ ↓ 8 │ gap sheetRecipe.body.gap │
122
124
  ┌──────────────────────┐ │ ┌─────────────────────────────────┐ │
123
125
  │ 최신순 ✓ │ 56 │ │ 최신순 ✓ │ │ 56 ↕ 스크롤
124
126
  │ 인기순 │ │ │ 인기순 │ │
125
127
  └──────────────────────┘ max 360 │ └─────────────────────────────────┘ │
126
- 트리거 폭, max 420 │ padding 16 + 안전 영역 │ max 75%
128
+ 트리거 폭, max 420 │ Sheet 공통 여백 + 안전 영역 │ auto
127
129
  └─────────────────────────────────────┘
128
130
  ```
129
131
 
@@ -81,7 +81,7 @@ import { Sheet } from "@hjmds/react-native/overlays";
81
81
  | --- | --- | --- | --- |
82
82
  | `initialFocus` | `single-input` · `first-input` · `none` | 하단: 자동 포커스 없음 / 측면: `single-input` | 1.33.0 정책 변경. 하단 시트는 이 prop·`initialFocusRef`·중첩 화면·`initialFocusTarget`으로 입력 자동 포커스를 켤 수 없다. Web은 모달 컨테이너로 초점을 옮겨 focus trap을 유지하고 Native는 키보드를 열지 않는다. 측면 시트는 기존 Dialog 계약을 따른다. 사용자가 직접 입력칸을 누르는 동작은 유지한다 |
83
83
  | `placement` | `bottom` · `start` · `end` | `bottom` | 나오는 가장자리 |
84
- | `size` | `auto` · `medium`(60%) · `large`(85%) · `full`(100%) | `auto` | `auto`는 내용 높이. 비율은 화면 높이 기준(Web `dvh`), 두 플랫폼 모두 위 safe area(Native는 키보드도)를 뺀 남은 높이를 넘지 않는다. `maxHeightRatio` 0.9 상한은 `auto`에만 걸린다 |
84
+ | `size` | `auto` · `small`(40%, 1.37.0) · `medium`(60%) · `large`(85%) · `full`(100%) | `auto` | `auto`는 내용 높이. 비율은 화면 높이 기준(Web `dvh`), 두 플랫폼 모두 위 safe area(Native는 키보드도)를 뺀 남은 높이를 넘지 않는다. `maxHeightRatio` 0.9 상한은 `auto`에만 걸린다 |
85
85
  | `open` · `defaultOpen` | `boolean` | 비제어 `false` | Web은 제어하면 `onOpenChange` 필수, 비제어면 `trigger` 필수 |
86
86
  | `onOpenChange` | `(open: boolean, detail: { reason }) => void` | — | reason: `trigger` · `close-action` · `escape` · `back` · `outside` · `swipe` · `programmatic`. Android 하드웨어 back은 Native에서만 `back` |
87
87
  | `onDismissComplete` | `(detail: { reason }) => void` | — | 표면이 실제로 사라진 뒤 한 번. 다음 modal·화면 이동은 여기서 |
@@ -107,6 +107,10 @@ import { Sheet } from "@hjmds/react-native/overlays";
107
107
 
108
108
  ## 배치
109
109
 
110
+ 2026-10-20 선택의창 위치 안내 사용자 지적: 버튼은 홈 인디케이터·하단 제스처 안전 영역 **위쪽**에 둔다. Native 앱은 `SafeAreaProvider` 아래에서 읽은 `useSafeAreaInsets()`를 `HjmNativeProvider safeAreaInsets`에 전달해야 한다. Provider 기본값은 실제 기기 inset의 자동 조회가 아니므로 누락 시 0이 된다. 시트는 이 값을 사용해 하단 여백을 확보하고 footer의 BottomCTA와 중복 적용하지 않는다. 시트 높이 축소로 안전 여백을 없애지 않는다.
111
+
112
+ 개발 소스·미게시: `size="small"`은 가용 높이의 40%로, `medium`(60%) 높이의 2/3이다. 짧은 안내에 내용 높이만 쓰면 지나치게 작고 medium은 빈 공간이 큰 경우 선택한다. Web·Native 공통 recipe가 크기를 소유한다.
113
+
110
114
  Native 스크롤 본문은 viewport 높이만큼 content를 채운다(미게시). 고정 크기 시트의 빈/오류/로딩 상태는 `CompositionLayout layoutStyle={{ flex: 1 }}`로 연결해 손잡이·머리·footer를 제외한 본문 중앙에 둔다. 목록이 길면 같은 본문에서 스크롤한다.
111
115
 
112
116
  선택형 Native `GestureSheetInput`의 실제 keyboard-tracking host에도 제품 `tokens.fontFamily.ui`가 이어진다. optional library·폰트 설치와 실제 기기 검증은 제품이 맡는다.
@@ -73,7 +73,7 @@ import { TimePicker } from '@hjmds/react/time-picker';
73
73
  | 간격 | 열 사이 spacing.sm(12), 영역 사이 spacing.md(16) | timePickerRecipe |
74
74
  | 고정·스크롤 | 제목·확인은 공통 overlay 영역, 시·분 목록만 세로 스크롤 | Popover / Sheet / TimePicker |
75
75
  | 좁은 폭·큰 글자 | 좁은 폭은 두 열을 같은 폭으로 유지. 최대 글자 조건은 사용자 지침으로 범위 제외 | 포트폴리오 AGENTS.md |
76
- | 선택 표시 | radius.md, surfaceAccent 배경·contentBrand 글자 | 공통 recipe·semantic palette |
76
+ | 선택 표시 | radius.md, surfaceAccent 배경·text 글자·500 굵기 | 시간 값은 주요 글자로 강조하고, 제품 선택 배경과 확정 버튼 대표색을 구분(2026-10-20 토스 공개 팔레트 참고 요청) |
77
77
  | 설명·오류 | 입력 아래 description/error. 오류는 보조기술에도 알림 | 양 renderer |
78
78
 
79
79
 
@@ -10,6 +10,9 @@
10
10
 
11
11
  ## 언제 쓰나
12
12
 
13
+ 저장 완료의 한 줄형·대상 요약형 선택은 [저장 결과 구성](../compositions/save-feedback.md)을 따른다.
14
+ 한 줄형은 `description`, 요약형은 기존 `title` + `description`을 사용한다. 카드형 저장 완료 예시는 제거했다.
15
+
13
16
  방금 한 행동의 결과처럼 **무시해도 안전한 짧은 알림**에 쓴다. 저장 완료, 복사 완료, 백그라운드 작업 완료
14
17
  알림이 여기에 속한다. 화면 흐름 밖에 떠서 3초 뒤 스스로 닫히고, 큐에 쌓인 다음 알림이 이어서 뜬다.
15
18
 
@@ -100,8 +103,8 @@ function useProfileSavedToast() {
100
103
  ### 디자인 프로필 상속
101
104
 
102
105
  2026-10-07 테마 소비 경로 점검에서 고정 foundation/recipe 값이 남은 곳을 보완했다.
103
- 모서리의 recipe 역할은 유지하고 값은 가장 가까운 Provider의 `designProfile.tokens.radius`를
104
- 읽는다. Dialog/AlertDialog/Sheet/일반 Toast의 그림자는 `tokens.shadow.floating`을 읽으며
106
+ Dialog/AlertDialog/Sheet의 모서리는 가장 가까운 Provider의 `designProfile.tokens.radius`를 읽는다.
107
+ 일반 Toast는 승인된 알림 시안의 `toastRecipe.material.radius` 24를 사용한다. Dialog/AlertDialog/Sheet/일반 Toast의 그림자는 `tokens.shadow.floating`을 읽으며
105
108
  프로필 없는 소비자의 기본값은 유지한다. 상태·초안·선택·Modal teardown은 이 축의 소유가 아니다.
106
109
  플랫폼 근사와 미검증 범위는 [프로필 계약](../../design-profile.md#오버레이선택-입력의-프로필-연결-보완)을 따른다.
107
110
 
@@ -118,7 +121,7 @@ Native의 선택형 `/toast-liquid`도 가장 가까운 프로필의 `radius.xl`
118
121
 
119
122
  | 항목 | 값 | 근거 |
120
123
  | --- | --- | --- |
121
- | 크기 | 카드 최대 폭 420(Web `min(420px, 100vw − 2×spacing.md)`, Native 화면 폭 − 안전 영역 − 2×16에서 420으로 자름). 최소 높이 56(`layout.rowHeight.singleLine`). 모서리 `radius.xl` 24(패널, 미게시 1.17.2 이후. 그전 `lg` 16). 열은 [아이콘 배지 32 또는 `media` 44][문구][닫기 44]. `action`은 높이 44(`control.minTouchTarget`), 좌우 여백 `spacing.md` 16의 pill. `progress` 점 6(현재 단계 1.4배) | `toastRecipe.viewport`·`surface`·`icon`·`action`, `.hjm-toast`, `react-native/src/feedback.tsx` |
124
+ | 크기 | 카드 최대 폭 420(Web `min(420px, 100vw − 2×spacing.md)`, Native 화면 폭 − 안전 영역 − 2×16에서 420으로 자름). 최소 높이 56(`layout.rowHeight.singleLine`). 모서리 `toastRecipe.material.radius` 24. 한 줄 열은 [장식 아이콘 32 또는 `media` 44][문구][닫기 44]. 요약형은 장식 아이콘 없이 [제목·설명][닫기 44]이며 제품 `media`는 유지한다. `action`은 높이 44(`control.minTouchTarget`), 좌우 여백 `spacing.md` 16의 pill. `progress` 점 6(현재 단계 1.4배) | `toastRecipe.viewport`·`surface`·`icon`·`action`, `.hjm-toast`, `react-native/src/feedback.tsx` |
122
125
  | 안쪽 여백 | 카드 위아래 `spacing.sm` 12, 좌우 양쪽 `spacing.md` 16(`toastRecipe.surface.padding`, 미게시). 닫기는 44 터치 영역을 유지하되 끝 쪽으로 (44 − 글리프 14)/2 = 15만큼 여백에 겹쳐(`close.inlineOverhang`) × 글리프 끝이 카드 끝에서 16 — 배지의 시작 여백 16과 같다. RTL에서도 논리 방향(시작·끝)으로 대칭이다. `action`·`progress`는 문구 열에 맞춘다 | `toastRecipe.surface`·`close`, `.hjm-toast`·`.hjm-toast__close`, `react-native/src/feedback.tsx` |
123
126
  | 첫 줄 기준 | 배지 32 상자(`toastRecipe.anchor.blockSize`)가 기준선이다. 첫 줄(제목, 없으면 설명)은 (32 − 그 줄의 행간)/2만큼 내려 배지 가운데에 맞추고, 닫기는 위아래 −(44 − 32)/2 = −6(`close.blockOverhang`)으로 같은 가운데에 둔 채 행을 늘리지 않는다. 한 줄이면 12 + 32 + 12 = 56에 배지·글자·닫기 가운데가 모두 28. 여러 줄이면 배지·닫기는 첫 줄 옆에 남고 나머지 줄은 아래로 자란다(문구 덩어리 전체의 가운데에 맞추지 않는다). `media` 44는 같은 위쪽에서 시작하고 기준 상자가 아니다 — 제목을 media 가운데에 맞추면 설명이 아바타 아래로 밀려나서다. Web은 첫 줄의 `1lh`, Native는 body 토큰 행간 × 현재 글자 배율로 계산해 프로필·글자 확대에서도 맞는다 | `toastRecipe.anchor`·`close`, `.hjm-toast__content`, `react-native/src/feedback.tsx` |
124
127
  | 간격 | 화면 가장자리에서 `spacing.md` 16 + 안전 영역(`safeAreaMode: "additive"`). 여러 장 사이 `spacing.sm` 12. 카드 안 열 간격 `spacing.sm` 12 | `toastRecipe.viewport`, `.hjm-toast-viewport` |
@@ -216,3 +219,12 @@ Web은 전체 영역의 투명한 button과 두 제어 버튼을 형제로, Nati
216
219
  닫기 글리프 30이 서로 달랐고(배지 `start`·문구 `center`·닫기 `start`−4의 세 정렬), 시작 여백 16과 닫기 글리프 끝 여백 22가 비대칭이었다
217
220
  (여백 12/8/12/16 안에 44 터치 영역). Native는 사방 16 여백과 문구 `center` 정렬로 따로 어긋났다. 첫 줄 기준과 대칭 여백을 recipe 값으로 두고
218
221
  두 renderer가 같은 값을 읽게 바꿨다. 수정 후 한 줄·여러 줄·action·media·RTL 모두 가운데 28(테두리 포함 29), 양쪽 여백 16으로 측정됐다.
222
+
223
+ ## 2026-10-20 라이트·다크 표면 갱신 (1.37.1)
224
+
225
+ 승인된 알림 시안을 따라 일반 Toast는 프로필의 큰 모서리 대신 `toastRecipe.material.radius` 24를 사용한다.
226
+ 위의 과거 프로필 모서리 설명은 선택형 리퀴드 표현에만 적용된다. 배지 색 면을 제거하고 한 줄의 아이콘과 본문은 primary 색,
227
+ 제목 아래의 설명은 secondary 색으로 구분한다. 요약형은 장식 아이콘을 생략한다. 문구·타이머·큐·닫기·행동 API는 유지한다.
228
+ 라이트는 우윳빛, 다크는 차콜이며 같은 material 값과 floating 그림자를 두 renderer가 사용한다.
229
+ 웹은 지원 환경에서 backdrop blur와 0.96 fill을 사용한다. Native 기본 표현은 blur host를 요구하지 않는 불투명 표면이다.
230
+ 이는 새 npm 게시나 운영 제품 배포를 뜻하지 않는다.
@@ -0,0 +1,64 @@
1
+ # 저장 결과
2
+
3
+ - 단계: 구성
4
+ - 상태: 실험
5
+ - 지원: Web · Native
6
+ - 적용: 1.37.1
7
+ - 검토일: 2026-10-20
8
+ - 근거: 사용자 요청으로 카드 안 저장 완료 예시(이전 3번)를 제거하고, 승인된 라이트·다크 토스트 시안으로 두 유형을 통일했다.
9
+ - 스토리북: `실험/구성/피드백과 복구/저장 결과`
10
+
11
+ ## 언제 쓰나
12
+
13
+ 실제 영속 저장 성공 뒤 짧게 결과를 알린다. 문구·대상·저장 판정은 제품이 소유하고, 표면·안전 영역·타이머·접근성은 기존 Toast가 소유한다.
14
+
15
+ ## 구성 요소
16
+
17
+ | 유형 | 쓰는 상황 | 공개 API |
18
+ | --- | --- | --- |
19
+ | 1 · 한 줄 토스트 | 짧은 완료 확인 | Toast `description`, `tone="success"` |
20
+ | 2 · 요약 토스트 | 저장한 대상까지 확인 | 같은 Toast의 `title` + `description` |
21
+
22
+ 두 유형은 별도 엔진이나 변형 prop이 아니다. 웹은 ToastProvider, Native는 ToastRegion을 앱 루트에 둔다. 카드형 저장 결과는 선택지에서 제거했다. 일반 Notice·Card·Badge의 다른 용도는 유지한다.
23
+
24
+ ## 배치
25
+
26
+ | 영역 | 위치·크기 |
27
+ | --- | --- |
28
+ | 바깥 틀 | 최대 폭 420, 최소 높이 56, 모서리 24 |
29
+ | 화면 가장자리 | 16 + 안전 영역. 광고 footer가 있으면 상단 |
30
+ | 내부 | 가로 16·세로 12, 첫 줄 기준 정렬, 닫기 터치 영역 44 유지 |
31
+ | 한 줄 | 장식 아이콘 → 본문 → 닫기 |
32
+ | 요약 | 제목 → 보조 설명, 오른쪽 닫기. 장식 아이콘은 생략; 제품 media는 유지 |
33
+
34
+ ```text
35
+ 한 줄: [ ✓ KE123 저장했어요 × ]
36
+ 요약: [ 항공편을 저장했어요 × ]
37
+ [ KE123 · 인천 → 도쿄 ]
38
+ ```
39
+
40
+ 라이트는 우윳빛 표면·짙은 글자, 다크는 차콜 표면·밝은 글자다. 얇은 경계와 floating 그림자를 사용한다. 값은 toastRecipe.material이 소유한다. 웹은 지원 환경에서 backdrop blur를 사용하고 투명도 감소 설정에서는 불투명하다. Native 기본 renderer는 추가 blur 의존성 없이 같은 색의 불투명 표면을 사용한다. 실제 배경 흐림 효과로 보고하지 않는다.
41
+
42
+ ## 흐름과 상태
43
+
44
+ | 상태 | 표시·행동 |
45
+ | --- | --- |
46
+ | 저장 중 | 기존 제출 상태, 성공 알림 없음 |
47
+ | 성공 | 저장 응답/영속 저장 완료 후 토스트 한 장, polite 낭독 한 번 |
48
+ | 실패 | 입력 보존, 기존 오류·재시도 |
49
+
50
+ 기본 3초이며 긴 요약은 제품에서 시간을 늘린다. 같은 성공을 카드 상태와 토스트로 중복 알리지 않는다. 이전 초안의 늦은 응답을 새 초안의 성공으로 표시하지 않는다.
51
+
52
+ ## 코드 골격
53
+
54
+ ```tsx
55
+ // 영속 저장 성공 뒤; Native useToastRegion / Web useToast의 기존 publish.
56
+ toast.publish({ id: "saved", description: savedLabel, tone: "success", closeLabel });
57
+ toast.publish({ id: "saved", title: savedTitle, description: subjectSummary, tone: "success", closeLabel });
58
+ ```
59
+
60
+ 실행 예제: [Web](../../../../../showcase/web/src/components/save-feedback-preview.tsx), [Native](../../../../../showcase/native/src/save-feedback-preview.tsx), [공통 문구](../../../../../showcase/shared/save-feedback.ts). 비교 예제의 durationMs: null은 제품 기본 타이머를 바꾸지 않는다.
61
+
62
+ ## 배포 경계
63
+
64
+ 새 라이트·다크 표면은 1.37.1부터 제공한다. npm 게시·소비 제품의 패키지 갱신·제품 운영 배포를 각각 확인한다.
@@ -19,7 +19,7 @@
19
19
  1. 컴포넌트에 `tone`이 있으면 그것으로 고른다([Text](../components/text.md), [Icon](../components/icon.md),
20
20
  [Badge](../components/badge.md), [Notice](../components/notice.md), [Button](../components/button.md)).
21
21
  2. 없으면 아래 표에서 **칠할 대상**(용도 열)으로 역할을 찾는다. 같은 대상에 후보가 둘이면 위쪽(더 강한) 것을 먼저 본다.
22
- 3. 채운 바탕 위의 글자는 반드시 짝(`onPrimary`·`onDanger`·`onAccentFill`)을 쓴다.
22
+ 3. 채운 바탕 위의 글자는 반드시 짝(`onPrimary`·`onDanger`·`onStatusFill`)을 쓴다.
23
23
 
24
24
  ## 값
25
25
 
@@ -65,8 +65,14 @@
65
65
  | `feedback.warning.foreground` | `#92400e` / `#fbbf24` | `--hjm-accent-warning` | `theme.palette.statusAccents.warning` | 주의가 필요한 상태 |
66
66
  | `feedback.attention.foreground` | `#9a3412` / `#fb923c` | `--hjm-accent-attention` | `theme.palette.statusAccents.attention` | 눈길을 끌 새 소식 |
67
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 |
68
+ | `accentFill.<tone>` | info `#6d28d9` · success `#065f46` · warning `#92400e` · attention `#9a3412`(두 테마 공통) | `--hjm-accent-fill-<tone>` | `theme.palette.statusAccentFills.<tone>` | 꽉 찬 상태 표지 바탕. 글자는 팔레트의 `onStatusFill`(기본 `#ffffff`) |
69
+ | `onStatusFill` | `#ffffff`(두 테마 공통) | `--hjm-color-on-status-fill` | `theme.colors.onStatusFill` | 채운 상태 표지의 글자. 모든 상태 fill과 4.5:1 이상 |
70
+ | `neutralFill` | `#4e5968`(두 테마 공통) | `--hjm-color-neutral-fill` | `theme.colors.neutralFill` | 중립 상태 표지 바탕 |
71
+ | `strongFill` | `#333d4b`(두 테마 공통) | `--hjm-color-strong-fill` | `theme.colors.strongFill` | 강한 상태 표지 바탕 |
72
+ | `accentTint` | `weak` 0.1 · `base` 0.15 · `strong` 0.2 · `border` 0.3 | — | `onStatusFill` | `#ffffff`(두 테마 공통) | `--hjm-color-on-status-fill` | `theme.colors.onStatusFill` | 채운 상태 표지의 글자. 모든 상태 fill과 4.5:1 이상 |
73
+ | `neutralFill` | `#4e5968`(두 테마 공통) | `--hjm-color-neutral-fill` | `theme.colors.neutralFill` | 중립 상태 표지 바탕 |
74
+ | `strongFill` | `#333d4b`(두 테마 공통) | `--hjm-color-strong-fill` | `theme.colors.strongFill` | 강한 상태 표지 바탕 |
75
+ | `accentTint` | 피드백 색을 옅게 깔 때 쓰는 alpha |
70
76
 
71
77
  도메인 상태(예: "배송 지연")는 제품 어댑터에서 `info`·`success`·`warning`·`attention`·`danger` 중 하나로 매핑한다.
72
78
  `info`·`success`·`warning`·`attention`의 accent 색은 `brandPalette` 변경 대상이 아니다.
@@ -114,7 +120,7 @@ const theme = useHjmNativeTheme();
114
120
  const tint = resolveColorReference(semanticColors.feedback.warning.background, theme.palette);
115
121
  ```
116
122
 
117
- 제품 브랜드는 Provider의 `brandPalette`로 17개 key 중 필요한 것만 바꾼다. 값은 [테마 편집](theme-studio.md)으로 고르고 대비를 확인한다.
123
+ 제품 브랜드는 Provider의 `brandPalette`로 20개 key 중 필요한 것만 바꾼다. 값은 [테마 편집](theme-studio.md)으로 고르고 대비를 확인한다.
118
124
 
119
125
  ```tsx
120
126
  // Web
@@ -131,7 +137,7 @@ import { HjmProvider } from "@hjmds/react/provider";
131
137
  - 제품 브랜드를 `--hjm-color-*` 재정의나 `.hjm-*` 덮어쓰기로 넣지 않는다. 색만 바꾸면 `brandPalette`, 여러 표현 축을 함께 선택하면 검증한 `designProfile`을 쓴다([브랜드 경계](../../brand-boundary.md)).
132
138
  - 성공·오류 색을 브랜드 색으로, 브랜드 색을 상태 색으로 쓰지 않는다.
133
139
  - `textWeak`로 읽혀야 하는 문구를 쓰지 않는다. 보조 문구는 `textMuted`·`textSub`다.
134
- - 채운 `primary`·`dangerFill`·`accentFill` 위에 `text`를 올리지 않는다. 짝(`onPrimary`·`onDanger`·`onAccentFill`)을 쓴다.
140
+ - 채운 `primary`·`dangerFill`·`accentFill` 위에 `text`를 올리지 않는다. 짝(`onPrimary`·`onDanger`·`onStatusFill`)을 쓴다.
135
141
  - Showcase의 예시 색을 제품 기본값으로 복사하지 않는다.
136
142
 
137
143
  ## 플랫폼 차이
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hjmds/design-contracts",
3
- "version": "1.36.1",
3
+ "version": "1.37.1",
4
4
  "description": "Renderer-neutral design contracts, tokens, recipes, and behaviors shared by HJM products.",
5
5
  "license": "MIT",
6
6
  "repository": {