@nurihaus/web-design-system 3.7.0 → 3.9.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 (143) hide show
  1. package/README.md +15 -4
  2. package/dist/ai/components.md +658 -12
  3. package/dist/ai/motion.md +431 -0
  4. package/dist/ai/tokens.md +93 -4
  5. package/dist/avatar.js +5 -3
  6. package/dist/bottom-bar.js +261 -0
  7. package/dist/bottom-nav.js +10 -57
  8. package/dist/breadcrumb.js +7 -5
  9. package/dist/callout.js +5 -3
  10. package/dist/card.js +278 -0
  11. package/dist/check-box.js +7 -5
  12. package/dist/chip-group.js +6 -4
  13. package/dist/chunk-1176.js +24 -0
  14. package/dist/chunk-3274.js +71 -0
  15. package/dist/chunk-345.js +72 -0
  16. package/dist/chunk-3547.js +56 -0
  17. package/dist/chunk-6459.js +107 -0
  18. package/dist/{chunk-6364.js → chunk-7335.js} +2 -67
  19. package/dist/chunk-7653.js +4 -0
  20. package/dist/chunk-910.js +26 -13
  21. package/dist/chunk-9497.js +66 -0
  22. package/dist/collapsible-menu.js +6 -4
  23. package/dist/components/badge/badge-props.d.ts +4 -4
  24. package/dist/components/badge/badge.stories.d.ts +1 -1
  25. package/dist/components/bottom-bar/base-bottom-bar.d.ts +4 -0
  26. package/dist/components/bottom-bar/bottom-bar-props.d.ts +1 -0
  27. package/dist/components/bottom-bar/bottom-bar-type.d.ts +3 -0
  28. package/dist/components/bottom-bar/bottom-bar.d.ts +6 -0
  29. package/dist/components/bottom-bar/bottom-bar.stories.d.ts +282 -0
  30. package/dist/components/button/button-type.d.ts +3 -1
  31. package/dist/components/button/button.stories.d.ts +1 -0
  32. package/dist/components/card/base-card.d.ts +3 -0
  33. package/dist/components/card/card-props.d.ts +9 -0
  34. package/dist/components/card/card-type.d.ts +8 -0
  35. package/dist/components/card/card.d.ts +5 -0
  36. package/dist/components/card/card.stories.d.ts +24 -0
  37. package/dist/components/description-list/base-description-list.d.ts +4 -0
  38. package/dist/components/description-list/description-list-props.d.ts +1 -0
  39. package/dist/components/description-list/description-list-type.d.ts +13 -0
  40. package/dist/components/description-list/description-list.d.ts +5 -0
  41. package/dist/components/description-list/description-list.stories.d.ts +20 -0
  42. package/dist/components/empty-state/base-empty-state.d.ts +4 -0
  43. package/dist/components/empty-state/empty-state-props.d.ts +10 -0
  44. package/dist/components/empty-state/empty-state-type.d.ts +14 -0
  45. package/dist/components/empty-state/empty-state.d.ts +5 -0
  46. package/dist/components/empty-state/empty-state.stories.d.ts +300 -0
  47. package/dist/components/icon/icon-paths.d.ts +4 -0
  48. package/dist/components/icon/icon.stories.d.ts +1 -1
  49. package/dist/components/icon-button/base-icon-button.d.ts +3 -0
  50. package/dist/components/icon-button/icon-button-props.d.ts +12 -0
  51. package/dist/components/icon-button/icon-button-type.d.ts +16 -0
  52. package/dist/components/icon-button/icon-button.d.ts +5 -0
  53. package/dist/components/icon-button/icon-button.stories.d.ts +31 -0
  54. package/dist/components/list/base-list.d.ts +7 -0
  55. package/dist/components/list/list-group.d.ts +5 -0
  56. package/dist/components/list/list-group.stories.d.ts +14 -0
  57. package/dist/components/list/list-props.d.ts +2 -0
  58. package/dist/components/list/list-row.d.ts +5 -0
  59. package/dist/components/list/list-row.stories.d.ts +304 -0
  60. package/dist/components/list/list-type.d.ts +25 -0
  61. package/dist/components/media/base-media.d.ts +16 -0
  62. package/dist/components/media/media-props.d.ts +5 -0
  63. package/dist/components/media/media-thumbnail.d.ts +4 -0
  64. package/dist/components/media/media-thumbnail.stories.d.ts +34 -0
  65. package/dist/components/media/media-type.d.ts +43 -0
  66. package/dist/components/media/media-viewer.d.ts +4 -0
  67. package/dist/components/media/media-viewer.stories.d.ts +24 -0
  68. package/dist/components/message/base-message.d.ts +11 -0
  69. package/dist/components/message/message-event.d.ts +6 -0
  70. package/dist/components/message/message-props.d.ts +17 -0
  71. package/dist/components/message/message-thread.d.ts +6 -0
  72. package/dist/components/message/message-type.d.ts +21 -0
  73. package/dist/components/message/message.d.ts +5 -0
  74. package/dist/components/message/message.stories.d.ts +313 -0
  75. package/dist/components/message-composer/base-message-composer.d.ts +11 -0
  76. package/dist/components/message-composer/message-composer-props.d.ts +1 -0
  77. package/dist/components/message-composer/message-composer-type.d.ts +31 -0
  78. package/dist/components/message-composer/message-composer.d.ts +5 -0
  79. package/dist/components/message-composer/message-composer.stories.d.ts +46 -0
  80. package/dist/components/motion/motion-provider.d.ts +5 -0
  81. package/dist/components/motion/motion-type.d.ts +6 -0
  82. package/dist/components/motion/motion.d.ts +2 -0
  83. package/dist/components/motion/motion.stories.d.ts +23 -0
  84. package/dist/components/notice/base-notice.d.ts +4 -0
  85. package/dist/components/notice/notice-props.d.ts +16 -0
  86. package/dist/components/notice/notice-type.d.ts +21 -0
  87. package/dist/components/notice/notice.d.ts +5 -0
  88. package/dist/components/notice/notice.stories.d.ts +304 -0
  89. package/dist/components/radio/base-radio.d.ts +2 -1
  90. package/dist/components/radio/radio-props.d.ts +9 -1
  91. package/dist/components/radio/radio-type.d.ts +9 -0
  92. package/dist/components/radio/radio.stories.d.ts +10 -0
  93. package/dist/components/tag/base-tag.d.ts +8 -0
  94. package/dist/components/tag/tag-props.d.ts +1 -0
  95. package/dist/components/tag/tag-type.d.ts +20 -0
  96. package/dist/components/tag/tag.d.ts +4 -0
  97. package/dist/components/tag/tag.stories.d.ts +14 -0
  98. package/dist/components.css +1330 -171
  99. package/dist/cta-button.js +38 -21
  100. package/dist/data-table.js +5 -3
  101. package/dist/description-list.js +274 -0
  102. package/dist/drawer.js +7 -5
  103. package/dist/drop-down.js +8 -6
  104. package/dist/empty-state.js +300 -0
  105. package/dist/helpers/utils/fillLinkChild.d.ts +6 -0
  106. package/dist/icon-button.js +338 -0
  107. package/dist/icon-text-badge.js +11 -9
  108. package/dist/icon-text-option-badge.js +8 -6
  109. package/dist/icon.js +7 -4
  110. package/dist/index.d.ts +30 -2
  111. package/dist/index.js +2482 -223
  112. package/dist/list-group.js +253 -0
  113. package/dist/list-row.js +308 -0
  114. package/dist/loading.js +6 -4
  115. package/dist/media-thumbnail.js +412 -0
  116. package/dist/media-viewer.js +274 -0
  117. package/dist/message-composer.js +399 -0
  118. package/dist/message-event.js +216 -0
  119. package/dist/message-thread.js +216 -0
  120. package/dist/message.js +271 -0
  121. package/dist/modal-with-double-button.js +15 -11
  122. package/dist/modal.js +9 -7
  123. package/dist/motion.js +52 -0
  124. package/dist/notice.js +346 -0
  125. package/dist/option-badge.js +11 -9
  126. package/dist/pagination.js +8 -6
  127. package/dist/radio.js +114 -30
  128. package/dist/skeleton.js +4 -2
  129. package/dist/step-progress.js +4 -2
  130. package/dist/styles.css +1215 -130
  131. package/dist/switch.js +6 -4
  132. package/dist/tabs.js +5 -3
  133. package/dist/tag.js +311 -0
  134. package/dist/text-area.js +8 -6
  135. package/dist/text-field.js +8 -6
  136. package/dist/text.js +5 -3
  137. package/dist/toast.js +15 -11
  138. package/dist/tokens/generated/index.d.ts +1 -0
  139. package/dist/tokens/generated/motion.d.ts +153 -0
  140. package/dist/tokens.css +122 -2
  141. package/dist/tokens.js +156 -1
  142. package/dist/tooltip.js +6 -4
  143. package/package.json +67 -1
@@ -22,7 +22,7 @@ import '@nurihaus/web-design-system/components.css';
22
22
 
23
23
  **size 는 전역 이름이다.** `xs · s · m · l · xl` 다섯 개뿐이고, 컴포넌트는 그중 지원하는 것만 받는다(props 표 참고). 같은 글자는 한 줄에 나란히 놓이는 컨트롤(CTAButton · DropDown · TextField)에서 같은 높이다. px 는 `tokens.md` 의 `--control-height-*` 같은 변수에 있고, 컴포넌트에 숫자로 높이를 주지 않는다.
24
24
 
25
- **색은 variant 가 정한다.** CTAButton · OptionBadge · IconTextBadge · IconTextOptionBadge 는 `variant` 로 글자·배경·테두리 색이 묶여서 정해진다. 색 prop 은 없다. 색을 받는 컴포넌트는 Text(`color`, 토큰 이름만) 하나다. Spinner · InlineLoading · OverlayLoading 은 CSS `color` 를 따라가므로 `className` 이나 `style` 의 `color` 로 바꾼다.
25
+ **색은 variant 가 정한다.** CTAButton · IconButton · OptionBadge · IconTextBadge · IconTextOptionBadge 는 `variant` 로 글자·배경·테두리 색이 묶여서 정해진다. 색 prop 은 없다. 색을 받는 컴포넌트는 Text(`color`, 토큰 이름만) 하나다. Spinner · InlineLoading · OverlayLoading 은 CSS `color` 를 따라가므로 `className` 이나 `style` 의 `color` 로 바꾼다.
26
26
 
27
27
  **커스텀은 `className` 과 `style` 로 한다.** 모든 컴포넌트가 둘을 받아 루트 요소에 붙인다(TextField 는 입력 상자, DataTable 은 `<table>`). variant 가 정한 색을 className 으로 덮으려면 `.cta-button.variant-primary` 보다 구체적인 선택자가 필요하다. 색·간격·글자 값은 `tokens.md` 의 변수만 쓴다.
28
28
 
@@ -34,7 +34,7 @@ import '@nurihaus/web-design-system/components.css';
34
34
 
35
35
  | 컴포넌트 | 보내는 때 | action | value |
36
36
  | --- | --- | --- | --- |
37
- | CTAButton | 누를 때 | `click` | 없음 |
37
+ | CTAButton · IconButton | 누를 때 | `click` | 없음 |
38
38
  | DataTable | `onClickRow` 가 있는 헤더 칸을 누를 때 | `click` | 그 칸의 `id` |
39
39
  | Modal · Drawer | 열고 닫을 때 | `open` · `close` | 없음 |
40
40
  | Tabs · DropDown · ChipGroup · Radio | 고를 때 | `change` | 탭 id · 옵션 value · 칩 id · 선택지 id |
@@ -45,10 +45,15 @@ import '@nurihaus/web-design-system/components.css';
45
45
  | Breadcrumb | 현재 위치가 아닌 링크를 누를 때 | `navigate` | 항목 `id` |
46
46
  | CollapsibleMenu | 링크·버튼 항목을 고를 때. 그룹 여닫기는 보내지 않는다 | `navigate` | 항목 `key` |
47
47
  | Toast · Tooltip | 뜰 때 | `show` | 없음 |
48
+ | ListRow | 링크 행·`onClick` 행을 누를 때. 보기만 하는 행은 보내지 않는다 | `navigate`(링크) · `click`(버튼) | 없음 |
49
+ | Tag | 지우기 버튼을 누를 때 | `click` | 없음 |
50
+ | MessageComposer | 보내기가 실제로 될 때. 막힌 보내기는 보내지 않는다 | `click` | 없음 |
51
+ | MediaThumbnail | 눌러서 보기를 열 때 | `open` | 없음 |
52
+ | MediaViewer | 열고 닫을 때 | `open` · `close` | 없음 |
48
53
 
49
54
  `value` 에는 개발자가 정한 id 만 들어가고 사용자가 입력한 글자는 들어가지 않는다. TextField · TextArea 가 값을 싣지 않는 것도 그래서다. 더 붙일 값은 `trackProps` 로 준다. `LogProvider` 가 없거나 `track` 이 없으면 아무것도 보내지 않는다. 컴포넌트 밖의 로그(화면 진입 등)는 `useLog` 를 쓴다.
50
55
 
51
- Avatar · OptionBadge · IconTextBadge · IconTextOptionBadge · Skeleton · Text · Icon · StepProgress · Spinner · InlineLoading · OverlayLoading 은 상호작용 핸들러가 없어 `track` 을 받지 않는다. ModalWithDoubleButton 은 `left` · `right` 가 CTAButton props 라 거기에 `track` 을 준다.
56
+ Avatar · OptionBadge · IconTextBadge · IconTextOptionBadge · Skeleton · Text · Icon · StepProgress · Spinner · InlineLoading · OverlayLoading · ListGroup · DescriptionList · EmptyState · MessageThread · Message · MessageEvent · BottomBar 는 상호작용 핸들러가 없어 `track` 을 받지 않는다. Card 도 받지 않는다 — 누르는 카드는 asChild 로 넘긴 링크·버튼이 로그를 맡는다. ModalWithDoubleButton 은 `left` · `right` 가 CTAButton props 라 거기에 `track` 을 준다. Notice 는 `action` 에 `track` 을 주면 그 버튼(CTAButton)이 보낸다. EmptyState 의 버튼은 넘긴 CTAButton 에 준다.
52
57
 
53
58
  **없는 것은 지어내지 않는다.** 이 문서 끝의 "아직 없는 것" 표에 있는 컴포넌트는 없다. 없는 prop · variant · 토큰 이름도 만들지 않는다. 틀린 variant 나 size 값은 타입 오류가 난다.
54
59
 
@@ -56,6 +61,7 @@ Avatar · OptionBadge · IconTextBadge · IconTextOptionBadge · Skeleton · Tex
56
61
 
57
62
  - [Avatar](#avatar)
58
63
  - [CTAButton](#ctabutton)
64
+ - [IconButton](#iconbutton)
59
65
  - [Callout](#callout)
60
66
  - [OptionBadge](#optionbadge)
61
67
  - [IconTextBadge](#icontextbadge)
@@ -87,7 +93,22 @@ Avatar · OptionBadge · IconTextBadge · IconTextOptionBadge · Skeleton · Tex
87
93
  - [BottomNav](#bottomnav)
88
94
  - [StepProgress](#stepprogress)
89
95
  - [ChipGroup](#chipgroup)
96
+ - [Card](#card)
97
+ - [ListGroup](#listgroup)
98
+ - [ListRow](#listrow)
99
+ - [DescriptionList](#descriptionlist)
100
+ - [Notice](#notice)
101
+ - [EmptyState](#emptystate)
102
+ - [Tag](#tag)
103
+ - [MessageThread](#messagethread)
104
+ - [Message](#message)
105
+ - [MessageEvent](#messageevent)
106
+ - [MessageComposer](#messagecomposer)
107
+ - [BottomBar](#bottombar)
108
+ - [MediaThumbnail](#mediathumbnail)
109
+ - [MediaViewer](#mediaviewer)
90
110
  - [LogProvider](#logprovider)
111
+ - [MotionProvider](#motionprovider)
91
112
 
92
113
  ## Avatar
93
114
 
@@ -104,6 +125,8 @@ import { Avatar } from '@nurihaus/web-design-system'; // 루트. 첫
104
125
  <Avatar size="m" src="https://placehold.co/80x80" alt="프로필 이미지" />
105
126
  ```
106
127
 
128
+ **모션.** 없음. onClick 을 받지 않는 표시용 이미지다. 누를 수 있게 하려면 쓰는 쪽이 버튼으로 감싸고 그 버튼이 press 를 받는다.
129
+
107
130
  ### props
108
131
 
109
132
  | prop | 타입 | 필수 | 기본값 |
@@ -124,7 +147,7 @@ import { CTAButton } from '@nurihaus/web-design-system/cta-button'; // 이 컴
124
147
  import { CTAButton } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
125
148
  ```
126
149
 
127
- **언제 쓴다.** 저장, 확인, 다음 단계처럼 사용자가 실행하는 동작 버튼에 쓴다. `variant`로 강조 단계를 고르고, `loading`이 true면 `text` 대신 Spinner가 표시된다. Google·Apple·Facebook 로그인 버튼은 `variant="social"`(흰 바탕·검정 글자, 공급자 가이드)에 공급자 로고를 `leftIcon`으로 준다. `callout`을 주면 버튼 오른쪽 위 모서리에 말풍선(Callout)을 끼운다(예: "최근 사용") — 버튼 글자는 움직이지 않고 자리는 DS 가 잡는다. 말풍선 글자도 버튼 안이라 버튼의 접근 가능한 이름에 이어진다(스크린 리더도 "최근 사용"을 듣는다).
150
+ **언제 쓴다.** 저장, 확인, 다음 단계처럼 사용자가 실행하는 동작 버튼에 쓴다. `variant`로 강조 단계를 고르고, `loading`이 true면 `text` 대신 Spinner가 표시된다. Google·Apple·Facebook 로그인 버튼은 `variant="social"`(흰 바탕·검정 글자, 공급자 가이드)에 공급자 로고를 `leftIcon`으로 준다. `callout`을 주면 버튼 오른쪽 위 모서리에 말풍선(Callout)을 끼운다(예: "최근 사용") — 버튼 글자는 움직이지 않고 자리는 DS 가 잡는다. 말풍선 글자도 버튼 안이라 버튼의 접근 가능한 이름에 이어진다(스크린 리더도 "최근 사용"을 듣는다). 다른 화면으로 옮겨 가는 버튼은 `asChild` 를 주고 링크 요소(`<a>`·라우터 Link) 하나를 children 으로 준다 — 그 링크가 버튼 모양이 되고 글자·아이콘은 링크 안에 들어간다. 링크에는 `loading` 을 쓰지 않는다.
128
151
 
129
152
  **쓰지 않는다.** 확인과 취소 두 버튼을 가진 대화상자는 버튼을 직접 배치하지 말고 ModalWithDoubleButton을 쓴다. 화면 안의 패널 전환은 Tabs, 사이드 메뉴 항목은 CollapsibleMenu를 쓴다.
130
153
 
@@ -138,16 +161,23 @@ import { CTAButton } from '@nurihaus/web-design-system'; // 루트.
138
161
  leftIcon={<img src="/icons/google.svg" alt="" />}
139
162
  callout="최근 사용"
140
163
  />
164
+ <CTAButton asChild variant="primary" size="l" text="홈으로">
165
+ <a href="/" />
166
+ </CTAButton>
141
167
  </>
142
168
  ```
143
169
 
170
+ **모션.** press · reject · appear(opt) · busy(`loading`). 토큰 값과 규칙은 `motion.md`.
171
+
144
172
  ### props
145
173
 
146
174
  | prop | 타입 | 필수 | 기본값 |
147
175
  | --- | --- | --- | --- |
176
+ | `asChild` | `boolean` | | |
148
177
  | `size` | `'xs' \| 's' \| 'm' \| 'l' \| 'xl'` | | `'m'` |
149
178
  | `track` | `string` | | |
150
179
  | `text` | `ReactNode` | 예 | |
180
+ | `children` | `ReactNode` | | |
151
181
  | `variant` | `'neutral' \| 'primary' \| 'outlined' \| 'secondary' \| 'ghost' \| 'confirm' \| 'social'` | | `'primary'` |
152
182
  | `leftIcon` | `ReactNode` | | |
153
183
  | `rightIcon` | `ReactNode` | | |
@@ -155,7 +185,44 @@ import { CTAButton } from '@nurihaus/web-design-system'; // 루트.
155
185
  | `loading` | `boolean` | | |
156
186
  | `trackProps` | `Record<string, string \| number \| boolean>` | | |
157
187
 
158
- 이 밖에 React·HTML 기본 속성 276개를 받는다.
188
+ 이 밖에 React·HTML 기본 속성 275개를 받는다.
189
+
190
+ ## IconButton
191
+
192
+ ```ts
193
+ import { IconButton } from '@nurihaus/web-design-system/icon-button'; // 이 컴포넌트만 싣는다 (권장)
194
+ import { IconButton } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
195
+ ```
196
+
197
+ **언제 쓴다.** 글자 없이 아이콘 하나로 뜻이 통하는 버튼에 쓴다(뒤로, 알림, 첨부, 떠 있는 추가 버튼). `icon` 은 Icon 이름, `label` 은 버튼의 이름(aria-label)이라 꼭 준다. `variant` 는 `ghost`(기본, 바탕 없이 회색 아이콘, 누르는 동안 회색 원)·`secondary`(연보라)·`primary`(진보라). `size` 는 `s`(36)·`m`(44, 기본)·`l`(56)이고 보이는 크기가 작아도 누르는 자리는 44 이상이다. 다른 화면으로 옮겨 가는 버튼은 `asChild` 를 주고 링크 요소 하나를 children 으로 준다 — 그 링크가 버튼 모양이 되고 아이콘은 링크 안에 들어간다. 떠 있는 버튼의 자리·그림자는 쓰는 쪽이 `className` 으로 준다(`--shadow-floating`). `className`·`style` 은 버튼(또는 링크)에 붙는다.
198
+
199
+ **쓰지 않는다.** 글자가 있는 버튼은 CTAButton 을 쓴다. 칩의 지우기는 Tag 의 `onRemove`, 대화 보내기는 MessageComposer 를 쓴다.
200
+
201
+ ```tsx
202
+ <>
203
+ <IconButton icon="caret-left" label="뒤로" onClick={() => {}} />
204
+ <IconButton asChild icon="plus" label="새로 만들기" variant="primary" size="l">
205
+ <a href="/new" />
206
+ </IconButton>
207
+ </>
208
+ ```
209
+
210
+ **모션.** press. 토큰 값과 규칙은 `motion.md`.
211
+
212
+ ### props
213
+
214
+ | prop | 타입 | 필수 | 기본값 |
215
+ | --- | --- | --- | --- |
216
+ | `asChild` | `boolean` | | |
217
+ | `size` | `'s' \| 'm' \| 'l'` | | `defaultSize` |
218
+ | `label` | `string` | 예 | |
219
+ | `track` | `string` | | |
220
+ | `children` | `ReactNode` | | |
221
+ | `variant` | `'primary' \| 'secondary' \| 'ghost'` | | `'ghost'` |
222
+ | `trackProps` | `Record<string, string \| number \| boolean>` | | |
223
+ | `icon` | `'link' \| 'circle' \| 'image' \| 'warning' \| 'x' \| 'house' \| 'megaphone-simple' \| 'chats-circle' \| 'user-circle' \| 'bell' \| 'caret-left' \| 'magnifying-glass' \| 'dots-three' \| 'gear-six' \| 'chat-circle' \| 'paper-plane-tilt' \| 'plus' \| 'camera' \| 'video-camera' \| 'play' \| 'upload-simple' \| 'calendar-blank' \| 'clock' \| 'map-pin' \| 'truck' \| 'ticket' \| 'qr-code' \| 'sparkle' \| 'gift' \| 'wallet' \| 'bank' \| 'credit-card' \| 'check-circle' \| 'check' \| 'info' \| 'question' \| 'lock' \| 'pencil-simple' \| 'trash' \| 'copy' \| 'share-network' \| 'eye' \| 'sliders-horizontal' \| 'heart' \| 'star' \| 'globe' \| 'sign-out' \| 'caret-right' \| 'arrow-right' \| 'instagram-logo' \| 'tiktok-logo' \| 'youtube-logo'` | 예 | |
224
+
225
+ 이 밖에 React·HTML 기본 속성 275개를 받는다.
159
226
 
160
227
  ## Callout
161
228
 
@@ -176,6 +243,8 @@ import { Callout } from '@nurihaus/web-design-system'; // 루트.
176
243
  </>
177
244
  ```
178
245
 
246
+ **모션.** enter · attention. 토큰 값과 규칙은 `motion.md`.
247
+
179
248
  ### props
180
249
 
181
250
  | prop | 타입 | 필수 | 기본값 |
@@ -192,7 +261,7 @@ import { OptionBadge } from '@nurihaus/web-design-system/option-badge'; // 이
192
261
  import { OptionBadge } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
193
262
  ```
194
263
 
195
- **언제 쓴다.** 상태값이나 분류처럼 짧은 값 하나(`option`)를 라벨로 보여줄 때 쓴다. `variant`로 색을 고른다(`neutral`·`primary`·`danger`·`outlined`). 색 prop 은 없다. `tooltip`을 주면 마우스를 올렸을 때 설명이 뜬다. 대상에 붙어 늘 떠 있는 말풍선은 Callout 을 쓴다.
264
+ **언제 쓴다.** 상태값이나 분류처럼 짧은 값 하나(`option`)를 라벨로 보여줄 때 쓴다. `variant`로 색을 고른다(`neutral`·`primary`·`danger`·`success`·`warning`·`outlined`). 색 prop 은 없다. `tooltip`을 주면 마우스를 올렸을 때 설명이 뜬다. 대상에 붙어 늘 떠 있는 말풍선은 Callout 을 쓴다.
196
265
 
197
266
  **쓰지 않는다.** 아이콘이 함께 필요하면 IconTextBadge를, 아이콘과 텍스트 오른쪽에 수치 같은 부가 값까지 붙여야 하면 IconTextOptionBadge를 쓴다. 클릭 동작이 필요한 요소에는 CTAButton을 쓴다.
198
267
 
@@ -200,11 +269,13 @@ import { OptionBadge } from '@nurihaus/web-design-system'; // 루트
200
269
  <OptionBadge option="진행 중" variant="primary" size="s" width="fit-content" />
201
270
  ```
202
271
 
272
+ **모션.** attention(opt). 토큰 값과 규칙은 `motion.md`.
273
+
203
274
  ### props
204
275
 
205
276
  | prop | 타입 | 필수 | 기본값 |
206
277
  | --- | --- | --- | --- |
207
- | `variant` | `'neutral' \| 'primary' \| 'danger' \| 'outlined'` | | `'neutral'` |
278
+ | `variant` | `'neutral' \| 'primary' \| 'danger' \| 'success' \| 'warning' \| 'outlined'` | | `'neutral'` |
208
279
  | `width` | `string \| number` | | `'100%'` |
209
280
  | `size` | `'xs' \| 's' \| 'm' \| 'l' \| 'xl'` | | `'xl'` |
210
281
  | `option` | `ReactNode` | 예 | |
@@ -236,11 +307,13 @@ import { IconTextBadge } from '@nurihaus/web-design-system'; // 루
236
307
  />
237
308
  ```
238
309
 
310
+ **모션.** attention(opt). 토큰 값과 규칙은 `motion.md`.
311
+
239
312
  ### props
240
313
 
241
314
  | prop | 타입 | 필수 | 기본값 |
242
315
  | --- | --- | --- | --- |
243
- | `variant` | `'neutral' \| 'primary' \| 'danger' \| 'outlined'` | | `'neutral'` |
316
+ | `variant` | `'neutral' \| 'primary' \| 'danger' \| 'success' \| 'warning' \| 'outlined'` | | `'neutral'` |
244
317
  | `width` | `string \| number` | | `'100%'` |
245
318
  | `size` | `'xs' \| 's' \| 'm' \| 'l' \| 'xl'` | | `'xl'` |
246
319
  | `icon` | `ReactNode` | 예 | |
@@ -273,11 +346,13 @@ import { IconTextOptionBadge } from '@nurihaus/web-design-system'; /
273
346
  />
274
347
  ```
275
348
 
349
+ **모션.** attention(opt). 토큰 값과 규칙은 `motion.md`.
350
+
276
351
  ### props
277
352
 
278
353
  | prop | 타입 | 필수 | 기본값 |
279
354
  | --- | --- | --- | --- |
280
- | `variant` | `'neutral' \| 'primary' \| 'danger' \| 'outlined'` | | `'neutral'` |
355
+ | `variant` | `'neutral' \| 'primary' \| 'danger' \| 'success' \| 'warning' \| 'outlined'` | | `'neutral'` |
281
356
  | `width` | `string \| number` | | `'100%'` |
282
357
  | `size` | `'xs' \| 's' \| 'm' \| 'l' \| 'xl'` | | `'xl'` |
283
358
  | `icon` | `ReactNode` | 예 | |
@@ -317,6 +392,8 @@ import { DataTable } from '@nurihaus/web-design-system'; // 루트.
317
392
  />
318
393
  ```
319
394
 
395
+ **모션.** press · attention(opt). 토큰 값과 규칙은 `motion.md`.
396
+
320
397
  ### props
321
398
 
322
399
  | prop | 타입 | 필수 | 기본값 |
@@ -358,6 +435,8 @@ import { TextField } from '@nurihaus/web-design-system'; // 루트.
358
435
  />
359
436
  ```
360
437
 
438
+ **모션.** 없음. 텍스트 입력은 press 의 avoid 대상이다. 글자가 바뀌는 것은 선택이 아니라 confirm 도 아니다..
439
+
361
440
  ### props
362
441
 
363
442
  | prop | 타입 | 필수 | 기본값 |
@@ -387,9 +466,11 @@ import { Radio } from '@nurihaus/web-design-system'; // 루트. 첫
387
466
 
388
467
  **언제 쓴다.** 몇 개의 선택지를 모두 펼쳐 보여주고 그중 하나만 고르게 할 때 쓴다. 선택 상태는 항상 부모가 가지므로 `onChange`에서 받은 id로 `selected`를 갱신해야 선택이 바뀐다.
389
468
 
469
+ `variant="chip"`이면 선택지를 칩으로 가로로 늘어놓는다. 폼 안에서 짧은 선택지 몇 개를 고를 때 쓴다. 처음에는 아무것도 고르지 않은 채로 둘 수 있다. `label`은 선택지 묶음의 `aria-label`이 된다.
470
+
390
471
  `track`을 주면 선택지를 누를 때 `change`와 그 선택지 `id`를 `value`에 담아 사용 로그로 보낸다.
391
472
 
392
- **쓰지 않는다.** 여러 개를 동시에 고르면 Checkbox를 쓴다. 선택지가 많아 모두 펼치기 어려우면 DropDown을 쓴다.
473
+ **쓰지 않는다.** 여러 개를 동시에 고르면 Checkbox를 쓴다. 선택지가 많아 모두 펼치기 어려우면 DropDown을 쓴다. 목록을 거르는 필터 칩은 ChipGroup을 쓴다(늘 하나가 골라져 있다).
393
474
 
394
475
  ```tsx
395
476
  <Radio
@@ -403,10 +484,14 @@ import { Radio } from '@nurihaus/web-design-system'; // 루트. 첫
403
484
  />
404
485
  ```
405
486
 
487
+ **모션.** press · confirm. 토큰 값과 규칙은 `motion.md`.
488
+
406
489
  ### props
407
490
 
408
491
  | prop | 타입 | 필수 | 기본값 |
409
492
  | --- | --- | --- | --- |
493
+ | `variant` | `'default' \| 'chip'` | | `'default'` |
494
+ | `label` | `string` | | |
410
495
  | `options` | `{ id: string; label: ReactNode; disabled?: boolean \| undefined; }[]` | 예 | |
411
496
  | `selected` | `string \| null` | | |
412
497
  | `onChange` | `((id: string \| null) => void)` | | |
@@ -432,6 +517,8 @@ import { Checkbox } from '@nurihaus/web-design-system'; // 루트.
432
517
  <Checkbox id="agree-terms" label="약관에 동의합니다" checked={false} onChange={() => {}} />
433
518
  ```
434
519
 
520
+ **모션.** press · confirm. 토큰 값과 규칙은 `motion.md`.
521
+
435
522
  ### props
436
523
 
437
524
  | prop | 타입 | 필수 | 기본값 |
@@ -466,6 +553,8 @@ import { Switch } from '@nurihaus/web-design-system'; // 루트. 첫
466
553
  <Switch id="push-alarm" label="푸시 알림 받기" checked={true} onChange={() => {}} />
467
554
  ```
468
555
 
556
+ **모션.** press · confirm. 토큰 값과 규칙은 `motion.md`.
557
+
469
558
  ### props
470
559
 
471
560
  | prop | 타입 | 필수 | 기본값 |
@@ -500,6 +589,8 @@ import { TextArea } from '@nurihaus/web-design-system'; // 루트.
500
589
  <TextArea name="memo" title="메모" placeholder="내용을 입력하세요" rows={4} />
501
590
  ```
502
591
 
592
+ **모션.** 없음. 텍스트 입력은 press 의 avoid 대상이다. 글자가 바뀌는 것은 선택이 아니라 confirm 도 아니다.
593
+
503
594
  ### props
504
595
 
505
596
  | prop | 타입 | 필수 | 기본값 |
@@ -534,6 +625,8 @@ import { Toast } from '@nurihaus/web-design-system'; // 루트. 첫
534
625
  <Toast open title="저장했습니다" description="변경 사항이 반영됐습니다." variant="success" onOpenChange={() => {}} />
535
626
  ```
536
627
 
628
+ **모션.** enter · exit. 토큰 값과 규칙은 `motion.md`.
629
+
537
630
  ### props
538
631
 
539
632
  | prop | 타입 | 필수 | 기본값 |
@@ -543,7 +636,7 @@ import { Toast } from '@nurihaus/web-design-system'; // 루트. 첫
543
636
  | `title` | `ReactNode` | 예 | |
544
637
  | `track` | `string` | | |
545
638
  | `onPause` | `(() => void)` | | |
546
- | `variant` | `'secondary' \| 'default' \| 'success' \| 'warning' \| 'error'` | | `'default'` |
639
+ | `variant` | `'success' \| 'warning' \| 'secondary' \| 'default' \| 'error'` | | `'default'` |
547
640
  | `trackProps` | `Record<string, string \| number \| boolean>` | | |
548
641
  | `onResume` | `(() => void)` | | |
549
642
  | `open` | `boolean` | | |
@@ -573,6 +666,8 @@ import { ToastProvider } from '@nurihaus/web-design-system'; // 루
573
666
  </ToastProvider>
574
667
  ```
575
668
 
669
+ **모션.** 없음. 토스트 자리(viewport)만 그린다. 모션은 각 Toast 가 받는다.
670
+
576
671
  ### props
577
672
 
578
673
  | prop | 타입 | 필수 | 기본값 |
@@ -599,6 +694,8 @@ import { Pagination } from '@nurihaus/web-design-system'; // 루트.
599
694
  <Pagination totalPages={10} page={3} onPageChange={() => {}} />
600
695
  ```
601
696
 
697
+ **모션.** press. 토큰 값과 규칙은 `motion.md`.
698
+
602
699
  ### props
603
700
 
604
701
  | prop | 타입 | 필수 | 기본값 |
@@ -638,6 +735,8 @@ import { Modal } from '@nurihaus/web-design-system'; // 루트. 첫
638
735
  </Modal>
639
736
  ```
640
737
 
738
+ **모션.** enter · exit. 토큰 값과 규칙은 `motion.md`.
739
+
641
740
  ### props
642
741
 
643
742
  | prop | 타입 | 필수 | 기본값 |
@@ -676,6 +775,8 @@ import { Drawer } from '@nurihaus/web-design-system'; // 루트. 첫
676
775
  </Drawer>
677
776
  ```
678
777
 
778
+ **모션.** enter · exit. 토큰 값과 규칙은 `motion.md`.
779
+
679
780
  ### props
680
781
 
681
782
  | prop | 타입 | 필수 | 기본값 |
@@ -713,6 +814,8 @@ import { Skeleton } from '@nurihaus/web-design-system'; // 루트.
713
814
  <Skeleton shape="text" lines={3} />
714
815
  ```
715
816
 
817
+ **모션.** busy(`animated`). 토큰 값과 규칙은 `motion.md`.
818
+
716
819
  ### props
717
820
 
718
821
  | prop | 타입 | 필수 | 기본값 |
@@ -748,6 +851,8 @@ import { Breadcrumb } from '@nurihaus/web-design-system'; // 루트.
748
851
  />
749
852
  ```
750
853
 
854
+ **모션.** press. 토큰 값과 규칙은 `motion.md`.
855
+
751
856
  ### props
752
857
 
753
858
  | prop | 타입 | 필수 | 기본값 |
@@ -790,6 +895,8 @@ import { DropDown } from '@nurihaus/web-design-system'; // 루트.
790
895
  />
791
896
  ```
792
897
 
898
+ **모션.** press · enter · exit · expand. 토큰 값과 규칙은 `motion.md`.
899
+
793
900
  ### props
794
901
 
795
902
  | prop | 타입 | 필수 | 기본값 |
@@ -860,6 +967,8 @@ import Link from 'next/link';
860
967
  />
861
968
  ```
862
969
 
970
+ **모션.** press · expand. 토큰 값과 규칙은 `motion.md`.
971
+
863
972
  ### props
864
973
 
865
974
  | prop | 타입 | 필수 | 기본값 |
@@ -894,6 +1003,8 @@ import { Text } from '@nurihaus/web-design-system'; // 루트. 첫
894
1003
  <Text as="h2" text="캠페인 목록" size="xl" weight="bold" color="grey-scale-900" />
895
1004
  ```
896
1005
 
1006
+ **모션.** 없음. 글자만 그린다.
1007
+
897
1008
  ### props
898
1009
 
899
1010
  | prop | 타입 | 필수 | 기본값 |
@@ -927,6 +1038,8 @@ import { Tooltip } from '@nurihaus/web-design-system'; // 루트.
927
1038
  <Tooltip trigger={<span>도움말</span>} content="최근 30일 기준입니다." direction="top" disabled={false} />
928
1039
  ```
929
1040
 
1041
+ **모션.** enter · exit. 토큰 값과 규칙은 `motion.md`.
1042
+
930
1043
  ### props
931
1044
 
932
1045
  | prop | 타입 | 필수 | 기본값 |
@@ -963,6 +1076,8 @@ import { TooltipProvider } from '@nurihaus/web-design-system'; //
963
1076
  </TooltipProvider>
964
1077
  ```
965
1078
 
1079
+ **모션.** 없음. DOM 을 그리지 않는다. 지연 시간만 나눠 준다.
1080
+
966
1081
  ### props
967
1082
 
968
1083
  | prop | 타입 | 필수 | 기본값 |
@@ -994,6 +1109,8 @@ import { Tabs } from '@nurihaus/web-design-system'; // 루트. 첫
994
1109
  />
995
1110
  ```
996
1111
 
1112
+ **모션.** press · progress. 토큰 값과 규칙은 `motion.md`.
1113
+
997
1114
  ### props
998
1115
 
999
1116
  | prop | 타입 | 필수 | 기본값 |
@@ -1045,6 +1162,8 @@ import { ModalWithDoubleButton } from '@nurihaus/web-design-system';
1045
1162
  />
1046
1163
  ```
1047
1164
 
1165
+ **모션.** enter · exit. 토큰 값과 규칙은 `motion.md`.
1166
+
1048
1167
  ### ModalWithDoubleButton.Row props
1049
1168
 
1050
1169
  | prop | 타입 | 필수 | 기본값 |
@@ -1096,6 +1215,8 @@ import { Spinner } from '@nurihaus/web-design-system'; // 루트.
1096
1215
  <Spinner style={{ color: 'var(--color-grey-scale-400)' }} />
1097
1216
  ```
1098
1217
 
1218
+ **모션.** busy. 토큰 값과 규칙은 `motion.md`.
1219
+
1099
1220
  ### props
1100
1221
 
1101
1222
  | prop | 타입 | 필수 | 기본값 |
@@ -1120,6 +1241,8 @@ import { InlineLoading } from '@nurihaus/web-design-system'; // 루
1120
1241
  <InlineLoading size={24} justifyContent="center" />
1121
1242
  ```
1122
1243
 
1244
+ **모션.** busy. 토큰 값과 규칙은 `motion.md`.
1245
+
1123
1246
  ### props
1124
1247
 
1125
1248
  | prop | 타입 | 필수 | 기본값 |
@@ -1146,6 +1269,8 @@ import { OverlayLoading } from '@nurihaus/web-design-system'; // 루
1146
1269
  <OverlayLoading open={true} message="저장 중입니다" />
1147
1270
  ```
1148
1271
 
1272
+ **모션.** busy · enter · exit. 토큰 값과 규칙은 `motion.md`.
1273
+
1149
1274
  ### props
1150
1275
 
1151
1276
  | prop | 타입 | 필수 | 기본값 |
@@ -1186,13 +1311,15 @@ import { Icon } from '@nurihaus/web-design-system'; // 루트. 첫
1186
1311
  <Icon name="x" size="m" label="닫기" />
1187
1312
  ```
1188
1313
 
1314
+ **모션.** 없음. 장식 그림이다. 모션은 감싸는 컴포넌트가 정한다.
1315
+
1189
1316
  ### props
1190
1317
 
1191
1318
  | prop | 타입 | 필수 | 기본값 |
1192
1319
  | --- | --- | --- | --- |
1193
1320
  | `size` | `'xs' \| 's' \| 'm' \| 'l' \| 'xl'` | | `'l'` |
1194
1321
  | `label` | `string` | | |
1195
- | `name` | `'link' \| 'image' \| 'x' \| 'warning' \| 'house' \| 'megaphone-simple' \| 'chats-circle' \| 'user-circle' \| 'bell' \| 'caret-left' \| 'magnifying-glass' \| 'dots-three' \| 'gear-six' \| 'chat-circle' \| 'paper-plane-tilt' \| 'plus' \| 'camera' \| 'video-camera' \| 'play' \| 'upload-simple' \| 'calendar-blank' \| 'clock' \| 'map-pin' \| 'truck' \| 'ticket' \| 'qr-code' \| 'sparkle' \| 'gift' \| 'wallet' \| 'bank' \| 'credit-card' \| 'check-circle' \| 'check' \| 'info' \| 'question' \| 'lock' \| 'pencil-simple' \| 'trash' \| 'copy' \| 'share-network' \| 'eye' \| 'sliders-horizontal' \| 'heart' \| 'star' \| 'globe' \| 'sign-out' \| 'caret-right' \| 'arrow-right' \| 'instagram-logo' \| 'tiktok-logo' \| 'youtube-logo'` | 예 | |
1322
+ | `name` | `'link' \| 'circle' \| 'image' \| 'warning' \| 'x' \| 'house' \| 'megaphone-simple' \| 'chats-circle' \| 'user-circle' \| 'bell' \| 'caret-left' \| 'magnifying-glass' \| 'dots-three' \| 'gear-six' \| 'chat-circle' \| 'paper-plane-tilt' \| 'plus' \| 'camera' \| 'video-camera' \| 'play' \| 'upload-simple' \| 'calendar-blank' \| 'clock' \| 'map-pin' \| 'truck' \| 'ticket' \| 'qr-code' \| 'sparkle' \| 'gift' \| 'wallet' \| 'bank' \| 'credit-card' \| 'check-circle' \| 'check' \| 'info' \| 'question' \| 'lock' \| 'pencil-simple' \| 'trash' \| 'copy' \| 'share-network' \| 'eye' \| 'sliders-horizontal' \| 'heart' \| 'star' \| 'globe' \| 'sign-out' \| 'caret-right' \| 'arrow-right' \| 'instagram-logo' \| 'tiktok-logo' \| 'youtube-logo'` | 예 | |
1196
1323
  | `weight` | `'fill' \| 'regular'` | | |
1197
1324
 
1198
1325
  이 밖에 React·HTML 기본 속성 474개를 받는다.
@@ -1235,6 +1362,8 @@ import Link from 'next/link';
1235
1362
  />
1236
1363
  ```
1237
1364
 
1365
+ **모션.** press · progress · appear(opt) · dismiss-on-scroll(opt). 토큰 값과 규칙은 `motion.md`.
1366
+
1238
1367
  ### props
1239
1368
 
1240
1369
  | prop | 타입 | 필수 | 기본값 |
@@ -1270,6 +1399,8 @@ import { StepProgress } from '@nurihaus/web-design-system'; // 루
1270
1399
  />
1271
1400
  ```
1272
1401
 
1402
+ **모션.** progress · reveal(opt). 토큰 값과 규칙은 `motion.md`.
1403
+
1273
1404
  ### props
1274
1405
 
1275
1406
  | prop | 타입 | 필수 | 기본값 |
@@ -1308,6 +1439,8 @@ import { ChipGroup } from '@nurihaus/web-design-system'; // 루트.
1308
1439
  />
1309
1440
  ```
1310
1441
 
1442
+ **모션.** press · confirm. 토큰 값과 규칙은 `motion.md`.
1443
+
1311
1444
  ### props
1312
1445
 
1313
1446
  | prop | 타입 | 필수 | 기본값 |
@@ -1324,6 +1457,490 @@ import { ChipGroup } from '@nurihaus/web-design-system'; // 루트.
1324
1457
 
1325
1458
  이 밖에 React·HTML 기본 속성 2개를 받는다.
1326
1459
 
1460
+ ## Card
1461
+
1462
+ ```ts
1463
+ import { Card } from '@nurihaus/web-design-system/card'; // 이 컴포넌트만 싣는다 (권장)
1464
+ import { Card } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
1465
+ ```
1466
+
1467
+ **언제 쓴다.** 목록이나 홈에서 내용 한 묶음을 흰 면(`variant="default"`) 또는 연보라 면(`variant="tinted"`)에 담을 때 쓴다. 안은 세로로 쌓이고 사이 간격은 정해져 있다. 누르면 다른 화면으로 가는 카드는 `asChild` 를 주고 링크 요소(`<a>`·라우터 Link) 하나를 children 으로 준다 — 그 링크가 카드가 되고 내용은 링크의 children 으로 쓴다. 링크·버튼 카드만 누를 때 살짝 줄어든다. `className`·`style` 은 루트(asChild 면 그 링크)에 붙는다.
1468
+
1469
+ **쓰지 않는다.** 설정·메뉴처럼 줄지어 놓이는 항목은 ListGroup 과 ListRow 를 쓴다. 이름과 값을 나란히 보여 주는 요약은 DescriptionList 를 쓴다.
1470
+
1471
+ ```tsx
1472
+ <>
1473
+ <Card>
1474
+ <strong>여름 신제품 리뷰</strong>
1475
+ <span>마감 3일 남음</span>
1476
+ </Card>
1477
+ <Card asChild variant="tinted">
1478
+ <a href="/campaigns/1">
1479
+ <strong>여름 신제품 리뷰</strong>
1480
+ <span>마감 3일 남음</span>
1481
+ </a>
1482
+ </Card>
1483
+ </>
1484
+ ```
1485
+
1486
+ **모션.** press · attention(opt). 토큰 값과 규칙은 `motion.md`.
1487
+
1488
+ ### props
1489
+
1490
+ | prop | 타입 | 필수 | 기본값 |
1491
+ | --- | --- | --- | --- |
1492
+ | `asChild` | `boolean` | | |
1493
+ | `variant` | `'default' \| 'tinted'` | | `'default'` |
1494
+
1495
+ 이 밖에 React·HTML 기본 속성 266개를 받는다.
1496
+
1497
+ ## ListGroup
1498
+
1499
+ ```ts
1500
+ import { ListGroup } from '@nurihaus/web-design-system/list-group'; // 이 컴포넌트만 싣는다 (권장)
1501
+ import { ListGroup } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
1502
+ ```
1503
+
1504
+ **언제 쓴다.** 설정·계정 메뉴처럼 ListRow 여러 줄을 흰 면 하나에 묶을 때 쓴다. 줄 사이에는 구분선이 자동으로 들어간다. `label` 을 주면 면 위에 작은 제목이 붙고 스크린 리더는 그 제목을 그룹 이름으로 읽는다. `className`·`style` 은 제목과 면을 감싼 루트에 붙는다.
1505
+
1506
+ **쓰지 않는다.** 내용이 자유로운 한 묶음은 Card 를 쓴다. 이름과 값만 나란히 보여 주는 요약은 DescriptionList 를 쓴다.
1507
+
1508
+ ```tsx
1509
+ <ListGroup label="계정">
1510
+ <ListRow label="내 정보" onClick={() => {}} />
1511
+ <ListRow label="연결된 계정" trailing="2개" asChild>
1512
+ <a href="/accounts" />
1513
+ </ListRow>
1514
+ </ListGroup>
1515
+ ```
1516
+
1517
+ **모션.** 없음. 면과 제목만 그린다. 눌림은 각 ListRow 가 받는다.
1518
+
1519
+ ### props
1520
+
1521
+ | prop | 타입 | 필수 | 기본값 |
1522
+ | --- | --- | --- | --- |
1523
+ | `label` | `ReactNode` | | |
1524
+
1525
+ 이 밖에 React·HTML 기본 속성 266개를 받는다.
1526
+
1527
+ ## ListRow
1528
+
1529
+ ```ts
1530
+ import { ListRow } from '@nurihaus/web-design-system/list-row'; // 이 컴포넌트만 싣는다 (권장)
1531
+ import { ListRow } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
1532
+ ```
1533
+
1534
+ **언제 쓴다.** ListGroup 안의 한 줄에 쓴다. `label` 이 이름, `description` 이 아래 보조 글, `leading` 이 왼쪽(Icon size m 이나 Avatar), `trailing` 이 오른쪽(값 글자, OptionBadge, Switch)이다. 다른 화면으로 가는 줄은 `asChild` 를 주고 링크 요소(`<a>`·라우터 Link) 하나를 children 으로 준다 — 그 링크가 줄이 되고 내용은 링크 안에 들어간다. 그 자리에서 동작하는 줄은 `onClick` 을 주면 button 이 된다. 누르는 줄은 오른쪽 끝에 화살표가 기본으로 붙고 `chevron={false}` 로 숨긴다. 누르면 배경이 잠깐 어두워진다. `className`·`style` 은 줄(button·링크) 자체에 붙는다.
1535
+
1536
+ **쓰지 않는다.** `trailing` 에 Switch 같은 버튼을 둔 줄에는 `onClick`·`asChild` 를 주지 않는다 — 버튼 안에 버튼이 들어간다. 줄을 ListGroup 밖에 홀로 두지 않는다. 사용 로그는 넘기는 링크·핸들러에서 남긴다.
1537
+
1538
+ ```tsx
1539
+ <ListGroup>
1540
+ <ListRow
1541
+ leading={<Icon name="user-circle" size="m" />}
1542
+ label="내 정보"
1543
+ description="이름 · 연락처"
1544
+ onClick={() => {}}
1545
+ />
1546
+ <ListRow label="등급" trailing={<OptionBadge option="골드" variant="primary" size="s" />} />
1547
+ <ListRow label="알림 받기" trailing={<Switch id="push" aria-label="알림 받기" />} />
1548
+ </ListGroup>
1549
+ ```
1550
+
1551
+ **모션.** press · attention(opt). 토큰 값과 규칙은 `motion.md`.
1552
+
1553
+ ### props
1554
+
1555
+ | prop | 타입 | 필수 | 기본값 |
1556
+ | --- | --- | --- | --- |
1557
+ | `asChild` | `boolean` | | |
1558
+ | `label` | `ReactNode` | 예 | |
1559
+ | `track` | `string` | | |
1560
+ | `children` | `ReactNode` | | |
1561
+ | `onClick` | `MouseEventHandler<HTMLElement>` | | |
1562
+ | `trackProps` | `Record<string, string \| number \| boolean>` | | |
1563
+ | `description` | `ReactNode` | | |
1564
+ | `chevron` | `boolean` | | |
1565
+ | `leading` | `ReactNode` | | |
1566
+ | `trailing` | `ReactNode` | | |
1567
+
1568
+ 이 밖에 React·HTML 기본 속성 264개를 받는다.
1569
+
1570
+ ## DescriptionList
1571
+
1572
+ ```ts
1573
+ import { DescriptionList } from '@nurihaus/web-design-system/description-list'; // 이 컴포넌트만 싣는다 (권장)
1574
+ import { DescriptionList } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
1575
+ ```
1576
+
1577
+ **언제 쓴다.** 금액 내역·신청 정보처럼 이름과 값을 줄마다 나란히 보여 주는 요약에 쓴다. 이름은 왼쪽 회색, 값은 오른쪽 진한 글자다. 합계 줄은 `total: true` 를 주면 위에 구분선이 생기고 값이 크고 굵어진다. 마지막 항목에 둔다. `className`·`style` 은 `<dl>` 에 붙는다.
1578
+
1579
+ **쓰지 않는다.** 줄을 눌러 다른 화면으로 가야 하면 ListGroup 과 ListRow 를 쓴다. 열이 여러 개인 표는 DataTable 을 쓴다.
1580
+
1581
+ ```tsx
1582
+ <DescriptionList
1583
+ items={[
1584
+ { id: 'fee', label: '원고료', value: '300,000원' },
1585
+ { id: 'tax', label: '원천징수 (3.3%)', value: '-9,900원' },
1586
+ { id: 'total', label: '실 지급액', value: '290,100원', total: true },
1587
+ ]}
1588
+ />
1589
+ ```
1590
+
1591
+ **모션.** 없음. 이름–값 글자만 그린다. 누르는 표면이 아니다.
1592
+
1593
+ ### props
1594
+
1595
+ | prop | 타입 | 필수 | 기본값 |
1596
+ | --- | --- | --- | --- |
1597
+ | `items` | `DescriptionListItem[]` | 예 | |
1598
+ | `className` | `string` | | |
1599
+ | `style` | `CSSProperties` | | |
1600
+
1601
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
1602
+
1603
+ ## Notice
1604
+
1605
+ ```ts
1606
+ import { Notice } from '@nurihaus/web-design-system/notice'; // 이 컴포넌트만 싣는다 (권장)
1607
+ import { Notice } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
1608
+ ```
1609
+
1610
+ **언제 쓴다.** 카드·바텀시트·폼 안에 머무르며 상태나 주의를 알릴 때 쓴다(예: "검토 중입니다", "저장하지 못했습니다"). `variant`(`neutral` 기본·`success`·`warning`·`danger`)가 바탕색·글자색과 앞 아이콘(정보·체크·경고·경고)을 정한다. `danger` 는 `role="alert"` 라 화면 낭독기가 바로 읽는다. 다른 variant 에 role 이 필요하면 쓰는 쪽이 `role` 을 준다(그 값이 앞선다). `title` 은 본문 위 굵은 한 줄, `children` 이 본문이다. `action`(`{ label, onClick, loading?, track? }`)을 주면 본문 아래 작은 버튼(CTAButton xs·neutral)이 하나 붙는다. `loading` 이면 버튼이 Spinner 로 바뀌고 처리 중에는 `onClick` 을 다시 부르지 않는다. 조건에 따라 나타나는 안내만 `motion`(`weak`·`strong`)을 준다 — 나타날 때 흐려졌다 선명해진다. 사라질 때는 움직이지 않는다. `className`·`style` 은 루트(`div`)에 붙는다.
1611
+
1612
+ **쓰지 않는다.** 행동 결과를 잠깐 알리고 스스로 사라져야 하면 Toast 를 쓴다. 입력칸 하나의 오류는 TextField 의 helper text 를 쓴다. 목록이 비었거나 화면 전체를 채우는 안내는 EmptyState 를 쓴다.
1613
+
1614
+ ```tsx
1615
+ <Notice variant="danger" title="저장하지 못했습니다" action={{ label: '다시 시도', onClick: () => {} }}>
1616
+ 네트워크 연결을 확인해 주세요.
1617
+ </Notice>
1618
+ ```
1619
+
1620
+ **모션.** enter(opt, `motion`). 토큰 값과 규칙은 `motion.md`.
1621
+
1622
+ ### props
1623
+
1624
+ | prop | 타입 | 필수 | 기본값 |
1625
+ | --- | --- | --- | --- |
1626
+ | `title` | `ReactNode` | | |
1627
+ | `children` | `ReactNode` | 예 | |
1628
+ | `variant` | `'neutral' \| 'danger' \| 'success' \| 'warning'` | | `'neutral'` |
1629
+ | `motion` | `'strong' \| 'weak'` | | |
1630
+ | `action` | `NoticeAction` | | |
1631
+
1632
+ 이 밖에 React·HTML 기본 속성 264개를 받는다.
1633
+
1634
+ ## EmptyState
1635
+
1636
+ ```ts
1637
+ import { EmptyState } from '@nurihaus/web-design-system/empty-state'; // 이 컴포넌트만 싣는다 (권장)
1638
+ import { EmptyState } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
1639
+ ```
1640
+
1641
+ **언제 쓴다.** 목록이 비었을 때, 불러오지 못해 다시 시도를 권할 때, 찾을 수 없거나 준비 중인 화면에 쓴다. 가운데에 아이콘 원·제목·설명·버튼을 세로로 쌓는다. `icon` 은 Icon 이름, `title` 은 필수, `description` 은 한두 줄 보충 설명이다. `action` 에는 버튼(보통 CTAButton)을 넘긴다. `variant`(`default` 기본·`neutral`)는 아이콘 원 색만 바꾼다. 기본은 연보라, 오류·준비 중처럼 힘을 뺄 때는 `neutral`(회색)이다. 제목은 제목 단계를 정하지 않으려고 `p` 로 그린다. 화면 제목이 필요하면 쓰는 쪽이 따로 둔다. `className`·`style` 은 루트(`div`)에 붙는다.
1642
+
1643
+ **쓰지 않는다.** 카드 안에서 상태를 짧게 알리는 안내는 Notice 를 쓴다. 불러오는 중에는 Skeleton 이나 로딩 컴포넌트를 쓴다.
1644
+
1645
+ ```tsx
1646
+ <EmptyState
1647
+ icon="warning"
1648
+ variant="neutral"
1649
+ title="불러오지 못했어요"
1650
+ description="잠시 뒤 다시 시도해 주세요."
1651
+ action={<CTAButton size="s" variant="secondary" text="다시 시도" />}
1652
+ />
1653
+ ```
1654
+
1655
+ **모션.** 없음. 누를 곳이 없는 표시용 화면이다. 버튼은 쓰는 쪽이 넘긴 CTAButton 이 press 를 받는다.
1656
+
1657
+ ### props
1658
+
1659
+ | prop | 타입 | 필수 | 기본값 |
1660
+ | --- | --- | --- | --- |
1661
+ | `title` | `ReactNode` | 예 | |
1662
+ | `variant` | `'neutral' \| 'default'` | | `'default'` |
1663
+ | `icon` | `'link' \| 'circle' \| 'image' \| 'warning' \| 'x' \| 'house' \| 'megaphone-simple' \| 'chats-circle' \| 'user-circle' \| 'bell' \| 'caret-left' \| 'magnifying-glass' \| 'dots-three' \| 'gear-six' \| 'chat-circle' \| 'paper-plane-tilt' \| 'plus' \| 'camera' \| 'video-camera' \| 'play' \| 'upload-simple' \| 'calendar-blank' \| 'clock' \| 'map-pin' \| 'truck' \| 'ticket' \| 'qr-code' \| 'sparkle' \| 'gift' \| 'wallet' \| 'bank' \| 'credit-card' \| 'check-circle' \| 'check' \| 'info' \| 'question' \| 'lock' \| 'pencil-simple' \| 'trash' \| 'copy' \| 'share-network' \| 'eye' \| 'sliders-horizontal' \| 'heart' \| 'star' \| 'globe' \| 'sign-out' \| 'caret-right' \| 'arrow-right' \| 'instagram-logo' \| 'tiktok-logo' \| 'youtube-logo'` | 예 | |
1664
+ | `description` | `ReactNode` | | |
1665
+ | `action` | `ReactNode` | | |
1666
+
1667
+ 이 밖에 React·HTML 기본 속성 264개를 받는다.
1668
+
1669
+ ## Tag
1670
+
1671
+ ```ts
1672
+ import { Tag } from '@nurihaus/web-design-system/tag'; // 이 컴포넌트만 싣는다 (권장)
1673
+ import { Tag } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
1674
+ ```
1675
+
1676
+ **언제 쓴다.** 고른 값을 보여 주고 하나씩 지울 수 있게 할 때 쓴다(예: 여러 개 고른 나라). `text` 가 글자다. `onRemove` 를 주면 오른쪽에 X 버튼이 붙고, 누르면 `onRemove` 가 불린다. 이때 `removeLabel`(X 버튼을 읽어 줄 이름, 예: "대한민국 지우기")도 꼭 준다 — 앱 언어로 쓰는 쪽이 정한다. `onRemove` 가 없으면 지울 수 없는 표시용 태그다. `track` 을 주면 X 를 누를 때 `click` 을 보낸다. `className`·`style` 은 루트(`span`)에 붙는다. 여러 개를 늘어놓을 때 줄 바꿈·간격은 쓰는 쪽 컨테이너가 정한다.
1677
+
1678
+ **쓰지 않는다.** 목록을 걸러 낼 선택지는 ChipGroup 을 쓴다. 상태값·분류 라벨은 OptionBadge 를 쓴다.
1679
+
1680
+ ```tsx
1681
+ <>
1682
+ <Tag text="대한민국" onRemove={() => {}} removeLabel="대한민국 지우기" />
1683
+ <Tag text="일본" />
1684
+ </>
1685
+ ```
1686
+
1687
+ **모션.** press. 토큰 값과 규칙은 `motion.md`.
1688
+
1689
+ ### props
1690
+
1691
+ | prop | 타입 | 필수 | 기본값 |
1692
+ | --- | --- | --- | --- |
1693
+ | `track` | `string` | | |
1694
+ | `text` | `ReactNode` | 예 | |
1695
+ | `trackProps` | `Record<string, string \| number \| boolean>` | | |
1696
+ | `onRemove` | `(() => void)` | | |
1697
+ | `removeLabel` | `string` | | |
1698
+
1699
+ 이 밖에 React·HTML 기본 속성 265개를 받는다.
1700
+
1701
+ ## MessageThread
1702
+
1703
+ ```ts
1704
+ import { MessageThread } from '@nurihaus/web-design-system/message-thread'; // 이 컴포넌트만 싣는다 (권장)
1705
+ import { MessageThread } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
1706
+ ```
1707
+
1708
+ **언제 쓴다.** 1:1 대화·문의 답변처럼 말풍선이 쌓이는 대화 목록에 쓴다. `<ol>` 이고 children 은 Message · MessageEvent 다. 항목 사이 간격만 정한다. 스크롤과 새 메시지로 내려가기는 쓰는 쪽이 정한다. `className`·`style`·`ref` 는 `<ol>` 에 붙는다.
1709
+
1710
+ **쓰지 않는다.** 댓글처럼 말풍선이 아닌 목록에는 쓰지 않는다.
1711
+
1712
+ ```tsx
1713
+ <MessageThread>
1714
+ <MessageEvent>10월 22일</MessageEvent>
1715
+ <Message name="담당자" time="오후 2:10">안녕하세요!</Message>
1716
+ <Message side="end" time="오후 2:12">네, 확인했어요.</Message>
1717
+ </MessageThread>
1718
+ ```
1719
+
1720
+ **모션.** 없음. 목록 틀이다. 등장 모션은 항목(Message)이 가진다.
1721
+
1722
+ ### props
1723
+
1724
+ 이 컴포넌트가 따로 정의한 prop 은 없다.
1725
+
1726
+ 이 밖에 React·HTML 기본 속성 269개를 받는다.
1727
+
1728
+ ## Message
1729
+
1730
+ ```ts
1731
+ import { Message } from '@nurihaus/web-design-system/message'; // 이 컴포넌트만 싣는다 (권장)
1732
+ import { Message } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
1733
+ ```
1734
+
1735
+ **언제 쓴다.** MessageThread 안의 말풍선 하나다. `side="end"` 는 내가 보낸 메시지(오른쪽, 보라), 기본 `start` 는 상대 메시지(왼쪽, 회색)다. 글은 children 이고 줄바꿈을 그대로 보인다. `name` 은 말풍선 위 이름, `time` 은 옆 시각(`<time>`, 기계용 값은 `dateTime`)이다. 언제 보일지(연속 메시지에서 숨기기 등)는 쓰는 쪽이 정한다. `media` 는 글 뒤 말풍선 안에 들어가는 썸네일이다. 글 없이 `media` 만 주면 안쪽 여백이 줄어든다. `motion`(`weak`·`strong`)을 주면 나타날 때 아래에서 올라온다 — 방금 보내거나 받은 메시지에만 준다. 처음 그리는 대화에는 주지 않는다. `className`·`style`·`ref` 는 `<li>` 에 붙는다.
1736
+
1737
+ **쓰지 않는다.** 날짜·상태 안내 줄은 MessageEvent 를 쓴다.
1738
+
1739
+ ```tsx
1740
+ <MessageThread>
1741
+ <Message side="end" time="오후 2:12" dateTime="2026-10-22T14:12" motion="weak">
1742
+ 방금 보낸 메시지
1743
+ </Message>
1744
+ </MessageThread>
1745
+ ```
1746
+
1747
+ **모션.** enter(opt, `motion`). 토큰 값과 규칙은 `motion.md`.
1748
+
1749
+ ### props
1750
+
1751
+ | prop | 타입 | 필수 | 기본값 |
1752
+ | --- | --- | --- | --- |
1753
+ | `time` | `ReactNode` | | |
1754
+ | `name` | `ReactNode` | | |
1755
+ | `motion` | `'strong' \| 'weak'` | | |
1756
+ | `side` | `'end' \| 'start'` | | `'start'` |
1757
+ | `media` | `ReactNode` | | |
1758
+ | `dateTime` | `string` | | |
1759
+
1760
+ 이 밖에 React·HTML 기본 속성 267개를 받는다.
1761
+
1762
+ ## MessageEvent
1763
+
1764
+ ```ts
1765
+ import { MessageEvent } from '@nurihaus/web-design-system/message-event'; // 이 컴포넌트만 싣는다 (권장)
1766
+ import { MessageEvent } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
1767
+ ```
1768
+
1769
+ **언제 쓴다.** MessageThread 안 가운데에 놓는 날짜·상태 줄이다(예: "10월 22일", "캠페인이 확정됐어요"). 움직이지 않는다. `className`·`style`·`ref` 는 `<li>` 에 붙는다.
1770
+
1771
+ **쓰지 않는다.** 누군가 보낸 말은 Message 를 쓴다. 화면 위에 잠깐 뜨는 알림은 Toast 를 쓴다.
1772
+
1773
+ ```tsx
1774
+ <MessageThread>
1775
+ <MessageEvent>캠페인이 확정됐어요</MessageEvent>
1776
+ </MessageThread>
1777
+ ```
1778
+
1779
+ **모션.** 없음. 대화 가운데 날짜·상태 줄. 움직이지 않는다.
1780
+
1781
+ ### props
1782
+
1783
+ 이 컴포넌트가 따로 정의한 prop 은 없다.
1784
+
1785
+ 이 밖에 React·HTML 기본 속성 267개를 받는다.
1786
+
1787
+ ## MessageComposer
1788
+
1789
+ ```ts
1790
+ import { MessageComposer } from '@nurihaus/web-design-system/message-composer'; // 이 컴포넌트만 싣는다 (권장)
1791
+ import { MessageComposer } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
1792
+ ```
1793
+
1794
+ **언제 쓴다.** 대화 화면 아래의 메시지 입력 줄이다. 입력 칸은 글자만큼 늘어나다가(다섯 줄쯤) 그 뒤로는 칸 안에서 스크롤한다. Enter 는 줄바꿈이고 보내기는 오른쪽 버튼으로만 한다. 값은 `value`·`defaultValue`·`onValueChange(value)` 로 다룬다. 보낼 수 있으면 버튼을 눌렀을 때 `onSend(value)` 가 불린다. **값을 스스로 비우지 않는다** — 보내기에 성공하면 쓰는 쪽이 `value` 를 비운다. 보낼 수 있는지는 기본으로 값이 공백만이 아닐 때다. 첨부만 보내는 화면은 `canSend` 를 직접 준다. 보낼 수 없거나 `disabled` 면 버튼이 회색(`aria-disabled`)이고 눌러도 보내지 않고 흔들린다. `loading` 동안은 Spinner 를 돌리고 보내지 않는다. `label`·`sendLabel` 은 입력 칸·보내기 버튼의 이름(aria-label)이다. `autoFocus` 는 사용자가 방금 입력을 연 화면(예: 다시 문의하기를 고른 뒤)에서만 준다. `maxLength` 는 입력 칸 최대 글자 수다. `leading` 은 입력 칸 왼쪽(첨부 아이콘 버튼), `attachments` 는 입력 줄 위(첨부 썸네일) 자리다. `track` 을 주면 실제로 보낼 때 `click` 을 한 번 보낸다(입력한 글은 싣지 않는다). `className`·`style`·`ref` 는 루트 `<div>` 에 붙는다. 화면 아래에 붙이려면 BottomBar 안에 둔다.
1795
+
1796
+ **쓰지 않는다.** 폼의 여러 줄 입력은 TextArea, 한 줄 입력은 TextField 를 쓴다.
1797
+
1798
+ ```tsx
1799
+ <BottomBar>
1800
+ <MessageComposer
1801
+ label="메시지"
1802
+ sendLabel="보내기"
1803
+ placeholder="메시지를 입력하세요"
1804
+ onSend={() => {}}
1805
+ />
1806
+ </BottomBar>
1807
+ ```
1808
+
1809
+ **모션.** press · reject · busy(`loading`). 토큰 값과 규칙은 `motion.md`.
1810
+
1811
+ ### props
1812
+
1813
+ | prop | 타입 | 필수 | 기본값 |
1814
+ | --- | --- | --- | --- |
1815
+ | `value` | `string` | | |
1816
+ | `defaultValue` | `string` | | |
1817
+ | `onValueChange` | `((value: string) => void)` | | |
1818
+ | `onSend` | `(value: string) => void` | 예 | |
1819
+ | `canSend` | `boolean` | | |
1820
+ | `loading` | `boolean` | | |
1821
+ | `placeholder` | `string` | | |
1822
+ | `label` | `string` | 예 | |
1823
+ | `sendLabel` | `string` | 예 | |
1824
+ | `leading` | `ReactNode` | | |
1825
+ | `attachments` | `ReactNode` | | |
1826
+ | `disabled` | `boolean` | | |
1827
+ | `autoFocus` | `boolean` | | |
1828
+ | `maxLength` | `number` | | |
1829
+ | `className` | `string` | | |
1830
+ | `style` | `CSSProperties` | | |
1831
+ | `track` | `string` | | |
1832
+ | `trackProps` | `Record<string, string \| number \| boolean>` | | |
1833
+
1834
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
1835
+
1836
+ ## BottomBar
1837
+
1838
+ ```ts
1839
+ import { BottomBar } from '@nurihaus/web-design-system/bottom-bar'; // 이 컴포넌트만 싣는다 (권장)
1840
+ import { BottomBar } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
1841
+ ```
1842
+
1843
+ **언제 쓴다.** 화면 아래에 붙어 그 화면의 주 행동(CTAButton 하나나 둘)이나 입력 줄(MessageComposer)을 담는 바다. `position: sticky; bottom: 0` 이라 스크롤하는 조상의 마지막 자식으로 둔다. 바로 아래 자식들이 폭을 똑같이 나눠 갖는다. 기기 홈 막대(안전 영역)만큼 아래를 더 비운다. 스크롤해도 숨지 않는다. `className`·`style`·`ref` 는 루트 `<div>` 에 붙는다.
1844
+
1845
+ **쓰지 않는다.** 화면을 오가는 주 메뉴는 BottomNav 를 쓴다.
1846
+
1847
+ ```tsx
1848
+ <BottomBar>
1849
+ <CTAButton variant="ghost" size="l" text="거절" />
1850
+ <CTAButton variant="primary" size="l" text="수락" />
1851
+ </BottomBar>
1852
+ ```
1853
+
1854
+ **모션.** 없음. 주 행동·입력을 담는 고정 바라 스크롤로 숨기지 않는다.
1855
+
1856
+ ### props
1857
+
1858
+ 이 컴포넌트가 따로 정의한 prop 은 없다.
1859
+
1860
+ 이 밖에 React·HTML 기본 속성 266개를 받는다.
1861
+
1862
+ ## MediaThumbnail
1863
+
1864
+ ```ts
1865
+ import { MediaThumbnail } from '@nurihaus/web-design-system/media-thumbnail'; // 이 컴포넌트만 싣는다 (권장)
1866
+ import { MediaThumbnail } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
1867
+ ```
1868
+
1869
+ **언제 쓴다.** 사진·영상을 작은 썸네일로 보여 주고 누르면 화면 안에서 크게 볼 때 쓴다. 앱 웹뷰에서 원본 주소로 이동하면 앱을 벗어나므로 링크 대신 쓴다. `type`(`image`·`video`, 기본 `image`), `size`(`s` 64·`m` 96 정사각형, `l` 폭을 채우되 240 까지 4:3 — 말풍선 안). 올리는 중이면 `loading`, 실패하면 `invalid` 를 준다. 둘 다 누르면 열리지 않는다. `onRemove`·`removeLabel` 을 주면 오른쪽 위에 지우기 버튼이 생긴다. `closeLabel` 은 뷰어 닫기 버튼 이름이라 필수다. 영상처럼 `alt` 가 없으면 `viewLabel` 이 버튼 이름이 된다. `className`·`style` 은 감싸는 div 에 붙는다.
1870
+
1871
+ **쓰지 않는다.** 누를 일 없는 사람 사진은 Avatar 를 쓴다. 썸네일 없이 버튼 하나로 크게 보이려면 MediaViewer 의 `trigger` 를 쓴다.
1872
+
1873
+ ```tsx
1874
+ <MediaThumbnail
1875
+ src="https://example.com/photo.jpg"
1876
+ alt="제품 사진"
1877
+ closeLabel="닫기"
1878
+ size="m"
1879
+ onRemove={() => {}}
1880
+ removeLabel="삭제"
1881
+ />
1882
+ ```
1883
+
1884
+ **모션.** press · busy(`loading`). 토큰 값과 규칙은 `motion.md`.
1885
+
1886
+ ### props
1887
+
1888
+ | prop | 타입 | 필수 | 기본값 |
1889
+ | --- | --- | --- | --- |
1890
+ | `size` | `'s' \| 'm' \| 'l'` | | `'m'` |
1891
+ | `viewLabel` | `string` | | |
1892
+ | `loading` | `boolean` | | |
1893
+ | `invalid` | `boolean` | | |
1894
+ | `onRemove` | `(() => void)` | | |
1895
+ | `removeLabel` | `string` | | |
1896
+ | `src` | `string` | 예 | |
1897
+ | `type` | `'video' \| 'image'` | | `'image'` |
1898
+ | `alt` | `string` | | |
1899
+ | `closeLabel` | `string` | 예 | |
1900
+ | `className` | `string` | | |
1901
+ | `style` | `CSSProperties` | | |
1902
+ | `track` | `string` | | |
1903
+ | `trackProps` | `Record<string, string \| number \| boolean>` | | |
1904
+
1905
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
1906
+
1907
+ ## MediaViewer
1908
+
1909
+ ```ts
1910
+ import { MediaViewer } from '@nurihaus/web-design-system/media-viewer'; // 이 컴포넌트만 싣는다 (권장)
1911
+ import { MediaViewer } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
1912
+ ```
1913
+
1914
+ **언제 쓴다.** 사진·영상 하나를 화면 전체에 띄워 볼 때 쓴다. 검은 바탕 위에 비율을 지켜 가운데 놓고, 영상은 열린 동안만 재생한다(닫으면 멈춘다). `trigger` 에 넘긴 요소를 누르면 열리고, `open`·`defaultOpen`·`onOpenChange` 로 제어한다. `closeLabel` 은 오른쪽 위 닫기 버튼 이름이라 필수다. 접근성 이름은 `title`, 없으면 `alt` 다(화면에는 안 보인다). Escape·닫기 버튼으로 닫힌다. `className`·`style` 은 화면 전체를 덮는 dialog 에 붙는다.
1915
+
1916
+ **쓰지 않는다.** 썸네일을 누르면 여는 경우는 MediaThumbnail 이 뷰어를 같이 그린다. 사진 여러 장을 넘겨 보는 갤러리는 아직 없다.
1917
+
1918
+ ```tsx
1919
+ <MediaViewer open src="https://example.com/photo.jpg" alt="제품 사진" closeLabel="닫기" onOpenChange={() => {}} />
1920
+ ```
1921
+
1922
+ **모션.** enter · exit. 토큰 값과 규칙은 `motion.md`.
1923
+
1924
+ ### props
1925
+
1926
+ | prop | 타입 | 필수 | 기본값 |
1927
+ | --- | --- | --- | --- |
1928
+ | `trigger` | `ReactElement<any, string \| JSXElementConstructor<any>>` | | |
1929
+ | `title` | `string` | | |
1930
+ | `open` | `boolean` | | |
1931
+ | `defaultOpen` | `boolean` | | |
1932
+ | `onOpenChange` | `((open: boolean) => void)` | | |
1933
+ | `src` | `string` | 예 | |
1934
+ | `type` | `'video' \| 'image'` | | `'image'` |
1935
+ | `alt` | `string` | | |
1936
+ | `closeLabel` | `string` | 예 | |
1937
+ | `className` | `string` | | |
1938
+ | `style` | `CSSProperties` | | |
1939
+ | `track` | `string` | | |
1940
+ | `trackProps` | `Record<string, string \| number \| boolean>` | | |
1941
+
1942
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
1943
+
1327
1944
  ## LogProvider
1328
1945
 
1329
1946
  ```ts
@@ -1344,6 +1961,8 @@ LogProvider 가 없거나 `track` 을 주지 않으면 아무것도 보내지
1344
1961
  </LogProvider>
1345
1962
  ```
1346
1963
 
1964
+ **모션.** 없음. DOM 을 그리지 않는다.
1965
+
1347
1966
  ### props
1348
1967
 
1349
1968
  | prop | 타입 | 필수 | 기본값 |
@@ -1351,6 +1970,33 @@ LogProvider 가 없거나 `track` 을 주지 않으면 아무것도 보내지
1351
1970
  | `onEvent` | `(event: LogEvent) => void` | 예 | |
1352
1971
  | `children` | `ReactNode` | | |
1353
1972
 
1973
+ ## MotionProvider
1974
+
1975
+ ```ts
1976
+ import { MotionProvider } from '@nurihaus/web-design-system/motion'; // 이 컴포넌트만 싣는다 (권장)
1977
+ import { MotionProvider } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
1978
+ ```
1979
+
1980
+ **언제 쓴다.** 앱 루트에 한 번 둔다. 구현층이 `motion` 인 모션 토큰(스프링·presence, `motion.md`)을 쓰는 컴포넌트가 이 아래에서 움직인다. 안에서 motion 컴포넌트를 직접 쓸 때는 `m.div` 처럼 `motion/react` 의 `m` 을 쓴다. `motion.div` 를 쓰면 개발 빌드에서 오류가 난다. `reducedMotion` 기본값 `'user'` 는 OS 의 동작 줄이기 설정을 따른다.
1981
+ `motion` 은 peerDependency 이고 React 18 이상에서만 동작한다. CSS 로 움직이는 모션은 이 Provider 없이도 같은 설정을 따른다.
1982
+
1983
+ **쓰지 않는다.** 화면마다 겹쳐 두지 않는다. 겹쳐도 동작은 같지만 안쪽 `reducedMotion` 이 바깥 것을 가린다. 모션을 끄려고 Provider 를 빼지 않는다. `reducedMotion="always"` 를 준다.
1984
+
1985
+ ```tsx
1986
+ <MotionProvider>
1987
+ <CTAButton text="저장" />
1988
+ </MotionProvider>
1989
+ ```
1990
+
1991
+ **모션.** 없음. DOM 을 그리지 않는다. 다른 컴포넌트의 motion 층이 이 아래에서 돈다.
1992
+
1993
+ ### props
1994
+
1995
+ | prop | 타입 | 필수 | 기본값 |
1996
+ | --- | --- | --- | --- |
1997
+ | `reducedMotion` | `'user' \| 'always' \| 'never'` | | `'user'` |
1998
+ | `children` | `ReactNode` | | |
1999
+
1354
2000
  ## 아직 없는 것
1355
2001
 
1356
2002
  아래는 디자인 시스템에 없다. 비슷한 이름의 컴포넌트를 지어내 import 하지 않는다.