@hjmds/design-contracts 1.22.0 → 1.23.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 (107) hide show
  1. package/dist/version.d.ts +1 -1
  2. package/dist/version.js +1 -1
  3. package/dist/version.js.map +1 -1
  4. package/docs/generated/component-maturity.md +1 -1
  5. package/docs/generated/renderer-evidence.json +3 -3
  6. package/docs/generated/renderer-evidence.md +1 -1
  7. package/docs/generated/showcase-manifest.json +1 -1
  8. package/docs/screen-patterns.md +11 -4
  9. package/docs/usage/README.md +2 -1
  10. package/docs/usage/SELECTION.md +36 -23
  11. package/docs/usage/STANDARD.md +5 -0
  12. package/docs/usage/components/comment-thread-screen.md +4 -1
  13. package/docs/usage/components/composition-layout.md +92 -0
  14. package/docs/usage/components/screen-layout.md +1 -0
  15. package/docs/usage/compositions/action-feedback.md +3 -1
  16. package/docs/usage/compositions/action-recovery-optimistic.md +4 -2
  17. package/docs/usage/compositions/action-recovery-save.md +4 -2
  18. package/docs/usage/compositions/action-recovery-undo.md +4 -2
  19. package/docs/usage/compositions/activity-status.md +3 -1
  20. package/docs/usage/compositions/adaptive-content.md +4 -2
  21. package/docs/usage/compositions/authoring-tools.md +2 -0
  22. package/docs/usage/compositions/collection-detail.md +3 -1
  23. package/docs/usage/compositions/collection-editing.md +2 -0
  24. package/docs/usage/compositions/command-records.md +3 -1
  25. package/docs/usage/compositions/common-message.md +3 -1
  26. package/docs/usage/compositions/common-notification.md +3 -1
  27. package/docs/usage/compositions/communication.md +2 -0
  28. package/docs/usage/compositions/compound-controls.md +4 -2
  29. package/docs/usage/compositions/content-transition-comparison.md +3 -1
  30. package/docs/usage/compositions/context-toolbar.md +4 -2
  31. package/docs/usage/compositions/data-layouts.md +4 -2
  32. package/docs/usage/compositions/data-workflows.md +2 -0
  33. package/docs/usage/compositions/date-entry.md +3 -1
  34. package/docs/usage/compositions/date-time-selection.md +3 -1
  35. package/docs/usage/compositions/design-profile-comparison.md +3 -1
  36. package/docs/usage/compositions/disclosure.md +6 -4
  37. package/docs/usage/compositions/document-resource.md +3 -1
  38. package/docs/usage/compositions/environment-matrix.md +3 -1
  39. package/docs/usage/compositions/expo-interactions.md +4 -2
  40. package/docs/usage/compositions/family-drawer.md +4 -2
  41. package/docs/usage/compositions/field-group.md +3 -1
  42. package/docs/usage/compositions/floating-action-button.md +3 -1
  43. package/docs/usage/compositions/illustrated-outcome.md +3 -1
  44. package/docs/usage/compositions/input-sheet.md +3 -1
  45. package/docs/usage/compositions/interaction-adapters.md +3 -1
  46. package/docs/usage/compositions/interaction-flow-apply.md +3 -1
  47. package/docs/usage/compositions/interaction-flow-draft.md +3 -1
  48. package/docs/usage/compositions/interaction-flow-search.md +3 -1
  49. package/docs/usage/compositions/live-list.md +3 -1
  50. package/docs/usage/compositions/media-transport.md +2 -0
  51. package/docs/usage/compositions/native-renderers.md +3 -1
  52. package/docs/usage/compositions/navigation-bar-collection.md +3 -1
  53. package/docs/usage/compositions/optional-adapters.md +3 -1
  54. package/docs/usage/compositions/optional-motion.md +3 -1
  55. package/docs/usage/compositions/origin-dialog.md +3 -1
  56. package/docs/usage/compositions/paper-surface.md +3 -1
  57. package/docs/usage/compositions/photo-source.md +3 -1
  58. package/docs/usage/compositions/purpose-input-comment.md +8 -2
  59. package/docs/usage/compositions/purpose-input-message.md +4 -2
  60. package/docs/usage/compositions/reference-first.md +3 -1
  61. package/docs/usage/compositions/reference-review.md +3 -1
  62. package/docs/usage/compositions/reference-settings.md +3 -1
  63. package/docs/usage/compositions/selection-motion.md +4 -2
  64. package/docs/usage/compositions/selection-scope.md +3 -1
  65. package/docs/usage/compositions/social-compositions.md +2 -0
  66. package/docs/usage/compositions/stea-event-ticket.md +3 -1
  67. package/docs/usage/compositions/stea-flip-card.md +4 -2
  68. package/docs/usage/compositions/stea-order-progress.md +5 -3
  69. package/docs/usage/compositions/stea-otp-verify.md +5 -3
  70. package/docs/usage/compositions/stea-pixel-empty.md +5 -3
  71. package/docs/usage/compositions/stea-schedule-card.md +5 -3
  72. package/docs/usage/compositions/stea-stat-summary.md +4 -2
  73. package/docs/usage/compositions/streaming-answer.md +2 -0
  74. package/docs/usage/compositions/task-controls.md +2 -0
  75. package/docs/usage/compositions/texture-comparison.md +3 -1
  76. package/docs/usage/compositions/time-selection.md +5 -3
  77. package/docs/usage/compositions/toast-layout.md +3 -1
  78. package/docs/usage/compositions/toggle-feedback.md +2 -0
  79. package/docs/usage/compositions/transfer-task.md +2 -0
  80. package/docs/usage/compositions/upload-recovery.md +4 -2
  81. package/docs/usage/compositions/video-dialog.md +3 -1
  82. package/docs/usage/compositions/visual-foundations.md +5 -3
  83. package/docs/usage/compositions/web-additions.md +6 -4
  84. package/docs/usage/compositions/web-navigation.md +4 -2
  85. package/docs/usage/screens/common-chat.md +3 -1
  86. package/docs/usage/screens/common-comments.md +6 -4
  87. package/docs/usage/screens/common-inbox.md +3 -1
  88. package/docs/usage/screens/common-login.md +3 -1
  89. package/docs/usage/screens/common-profile.md +3 -1
  90. package/docs/usage/screens/common-saved.md +3 -1
  91. package/docs/usage/screens/common-search.md +3 -1
  92. package/docs/usage/screens/common-settings.md +3 -1
  93. package/docs/usage/screens/common-shell.md +3 -1
  94. package/docs/usage/screens/dashboard.md +3 -1
  95. package/docs/usage/screens/discovery-gallery.md +3 -1
  96. package/docs/usage/screens/flow-collection.md +3 -1
  97. package/docs/usage/screens/flow-editor.md +3 -1
  98. package/docs/usage/screens/flow-media.md +3 -1
  99. package/docs/usage/screens/flow-moderation.md +3 -1
  100. package/docs/usage/screens/flow-onboarding.md +3 -1
  101. package/docs/usage/screens/flow-permission.md +3 -1
  102. package/docs/usage/screens/landing.md +4 -2
  103. package/docs/usage/screens/mockup-studio.md +3 -1
  104. package/docs/usage/screens/notification-settings.md +32 -138
  105. package/docs/usage/screens/product-bento.md +3 -1
  106. package/docs/usage/screens/reference-comparison.md +3 -1
  107. package/package.json +1 -1
@@ -4,10 +4,12 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 구성 API 지침 참조
7
- - 검토일: 2026-10-13
7
+ - 검토일: 2026-10-15
8
8
  - 근거: [반복 화면 조합](../../screen-patterns.md); 공통 API와 실제 Web·Native 예제의 슬롯·상태를 대조해 중복 조립 방지. 2026-10-06 사용자 승인으로 스토리북 배포(이전 `실험/화면/공통 화면/로그인`, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
9
9
  - 스토리북: `배포/화면/계정/로그인`
10
10
 
11
+ 공통 구조·배치·상태·배지의 소유권은 [화면과 구성 선택](../SELECTION.md#소유권)을 따른다. 앱은 데이터·문구·브랜드·추가 기능을 공개 슬롯과 콜백으로 연결한다. 아래 코드 골격을 앱별 기본 구조 복제의 근거로 사용하지 않는다. 전용 화면 API가 없는 제품 콘텐츠는 `ScreenLayout`의 본문에 HJM 구성을 연결한다.
12
+
11
13
  ## 목적
12
14
 
13
15
  AuthScreenLayout을 사용해 로그인 흐름을 구성한다. 제품이 데이터·권한·서버 확정·문구를 공급하며, 예제의 메모리 저장을 운영 저장으로 취급하지 않는다.
@@ -4,10 +4,12 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 구성 API 지침 참조
7
- - 검토일: 2026-10-13
7
+ - 검토일: 2026-10-15
8
8
  - 근거: [반복 화면 조합](../../screen-patterns.md), Web·Native `src/screen-flows.tsx`(`ProfileScreen`·`EditorScreen`), 예제 `showcase/*/screen-flow-previews.tsx`(`AccountFlowPreview`)·`saved-profile-previews.tsx`(`ProfileEditFields`), 기본 얼굴 `showcase/web/src/patterns/ProfileStudio.previews.tsx`·`showcase/native/src/profile-face-preview.tsx`·`showcase/shared/profile-studio.ts`. 2026-10-06 사용자 승인으로 실험 `공통 화면/프로필`을 배포하면서 같은 프로필 수정을 직접 조립으로 보이던 배포 `화면/프로필 편집`과 합쳤다(API 기반 우선, Web id `patterns-profile-studio` 보존, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
9
9
  - 스토리북: `배포/화면/계정/프로필`
10
10
 
11
+ 공통 구조·배치·상태·배지의 소유권은 [화면과 구성 선택](../SELECTION.md#소유권)을 따른다. 앱은 데이터·문구·브랜드·추가 기능을 공개 슬롯과 콜백으로 연결한다. 아래 코드 골격을 앱별 기본 구조 복제의 근거로 사용하지 않는다. 전용 화면 API가 없는 제품 콘텐츠는 `ScreenLayout`의 본문에 HJM 구성을 연결한다.
12
+
11
13
  ## 목적
12
14
 
13
15
  내 프로필을 보고(요약·게시물·계정 메뉴) 고치는(사진·이름·소개) 화면을 ProfileScreen과 EditorScreen으로 구성한다.
@@ -4,10 +4,12 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 구성 API 지침 참조
7
- - 검토일: 2026-10-13
7
+ - 검토일: 2026-10-15
8
8
  - 근거: [반복 화면 조합](../../screen-patterns.md); 공통 API와 실제 Web·Native 예제의 슬롯·상태를 대조해 중복 조립 방지. 2026-10-06 사용자 승인으로 스토리북 배포(이전 `실험/화면/공통 화면/저장한 항목`, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
9
9
  - 스토리북: `배포/화면/콘텐츠/저장한 항목`
10
10
 
11
+ 공통 구조·배치·상태·배지의 소유권은 [화면과 구성 선택](../SELECTION.md#소유권)을 따른다. 앱은 데이터·문구·브랜드·추가 기능을 공개 슬롯과 콜백으로 연결한다. 아래 코드 골격을 앱별 기본 구조 복제의 근거로 사용하지 않는다. 전용 화면 API가 없는 제품 콘텐츠는 `ScreenLayout`의 본문에 HJM 구성을 연결한다.
12
+
11
13
  ## 목적
12
14
 
13
15
  SavedItemsScreen을 사용해 저장한 항목 흐름을 구성한다. 제품이 데이터·권한·서버 확정·문구를 공급하며, 예제의 메모리 저장을 운영 저장으로 취급하지 않는다.
@@ -4,10 +4,12 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 구성 API 지침 참조
7
- - 검토일: 2026-10-13
7
+ - 검토일: 2026-10-15
8
8
  - 근거: [반복 화면 조합](../../screen-patterns.md), Web·Native `src/screen-flows.tsx`(`SearchScreen` 두 단계 검색 prop, 2026-10-06 사용자 위임 결정으로 미리보기 로직을 공개 API로 올림), 예제 `showcase/*/search-discovery-preview.tsx`·`showcase/shared/search-discovery.ts`. 2026-10-06 사용자 요청("실험에 있는 검색화면 UI도 개편해. 검색과 필터 있는 앱 서비스 따라해.")에 따라 Material 3 Search·Apple HIG search fields·Baymard·NN/g·eBay Playbook 필터 패턴과 국내 커머스·생활 서비스 모바일 웹의 **구조·상호작용만** 조사해 다시 설계했다(브랜드 색·로고·문구·자산은 가져오지 않았다). 2026-10-06 사용자 승인으로 실험 `공통 화면/검색`을 배포하면서 같은 일을 직접 조립하던 배포 `화면/검색`(내 기록 검색)을 대체했다(Web id `patterns-search` 보존, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
9
9
  - 스토리북: `배포/화면/검색/검색 결과와 필터`
10
10
 
11
+ 공통 구조·배치·상태·배지의 소유권은 [화면과 구성 선택](../SELECTION.md#소유권)을 따른다. 앱은 데이터·문구·브랜드·추가 기능을 공개 슬롯과 콜백으로 연결한다. 아래 코드 골격을 앱별 기본 구조 복제의 근거로 사용하지 않는다. 전용 화면 API가 없는 제품 콘텐츠는 `ScreenLayout`의 본문에 HJM 구성을 연결한다.
12
+
11
13
  ## 목적
12
14
 
13
15
  입력 중 제안 → 확정 → 결과·필터의 두 단계 검색 화면을 SearchScreen 하나로 구성한다. 단계 전환·확정 경로·섹션 배치·필터 시트 초안·0건 복구·포커스·개수 낭독은 SearchScreen이 소유하고 제품은 데이터·요청·문구·아이콘만 공급한다(2026-10-06 전에는 이 모두가 Storybook 미리보기 코드에만 있었다). 스토리는 상태별 정지 화면(`입력 중`·`결과`·`필터 적용`·`필터 시트`·`불러오는 중`·`결과 없음`·`오류`·`로그인 필요`)과 `실패와 복구`(다음 검색 실패 → 다시 시도)를 보여 주고, `어두운 테마`·`큰 글자`는 필터 적용 화면으로 연다. `기본`(검색 전)에서 검색 전 → 입력 → 확정 → 필터 → 상세 → 돌아오기에서 검색어·조건·정렬·스크롤이 남는 흐름을 직접 조작한다. 2026-10-06 같은 예제를 그리던 `기본 흐름/검색과 필터`(복구 흐름)와 `구성/검색/여러 조건 적용과 초기화`(초안·적용 필터 시트)를 이 항목으로 합쳤다([옛 링크](../../../../../docs/STORYBOOK_NAVIGATION.md#실험-정리와-합친-항목-2026-10-06)). 데이터·제안·권한·문구는 제품이 공급하며 예제의 메모리 검색을 운영 검색으로 취급하지 않는다.
@@ -4,10 +4,12 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 구성 API 지침 참조
7
- - 검토일: 2026-10-08
7
+ - 검토일: 2026-10-15
8
8
  - 근거: [반복 화면 조합](../../screen-patterns.md); 공통 API와 실제 Web·Native 예제의 슬롯·상태를 대조해 중복 조립 방지. 2026-10-06 사용자 승인으로 스토리북 배포(이전 `실험/화면/공통 화면/설정`, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
9
9
  - 스토리북: `배포/화면/설정/앱 설정`
10
10
 
11
+ 공통 구조·배치·상태·배지의 소유권은 [화면과 구성 선택](../SELECTION.md#소유권)을 따른다. 앱은 데이터·문구·브랜드·추가 기능을 공개 슬롯과 콜백으로 연결한다. 아래 코드 골격을 앱별 기본 구조 복제의 근거로 사용하지 않는다. 전용 화면 API가 없는 제품 콘텐츠는 `ScreenLayout`의 본문에 HJM 구성을 연결한다.
12
+
11
13
  ## 목적
12
14
 
13
15
  SettingsScreen을 사용해 설정 흐름을 구성한다. 제품이 데이터·권한·서버 확정·문구를 공급하며, 예제의 메모리 저장을 운영 저장으로 취급하지 않는다.
@@ -4,10 +4,12 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 구성 API 지침 참조
7
- - 검토일: 2026-10-13
7
+ - 검토일: 2026-10-15
8
8
  - 근거: [반복 화면 조합](../../screen-patterns.md); 공통 API와 실제 Web·Native 예제의 슬롯·상태를 대조해 중복 조립 방지. 2026-10-06 사용자 승인으로 스토리북 배포(이전 `실험/화면/공통 화면/화면 골격`, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
9
9
  - 스토리북: `배포/화면/화면 틀과 도구/화면 골격과 상태`
10
10
 
11
+ 공통 구조·배치·상태·배지의 소유권은 [화면과 구성 선택](../SELECTION.md#소유권)을 따른다. 앱은 데이터·문구·브랜드·추가 기능을 공개 슬롯과 콜백으로 연결한다. 아래 코드 골격을 앱별 기본 구조 복제의 근거로 사용하지 않는다. 전용 화면 API가 없는 제품 콘텐츠는 `ScreenLayout`의 본문에 HJM 구성을 연결한다.
12
+
11
13
  ## 목적
12
14
 
13
15
  ScreenLayout을 사용해 화면 골격 흐름을 구성한다. 제품이 데이터·권한·서버 확정·문구를 공급하며, 예제의 메모리 저장을 운영 저장으로 취급하지 않는다.
@@ -4,10 +4,12 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-13
7
+ - 검토일: 2026-10-15
8
8
  - 근거: `showcase/web/src/patterns/Dashboard.stories.tsx`, `showcase/native/src/Dashboard.stories.tsx`, `showcase/shared/dashboard-pattern.ts`, `showcase/native/src/pattern-status.tsx`, `packages/react/src/styles.css`(`.hjm-statistic`, `.hjm-text`)
9
9
  - 스토리북: `배포/화면/콘텐츠/대시보드`
10
10
 
11
+ 공통 구조·배치·상태·배지의 소유권은 [화면과 구성 선택](../SELECTION.md#소유권)을 따른다. 앱은 데이터·문구·브랜드·추가 기능을 공개 슬롯과 콜백으로 연결한다. 아래 코드 골격을 앱별 기본 구조 복제의 근거로 사용하지 않는다. 전용 화면 API가 없는 제품 콘텐츠는 `ScreenLayout`의 본문에 HJM 구성을 연결한다.
12
+
11
13
  ## 목적
12
14
 
13
15
  한 기간의 개인 활동을 숫자 요약 → 날짜별 활동 → 기록 목록 순서로 돌아보는 화면이다.
@@ -4,10 +4,12 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-13
7
+ - 검토일: 2026-10-15
8
8
  - 근거: `showcase/web/src/patterns/DiscoveryGallery.stories.tsx`, `showcase/native/src/DiscoveryGallery.stories.tsx`, `showcase/shared/discovery-gallery.ts`, `src/base-recipes.ts`(`buttonRecipe.states.selected`), `src/grid.ts`
9
9
  - 스토리북: `배포/화면/검색/작품 탐색`
10
10
 
11
+ 공통 구조·배치·상태·배지의 소유권은 [화면과 구성 선택](../SELECTION.md#소유권)을 따른다. 앱은 데이터·문구·브랜드·추가 기능을 공개 슬롯과 콜백으로 연결한다. 아래 코드 골격을 앱별 기본 구조 복제의 근거로 사용하지 않는다. 전용 화면 API가 없는 제품 콘텐츠는 `ScreenLayout`의 본문에 HJM 구성을 연결한다.
12
+
11
13
  ## 목적
12
14
 
13
15
  여러 사람의 작품(카드)을 검색·카테고리·정렬로 훑고, 마음에 드는 것을 저장하고, 하나를 시트로 크게 보는 갤러리 화면이다.
@@ -4,10 +4,12 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 구성 API 지침 참조
7
- - 검토일: 2026-10-13
7
+ - 검토일: 2026-10-15
8
8
  - 근거: [반복 화면 조합](../../screen-patterns.md); 공통 API와 실제 Web·Native 예제의 슬롯·상태를 대조해 중복 조립 방지. 2026-10-06 사용자 승인으로 스토리북 배포(이전 `실험/화면/기본 흐름/목록과 상세`, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
9
9
  - 스토리북: `배포/화면/콘텐츠/목록과 상세`
10
10
 
11
+ 공통 구조·배치·상태·배지의 소유권은 [화면과 구성 선택](../SELECTION.md#소유권)을 따른다. 앱은 데이터·문구·브랜드·추가 기능을 공개 슬롯과 콜백으로 연결한다. 아래 코드 골격을 앱별 기본 구조 복제의 근거로 사용하지 않는다. 전용 화면 API가 없는 제품 콘텐츠는 `ScreenLayout`의 본문에 HJM 구성을 연결한다.
12
+
11
13
  ## 목적
12
14
 
13
15
  ListDetailScreen을 사용해 목록과 상세 흐름을 구성한다. 제품이 데이터·권한·서버 확정·문구를 공급하며, 예제의 메모리 저장을 운영 저장으로 취급하지 않는다.
@@ -4,10 +4,12 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 구성 API 지침 참조
7
- - 검토일: 2026-10-13
7
+ - 검토일: 2026-10-15
8
8
  - 근거: [반복 화면 조합](../../screen-patterns.md); 공통 API와 실제 Web·Native 예제의 슬롯·상태를 대조해 중복 조립 방지. 2026-10-06 사용자 승인으로 스토리북 배포(이전 `실험/화면/기본 흐름/작성과 수정`, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
9
9
  - 스토리북: `배포/화면/콘텐츠/작성과 수정`
10
10
 
11
+ 공통 구조·배치·상태·배지의 소유권은 [화면과 구성 선택](../SELECTION.md#소유권)을 따른다. 앱은 데이터·문구·브랜드·추가 기능을 공개 슬롯과 콜백으로 연결한다. 아래 코드 골격을 앱별 기본 구조 복제의 근거로 사용하지 않는다. 전용 화면 API가 없는 제품 콘텐츠는 `ScreenLayout`의 본문에 HJM 구성을 연결한다.
12
+
11
13
  ## 목적
12
14
 
13
15
  EditorScreen을 사용해 작성과 수정 흐름을 구성한다. 제품이 데이터·권한·서버 확정·문구를 공급하며, 예제의 메모리 저장을 운영 저장으로 취급하지 않는다.
@@ -4,10 +4,12 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 구성 API 지침 참조
7
- - 검토일: 2026-10-13
7
+ - 검토일: 2026-10-15
8
8
  - 근거: [반복 화면 조합](../../screen-patterns.md); 공통 API와 실제 Web·Native 예제의 슬롯·상태를 대조해 중복 조립 방지. 2026-10-06 사용자 승인으로 스토리북 배포(이전 `실험/화면/기본 흐름/사진 선택과 업로드`, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
9
9
  - 스토리북: `배포/화면/콘텐츠/사진 선택과 업로드`
10
10
 
11
+ 공통 구조·배치·상태·배지의 소유권은 [화면과 구성 선택](../SELECTION.md#소유권)을 따른다. 앱은 데이터·문구·브랜드·추가 기능을 공개 슬롯과 콜백으로 연결한다. 아래 코드 골격을 앱별 기본 구조 복제의 근거로 사용하지 않는다. 전용 화면 API가 없는 제품 콘텐츠는 `ScreenLayout`의 본문에 HJM 구성을 연결한다.
12
+
11
13
  ## 목적
12
14
 
13
15
  MediaSelectionScreen을 사용해 사진 선택과 업로드 흐름을 구성한다. 제품이 데이터·권한·서버 확정·문구를 공급하며, 예제의 메모리 저장을 운영 저장으로 취급하지 않는다.
@@ -4,10 +4,12 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 구성 API 지침 참조
7
- - 검토일: 2026-10-13
7
+ - 검토일: 2026-10-15
8
8
  - 근거: [반복 화면 조합](../../screen-patterns.md); 공통 API와 실제 Web·Native 예제의 슬롯·상태를 대조해 중복 조립 방지. 2026-10-06 사용자 승인으로 스토리북 배포(이전 `실험/화면/기본 흐름/신고와 차단`, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
9
9
  - 스토리북: `배포/화면/소통/신고와 차단`
10
10
 
11
+ 공통 구조·배치·상태·배지의 소유권은 [화면과 구성 선택](../SELECTION.md#소유권)을 따른다. 앱은 데이터·문구·브랜드·추가 기능을 공개 슬롯과 콜백으로 연결한다. 아래 코드 골격을 앱별 기본 구조 복제의 근거로 사용하지 않는다. 전용 화면 API가 없는 제품 콘텐츠는 `ScreenLayout`의 본문에 HJM 구성을 연결한다.
12
+
11
13
  ## 목적
12
14
 
13
15
  ModerationScreen을 사용해 신고와 차단 흐름을 구성한다. 제품이 데이터·권한·서버 확정·문구를 공급하며, 예제의 메모리 저장을 운영 저장으로 취급하지 않는다.
@@ -4,10 +4,12 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 구성 API 지침 참조
7
- - 검토일: 2026-10-13
7
+ - 검토일: 2026-10-15
8
8
  - 근거: [반복 화면 조합](../../screen-patterns.md), Web·Native `src/screen-flows.tsx`(`OnboardingScreen`), 예제 `showcase/*/screen-flow-previews.tsx`(`OnboardingFlowPreview`)·`showcase/shared/onboarding-pattern.ts`(주제·요약). 2026-10-06 사용자 승인으로 실험 `기본 흐름/온보딩`(OnboardingScreen)을 배포하면서 같은 일을 직접 조립하던 배포 `화면/온보딩`을 대체했다(Web id `patterns-onboarding` 보존, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
9
9
  - 스토리북: `배포/화면/소개/온보딩`
10
10
 
11
+ 공통 구조·배치·상태·배지의 소유권은 [화면과 구성 선택](../SELECTION.md#소유권)을 따른다. 앱은 데이터·문구·브랜드·추가 기능을 공개 슬롯과 콜백으로 연결한다. 아래 코드 골격을 앱별 기본 구조 복제의 근거로 사용하지 않는다. 전용 화면 API가 없는 제품 콘텐츠는 `ScreenLayout`의 본문에 HJM 구성을 연결한다.
12
+
11
13
  ## 목적
12
14
 
13
15
  첫 실행 사용자를 몇 단계(소개 → 관심 주제 → 시작)로 안내하고 마지막 단계에서 완료를 저장하는 화면을 OnboardingScreen 하나로 구성한다.
@@ -4,10 +4,12 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 구성 API 지침 참조
7
- - 검토일: 2026-10-13
7
+ - 검토일: 2026-10-15
8
8
  - 근거: [반복 화면 조합](../../screen-patterns.md); 공통 API와 실제 Web·Native 예제의 슬롯·상태를 대조해 중복 조립 방지. 2026-10-06 사용자 승인으로 스토리북 배포(이전 `실험/화면/기본 흐름/권한 안내`, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
9
9
  - 스토리북: `배포/화면/소개/권한 안내`
10
10
 
11
+ 공통 구조·배치·상태·배지의 소유권은 [화면과 구성 선택](../SELECTION.md#소유권)을 따른다. 앱은 데이터·문구·브랜드·추가 기능을 공개 슬롯과 콜백으로 연결한다. 아래 코드 골격을 앱별 기본 구조 복제의 근거로 사용하지 않는다. 전용 화면 API가 없는 제품 콘텐츠는 `ScreenLayout`의 본문에 HJM 구성을 연결한다.
12
+
11
13
  ## 목적
12
14
 
13
15
  PermissionScreen을 사용해 권한 안내 흐름을 구성한다. 제품이 데이터·권한·서버 확정·문구를 공급하며, 예제의 메모리 저장을 운영 저장으로 취급하지 않는다.
@@ -4,10 +4,12 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-13
7
+ - 검토일: 2026-10-15
8
8
  - 근거: `showcase/web/src/patterns/Landing.stories.tsx`·`Landing.previews.tsx`(진입점)·`showcase/web/src/patterns/landing-preview.tsx`(기본 본체), `showcase/native/src/Landing.stories.tsx`·`introduction-preview.tsx`(진입점)·`showcase/native/src/landing-preview.tsx`(기본 본체), 변형 `showcase/*/reference-flow-previews.tsx`(`ServiceIntroduction`), `showcase/shared/landing-pattern.ts`, `showcase/native/src/pattern-status.tsx`, `packages/react/src/effect-surface.tsx`, `packages/react-native/src/effect-surface.tsx`. 2026-10-06 사용자 승인으로 실험 `서비스 소개/제품 체험 중심`(설명과 사례 중심 포함)을 배포하면서 같은 소개 목적의 배포 `화면/랜딩 화면`과 한 항목으로 합쳤다(Web id `patterns-landing` 보존, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
9
9
  - 스토리북: `배포/화면/소개/서비스 소개`
10
10
 
11
+ 공통 구조·배치·상태·배지의 소유권은 [화면과 구성 선택](../SELECTION.md#소유권)을 따른다. 앱은 데이터·문구·브랜드·추가 기능을 공개 슬롯과 콜백으로 연결한다. 아래 코드 골격을 앱별 기본 구조 복제의 근거로 사용하지 않는다. 전용 화면 API가 없는 제품 콘텐츠는 `ScreenLayout`의 본문에 HJM 구성을 연결한다.
12
+
11
13
  ## 목적
12
14
 
13
15
  제품을 처음 보는 사람에게 한 문장 가치 제안을 보여 주고 같은 화면에서 첫 행동(짧은 입력)을 체험하게 하는 소개 화면이다.
@@ -25,7 +27,7 @@
25
27
  │ (폭 < 600 compact 16 · 이상 regular 20) │
26
28
  │ Native 위아래 spacing.lg 20 │
27
29
  │ ┌ ① 히어로 EffectSurface(mesh+grain) ──┐ │
28
- │ │ (안쪽 여백 spacing.xl 24 — 제품이 줌) │ │
30
+ │ │ (안쪽 여백 spacing.xl 24 — HJM 구성 소유) │ │
29
31
  │ │ eyebrow Text tone=muted │ │ Stack gap lg 20
30
32
  │ │ 제목 Heading level1 (h1) │ │
31
33
  │ │ 소개 Text as=p │ │
@@ -4,10 +4,12 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-13
7
+ - 검토일: 2026-10-15
8
8
  - 근거: `showcase/web/src/foundations/MockupStudio.stories.tsx`, `src/responsive.ts`(`resolveWindowClass`), `showcase/web/src/studio/MockupStudio.tsx`, `showcase/web/src/studio/SceneTimeline.tsx`, `showcase/shared/mockup-scene.ts`, `showcase/shared/scene-timeline.ts`
9
9
  - 스토리북: `배포/화면/화면 틀과 도구/목업 편집`
10
10
 
11
+ 공통 구조·배치·상태·배지의 소유권은 [화면과 구성 선택](../SELECTION.md#소유권)을 따른다. 앱은 데이터·문구·브랜드·추가 기능을 공개 슬롯과 콜백으로 연결한다. 아래 코드 골격을 앱별 기본 구조 복제의 근거로 사용하지 않는다. 전용 화면 API가 없는 제품 콘텐츠는 `ScreenLayout`의 본문에 HJM 구성을 연결한다.
12
+
11
13
  ## 목적
12
14
 
13
15
  제품 화면 캡처를 휴대폰·브라우저 프레임에 넣어 스토어·소개용 이미지(PNG)와 짧은 장면 영상을 만드는 Web 전용 작업 도구 화면이다.
@@ -4,10 +4,12 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-13
7
+ - 검토일: 2026-10-15
8
8
  - 근거: `showcase/web/src/patterns/NotificationSettings.stories.tsx`, `showcase/native/src/NotificationSettings.stories.tsx`, `src/component-recipes.ts`(`topBarRecipe`·`bottomCtaRecipe`·`sectionRecipe`·`switchRecipe`), `src/container.ts`, `src/responsive.ts`(`resolveWindowClass`), `packages/react/src/styles.css`(`.hjm-top-bar`·`.hjm-bottom-cta`)
9
9
  - 스토리북: `배포/화면/설정/알림 설정`
10
10
 
11
+ 공통 구조·배치·상태·배지의 소유권은 [화면과 구성 선택](../SELECTION.md#소유권)을 따른다. 앱은 데이터·문구·브랜드·추가 기능을 공개 슬롯과 콜백으로 연결한다. 아래 코드 골격을 앱별 기본 구조 복제의 근거로 사용하지 않는다. 전용 화면 API가 없는 제품 콘텐츠는 `ScreenLayout`의 본문에 HJM 구성을 연결한다.
12
+
11
13
  ## 목적
12
14
 
13
15
  알림 종류 몇 개를 스위치로 켜고 끈 뒤 하단 버튼 하나로 저장하는 설정 화면이다. 상단 바 · 읽기 폭 본문 · 하단 행동 바의
@@ -17,61 +19,27 @@
17
19
  ## 영역 구조
18
20
 
19
21
  ```text
20
- 좁은 폭(Native·모바일 Web) — 상단·하단 바 고정, 본문만 스크롤
21
- ┌ 상단 안전 영역(TopBar safeAreaTop, Web은 env()와 큰 값) ┐
22
- │ ① TopBar 알림 설정 │ 최소 높이 52, 좌우 spacing.md 16 (고정)
23
- ├──────────────────────────────────────────────┤ ↕ spacing.lg 20
24
- │ ② Container reading (최대 720, │ ─ 스크롤 ─
25
- │ 좌우 gutter: 폭<600 compact 16 · 이상 20) │
26
- │ Section │
27
- │ 필요한 소식만 받아요 ← title │ title-description spacing.xxs 4
28
- │ 원하는 알림을 골라 주세요… ← description │ 머리-내용 spacing.xs 8
29
- │ ┌────────────────────────────────────────┐ │
30
- │ │ 내 활동 알림 (●──) │ │ Switch presentation=row
31
- │ │ 댓글과 답글이 도착하면… │ │ 라벨 왼쪽, 스위치 끝
32
- │ │ ↕ spacing.md 16 │ │
33
- │ │ 주간 모아보기 (──○) │ │
34
- │ │ 일주일의 소식을 한 번에… │ │
35
- │ └────────────────────────────────────────┘ │
36
- │ ┌ Notice success(저장 후) · danger(실패) ┐ │
37
- │ └────────────────────────────────────────┘ │
38
- ├──────────────────────────────────────────────┤ ↕ 20
39
- │ ③ BottomCTA (위 테두리 1 + 위쪽 그림자) │ 최소 높이 64 (고정)
40
- │ 선택한 알림만 보내드릴게요. ← description │ caption, muted
41
- │ [ 이 설정으로 저장하기 ] │ ← 주 행동, 폭 전체
42
- └ 하단 안전 영역(BottomCTA safeAreaBottom) ────┘
22
+ ScreenLayout: 제목(고정) → 본문(스크롤) → footer(고정)
23
+ 본문: CompositionLayout(설정 제목·설명 → 설정 행 → 저장 안내)
24
+ footer: 저장 행동
43
25
  ```
44
26
 
45
- 넓은 폭 Web: TopBar와 BottomCTA는 화면 폭 전체, 본문만 Container `reading`으로 가운데 720 폭에 묶인다.
46
-
47
27
  | 영역 | 컴포넌트 | 위치 | 크기·간격 |
48
28
  | --- | --- | --- | --- |
49
- | 바깥 틀 | Web `main` > `Stack gap="lg"` > TopBar · Container · BottomCTA `position="sticky"`, 문서 스크롤 · Native `View`(`flex: 1`) > TopBar · `ScrollView contentContainerStyle={{ paddingVertical: spacing.lg }}` > Container · BottomCTA | 화면 전체. 본문만 스크롤하고 하단 바는 고정(Web TopBar는 고정 prop이 없어 문서와 함께 움직이며, 고정하려면 제품 레이아웃이 맡는다) | 세 층 사이 `spacing.lg` 20(Web Stack gap, Native ScrollView 위아래 여백). 좌우 여백은 Container gutter. 안전 영역: 위는 TopBar `safeAreaTop`, 아래는 BottomCTA `safeAreaBottom`(Native는 기기 inset을 넘긴다, 기본 0). 키보드 입력이 없어 키보드 처리는 없다 |
50
- | ① 상단 바 | TopBar `title` | 맨 위 | 최소 높이 52(`control.buttonHeight.large`), 좌우 `spacing.md` 16, 슬롯 사이 `spacing.xs` 8, 양 끝 슬롯 최소 44 |
51
- | ② 본문 | Container `size="reading"` `gutter={gutter}` > Section `title` `description` > Stack `gap="md"` > Switch `presentation="row"` ×n, 그 아래 Notice | 상단 바 아래, 스크롤 | 최대 폭 720(`layout.readingMaxWidth`), 좌우 gutter 폭 600 미만 `compact` 16 · 이상 `regular` 20(`layout.pagePadding`), 스위치 사이 `spacing.md` 16, 트랙 52×32 |
52
- | ③ 하단 행동 | BottomCTA `description` `primaryAction` | 맨 아래 고정. Web `position="sticky"`(`inset-block-end: 0`, `z-index: var(--hjm-layer-sticky)`), Native는 `ScrollView` 바깥 | 최소 높이 64, 위아래 `spacing.sm` 12 + 안전 영역(`max(safeAreaBottom, spacing.sm 12)`), 좌우 `layout.pagePadding.regular` 20, 설명-버튼 `spacing.sm` 12 |
53
-
54
- gutter는 창 폭으로 고른다: `resolveWindowClass(width) === "compact" ? "compact" : "regular"`([화면 여백과 너비](../tokens/layout.md)).
55
- 상단·스크롤 본문·하단 행동은 별도 영역으로 둔다. 목록이 길어져도 저장 행동이 본문과 함께 밀려나지 않는다.
56
-
57
- ```text
58
- Native 고정 배치
59
- ┌ TopBar safeAreaTop={insets.top} ─────┐ 고정
60
- ├ ScrollView paddingVertical 20 ───────┤
61
- │ Container > Section > Switch … │ 스크롤
62
- ├ BottomCTA safeAreaBottom={insets.bottom} ┤ 고정
63
- └──────────────────────────────────────┘
64
- ```
29
+ | 바깥 틀 | ScreenLayout | host의 남은 높이 | 공통 화면 폭·padding·본문 스크롤 |
30
+ | 설정 묶음 | CompositionLayout | 본문 | 공통 제목·설명·내용 간격 |
31
+ | 설정 값 | Switch | 구성 본문 | 제품 데이터·변경 콜백 |
32
+ | 저장 | footer | 화면 하단 | HJM 고정 영역; 제품 저장 요청과 pending 연결 |
65
33
 
66
34
  ## 버튼과 행동 위치
67
35
 
68
36
  | 행동 | 컴포넌트·tone | 위치 | 개수·순서 |
69
37
  | --- | --- | --- | --- |
70
- | 저장(주 행동) | BottomCTA `primaryAction`(primary, 폭 전체) | 화면 맨 아래 바 | 1. 저장 중 `loading`, 저장 뒤에는 라벨을 `t("settings.saved")`로 바꾸고 `disabled` |
71
- | 보조 행동 | BottomCTA `secondaryAction`(기본 `secondary`) | 같은 바, 주 행동 앞(왼쪽) | 스토리에는 없음. 둘 다 있으면 [보조][주] 같은 폭으로 나뉜다 |
72
- | 항목 켜기·끄기 | Switch `presentation="row"` 행 전체 | 본문 Section 안 | 항목 수만큼, 위→아래 |
38
+ | 저장(주 행동) | Button(primary) | 화면 맨 아래 바 | 1. 저장 중 `loading`, 저장 뒤에는 라벨을 `t("settings.saved")`로 바꾸고 `disabled` |
39
+ | 보조 행동 | Button(secondary) | 같은 바, 주 행동 앞(왼쪽) | 스토리에는 없음. 둘 다 있으면 [보조][주] 같은 폭으로 나뉜다 |
40
+ | 항목 켜기·끄기 | Switch `presentation="row"` 행 전체 | 본문 CompositionLayout 안 | 항목 수만큼, 위→아래 |
73
41
  | 다시 불러오기 | Notice `action` > Button `tone="secondary" size="small"` | 불러오기 실패 Notice 안 | 스토리에 없음. 1 |
74
- | 뒤로 | TopBar `leading` | 상단 바 시작 끝 | 스토리에는 없음. 하위 설정 화면이면 1 |
42
+ | 뒤로 | ScreenLayout `leading` | 상단 바 시작 끝 | 스토리에는 없음. 하위 설정 화면이면 1 |
75
43
  | 파괴 행동 | — | — | 없음 |
76
44
 
77
45
  ## 상태
@@ -79,10 +47,10 @@ Native 고정 배치
79
47
  | 상태 | 화면 모습 | 행동 |
80
48
  | --- | --- | --- |
81
49
  | 기본 | 스위치가 메모리 예제의 초기 저장값. 변경이 없으면 저장 버튼 비활성 | 스위치를 바꾸면 저장 가능. 저장값으로 되돌리면 다시 비활성 |
82
- | 로딩 | 스토리에 없음. 첫 값을 불러오는 동안 Section 안 스위치 자리에 [Skeleton](../components/skeleton.md) `shape="text"`, 저장 버튼 `disabled`. 저장 중이면 `primaryAction`에 `loading`(누름을 막고 같은 자리에 스피너) | 기다림 |
83
- | 빈 | 해당 없음(설정 항목은 항상 있다). 항목이 조건부면 Section 아래 안내 Text | — |
84
- | 오류 | **최초 불러오기 실패**(스토리에는 없음): 스위치 대신 [Notice](../components/notice.md) `tone="danger"` + `action` 다시 시도, 저장 버튼 `disabled`. **저장 실패**(스토리에서 `다음 저장 실패시키기`로 실행): Section 끝에 Notice `tone="danger"`(action 없음 — 다시 시도는 저장 버튼이 맡는다), 사용자가 고른 스위치 값을 그대로 두고 `loading`만 푼다. 문구는 네트워크 실패 `settings.error.offline`·서버 실패 `settings.error.server`로 나눈다. 다시 시도가 또 실패하면 같은 Notice가 같은 자리에 남고 문구만 갱신한다. Native는 `announcement="assertive"`(기본 `none`) | 다시 시도 / 다시 저장 |
85
- | 저장됨 | Section 끝에 Notice `tone="success"`(제목 + 켜짐/꺼짐 요약). 버튼은 "저장했어요"로 바뀌고 비활성. Native Notice `announcement="polite"` | 스위치를 다시 바꾸면 저장 상태가 풀린다 |
50
+ | 로딩 | 스토리에 없음. 첫 값을 불러오는 동안 CompositionLayout 안 스위치 자리에 [Skeleton](../components/skeleton.md) `shape="text"`, 저장 버튼 `disabled`. 저장 중이면 Button에 `loading`(누름을 막고 같은 자리에 스피너) | 기다림 |
51
+ | 빈 | 해당 없음(설정 항목은 항상 있다). 항목이 조건부면 CompositionLayout 아래 안내 Text | — |
52
+ | 오류 | **최초 불러오기 실패**(스토리에는 없음): 스위치 대신 [Notice](../components/notice.md) `tone="danger"` + `action` 다시 시도, 저장 버튼 `disabled`. **저장 실패**(스토리에서 `다음 저장 실패시키기`로 실행): CompositionLayout 끝에 Notice `tone="danger"`(action 없음 — 다시 시도는 저장 버튼이 맡는다), 사용자가 고른 스위치 값을 그대로 두고 `loading`만 푼다. 문구는 네트워크 실패 `settings.error.offline`·서버 실패 `settings.error.server`로 나눈다. 다시 시도가 또 실패하면 같은 Notice가 같은 자리에 남고 문구만 갱신한다. Native는 `announcement="assertive"`(기본 `none`) | 다시 시도 / 다시 저장 |
53
+ | 저장됨 | CompositionLayout 끝에 Notice `tone="success"`(제목 + 켜짐/꺼짐 요약). 버튼은 "저장했어요"로 바뀌고 비활성. Native Notice `announcement="polite"` | 스위치를 다시 바꾸면 저장 상태가 풀린다 |
86
54
 
87
55
 
88
56
  ### 설정 초안의 저장 경계
@@ -102,115 +70,41 @@ Native 고정 배치
102
70
 
103
71
  | 지침 | 쓰는 곳 |
104
72
  | --- | --- |
105
- | [TopBar](../components/top-bar.md) | ① 상단 바 |
106
- | [Container](../components/container.md) | ② 읽기 폭 |
107
- | [Section](../components/section.md) | ② 설정 묶음 제목·설명 |
73
+ | [ScreenLayout](../components/screen-layout.md) | 제목·본문·고정 footer |
74
+ | [CompositionLayout](../components/composition-layout.md) | ② 설정 묶음 제목·설명 |
108
75
  | [Switch](../components/switch.md) | ② 항목 행 |
109
76
  | [Notice](../components/notice.md) | 저장 결과·오류 |
110
77
  | [Skeleton](../components/skeleton.md) | 첫 값 불러오기 |
111
- | [BottomCTA](../components/bottom-cta.md) | ③ 하단 저장 |
78
+ | [Button](../components/button.md) | ③ 하단 저장 |
112
79
  | [Stack](../components/stack.md) | 세 층 사이, 스위치 목록 |
113
80
  | [화면 여백과 너비](../tokens/layout.md) | `layout.readingMaxWidth`, `layout.pagePadding.compact`·`regular`, `resolveWindowClass` |
114
81
  | [간격](../tokens/spacing.md) | `spacing.xxs`·`xs`·`sm`·`md`·`lg` |
115
82
 
116
83
  ## 코드 골격
117
84
 
118
- 설정 항목·문구는 제품 소유다. 문구 키는 상태→키 표로 고르고 템플릿 문자열로 만들지 않는다.
119
-
120
- ```tsx
121
- // Web
122
- import { TopBar } from "@hjmds/react/top-bar";
123
- import { BottomCTA } from "@hjmds/react/bottom-cta";
124
- import { Container, Stack, Section } from "@hjmds/react/layout";
125
- import { Switch } from "@hjmds/react/selection";
126
- import { Notice, Skeleton } from "@hjmds/react/feedback";
127
- import { Button } from "@hjmds/react/actions";
128
- import { resolveWindowClass } from "@hjmds/design-contracts/responsive";
129
-
130
- const failureKey = { offline: "settings.error.offline", server: "settings.error.server" } as const;
131
- const onOffKey = { on: "settings.on", off: "settings.off" } as const;
132
-
133
- const gutter = resolveWindowClass(window.innerWidth) === "compact" ? "compact" : "regular";
134
- const summary = t("settings.saved.summary", {
135
- activity: t(onOffKey[activity ? "on" : "off"]),
136
- digest: t(onOffKey[digest ? "on" : "off"]),
137
- });
138
-
139
- <main><Stack gap="lg">
140
- <TopBar title={t("settings.notifications.title")} />
141
- <Container size="reading" gutter={gutter}>
142
- <Section title={t("settings.notifications.section")} description={t("settings.notifications.hint")}>
143
- {status === "loading" ? <Stack gap="md"><Skeleton shape="text" width="60%" /><Skeleton shape="text" width="40%" /></Stack> : null}
144
- {status === "loadFailed" && failure ? <Notice tone="danger" title={t(failureKey[failure])}
145
- action={<Button tone="secondary" size="small" onClick={reload}>{t("common.retry")}</Button>} /> : null}
146
- {status === "ready" ? <Stack gap="md">
147
- <Switch presentation="row" label={t("settings.activity")} description={t("settings.activity.hint")}
148
- checked={activity} onCheckedChange={v => { setActivity(v); setSaved(false); }} />
149
- </Stack> : null}
150
- {status === "ready" && failure ? <Notice tone="danger" title={t(failureKey[failure])} description={t("settings.error.kept")} /> : null}
151
- {saved ? <Notice tone="success" title={t("settings.saved.title")} description={summary} /> : null}
152
- </Section>
153
- </Container>
154
- <BottomCTA position="sticky" description={t("settings.cta.hint")}
155
- primaryAction={{ label: saved ? t("settings.saved") : t("settings.save"), onClick: save, loading: saving, disabled: saved || status !== "ready" }} />
156
- </Stack></main>
157
- ```
85
+ <!-- usage-example: Web ../../../../../showcase/web/src/patterns/NotificationSettings.stories.tsx -->
86
+ - [Web 실행 예제](../../../../../showcase/web/src/patterns/NotificationSettings.stories.tsx)
87
+ <!-- usage-example: Native ../../../../../showcase/native/src/NotificationSettings.stories.tsx -->
88
+ - [Native 실행 예제](../../../../../showcase/native/src/NotificationSettings.stories.tsx)
158
89
 
159
- ```tsx
160
- // Native — 상단·하단 바를 고정한 형태
161
- import { ScrollView, View, useWindowDimensions } from "react-native";
162
- import { useSafeAreaInsets } from "react-native-safe-area-context"; // 제품 의존성
163
- import { spacing } from "@hjmds/design-contracts/foundations";
164
- import { resolveWindowClass } from "@hjmds/design-contracts/responsive";
165
- import { TopBar } from "@hjmds/react-native/top-bar";
166
- import { BottomCTA } from "@hjmds/react-native/bottom-cta";
167
- import { Container, Stack, Section } from "@hjmds/react-native/primitives";
168
- import { Switch } from "@hjmds/react-native/inputs";
169
- import { Notice } from "@hjmds/react-native/feedback";
170
-
171
- const failureKey = { offline: "settings.error.offline", server: "settings.error.server" } as const;
172
- const insets = useSafeAreaInsets();
173
- const { width } = useWindowDimensions();
174
- const gutter = resolveWindowClass(width) === "compact" ? "compact" : "regular";
175
-
176
- <View style={{ flex: 1 }}>
177
- <TopBar title={t("settings.notifications.title")} safeAreaTop={insets.top} />
178
- <ScrollView contentContainerStyle={{ paddingVertical: spacing.lg }}>
179
- <Container size="reading" gutter={gutter}>
180
- <Section title={t("settings.notifications.section")} description={t("settings.notifications.hint")}>
181
- <Stack gap="md">
182
- <Switch presentation="row" label={t("settings.activity")} description={t("settings.activity.hint")}
183
- checked={activity} onCheckedChange={v => { setActivity(v); setSaved(false); }} />
184
- </Stack>
185
- {failure ? <Notice announcement="assertive" tone="danger" title={t(failureKey[failure])} description={t("settings.error.kept")} /> : null}
186
- {saved ? <Notice announcement="polite" tone="success" title={t("settings.saved.title")} description={summary} /> : null}
187
- </Section>
188
- </Container>
189
- </ScrollView>
190
- <BottomCTA safeAreaBottom={insets.bottom} description={t("settings.cta.hint")}
191
- primaryAction={{ label: saved ? t("settings.saved") : t("settings.save"), onPress: save, loading: saving, disabled: saved }} />
192
- </View>
193
- ```
194
-
195
- `react-native-safe-area-context`는 HJM 의존성이 아니다. 제품이 쓰는 inset 출처를 넘긴다. Native 로딩·불러오기 실패는 Web과 같은
196
- 자리에 같은 컴포넌트를 둔다(Button은 `onPress`).
90
+ 예제는 ScreenLayout·CompositionLayout에 설정 데이터와 저장 콜백을 연결한다. 메모리 저장 fixture를 운영 요청으로 취급하지 않는다.
197
91
 
198
92
  ## 큰 글자·다크·좁은 폭
199
93
 
200
94
  | 조건 | 바뀌는 것 |
201
95
  | --- | --- |
202
- | 큰 글자(글자 배율 ≥ 1.6, `largeTextThreshold`) | TopBar 제목이 둘째 줄로 내려가 시작 정렬된다. BottomCTA 버튼이 세로로 쌓이고 **주 행동이 위**(`column-reverse`). Switch 행은 설명이 좁은 열이 되지 않게 세로로 쌓인다 |
203
- | 다크 | 테마 토큰만 쓴다. Switch 꺼짐 상태는 트랙·손잡이 테두리(`trackOffBorder`·`thumbOffBorder`)로 다크에서도 모양이 보인다 |
204
- | 좁은 폭 | 폭 600 미만이면 Container gutter `compact` 16. Section 머리는 Web 640 이하에서 제목·행동을 세로로 쌓는다 |
205
- | 넓은 폭 Web | 본문만 720 폭 가운데, 상단·하단 바는 전체 폭 |
96
+ | 다크 | 공통 semantic token으로 전환 |
97
+ | 좁은 폭 | 공통 화면·구성의 줄바꿈과 간격을 사용 |
98
+
99
+ ScreenLayout이 화면 여백과 스크롤을, CompositionLayout이 묶음 간격을 관리한다. 테마는 공통 semantic token을 사용한다. footer 안에서 BottomCTA를 중첩해 여백과 경계선을 두 번 적용하지 않는다.
206
100
 
207
101
  ## 플랫폼 차이
208
102
 
209
103
  | 항목 | Web | Native |
210
104
  | --- | --- | --- |
211
105
  | Switch 기본 배치 | `inline`(행으로 쓰려면 `presentation="row"` 명시) | `row` |
212
- | 하단 바 고정 | `position="sticky"` | prop 없음 — `ScrollView` 바깥에 둔다 |
213
- | 안전 영역 | CSS `env(safe-area-inset-*)`와 `safeAreaTop`/`safeAreaBottom` 중 큰 값 | `safeAreaTop`/`safeAreaBottom`만(기본 0) |
106
+ | 하단 바 고정 | ScreenLayout footer | ScreenLayout footer |
107
+ | 안전 영역 | 제품 host에서 연결 | 제품 host에서 연결 |
214
108
  | 저장·오류 알림 | Notice가 `role="status"` | Notice `announcement` 명시(기본 `none`): 저장됨 `polite`, 오류 `assertive` |
215
109
  | 스토리 상태 | 기본·다크·큰 글자, 기본 화면에서 저장 실패와 재시도 실행 | 기본·다크·큰 글자, 동일 메모리 저장 흐름 |
216
110
 
@@ -4,12 +4,14 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.14.0
7
- - 검토일: 2026-10-13
7
+ - 검토일: 2026-10-15
8
8
  - 근거: `reference-adoption-previews.tsx`, `reference-adoption.css`
9
9
  - 스토리북: `배포/화면/소개/기능 카드와 주 행동`
10
10
 
11
11
  승급: 2026-10-07 사용자 승인, [검토 결과](../../../../../docs/qa/2026-10-07-experiment-promotion-release.md). Storybook 분류이며 제품 적용 증거는 별도다.
12
12
 
13
+ 공통 구조·배치·상태·배지의 소유권은 [화면과 구성 선택](../SELECTION.md#소유권)을 따른다. 앱은 데이터·문구·브랜드·추가 기능을 공개 슬롯과 콜백으로 연결한다. 아래 코드 골격을 앱별 기본 구조 복제의 근거로 사용하지 않는다. 전용 화면 API가 없는 제품 콘텐츠는 `ScreenLayout`의 본문에 HJM 구성을 연결한다.
14
+
13
15
  ## 목적
14
16
 
15
17
  기능을 실제 미리보기로 보여 주고 첫 행동으로 이어지는 소개 화면이다. 기존 Surface/Stack/Button/ImageComparison을 조합한다.
@@ -4,10 +4,12 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 구성 API 지침 참조
7
- - 검토일: 2026-10-13
7
+ - 검토일: 2026-10-15
8
8
  - 근거: [반복 화면 조합](../../screen-patterns.md); 공통 API와 실제 Web·Native 예제의 슬롯·상태를 대조해 중복 조립 방지. 2026-10-06 사용자 승인으로 스토리북 배포(이전 `실험/화면/표현 비교/같은 기록의 세 가지 구성`, [승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정))
9
9
  - 스토리북: `배포/화면/화면 틀과 도구/기록 표현 비교`
10
10
 
11
+ 공통 구조·배치·상태·배지의 소유권은 [화면과 구성 선택](../SELECTION.md#소유권)을 따른다. 앱은 데이터·문구·브랜드·추가 기능을 공개 슬롯과 콜백으로 연결한다. 아래 코드 골격을 앱별 기본 구조 복제의 근거로 사용하지 않는다. 전용 화면 API가 없는 제품 콘텐츠는 `ScreenLayout`의 본문에 HJM 구성을 연결한다.
12
+
11
13
  ## 목적
12
14
 
13
15
  ScreenLayout을 사용해 같은 기록의 세 가지 구성 흐름을 구성한다. 제품이 데이터·권한·서버 확정·문구를 공급하며, 예제의 메모리 저장을 운영 저장으로 취급하지 않는다.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hjmds/design-contracts",
3
- "version": "1.22.0",
3
+ "version": "1.23.0",
4
4
  "description": "Renderer-neutral design contracts, tokens, recipes, and behaviors shared by HJM products.",
5
5
  "license": "MIT",
6
6
  "repository": {