@hjmds/design-contracts 1.13.0 → 1.14.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 (126) hide show
  1. package/dist/agreement.d.ts +7 -1
  2. package/dist/agreement.d.ts.map +1 -1
  3. package/dist/agreement.js +14 -3
  4. package/dist/agreement.js.map +1 -1
  5. package/dist/behaviors.d.ts +1 -1
  6. package/dist/catalog.d.ts +5 -0
  7. package/dist/catalog.d.ts.map +1 -1
  8. package/dist/component-recipes.d.ts +14 -0
  9. package/dist/component-recipes.d.ts.map +1 -1
  10. package/dist/component-recipes.js +16 -1
  11. package/dist/component-recipes.js.map +1 -1
  12. package/dist/content-transition.d.ts +17 -0
  13. package/dist/content-transition.d.ts.map +1 -1
  14. package/dist/content-transition.js +21 -0
  15. package/dist/content-transition.js.map +1 -1
  16. package/dist/date-entry.d.ts +71 -0
  17. package/dist/date-entry.d.ts.map +1 -0
  18. package/dist/date-entry.js +78 -0
  19. package/dist/date-entry.js.map +1 -0
  20. package/dist/document-resource.d.ts +104 -0
  21. package/dist/document-resource.d.ts.map +1 -0
  22. package/dist/document-resource.js +77 -0
  23. package/dist/document-resource.js.map +1 -0
  24. package/dist/effect-surface.d.ts +6 -1
  25. package/dist/effect-surface.d.ts.map +1 -1
  26. package/dist/effect-surface.js +5 -2
  27. package/dist/effect-surface.js.map +1 -1
  28. package/dist/field-group.d.ts +47 -0
  29. package/dist/field-group.d.ts.map +1 -0
  30. package/dist/field-group.js +92 -0
  31. package/dist/field-group.js.map +1 -0
  32. package/dist/image.d.ts +17 -0
  33. package/dist/image.d.ts.map +1 -1
  34. package/dist/image.js +19 -0
  35. package/dist/image.js.map +1 -1
  36. package/dist/internal/effect-noise.d.ts +2 -0
  37. package/dist/internal/effect-noise.d.ts.map +1 -0
  38. package/dist/internal/effect-noise.js +4 -0
  39. package/dist/internal/effect-noise.js.map +1 -0
  40. package/dist/progressive-blur.d.ts +32 -0
  41. package/dist/progressive-blur.d.ts.map +1 -0
  42. package/dist/progressive-blur.js +28 -0
  43. package/dist/progressive-blur.js.map +1 -0
  44. package/dist/reference-controls.d.ts +32 -0
  45. package/dist/reference-controls.d.ts.map +1 -0
  46. package/dist/reference-controls.js +28 -0
  47. package/dist/reference-controls.js.map +1 -0
  48. package/dist/scroll-progress.d.ts +7 -1
  49. package/dist/scroll-progress.d.ts.map +1 -1
  50. package/dist/scroll-progress.js +21 -2
  51. package/dist/scroll-progress.js.map +1 -1
  52. package/dist/text-annotation.d.ts +43 -0
  53. package/dist/text-annotation.d.ts.map +1 -0
  54. package/dist/text-annotation.js +137 -0
  55. package/dist/text-annotation.js.map +1 -0
  56. package/dist/version.d.ts +1 -1
  57. package/dist/version.js +1 -1
  58. package/dist/version.js.map +1 -1
  59. package/docs/agreement.md +14 -0
  60. package/docs/dialog.md +16 -1
  61. package/docs/effect-surface.md +19 -3
  62. package/docs/generated/component-maturity.md +1 -1
  63. package/docs/generated/renderer-evidence.json +3 -3
  64. package/docs/generated/renderer-evidence.md +1 -1
  65. package/docs/generated/showcase-manifest.json +1 -1
  66. package/docs/image.md +17 -0
  67. package/docs/optional-adapters.md +25 -0
  68. package/docs/popover.md +15 -0
  69. package/docs/rating.md +6 -1
  70. package/docs/reference-controls.md +40 -0
  71. package/docs/screen-patterns.md +2 -0
  72. package/docs/sheet.md +9 -0
  73. package/docs/task-list.md +15 -1
  74. package/docs/text-annotation.md +85 -0
  75. package/docs/toggle-group.md +6 -0
  76. package/docs/usage/README.md +18 -1
  77. package/docs/usage/components/agreement.md +13 -3
  78. package/docs/usage/components/alert-dialog.md +7 -1
  79. package/docs/usage/components/avatar.md +33 -1
  80. package/docs/usage/components/card.md +7 -1
  81. package/docs/usage/components/carousel.md +5 -1
  82. package/docs/usage/components/chat-message.md +11 -1
  83. package/docs/usage/components/chip.md +10 -2
  84. package/docs/usage/components/content-transition.md +28 -4
  85. package/docs/usage/components/dialog.md +18 -1
  86. package/docs/usage/components/effect-surface.md +4 -2
  87. package/docs/usage/components/empty-state.md +10 -2
  88. package/docs/usage/components/field.md +23 -0
  89. package/docs/usage/components/form.md +6 -1
  90. package/docs/usage/components/image-comparison.md +82 -0
  91. package/docs/usage/components/image.md +81 -2
  92. package/docs/usage/components/keyboard-avoiding.md +6 -1
  93. package/docs/usage/components/link.md +3 -1
  94. package/docs/usage/components/list-row.md +3 -1
  95. package/docs/usage/components/list.md +6 -1
  96. package/docs/usage/components/message-composer.md +5 -0
  97. package/docs/usage/components/popover.md +7 -1
  98. package/docs/usage/components/progress.md +28 -0
  99. package/docs/usage/components/progressive-blur.md +113 -0
  100. package/docs/usage/components/rating.md +74 -0
  101. package/docs/usage/components/search-field.md +10 -0
  102. package/docs/usage/components/search-screen.md +16 -5
  103. package/docs/usage/components/segmented-control.md +29 -4
  104. package/docs/usage/components/sheet.md +7 -1
  105. package/docs/usage/components/statistic.md +11 -0
  106. package/docs/usage/components/tags-input.md +5 -0
  107. package/docs/usage/components/toast.md +5 -0
  108. package/docs/usage/components/upload-item.md +3 -1
  109. package/docs/usage/compositions/action-feedback.md +77 -0
  110. package/docs/usage/compositions/adaptive-content.md +81 -0
  111. package/docs/usage/compositions/context-toolbar.md +85 -0
  112. package/docs/usage/compositions/date-entry.md +108 -0
  113. package/docs/usage/compositions/document-resource.md +124 -0
  114. package/docs/usage/compositions/field-group.md +104 -0
  115. package/docs/usage/compositions/illustrated-outcome.md +91 -0
  116. package/docs/usage/compositions/live-list.md +104 -0
  117. package/docs/usage/compositions/optional-adapters.md +1 -1
  118. package/docs/usage/compositions/origin-dialog.md +108 -0
  119. package/docs/usage/compositions/selection-motion.md +73 -0
  120. package/docs/usage/compositions/texture-comparison.md +86 -0
  121. package/docs/usage/compositions/upload-recovery.md +80 -0
  122. package/docs/usage/compositions/video-dialog.md +100 -0
  123. package/docs/usage/screens/common-search.md +7 -2
  124. package/docs/usage/screens/flow-onboarding.md +5 -3
  125. package/docs/usage/screens/product-bento.md +117 -0
  126. package/package.json +37 -1
@@ -26,3 +26,9 @@ single 모드를 넣지 않는 것이 규칙이다.
26
26
  2026-10-06 요청에 따라 단일 선택 카테고리는 `SegmentedControl presentation="pills"`로 제공한다.
27
27
  복수 선택 ToggleGroup의 계약은 바꾸지 않는다. 필터 UI가 서로 비슷하더라도 선택 개수를 합치면 해제·키보드 의미가 달라지기 때문이다.
28
28
  자세한 크기·테마·배치는 [SegmentedControl 사용 지침](usage/components/segmented-control.md)을 따른다.
29
+
30
+ 1.13.1 patch(2026-10-06 utilverse 1.13.0 적용 결함): `pills`는 큰 글자에서 세로로 쌓지 않는다(`segmentedControlRecipe.pills.largeTextLayout`
31
+ `"wrap"`). 블록 안에서는 줄바꿈하고 가로 스크롤 줄 안에서는 한 줄로 남는다. 쌓기(`adaptive.largeTextLayout` `"stacked"`)는 같은 폭으로
32
+ 나뉘는 `connected` 트랙의 규칙이다. 내용 폭인 pills까지 쌓아 주제 7개 레일이 accessibility-large에서 약 440pt 기둥이 됐고, 제품은 그 크기부터
33
+ Select로 바꿨다. pills 전용 스크롤 prop은 버렸다: 레일은 바깥(SearchScreen `filtersOverflow="scroll"`, 제품 ScrollView)이 소유하고,
34
+ 그 안에 두 번째 가로 스크롤을 겹치게 된다. 선택(radio)·포커스 이동 의미는 바꾸지 않았다.
@@ -46,7 +46,7 @@
46
46
  | [Breadcrumb](components/breadcrumb.md) | 탐색 | Web의 깊은 계층 화면에서 현재 위치까지의 경로를 보여 주고 상위 계층으로 바로 돌아가게 할 때 쓴다. | 배포 | Web |
47
47
  | [Button](components/button.md) | 동작 | 사용자가 누르면 무언가가 일어나는 텍스트 행동에 쓴다. | 배포 | Web · Native |
48
48
  | [Calendar](components/calendar.md) | 데이터 표시 | 화면에 항상 펼쳐진 한 달 격자에서 날짜 하나를 고를 때 쓴다. | 배포 | Web · Native |
49
- | [Card](components/card.md) | 데이터 표시 | 제목·설명·본문·행동이 한 덩어리로 읽히는 독립된 콘텐츠 단위에 쓴다. | 배포 | Web · Native |
49
+ | [Card](components/card.md) | 데이터 표시 | 문서 metadata·미리보기·내보내기 상태는 실험 구성 DocumentResource를 먼저 대조한다. | 배포 | Web · Native |
50
50
  | [Carousel](components/carousel.md) | 데이터 표시 | 한 번에 카드 하나만 보이고 사용자가 순서대로 넘겨 보는 유한한 묶음에 쓴다. | 배포 | Web · Native |
51
51
  | [Celebration](components/celebration.md) | 구성/직접 조작과 모션 | 목표 달성, 첫 완료처럼 드물게 일어나는 성공 순간에 한 번 터지는 색종이 효과에 쓴다. | 배포 | Web · Native |
52
52
  | [ChatMessage](components/chat-message.md) | 구성/정보 표시 | DM·대화 타임라인의 메시지 한 개에 쓴다. | 배포 | Web · Native |
@@ -83,6 +83,7 @@
83
83
  | [Icon](components/icon.md) | 글자와 아이콘 | HJM semantic 이름(`search`, `back`, `chevronEnd`, `notifications` 등 43개)으로 고르는 그림 기호에 쓴다. | 배포 | Web · Native |
84
84
  | [IconButton](components/icon-button.md) | 동작 | 보이는 글자 없이 아이콘만으로 표시하는 행동에 쓴다. | 배포 | Web · Native |
85
85
  | [Image](components/image.md) | 데이터 표시 | 원본 크기를 아는 사진·차트 이미지를 로드 전에 자리를 잡아 두고, 실패해도 의미를 잃지 않게 보여 줄 때 쓴다. | 배포 | Web · Native |
86
+ | [ImageComparison](components/image-comparison.md) | 데이터 표시 | 같은 좌표와 비율의 두 이미지를 겹쳐 변화량을 비교할 때 쓴다. | 배포 | Web · Native |
86
87
  | [KeyboardAvoiding](components/keyboard-avoiding.md) | — | 추가 native peer 없이 하단 행동(BottomCTA, 채팅 입력창)이 소프트웨어 키보드에 가려지지 않게 할 때 쓴다. | 배포 | Native |
87
88
  | [KeyboardDock](components/keyboard-dock.md) | 구성/직접 조작과 모션 | `react-native-keyboard-controller`를 설치한 앱에서 화면 하단에 고정된 행동(BottomCTA, 채팅 입력창)이 키보드와 함께 위아래로 움직이게 할 때 쓴다(Native 전용, 별도 보조 기능. API 성숙도는 실험적 어댑터). 내부는 `KeyboardStickyView`이며, 여백을 바꾸는 대신 키보드 움직임을 따라 translate 한다. | 배포 | Native |
88
89
  | [KeyboardFormScrollView](components/keyboard-form-scroll-view.md) | 구성/직접 조작과 모션 | 입력 필드가 여러 개인 세로 스크롤 폼(가입, 프로필 수정, 주소 입력)에서 포커스된 필드가 키보드에 가려지지 않게 스크롤해 줄 때 쓴다(Native 전용, 별도 보조 기능. API 성숙도는 실험적 어댑터). 내부는 `react-native-keyboard-controller`의 `KeyboardAwareScrollView`이고, `keyboardShouldPersistTaps="handled"`로 고정돼 키보드가 열린 채 버튼을 눌러도 탭이 전달된다. | 배포 | Native |
@@ -113,9 +114,11 @@
113
114
  | [Popover](components/popover.md) | 오버레이 | 트리거에 붙어 뜨는 비모달 표면 안에 **포커스를 받는 임의 콘텐츠**를 둘 때 쓴다. | 배포 | Web |
114
115
  | [ProfileScreen](components/profile-screen.md) | 화면/계정 | 내 프로필(또는 계정) 화면 틀에 쓴다. | 배포 | Web · Native |
115
116
  | [Progress](components/progress.md) | 상태와 알림 | 작업이 얼마나 진행됐는지 보여 줄 때 쓴다. | 배포 | Web · Native |
117
+ | [ProgressiveBlur](components/progressive-blur.md) | 시각 효과 | 스크롤 영역의 바깥에 더 내용이 있음을 알리거나 장식 이미지 가장자리를 흐릴 때 쓴다. | 배포 | Web · Native |
116
118
  | [QRCode](components/qr-code.md) | 데이터 표시 | 문자열(초대 링크, 연결 코드, 결제·체크인 URL)을 다른 기기의 카메라로 스캔하게 할 때 쓴다. | 배포 | Web · Native |
117
119
  | [Radio](components/radio.md) | 입력 | 라디오 한 개를 제품이 직접 배치해야 할 때만 쓴다. | 배포 | Web · Native |
118
120
  | [RadioGroup](components/radio-group.md) | 입력 | 한 화면에 펼쳐 둔 선택지 중 정확히 하나를 고를 때 쓴다. | 배포 | Web · Native |
121
+ | [Rating](components/rating.md) | 입력 | 정수 점수를 선택하거나 계산된 소수 평균을 읽기 전용으로 보여 줄 때 쓴다. | 배포 | Web · Native |
119
122
  | [Result](components/result.md) | 상태와 알림 | 사용자 행동 뒤 흐름이 **끝난** 화면에 쓴다. | 배포 | Web · Native |
120
123
  | [SavedItemsScreen](components/saved-items-screen.md) | 화면/콘텐츠 | 저장한 이미지·게시물을 컬렉션 표지 → 사진 격자 → 상세 순서로 탐색할 때 쓴다. | 배포 | Web · Native |
121
124
  | [ScreenLayout](components/screen-layout.md) | 화면/화면 틀과 도구 | 한 라우트 화면의 뼈대가 필요할 때 쓴다. | 배포 | Web · Native |
@@ -168,15 +171,19 @@
168
171
 
169
172
  | 지침 | 분류 | 언제 쓰나 | 상태 | 지원 |
170
173
  | --- | --- | --- | --- | --- |
174
+ | [관련 입력 묶음](compositions/field-group.md) | 입력과 작성 | 주소·연락처처럼 여러 입력이 하나의 질문에 답할 때 쓴다. | 배포 | Web · Native |
175
+ | [날짜 직접 입력](compositions/date-entry.md) | 입력과 작성 | 사용자가 알고 있는 날짜를 직접 입력할 때 쓴다. | 배포 | Web · Native |
171
176
  | [늦은 응답보다 최신 검색 유지](compositions/interaction-flow-search.md) | 입력과 작성 | 검색어를 바꿔 다시 검색했을 때 먼저 보낸 요청이 늦게 도착해도 최신 검색 결과를 덮어쓰지 않게 할 때 쓴다. | 배포 | Web · Native |
172
177
  | [단계별 드로어](compositions/family-drawer.md) | 입력과 작성 | 초대 → 설정 → 확인처럼 짧은 단계 2~5개를 현재 화면을 떠나지 않고 하단 시트 안에서 차례로 진행할 때 쓴다. | 배포 | Web · Native |
173
178
  | [닫았다 열고 초안 이어쓰기](compositions/interaction-flow-draft.md) | 입력과 작성 | 메모·댓글처럼 시트에서 쓰던 글을 저장하지 않고 닫았다가 다시 열었을 때, 쓰던 초안을 그대로 이어 쓰게 할 때 쓴다. | 배포 | Web · Native |
174
179
  | [댓글 작성](compositions/purpose-input-comment.md) | 입력과 작성 | 게시물·기록 아래에서 댓글이나 특정 댓글에 대한 답글을 남기고, 실패하면 글과 답글 대상을 그대로 남겨 다시 등록하게 할 때 쓴다. | 배포 | Web · Native |
175
180
  | [메시지 작성](compositions/purpose-input-message.md) | 입력과 작성 | 대화 화면 하단에서 글과 사진 여러 장을 함께 보내고, 실패하면 글·사진·답장 대상을 그대로 남겨 다시 보내게 할 때 쓴다. | 배포 | Web · Native |
181
+ | [버튼에서 이어지는 편집](compositions/origin-dialog.md) | 입력과 작성 | 현재 화면의 항목을 짧게 편집하고 돌아올 때 출발 위치를 시각적으로 연결한다. | 배포 | Web · Native |
176
182
  | [빠른 메모 작성](compositions/floating-action-button.md) | 입력과 작성 | 스크롤되는 기록 목록 위에 떠 있는 생성 버튼으로 짧은 입력 대화상자를 열고, 저장하면 새 항목을 목록 맨 위에 넣을 때 쓴다. | 배포 | Web · Native |
177
183
  | [선택 내용 검토와 수정](compositions/reference-review.md) | 입력과 작성 | 선택 내용을 검토하고 수정 후 명시적으로 확정 흐름이 필요할 때 쓴다. | 배포 | Web · Native |
178
184
  | [인증번호 확인과 다시 입력](compositions/stea-otp-verify.md) | 입력과 작성 | 문자·메일로 받은 숫자 인증번호를 입력하고 서버 확인을 기다린 뒤, 틀리면 남은 횟수를 보여 주고 다시 받게 하는 흐름에 쓴다. | 배포 | Web · Native |
179
185
  | [입력 시트](compositions/input-sheet.md) | 입력과 작성 | 현재 화면 위에 하단 시트를 띄워 짧은 입력(이름 바꾸기, 메모 한 줄)을 받고, 키보드가 올라와도 본문을 스크롤하며 완료 버튼에 닿게 할 때 쓴다. | 배포 | Web · Native |
186
+ | [입력을 유지하는 도구](compositions/context-toolbar.md) | 입력과 작성 | 작성 중인 입력을 보존한 채 선택적 도구를 펼쳐야 할 때 쓴다. | 배포 | Web · Native |
180
187
  | [첫 작업을 만들고 이어하기](compositions/reference-first.md) | 입력과 작성 | 첫 기록을 단계별 작성하고 중단한 초안 이어가기 흐름이 필요할 때 쓴다. | 배포 | Web · Native |
181
188
  | [날짜 선택과 예정 목록](compositions/stea-schedule-card.md) | 선택과 필터 | 한 주처럼 짧은 날짜 범위에서 날짜 하나를 고르면 같은 카드 안의 일정 목록이 그 날짜로 바뀌는 요약 카드에 쓴다. | 배포 | Web · Native |
182
189
  | [대표 항목과 묶음 전체 선택](compositions/selection-scope.md) | 선택과 필터 | 사진 묶음·스레드처럼 대표 항목 하나와 묶음 전체가 같은 모양으로 보일 때, 공유·삭제·이동 전에 대상 범위와 개수를 고르고 문구로 확인한 뒤 적용하게 할 때 쓴다. | 배포 | Web · Native |
@@ -186,19 +193,28 @@
186
193
  | [보관함과 페이지 이동](compositions/web-navigation.md) | 탐색과 이동 | Web에서 상위 보관함 → 하위 모음으로 들어가고, 그 모음의 긴 목록을 페이지 단위로 넘겨 보는 탐색에 쓴다. | 배포 | Web |
187
194
  | [펼침과 메뉴](compositions/disclosure.md) | 탐색과 이동 | Web에서 내용을 숨겼다 펼치거나(Collapsible), 대상에 붙은 작업 메뉴를 우클릭·키보드로 열거나(ContextMenu), 데스크톱 앱처럼 상단 메뉴 막대를 두는(Menubar) 세 방식을 각각 보여 주는 모음이다. | 배포 | Web |
188
195
  | [대화 메시지](compositions/common-message.md) | 정보 표시 | 말풍선 하나하나에 반응·답장·원문 이동·전송 실패 후 다시 보내기를 붙일 때 쓴다. | 배포 | Web · Native |
196
+ | [문서와 파일](compositions/document-resource.md) | 정보 표시 | 이름·형식·크기와 미리보기·내보내기·별도 메뉴를 함께 제공하는 문서에 쓴다. | 배포 | Web · Native |
189
197
  | [수치와 이전 대비 변화](compositions/stea-stat-summary.md) | 정보 표시 | 매출·주문·반품처럼 몇 개의 핵심 수치를 비교 기간과 함께 보이고, 증감의 방향과 좋고 나쁨을 색 없이도 읽히게 할 때 쓴다. | 배포 | Web · Native |
190
198
  | [알림 항목](compositions/common-notification.md) | 정보 표시 | 알림 한 행을 누르면 바로 읽음으로 바꾸고, 서버가 실패하면 읽지 않음으로 되돌릴 때 쓴다. | 배포 | Web · Native |
191
199
  | [앞면과 상세 정보 전환](compositions/stea-flip-card.md) | 정보 표시 | 모임·상품처럼 한 카드에 요약(앞면)과 상세 항목(뒷면)이 있고, 사용자가 버튼 하나로 두 면을 오가게 할 때 쓴다. | 배포 | Web · Native |
200
+ | [영상 미리보기](compositions/video-dialog.md) | 정보 표시 | 현재 입력을 유지하면서 짧은 영상 설명을 확인할 때 쓴다. | 배포 | Web · Native |
192
201
  | [일정과 식별 정보 티켓](compositions/stea-event-ticket.md) | 정보 표시 | 공연·예약 입장권처럼 일시·장소·좌석 정보와 함께, 현장에서 보여 줄 QR 코드와 사람이 읽을 예매 번호를 한 카드에 담을 때 쓴다. | 배포 | Web · Native |
202
+ | [질감 비교](compositions/texture-comparison.md) | 정보 표시 | 기존 반복 점 grain과 불규칙한 정적 noise를 같은 배경·강도로 비교할 때 쓴다. | 배포 | Web · Native |
203
+ | [추가해도 유지되는 목록](compositions/live-list.md) | 정보 표시 | 입력 중인 목록에 새 데이터가 추가되거나 순서가 바뀌어도 기존 초안과 항목의 정체성을 유지할 때 쓴다. | 배포 | Web · Native |
193
204
  | [카드 묶음과 긴 목록](compositions/data-layouts.md) | 정보 표시 | 많은 항목을 화면에 늘어놓을 방식을 고를 때 쓴다. | 배포 | Web · Native |
205
+ | [그림과 시작 안내](compositions/illustrated-outcome.md) | 피드백과 복구 | 빈 목록에서 시작을 안내하고 짧은 온보딩을 거쳐 결과를 보여 줄 때 쓴다. | 배포 | Web · Native |
206
+ | [버튼 완료 피드백](compositions/action-feedback.md) | 피드백과 복구 | 입력을 유지하며 저장의 진행·성공·재시도 가능 실패를 보여 줄 때 쓴다. | 배포 | Web · Native |
194
207
  | [변경 저장과 이탈 확인](compositions/reference-settings.md) | 피드백과 복구 | 저장값과 편집 초안을 비교해 이탈 확인 흐름이 필요할 때 쓴다. | 배포 | Web · Native |
195
208
  | [보관과 실행 취소](compositions/action-recovery-undo.md) | 피드백과 복구 | 보관·숨기기·목록에서 빼기처럼 제품이 역연산을 제공하는 작업 뒤에, 같은 자리에서 실행 취소를 주고 그 복구 요청이 성공해야 화면을 되돌릴 때 쓴다. | 배포 | Web · Native |
209
+ | [선택과 오류 복구](compositions/upload-recovery.md) | 피드백과 복구 | 파일 선택과 전송 상태의 취소·재시도를 연결할 때 쓴다. | 배포 | Web · Native |
196
210
  | [저장과 재시도](compositions/action-recovery-save.md) | 피드백과 복구 | 입력한 내용을 서버에 저장하는 폼 한 덩어리에서 저장 중 중복 실행을 막고, 실패하면 입력을 지우지 않은 채 제출했던 값 그대로 다시 보낼 때 쓴다. | 배포 | Web · Native |
197
211
  | [중단해도 남는 현재 상태](compositions/expo-interactions.md) | 피드백과 복구 | 버튼으로 상태를 빠르게 바꾸거나 전환 도중 내용을 닫아도 현재 상태가 바로 보이고 남아야 하는 영역에 쓴다. | 배포 | Native |
198
212
  | [즉시 반영과 복구](compositions/action-recovery-optimistic.md) | 피드백과 복구 | 북마크·좋아요·알림 켜기처럼 되돌려도 피해가 없는 저위험 토글을 누르는 즉시 화면에 반영하고, 서버가 실패하면 직전 확인 값으로 되돌릴 때 쓴다. | 배포 | Web · Native |
199
213
  | [처리 단계와 재시도](compositions/stea-order-progress.md) | 피드백과 복구 | 주문·신청처럼 서버가 단계를 하나씩 확정하는 처리 과정을 보여 주고, 확정에 실패하면 같은 단계를 다시 요청하게 할 때 쓴다. | 배포 | Web · Native |
200
214
  | [캐릭터와 시작 행동](compositions/stea-pixel-empty.md) | 피드백과 복구 | 아직 만든 것이 없는 첫 빈 화면에 제품 캐릭터를 움직여 보이고 첫 행동 하나로 이끌 때 쓴다. | 배포 | Web · Native |
201
215
  | [끌기·밀기·화면 전환](compositions/interaction-adapters.md) | 직접 조작과 모션 | 순서 바꾸기·행 작업·내용 전환·카드 넘기기·달성 축하·카드 확대 화면 전환 같은 선택형 상호작용 어댑터를 한 화면에서 함께 쓸 때, 각 어댑터를 어디에 놓고 무엇으로 감싸야 하는지 확인하는 구성이다. | 배포 | Web · Native |
216
+ | [높이가 이어지는 패널](compositions/adaptive-content.md) | 직접 조작과 모션 | 패널의 길이가 달라질 때 아래 행동이 새 높이로 이동해야 하는 작은 내용 영역에 쓴다. | 배포 | Web · Native |
217
+ | [선택 배경 이동](compositions/selection-motion.md) | 직접 조작과 모션 | 짧은 단일 선택의 현재 항목을 이어지는 배경으로 보여 줄 때 쓴다. | 배포 | Web · Native |
202
218
  | [숫자 변화와 메뉴 변형](compositions/optional-motion.md) | 직접 조작과 모션 | 선택 설치 모션(숫자 자리 단위 변화, 메뉴 형태 변환)을 기존 컴포넌트 자리에 끼워 넣을 때 쓴다. | 배포 | Web |
203
219
  | [이미지·시트·키보드 조작](compositions/optional-adapters.md) | 직접 조작과 모션 | Native 앱 한 화면에서 이미지 확대 보기, 끌어서 높이를 바꾸는 시트, OS 길게 누르기 메뉴, 키보드를 따라 올라가는 하단 행동을 함께 쓸 때 provider 중첩 순서와 각 요소의 자리를 확인하는 구성이다. | 배포 | Native |
204
220
  | [내비게이션 바 비교](compositions/navigation-bar-collection.md) | 비교와 검증 | 하단 탭에 목적지 이동과 별개의 행동(작성·전원·기록 추가)을 함께 둘지, 선택한 목적지를 어떻게 보여 줄지 고를 때 이 비교를 본다. | 배포 | Web · Native |
@@ -216,6 +232,7 @@
216
232
  | [서비스 소개](screens/landing.md) | 소개 | 제품을 처음 보는 사람에게 한 문장 가치 제안을 보여 주고 같은 화면에서 첫 행동(짧은 입력)을 체험하게 하는 소개 화면이다. | 배포 | Web · Native |
217
233
  | [온보딩](screens/flow-onboarding.md) | 소개 | 첫 실행 사용자를 몇 단계(소개 → 관심 주제 → 시작)로 안내하고 마지막 단계에서 완료를 저장하는 화면을 OnboardingScreen 하나로 구성한다. | 배포 | Web · Native |
218
234
  | [권한 안내](screens/flow-permission.md) | 소개 | PermissionScreen을 사용해 권한 안내 흐름을 구성한다. | 배포 | Web · Native |
235
+ | [기능 카드와 주 행동](screens/product-bento.md) | 소개 | 기능을 실제 미리보기로 보여 주고 첫 행동으로 이어지는 소개 화면이다. | 배포 | Web · Native |
219
236
  | [로그인](screens/common-login.md) | 계정 | AuthScreenLayout을 사용해 로그인 흐름을 구성한다. | 배포 | Web · Native |
220
237
  | [프로필](screens/common-profile.md) | 계정 | 내 프로필을 보고(요약·게시물·계정 메뉴) 고치는(사진·이름·소개) 화면을 ProfileScreen과 EditorScreen으로 구성한다. | 배포 | Web · Native |
221
238
  | [알림 설정](screens/notification-settings.md) | 설정 | 알림 종류 몇 개를 스위치로 켜고 끈 뒤 하단 버튼 하나로 저장하는 설정 화면이다. | 배포 | Web · Native |
@@ -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
  - 근거: [Agreement contract](../../agreement.md), `agreementRecipe`·`resolveAgreementState`(`src/agreement.ts`)
9
9
  - 스토리북: `배포/컴포넌트/입력/약관 동의`
10
10
 
@@ -68,6 +68,7 @@ import { Agreement } from "@hjmds/react-native/agreement";
68
68
  | prop | 값 | 기본값 | 설명 |
69
69
  | --- | --- | --- | --- |
70
70
  | `descriptor` | `{ accessibilityLabel, allLabel, items }`(`AgreementDescriptor`) | 필수 | 문구는 모두 i18n |
71
+ | 묶음 `descriptor.disabled` | `boolean` | `false` | 제출 중 전체·개별 동의 변경만 잠금. 선택/필수 판정 유지, 전문 읽기는 가능 |
71
72
  | 항목 | `{ id, label, description?, disabled?, required?, detail?: { label, href? } }` | — | 필수이면서 `disabled`인 항목은 throw |
72
73
  | 항목 `required` | `boolean` | `false`(선택) | 필수 항목이 하나라도 비면 `satisfied`가 `false`다 |
73
74
  | 전체 동의 | tri-state(파생) | — | 저장되는 값이 아니라 개별 항목에서 파생한다. 비활성 항목은 분모에서 빠진다 |
@@ -85,7 +86,7 @@ import { Agreement } from "@hjmds/react-native/agreement";
85
86
  | --- | --- | --- |
86
87
  | 크기 | 폭을 꽉 채운다. 전체 동의 줄 최소 44(`control.minTouchTarget`), 항목 줄 최소 44, [전문 보기] 높이 44, 체크 표시 16×16(`spacing.md`) | `agreementRecipe`, `collectionItemContract`, `.hjm-agreement__mark` |
87
88
  | 간격 | 전체 동의 ↔ 목록 `spacing.xs` 8. 전체 동의 안쪽 위아래 `spacing.sm` 12 · 좌우 `spacing.md` 16, 배경 `canvas`, 모서리 `radius.md` 12. 항목 좌우 `spacing.sm` 12, 체크↔라벨 `spacing.sm` 12, [전문 보기] 좌우 `spacing.xs` 8 | `agreementRecipe`, `.hjm-agreement*` |
88
- | 순서·정렬 | 가입·결제 화면에서 입력 필드 아래, 제출 버튼 바로 위. 안쪽은 [전체 동의] → 항목 목록, 항목은 체크·라벨이 시작 쪽, [전문 보기]가 끝 쪽, 설명은 라벨 아래 줄. 스토리는 Top → TextField → Agreement → 남은 필수 항목 안내(`role="status"`) → Button을 `Stack gap="md"`(16)로 쌓는다 | `showcase/web/src/patterns/Agreement.stories.tsx` |
89
+ | 순서·정렬 | 가입·결제 화면에서 입력 필드 아래, 제출 버튼 바로 위. 안쪽은 [전체 동의] → 항목 목록, 항목은 체크·라벨이 시작 쪽, [전문 보기]가 뒤쪽에 놓인다. 라벨은 행 폭의 70%를 기준으로 확보하고 긴 전문 버튼은 다음 줄로 이동한다. 설명은 라벨 아래 줄. 스토리는 Top → TextField → Agreement → 남은 필수 항목 안내(`role="status"`) → Button을 `Stack gap="md"`(16)로 쌓는다 | `showcase/web/src/patterns/Agreement.stories.tsx` |
89
90
  | 고정·스크롤 | Agreement는 스크롤 본문에 둔다. 하단에 고정할 제출 버튼은 [BottomCTA](bottom-cta.md)로 두고 `satisfied`가 `false`면 비활성 | 같은 스토리 |
90
91
  | 좁은 폭·큰 글자 | 라벨이 줄바꿈되고(`overflow-wrap: anywhere`) [전문 보기]는 끝 쪽에 남는다 | `.hjm-agreement__copy` |
91
92
 
@@ -106,7 +107,8 @@ import { Agreement } from "@hjmds/react-native/agreement";
106
107
 
107
108
  ## 꼭 지킬 것
108
109
 
109
- - 제출 버튼은 `onStateChange`로 받은 `satisfied`만 읽는다. 남은 필수 항목 안내는 `missingRequiredIds`로 제품이 문장을 만든다.
110
+ - 필수 동의 판정은 `resolveAgreementState`의 `satisfied`를 사용하고 제출 가능 여부는 제품의 입력 검증·진행 중 상태와 함께 결정한다. 묶음 잠금은 동의 사실을 바꾸지 않으므로 `satisfied`가 true인 채 잠길 수 있다. 남은 필수 항목 안내는 `missingRequiredIds`로 제품이 문장을 만든다.
111
+ - `onStateChange`는 마운트와 사용자 토글 때 알린다. 제품이 약관 버전이나 제어 `checkedIds`를 직접 바꾸면 같은 렌더에서 `resolveAgreementState(descriptor, checkedIds)`로 판정한다. 문서 버전 변경 시 이전 동의를 초기화하는 책임은 제품에 있다.
110
112
  - `accessibilityLabel`·`allLabel`·항목 `label`·`requiredLabel`·`optionalLabel`은 모두 제품의 i18n 문구다.
111
113
  빈 문자열, 빈 목록, 중복 id, 필수이면서 `disabled`인 항목은 throw다.
112
114
  - 약관 문구·링크 주소·법적 유효성·동의 기록 저장은 제품 소유다. HJM은 판정과 배치만 가진다.
@@ -127,3 +129,11 @@ import { Agreement } from "@hjmds/react-native/agreement";
127
129
  `satisfied`를 받지 못했다. 미게시(1.12.1 이후) 버전은 마운트 때 초기 상태를 한 번 알린다. 1.12.1에서는 제출 버튼의 첫 상태를
128
130
  `resolveAgreementState(descriptor, checkedIds)`(`@hjmds/design-contracts/components/agreement`)로 직접 계산한다.
129
131
  호출 횟수를 세는 코드는 마운트 1회가 늘어난다.
132
+
133
+ 2026-10-07 Utilverse 가입은 요청 중 필수 Checkbox 둘을 잠그는데, 개별 필수 item에 disabled를
134
+ 주면 Agreement 검증이 거절했다. 묶음 `descriptor.disabled`를 추가해 필수 항목을 분모에서
135
+ 빼거나 동의 값을 삭제하지 않고 변경만 막는다. 양 Showcase의 `비활성`에서 전문 읽기와 잠금
136
+ 해제 후 이어서 선택을 확인할 수 있다. Web은 aria-disabled로 초점을 유지하고 키보드 토글을
137
+ 막으며, Native는 disabled/accessibilityState를 함께 적용한다. 별도 전문 행동은 잠그지 않는다.
138
+
139
+ 큰 글자에서도 체크 원은 16×16을 유지한다. 내부 체크는 8×4 도형(테두리 2), 혼합 표시는 10×2 도형이며 글꼴 확대에 따라 원 밖으로 커지지 않는다. Native는 Yoga의 축소 계산 때문에 70% 최소 폭도 함께 적용한다.
@@ -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
  - 근거: `AlertDialogRequest`·`createAlertDialogSession`(`src/alert-dialog.ts`), recipe `alertDialogRecipe`, Popover와의 경계 [ConfirmPopover 결정](../../confirm-popover.md), Native 긴 문구 처리 [Dialog](../../dialog.md)
9
9
  - 스토리북: `배포/컴포넌트/오버레이/확인 대화상자`
10
10
 
@@ -126,5 +126,11 @@ import { AlertDialog } from "@hjmds/react-native/overlays";
126
126
 
127
127
  ## 함정
128
128
 
129
+ - `onConfirm`은 Promise가 resolve되면 확인 성공으로 처리한다. 제품 command가 실패를 자체
130
+ snapshot에 기록하고 Promise를 resolve하는 경우 그대로 연결하지 않는다. 2026-10-07 Utilverse
131
+ 삭제·신고 조사에서 이 차이를 확인했다. 작업까지 대화상자가 기다리는 경로는 receipt/실패를
132
+ 명시적으로 변환하는 adapter가 필요하다. 사용자 동의만 받는 경로는 `onConfirm` 없이 확인을
133
+ 마치고, Native `onResult`에서 제품 작업을 시작한다. 이때 `confirmed`는 동의 결과이며 서버
134
+ 삭제·Apple 인증·로컬 정리의 성공을 뜻하지 않는다. 처리 결과는 제품 상태 화면이 계속 보여 준다.
129
135
  - Native에서 매우 긴 확인 문구는 본문이 스크롤되도록 바뀌었지만 큰 글자 실기기 검증은 끝나지 않았다([Dialog](../../dialog.md)).
130
136
  문구를 자르거나 글자 크기 상한으로 피하지 않는다.
@@ -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
  - 근거: [Avatar fallback and Blobatar](../../avatar-fallback.md), recipe `avatarRecipe`(`src/component-recipes.ts`)
9
9
  - 스토리북: `배포/컴포넌트/데이터 표시/아바타` · `배포/컴포넌트/데이터 표시/블로바타 캐릭터` · `배포/컴포넌트/데이터 표시/움직이는 블로바타 캐릭터`
10
10
 
@@ -76,6 +76,7 @@ import { Avatar } from "@hjmds/react-native/data-display";
76
76
  | Web `alt` | `string` | `name` | `""`이면 보조기기에서 숨긴다 |
77
77
  | Web `imageProps` | `img` 속성(`alt`·`src` 제외) | — | `onError`는 `(event: SyntheticEvent<HTMLImageElement>) => void`이며 대체 표시로 바꾼 뒤 불린다 |
78
78
  | Native `accessibilityLabel` · `decorative` | `{ accessibilityLabel: string }` 또는 `{ decorative: true }` | — | 둘 중 하나가 타입으로 강제된다 |
79
+ | Native `renderImage` | `(context: AvatarImageRenderProps) => ReactNode` | 기본 Native Image | 제품의 이미지 캐시·표시 호스트를 연결한다(미게시, 1.13.1 이후). `source`, `size`, HJM `fallback`, 세대가 보호된 `onError` 제공 |
79
80
  | Native `initials` | `string` | 이름에서 계산 | 앞뒤 공백을 지우고 최대 3자, 대문자 |
80
81
  | `layoutStyle` | margin·width·flex·`alignSelf` | — | 배치 전용. Native `style`·`imageStyle`은 deprecated — layoutStyle 또는 `size`/`renderFallback` |
81
82
  | `AvatarGroup`(Web) `label` · `size` · `overflow` | `string` · Avatar 크기 · `ReactNode` | `label` 필수 · `size` `medium` | 빈 `label`은 `TypeError`. `overflow`는 제품이 만든 "+3" 같은 문구이며 `aria-hidden`이다(남은 인원은 `label`에 담는다). 겹침은 크기의 30%다 |
@@ -107,8 +108,39 @@ import { Avatar } from "@hjmds/react-native/data-display";
107
108
  | 크기·모양 축 | 4단 이름 · `circle`/`rounded` | 숫자 · 원만 |
108
109
  | 묶음 | `AvatarGroup` | 없음 |
109
110
 
111
+ 2026-10-07 실제 Showcase 비교에서 `AvatarGroup`을 `ListRow.leading`에 넣으면 단일 이미지용
112
+ 40×40 프레임에 그룹과 남은 인원이 잘렸다. 단일 Avatar는 그 슬롯을 사용하고, 묶음은 Card 본문의
113
+ Stack처럼 내용 폭을 수용하는 영역에 둔다. leading의 overflow/크기를 CSS로 우회하지 않는다.
114
+
110
115
  ## 함정
111
116
 
112
117
  - `src`/`source`에 `undefined`를 직접 넘기면 `exactOptionalPropertyTypes`에서 타입 오류다. 사진이 없으면 prop을 빼거나 spread로 조건부로 넣는다.
113
118
  - Web `AvatarGroup`은 이제 `style`을 버리지 않고 `layoutStyle`과 합친다. 겹침 변수(`--hjm-avatar-overlap`)는 마지막에 덮이므로 `style`로 겹침을 바꿀 수 없다.
114
119
  - 이니셜은 두 플랫폼 모두 `resolveAvatarInitials`(`@hjmds/design-contracts/avatar-fallback`)로 첫 단어와 마지막 단어의 첫 글자(code point)다. 1.12.1까지 Web은 앞 두 단어를 써서 "Kim Min Jun"이 Web "KM", Native "KJ"였다(미게시 변경). 1.12.1에서 일치가 필요하면 Native `initials`·Web `fallback`으로 같은 값을 준다.
120
+
121
+ ### Native 제품 이미지 호스트
122
+
123
+ Utilverse는 Expo Image의 disk 캐시와 로딩 동안 이니셜을 유지한다. 공통 Avatar로 바꿀 때
124
+ 그 계약을 잃지 않도록 `renderImage`를 제공한다. 새 아바타 컴포넌트나 Expo 의존성을
125
+ HJM에 추가하지 않고 원형 프레임·대체 문자·접근성은 기존 Avatar에 남긴다.
126
+
127
+ ```tsx
128
+ import { Image as ExpoImage } from "expo-image";
129
+ import { Avatar } from "@hjmds/react-native/data-display";
130
+
131
+ <Avatar name={displayName} decorative source={{ uri }} size={28}
132
+ renderImage={({ source, size, fallback, onError }) => (
133
+ <View style={{ width: size, height: size, alignItems: "center", justifyContent: "center" }}>
134
+ {fallback}
135
+ <ExpoImage source={source} cachePolicy="disk" contentFit="cover" accessible={false}
136
+ style={{ position: "absolute", width: size, height: size }} onError={onError} />
137
+ </View>
138
+ )}
139
+ />
140
+ ```
141
+
142
+ 제품이 import하는 `View`·Expo Image는 호스트 구현이며 HJM 색·원형·테두리를 덮지 않는다.
143
+ 실패 시 전달된 `onError`를 호출해야 공통 대체 표시로 바뀐다. 이전 source의 callback은
144
+ A→B→A로 되돌아와도 무시한다. source를 같은 값으로 재생성하는 것은 재시도가 아니다.
145
+ 슬롯의 자식은 장식 전용이며 공통 wrapper가 접근성 트리에서 숨긴다. 버튼·링크를 넣지 않는다.
146
+ 기본 Native Image 경로는 그대로다. Web은 기존 img/imageProps 경로를 사용한다.
@@ -4,12 +4,17 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-07
8
8
  - 근거: recipe `cardRecipe`(`src/card.ts`), 바탕 `surfaceRecipe`(`src/base-recipes.ts`). 별도 계약 문서는 없다
9
9
  - 스토리북: `배포/컴포넌트/데이터 표시/카드`
10
10
 
11
11
  ## 언제 쓰나
12
12
 
13
+ 문서 metadata·미리보기·내보내기 상태는 실험 구성 [DocumentResource](../compositions/document-resource.md)를
14
+ 먼저 대조한다. 2026-10-07 파일 사례에서 일반 카드만으로 다운로드/저장 상태까지 동등하다고
15
+ 판단할 수 없음을 확인해 별도 구성으로 연결했다.
16
+
17
+
13
18
  제목·설명·본문·행동이 한 덩어리로 읽히는 독립된 콘텐츠 단위에 쓴다. 주문 요약, 설정 묶음,
14
19
  미리보기처럼 화면 안에서 경계가 보여야 하는 블록이 여기에 속한다. 위에서부터
15
20
  `media` → `leading`+`title`+`description` → `children` → `actions` 순서가 고정이다.
@@ -30,6 +35,7 @@
30
35
  | 이름 | 역할 | Web | Native |
31
36
  | --- | --- | --- | --- |
32
37
  | `Card` | 기본 | `@hjmds/react`, `/display` | `@hjmds/react-native`, `/data-display` |
38
+ | `DocumentResource` | 실험 문서 구성 | `@hjmds/react/document-resource` | `@hjmds/react-native/document-resource` |
33
39
 
34
40
  ## 최소 사용 예
35
41
 
@@ -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
  - 근거: [Carousel](../../carousel.md), `CarouselMotion`은 [선택형 어댑터](../../optional-adapters.md), contract `src/carousel.ts`
9
9
  - 스토리북: `배포/컴포넌트/데이터 표시/캐러셀`
10
10
 
@@ -99,6 +99,10 @@ import { Carousel } from "@hjmds/react-native/carousel";
99
99
  - 슬라이드 id는 유일하고 앞뒤 공백이 없어야 한다. 빈 배열은 던지므로 로딩·빈 상태는 마운트 전에 제품이 처리한다.
100
100
  - `composeAccessibleName`의 어순·조사는 제품 문구다. HJM은 위치 정보만 넘긴다.
101
101
  - 슬라이드 안의 시각 콘텐츠(카드·이미지)는 제품 소유다. 컨트롤·점·접근성 구조는 HJM 소유라 다시 만들지 않는다.
102
+ - 두 renderer는 숨겨진 슬라이드에도 `renderSlide`를 호출한다. 숨김은 네트워크 요청 취소나
103
+ 자식 unmount가 아니다. 권한 확인이 필요한 사진을 현재 페이지만 읽는 제품은 제어된
104
+ `currentKey`와 비교해 선택되지 않은 콘텐츠를 `null`로 반환한다. 2026-10-07 Utilverse의
105
+ 선택 페이지 단독 조회·로그아웃 후 캐시 제거 계약을 대조하며 확인한 경계다.
102
106
  - 배치는 `layoutStyle`로만 한다. Native `style`은 deprecated(개발 모드 1회 경고, 다음 major 제거)다.
103
107
 
104
108
  ## 플랫폼 차이
@@ -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-patterns.md), Web·Native `src/screens.tsx`·`src/screen-flows.tsx`; 기존 개별 지침을 새 규격으로 통합. 예제 스토리는 2026-10-06 사용자 승인으로 스토리북 배포([승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정)). 스토리북 배포는 API 게시가 아니다(`적용` 참고)
9
9
  - 스토리북: `배포/구성/정보 표시/대화 메시지`, `배포/화면/소통/채팅`
10
10
 
@@ -103,6 +103,11 @@ import { Text } from "@hjmds/react-native/primitives";
103
103
  답장·반응은 접근성 동작(`reply`·`activate`)으로 남는다. 2026-10-06 리뷰에서 행 이름(`author`)과 반응 이름(`picker.label`)이
104
104
  본문을 가려 화면 낭독기가 메시지를 읽지 못했다.
105
105
  - 타임라인 전체를 live region으로 감싸지 않는다(과거 메시지 로딩 때 읽기 순서를 가로챈다).
106
+ - 반응 **선택 메뉴**와 현재 반응 **집계 버튼**은 다른 행동이다. 집계를 눌러 메시지 메뉴를 열 때는
107
+ `actions` 슬롯에 Button을 공급할 수 있다. 본문 안에 집계 버튼을 넣는다면 사진·링크가 없어도
108
+ `interactiveContent=true`가 필요하다. 2026-10-07 Utilverse 소스 대조에서 사진/도구만 검사해
109
+ 집계 버튼이 부모 접근성 그룹에 들어갈 수 있는 경로를 발견했다. `actions`에 둘 때도 긴 집계와
110
+ 시각이 좁은 폭·큰 글자에서 함께 읽히고 조작 가능한지 확인한다.
106
111
 
107
112
  ## 플랫폼 차이
108
113
 
@@ -120,3 +125,8 @@ import { Text } from "@hjmds/react-native/primitives";
120
125
  (부작용·고유 ref를 두지 않는다).
121
126
  - `reactions`의 `options`와 `more.options`는 id가 합쳐서 유일하고 emoji·label이 비어 있지 않아야 한다. `value`가
122
127
  목록에 없는 id면 렌더 중 `TypeError`가 난다(`validateReactions`). 서버가 모르는 반응을 돌려줄 때를 대비한다.
128
+
129
+
130
+ ### 고정 아이콘과 큰 글자
131
+
132
+ 2026-10-06 최근 검색 삭제 기호가 큰 글자에서 잘린 재현에 따라 Native 내장 삭제·메뉴 기호는 고정 아이콘 틀의 크기를 유지한다. 주변 제목·라벨은 계속 확대한다. Chip의 체크와 Toast 닫기는 기존 비확대 경로를 유지하며 회귀 검사에 포함한다. 제품이 전달한 아이콘 슬롯은 제품이 같은 조건을 검증한다.
@@ -74,11 +74,11 @@ import { Chip } from "@hjmds/react-native/inputs";
74
74
 
75
75
  | 항목 | 값 | 근거 |
76
76
  | --- | --- | --- |
77
- | 크기 | 내용 폭. 높이 `small` 36 · `medium` 44(`control.chipHeight`), 모서리 `radius.full`. `small`은 위아래 4를 넓혀(Native hitSlop, Web `::after`) 터치 영역을 44에 맞춘다 | `chipRecipe.sizes`, `react-native/src/inputs.tsx` |
77
+ | 크기 | 내용 폭. 최소 높이 `small` 36 · `medium` 44(`control.chipHeight`, 두 플랫폼 모두 최소값이라 라벨이 크면 늘어난다), 모서리 `radius.full`. `small`은 위아래 4를 넓혀(Native hitSlop, Web `::after`) 터치 영역을 44에 맞춘다 | `chipRecipe.sizes`, `react-native/src/inputs.tsx` |
78
78
  | 간격 | 좌우 여백 `small` `spacing.sm` 12 · `medium` `spacing.md` 16, 아이콘↔라벨 `small` `spacing.xxs` 4 · `medium` `spacing.xs` 8(두 플랫폼). 칩 사이는 부모가 정한다(`spacing.xs` 8 권장) | `chipRecipe.sizes`, `.hjm-chip` |
79
79
  | 순서·정렬 | 필터 줄·태그 묶음으로 가로로 나란히 둔다. 안쪽은 [선택 표시] → [leading] → [라벨] → [trailing]. 선택 상태는 브랜드 테두리·글자색 | `chipRecipe.slots`, `.hjm-chip[data-selected]` |
80
80
  | 고정·스크롤 | 고정 영역이 없다. 칩이 많으면 부모가 줄바꿈하거나 가로 스크롤 영역을 둔다. 검색 화면의 필터 칩 줄은 [SearchScreen](search-screen.md) `filtersOverflow="scroll"`이 가로 스크롤을 소유한다 | `SearchScreen` |
81
- | 좁은 폭·큰 글자 | 라벨이 줄바꿈된다(`overflow-wrap: anywhere`). 높이는 최소값이라 늘어난다 | `.hjm-chip__label` |
81
+ | 좁은 폭·큰 글자 | 라벨이 줄바꿈된다(Web `overflow-wrap: anywhere`). 높이는 최소값이라 라벨 높이만큼 늘어난다(Web `min-block-size`, Native `minHeight`) | `.hjm-chip__label`, Native `Chip` |
82
82
 
83
83
  ## 꼭 지킬 것
84
84
 
@@ -97,8 +97,16 @@ import { Chip } from "@hjmds/react-native/inputs";
97
97
  | `label` 타입 | `ReactNode` | `string` |
98
98
  | 배치 | `layoutStyle` | `layoutStyle`, 슬롯별 `leadingStyle`·`indicatorStyle`·`trailingStyle`(배치 key만) |
99
99
  | 선택 표시 위치 | 표시 → leading → 라벨 | leading → 표시 → 라벨 |
100
+ | 높이 | `min-block-size` | `minHeight`(미게시(1.13.1 이후). 1.13.0 이하는 고정 `height`) |
100
101
 
101
102
  ## 함정
102
103
 
103
104
  - 두 renderer의 선택 콜백 이름이 다르다. Web 코드를 옮기면서 `onSelectedChange`를 Native에 넘기면
104
105
  타입 오류가 나고, Native의 `onPress(next, event)`를 Web에 넘기면 첫 인자가 이벤트다.
106
+ - 1.13.0 이하 Native Chip은 높이가 고정(`height` 36)이라 큰 글자에서 라벨 아래가 잘렸다(2026-10-06 utilverse 적용, SearchScreen
107
+ `필터` 칩·추천 검색어 칩, accessibility-large). 1.13.1부터 Button처럼 최소 높이다. 제품이 `layoutStyle`로 높이를 다시 고정하지 않는다.
108
+
109
+
110
+ ### 고정 아이콘과 큰 글자
111
+
112
+ 2026-10-06 최근 검색 삭제 기호가 큰 글자에서 잘린 재현에 따라 Native 내장 삭제·메뉴 기호는 고정 아이콘 틀의 크기를 유지한다. 주변 제목·라벨은 계속 확대한다. Chip의 체크와 Toast 닫기는 기존 비확대 경로를 유지하며 회귀 검사에 포함한다. 제품이 전달한 아이콘 슬롯은 제품이 같은 조건을 검증한다.
@@ -4,14 +4,14 @@
4
4
  - 상태: 배포
5
5
  - 지원: Web · Native
6
6
  - 적용: 1.12.1
7
- - 검토일: 2026-10-06
7
+ - 검토일: 2026-10-07
8
8
  - 근거: contract `src/content-transition.ts`(`resolveContentTransition`)
9
9
  - 스토리북: `배포/컴포넌트/시각 효과/내용 전환`
10
10
 
11
11
  ## 언제 쓰나
12
12
 
13
13
  같은 자리의 내용이 상태에 따라 바뀔 때(필터 결과 패널, 단계별 본문) 새 내용이 짧게 나타나도록 감싼다.
14
- `stateKey`가 바뀔 때만 움직이고, 화면에는 현재 내용 하나만 남는다.
14
+ 기본적으로 `stateKey`가 바뀔 때 움직이고, 화면에는 현재 내용 하나만 남는다.
15
15
 
16
16
  ## 쓰지 않을 때
17
17
 
@@ -69,18 +69,20 @@ import { ContentTransition } from "@hjmds/react-native/content-transition";
69
69
  | `preset` | `fade` · `rise` · `slide` · `scale` | `fade` | `rise`는 아래 12에서, `slide`는 가로 16(RTL이면 반대), `scale`은 0.96에서 시작 |
70
70
  | `motion` | `system` · `none` | `system` | `system`은 reduced motion을 따르고 `none`은 항상 즉시 교체 |
71
71
  | `stateKey` | `string` | — (필수) | 바뀔 때만 새 내용이 나타난다 |
72
+ | `enterOnMount` | boolean | false | 미게시: 새 데이터 항목이 처음 추가될 때도 등장 전환. 이미 표시한 항목은 stable key를 유지한다 |
73
+ | `animateHeight` | boolean | false | 내용의 측정 높이가 바뀔 때 주변 틀 높이를 전환한다. 모션 감소에서는 즉시 반영 |
72
74
  | Web `focusTarget` | `RefObject<HTMLElement \| null>` | — | 바뀌기 전 포커스가 안에 있었으면 전환 뒤 이 요소로 옮긴다 |
73
75
  | Web `layoutStyle` | 배치 전용 style | — | 바깥 고정 wrapper에 붙는다(키가 바뀌는 안쪽 패널이 아님) |
74
76
 
75
77
  콜백 prop은 없다. `TextTransition`은 `text: string`을 `stateKey`로 쓴다.
76
78
 
77
- - 첫 렌더는 움직이지 않는다. 시간은 `motion.normal`, 곡선은 `easing.enter` 토큰이다.
79
+ - 기본 첫 렌더는 움직이지 않는다. `enterOnMount`는 새로 추가한 항목에만 선택하며 초기 서버 렌더 목록을 지연시키는 용도로 쓰지 않는다. 시간은 `motion.normal`, 곡선은 `easing.enter` 토큰이다.
78
80
 
79
81
  ## 배치
80
82
 
81
83
  | 항목 | 값 | 근거 |
82
84
  | --- | --- | --- |
83
- | 크기 | 자체 크기·여백이 없다. Web은 `div` 두 겹(블록), Native는 `Animated.View` 하나로 감싼다 | `packages/react/src/content-transition.tsx`, `packages/react-native/src/content-transition.tsx` |
85
+ | 크기 | 자체 크기·여백이 없다. Web은 배치 wrapper·높이 틀·측정 flow·keyed panel, Native는 바깥 Animated.View·측정 View·표현 Animated.View로 감싼다 | `packages/react/src/content-transition.tsx`, `packages/react-native/src/content-transition.tsx` |
84
86
  | 간격 | 자체 간격이 없다. 위아래 간격은 감싸는 [Stack](stack.md) 등이 정한다. 움직임 폭(세로 12·가로 16·0.96배)만큼 래퍼 밖으로 잠깐 밀려 나오므로 바로 옆 요소와 간격을 둔다 | `src/content-transition.ts` |
85
87
  | 순서·정렬 | 바뀌는 영역 하나만 감싼다(결과 패널, 단계 본문). 필터 막대·탭·제목처럼 그대로 남는 부분은 바깥에 둔다 | — |
86
88
  | 고정·스크롤 | Native 래퍼에는 `flex`가 없어 남은 높이를 채우지 않는다. 화면 높이를 채워야 하는 내용이면 바깥 View가 높이를 정한다 | `packages/react-native/src/content-transition.tsx` |
@@ -99,3 +101,25 @@ import { ContentTransition } from "@hjmds/react-native/content-transition";
99
101
  | 포커스 복원 | `focusTarget`: 바뀌기 전 포커스가 안에 있었으면 그 요소로 옮긴다 | 없음 |
100
102
  | 앱이 백그라운드로 감 | 해당 없음 | 진행 중 전환을 멈추고 바로 표시 |
101
103
  | 배치 prop | `layoutStyle`(바깥 wrapper) | 없음 |
104
+
105
+ ### 측정 기반 overlay 전환 준비 (미게시)
106
+
107
+ `@hjmds/design-contracts/content-transition`의 `resolveOriginTransition(origin, destination, reducedMotion)`은
108
+ 같은 물리 viewport 좌표계의 `TransitionRect { x, y, width, height }` 두 개를 받는다.
109
+ 도착 경계의 중심 기준 translateX/Y와 scaleX/Y를 반환한다. RTL 좌표를 다시 뒤집지 않는다.
110
+ 미측정·0 크기·비유한 값·계산 overflow·모션 감소에서는 null로 일반 overlay 표현을 유지한다.
111
+
112
+ 이 함수 자체는 renderer나 상태 엔진이 아니다. Dialog의 선택적 motionOrigin 표현이
113
+ 이 계산을 사용한다([Dialog 지침](dialog.md)). 기존 ContentTransition의
114
+ 단일 subtree 전환과 Native SharedTransitionElement의 라우터 전환을 대체하지 않는다.
115
+ trigger 측정 시점과 같은 좌표계 보장은 제품이, 취소·재열기·exit presence·초점 복귀는
116
+ 해당 renderer가 소유한다. Motion Primitives의 원본에서 닫기 후 초점 손실과 작성 예제의 초안 소실을
117
+ 확인했으므로 geometry만 흡수하고 기존 HJM overlay 상태 엔진을 유지한다.
118
+
119
+
120
+ ### 실제 목록 데이터의 등장
121
+
122
+ [추가해도 유지되는 목록](../compositions/live-list.md)은 stable id를 React key와 stateKey로
123
+ 쓰고 새 항목에만 enterOnMount를 켠다. 목록 전체의 stateKey를 배열 직렬화 값으로 바꾸면 Web의
124
+ 입력 subtree가 교체되므로 하지 않는다. 데이터는 즉시 전달하며 stagger 타이머로 감추지 않는다.
125
+ 삭제된 항목은 즉시 제거한다. 퇴장 사본을 남기지 않으므로 키보드·터치 대상도 함께 사라진다.
@@ -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
  - 근거: [Dialog](../../dialog.md), [명령형 오버레이](../../overlay-stack.md), recipe `dialogRecipe`(`src/component-recipes.ts`)
9
9
  - 스토리북: `배포/컴포넌트/오버레이/대화상자`
10
10
 
@@ -174,3 +174,20 @@ Native, 큰 글자 또는 폭 < 480: [ 취소 ]
174
174
  - Native `DialogProps`는 RN `Modal` props(`style` 포함)를 그대로 받는다. 배치는 `contentStyle`로만 준다.
175
175
  - Native는 제어형과 비제어형을 렌더 중에 바꾸면 예외가 난다.
176
176
  - Native 기본 렌더러 예제는 실제 저장 서버가 없는 동기 완료 예시다. 제품에서는 저장 Promise를 반환하며, 예제의 영문 고정 문구는 제품 i18n 키로 치환한다. 닫기는 action이 요청하는 `close-action`이 맡는다.
177
+
178
+ ### 선택적인 트리거 형태 전환 (미게시 실험)
179
+
180
+ 양 renderer의 `motionOrigin?: TransitionRect`는 열기 직전에 측정한 트리거의
181
+ `{ x, y, width, height }`를 받는다. Web은 getBoundingClientRect, Native는 measureInWindow로
182
+ 얻은 물리 viewport/window 좌표를 전달한다. 별도 HJM morph wrapper를 만들지 않고 기존
183
+ Dialog의 제목·닫기·busy·초점 복귀·초안 소유 계약을 그대로 사용한다.
184
+
185
+ Web은 닫기 전환이 끝나야 portal을 제거하고 onDismissComplete와 초점 복귀를 실행한다.
186
+ 닫는 동안 내용은 inert이며 빠른 재열기는 같은 subtree를 유지하고 오래된 완료를 취소한다.
187
+ Native는 실제 Modal 콘텐츠를 측정하며 콜백이 오지 않으면 기존 enter 시간 내 일반 표시로
188
+ 복귀한다. 뒤늦은 측정으로 닫힌 세션을 되살리지 않는다. 측정이 없거나 모션 감소이면
189
+ 공간 전환 없이 기존 표현을 쓴다. Native에서 진입 후 크기가 달라지면 이전 측정으로
190
+ 닫지 않고 일반 fade로 복귀한다. 다음 open에서 다시 측정한다. 이 옵션의 실험은 `버튼에서 이어지는 편집` 구성이다.
191
+
192
+ 초안은 제품 상태에 두고 닫을 때 삭제하지 않는다. 내용·화면 회전·키보드로 목적지 크기가
193
+ 달라지는 흐름은 실제 기기 검증 후 채택한다. 원점이 다른 좌표계를 혼합하지 않는다.
@@ -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
  - 근거: [Composable decorative surfaces](../../effect-surface.md), 별도 보조 기능(supplemental)
9
9
  - 스토리북: `배포/컴포넌트/시각 효과/배경 시각 효과`
10
10
 
@@ -62,7 +62,7 @@ import { EffectSurface } from "@hjmds/react-native/effect-surface";
62
62
 
63
63
  | prop | 값 | 기본값 | 설명 |
64
64
  | --- | --- | --- | --- |
65
- | `descriptor.layers` | `mesh` · `glow` · `grain` | `["mesh"]` | 서로 다른 1~3개 |
65
+ | `descriptor.layers` | `mesh` · `glow` · `grain` · `noise` | `["mesh"]` | 서로 다른 1~4개. noise는 미게시 실험 |
66
66
  | `descriptor.intensity` | 0~1 | `0.22` | — |
67
67
  | `descriptor.period` | 2~120초 | `12` | — |
68
68
  | `descriptor.seed` | 문자열 | `"hjm"` | 빈 문자열 금지 |
@@ -118,3 +118,5 @@ import { EffectSurface } from "@hjmds/react-native/effect-surface";
118
118
  - Native의 장식 실패 대비(error boundary)는 **렌더 오류**만 잡는다. peer가 없어 모듈 해석이 실패하면 앱 번들 자체가
119
119
  깨진다. `tsc` 통과를 설치 확인으로 보지 않는다.
120
120
  - descriptor 검증 오류는 대비 밖에 있어 그대로 던져진다. 값 범위를 지킨다.
121
+
122
+ `noise`의 구현 차이·실험 조건은 [질감 비교](../compositions/texture-comparison.md)를 따른다. 기존 grain을 교체하지 않는다.
@@ -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
  - 근거: [Result와의 경계](../../result.md#emptystate와의-경계), [ContentState 범위 축](../../content-state.md), recipe `emptyStateRecipe`(`src/component-recipes.ts`)
9
9
  - 스토리북: `배포/컴포넌트/상태와 알림/빈 상태`
10
10
 
@@ -69,7 +69,9 @@ import { EmptyState } from "@hjmds/react-native/feedback";
69
69
  | Web `icon` · Native `illustration` | `ReactNode` | — | 장식, 접근성에서 숨김 |
70
70
  | `layoutStyle` | 배치 전용 style | — | 루트 배치. Native `style`·`illustrationStyle`·`titleStyle`·`descriptionStyle`·`actionStyle`은 deprecated |
71
71
 
72
- EmptyState 자체에는 콜백 prop이 없다.
72
+ EmptyState 자체에는 콜백 prop이 없다. 2026-10-07 기존 예제의 생성 버튼에 동작이 없음을 확인해
73
+ 양 기본 스토리를 예시 생성 → 검색 0건 → 검색어 지우기 → 목록 복구로 연결했다. 이 데이터는
74
+ 메모리에만 존재한다. 검색·필터 화면 전체가 필요하면 [SearchScreen](../screens/common-search.md)을 쓴다.
73
75
 
74
76
  ## 배치
75
77
 
@@ -96,6 +98,12 @@ EmptyState 자체에는 콜백 prop이 없다.
96
98
 
97
99
  ## 꼭 지킬 것
98
100
 
101
+ - 외부 라이브러리가 권한·실패·성공까지 EmptyState라고 불러도 HJM의 의미 경계를 유지한다.
102
+ 부분 실패는 Notice, 끝난 결과는 Result다. 기기 권한과 서비스 접근 권한을 같은 복구 행동으로 연결하지 않는다.
103
+ 2026-10-07 Primer/PatternFly 대조에서 이름보다 사용자 다음 행동을 기준으로 흡수하기로 했다.
104
+ - 첫 표시부터 있던 빈 화면은 조용히 둔다(Web `aria-live="off"`, Native `announcement="none"`).
105
+ 검색 후 0건으로 바뀔 때는 Web 기본 status, Native `announcement="polite"`로 알리며 입력 초점을 빼앗지 않는다.
106
+
99
107
  - 제목·설명·행동 문구는 i18n 키로 넣는다. "검색 0건"과 "아직 만든 것 없음"은 다른 문구로 구분한다.
100
108
  - 다음 행동이 있으면 `action`에 Button 하나를 둔다. 아이콘·일러스트는 장식이라 접근성에서 숨겨진다.
101
109
  - Native는 `title`·`description`·`accessibilityLabel` 중 하나는 있어야 한다. 모두 없으면 `TypeError`를 던진다.
@@ -127,3 +127,26 @@ TextField와 같은 테두리·글꼴·placeholder 색을 입힌다. 라벨·도
127
127
  - Native `TextField`·`Field`의 `error`·`description`은 `string`이라 `exactOptionalPropertyTypes`에서 `undefined`를 받지
128
128
  않는다(TS2375). 위 예처럼 조건부 spread로 넘긴다. Web은 `ReactNode`라 `undefined`를 그대로 넘겨도 된다.
129
129
  - Web `Field`는 `controlId`가 필수이고, Native `Field`에는 `controlId`가 없다. 공용 코드에서 같은 props 객체를 넘기지 않는다.
130
+
131
+ ### 날짜 조각 직접 입력 (실험·미게시)
132
+
133
+ 알고 있는 날짜를 년·월·일로 직접 편집하려면 `DateEntry`를 사용한다.
134
+ Web `@hjmds/react/date-entry`, Native `@hjmds/react-native/date-entry`의 Field 확장이다.
135
+ 원문 초안·입력 순서·오류 대상을 공유하며 입력은 기존 TextField로 렌더링한다.
136
+ [날짜 직접 입력 지침](../compositions/date-entry.md)에 배치·props·날짜 파싱 소유권이 있다.
137
+ 달력에서 날짜를 고르는 경우에는 기존 DatePicker를 쓴다.
138
+
139
+
140
+ ### 그룹 오류를 한 번 표시할 때
141
+
142
+ Web TextField의 `aria-invalid={true}`는 외부 오류 ID를 `aria-describedby`로 연결하는 경우에도
143
+ 오류 테두리를 표시한다. Native TextField/TextArea의 `invalid`는 인라인 error 문구 없이 오류
144
+ 테두리를 표시하는 미게시 옵션이다. 같은 오류를 `accessibilityHint`로 연결하고 그룹 안내를
145
+ 별도로 보여 준다. error가 있으면 해당 문구가 hint보다 우선하고, hint가 없으면 description을 쓴다.
146
+ 2026-10-07 날짜 직접 입력의 큰 글자 오류가 세 번 반복된 관찰에 따라 이 경로를 추가했다.
147
+
148
+ ### 관련 입력 그룹 (실험·미게시)
149
+
150
+ 여러 입력이 한 질문에 답하면 `FieldGroup`을 사용한다. Form의 제출 경계와 구분하며
151
+ `@hjmds/react/field-group`, `@hjmds/react-native/field-group`에서 제공한다.
152
+ [관련 입력 묶음 지침](../compositions/field-group.md)의 renderField 연결·오류·잠금 규칙을 따른다.
@@ -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
  - 근거: [Form](../../form.md), recipe `formRecipe`(`src/form.ts`)
9
9
  - 스토리북: `배포/컴포넌트/입력/입력 양식`
10
10
 
@@ -14,6 +14,11 @@
14
14
  **제출 세션**(중복 제출 차단, 진행 중 잠금, 폼 단위 오류 표시)만 소유한다. 값·검증·dirty 판단은
15
15
  제품(React Hook Form 등)이 소유한다.
16
16
 
17
+ Form은 주소·연락처처럼 관련 입력을 이름 붙여 묶는 일반 fieldset API가 아니다.
18
+ Web 내부 fieldset은 제출 중 잠금을 위한 것이며 그룹 legend를 제공하지 않는다.
19
+ 그룹 제목을 만들려고 Form을 중첩하지 않는다. 일반 관련 입력은 [FieldGroup](../compositions/field-group.md) 실험에서 제공한다. 2026-10-07 GOV.UK 주소 그룹과 대조해
20
+ 제출 경계와 입력 그룹을 구분했다. 선택 묶음은 CheckboxGroup/RadioGroup, 날짜 부분 입력은 DateEntry를 사용한다.
21
+
17
22
  ## 쓰지 않을 때
18
23
 
19
24
  | 상황 | 대신 쓸 것 |