@nurihaus/web-design-system 3.7.0 → 3.8.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 (137) hide show
  1. package/README.md +15 -4
  2. package/dist/ai/components.md +616 -10
  3. package/dist/ai/motion.md +429 -0
  4. package/dist/ai/tokens.md +90 -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-3274.js +71 -0
  14. package/dist/chunk-345.js +72 -0
  15. package/dist/chunk-3547.js +56 -0
  16. package/dist/chunk-6459.js +107 -0
  17. package/dist/{chunk-6364.js → chunk-7335.js} +2 -67
  18. package/dist/chunk-7653.js +4 -0
  19. package/dist/chunk-910.js +17 -6
  20. package/dist/chunk-9497.js +66 -0
  21. package/dist/collapsible-menu.js +6 -4
  22. package/dist/components/badge/badge-props.d.ts +4 -4
  23. package/dist/components/badge/badge.stories.d.ts +1 -1
  24. package/dist/components/bottom-bar/base-bottom-bar.d.ts +4 -0
  25. package/dist/components/bottom-bar/bottom-bar-props.d.ts +1 -0
  26. package/dist/components/bottom-bar/bottom-bar-type.d.ts +3 -0
  27. package/dist/components/bottom-bar/bottom-bar.d.ts +6 -0
  28. package/dist/components/bottom-bar/bottom-bar.stories.d.ts +282 -0
  29. package/dist/components/button/button-type.d.ts +3 -1
  30. package/dist/components/button/button.stories.d.ts +1 -0
  31. package/dist/components/card/base-card.d.ts +3 -0
  32. package/dist/components/card/card-props.d.ts +9 -0
  33. package/dist/components/card/card-type.d.ts +8 -0
  34. package/dist/components/card/card.d.ts +5 -0
  35. package/dist/components/card/card.stories.d.ts +24 -0
  36. package/dist/components/description-list/base-description-list.d.ts +4 -0
  37. package/dist/components/description-list/description-list-props.d.ts +1 -0
  38. package/dist/components/description-list/description-list-type.d.ts +13 -0
  39. package/dist/components/description-list/description-list.d.ts +5 -0
  40. package/dist/components/description-list/description-list.stories.d.ts +20 -0
  41. package/dist/components/empty-state/base-empty-state.d.ts +4 -0
  42. package/dist/components/empty-state/empty-state-props.d.ts +10 -0
  43. package/dist/components/empty-state/empty-state-type.d.ts +14 -0
  44. package/dist/components/empty-state/empty-state.d.ts +5 -0
  45. package/dist/components/empty-state/empty-state.stories.d.ts +300 -0
  46. package/dist/components/icon/icon-paths.d.ts +4 -0
  47. package/dist/components/icon/icon.stories.d.ts +1 -1
  48. package/dist/components/list/base-list.d.ts +7 -0
  49. package/dist/components/list/list-group.d.ts +5 -0
  50. package/dist/components/list/list-group.stories.d.ts +14 -0
  51. package/dist/components/list/list-props.d.ts +2 -0
  52. package/dist/components/list/list-row.d.ts +5 -0
  53. package/dist/components/list/list-row.stories.d.ts +304 -0
  54. package/dist/components/list/list-type.d.ts +25 -0
  55. package/dist/components/media/base-media.d.ts +16 -0
  56. package/dist/components/media/media-props.d.ts +5 -0
  57. package/dist/components/media/media-thumbnail.d.ts +4 -0
  58. package/dist/components/media/media-thumbnail.stories.d.ts +34 -0
  59. package/dist/components/media/media-type.d.ts +43 -0
  60. package/dist/components/media/media-viewer.d.ts +4 -0
  61. package/dist/components/media/media-viewer.stories.d.ts +24 -0
  62. package/dist/components/message/base-message.d.ts +11 -0
  63. package/dist/components/message/message-event.d.ts +6 -0
  64. package/dist/components/message/message-props.d.ts +17 -0
  65. package/dist/components/message/message-thread.d.ts +6 -0
  66. package/dist/components/message/message-type.d.ts +21 -0
  67. package/dist/components/message/message.d.ts +5 -0
  68. package/dist/components/message/message.stories.d.ts +313 -0
  69. package/dist/components/message-composer/base-message-composer.d.ts +11 -0
  70. package/dist/components/message-composer/message-composer-props.d.ts +1 -0
  71. package/dist/components/message-composer/message-composer-type.d.ts +27 -0
  72. package/dist/components/message-composer/message-composer.d.ts +5 -0
  73. package/dist/components/message-composer/message-composer.stories.d.ts +44 -0
  74. package/dist/components/motion/motion-provider.d.ts +5 -0
  75. package/dist/components/motion/motion-type.d.ts +6 -0
  76. package/dist/components/motion/motion.d.ts +2 -0
  77. package/dist/components/motion/motion.stories.d.ts +23 -0
  78. package/dist/components/notice/base-notice.d.ts +4 -0
  79. package/dist/components/notice/notice-props.d.ts +16 -0
  80. package/dist/components/notice/notice-type.d.ts +19 -0
  81. package/dist/components/notice/notice.d.ts +5 -0
  82. package/dist/components/notice/notice.stories.d.ts +304 -0
  83. package/dist/components/radio/base-radio.d.ts +2 -1
  84. package/dist/components/radio/radio-props.d.ts +9 -1
  85. package/dist/components/radio/radio-type.d.ts +9 -0
  86. package/dist/components/radio/radio.stories.d.ts +10 -0
  87. package/dist/components/skeleton/skeleton.stories.d.ts +7 -7
  88. package/dist/components/tag/base-tag.d.ts +8 -0
  89. package/dist/components/tag/tag-props.d.ts +1 -0
  90. package/dist/components/tag/tag-type.d.ts +20 -0
  91. package/dist/components/tag/tag.d.ts +4 -0
  92. package/dist/components/tag/tag.stories.d.ts +14 -0
  93. package/dist/components/toast/toast.stories.d.ts +12 -12
  94. package/dist/components.css +1234 -171
  95. package/dist/cta-button.js +27 -14
  96. package/dist/data-table.js +5 -3
  97. package/dist/description-list.js +274 -0
  98. package/dist/drawer.js +7 -5
  99. package/dist/drop-down.js +8 -6
  100. package/dist/empty-state.js +300 -0
  101. package/dist/icon-text-badge.js +11 -9
  102. package/dist/icon-text-option-badge.js +8 -6
  103. package/dist/icon.js +7 -4
  104. package/dist/index.d.ts +28 -2
  105. package/dist/index.js +2297 -228
  106. package/dist/list-group.js +253 -0
  107. package/dist/list-row.js +302 -0
  108. package/dist/loading.js +6 -4
  109. package/dist/media-thumbnail.js +412 -0
  110. package/dist/media-viewer.js +274 -0
  111. package/dist/message-composer.js +395 -0
  112. package/dist/message-event.js +216 -0
  113. package/dist/message-thread.js +216 -0
  114. package/dist/message.js +271 -0
  115. package/dist/modal-with-double-button.js +11 -9
  116. package/dist/modal.js +9 -7
  117. package/dist/motion.js +52 -0
  118. package/dist/notice.js +342 -0
  119. package/dist/option-badge.js +11 -9
  120. package/dist/pagination.js +8 -6
  121. package/dist/radio.js +114 -30
  122. package/dist/skeleton.js +4 -2
  123. package/dist/step-progress.js +4 -2
  124. package/dist/styles.css +1124 -122
  125. package/dist/switch.js +6 -4
  126. package/dist/tabs.js +5 -3
  127. package/dist/tag.js +311 -0
  128. package/dist/text-area.js +8 -6
  129. package/dist/text-field.js +8 -6
  130. package/dist/text.js +5 -3
  131. package/dist/toast.js +12 -10
  132. package/dist/tokens/generated/index.d.ts +1 -0
  133. package/dist/tokens/generated/motion.d.ts +153 -0
  134. package/dist/tokens.css +117 -2
  135. package/dist/tokens.js +156 -1
  136. package/dist/tooltip.js +6 -4
  137. package/package.json +63 -1
package/dist/styles.css CHANGED
@@ -152,6 +152,74 @@
152
152
  --line-height-4xl: 43px;
153
153
  --line-height-5xl: 66px;
154
154
  --line-height-6xl: 82px;
155
+ --duration-xs: 120ms;
156
+ --duration-s: 160ms;
157
+ --duration-m: 240ms;
158
+ --duration-l: 350ms;
159
+ --duration-xl: 500ms;
160
+ --ease-out: cubic-bezier(0.25, 0.1, 0.25, 1);
161
+ --ease-in: cubic-bezier(0.4, 0, 1, 1);
162
+ --ease-in-out: cubic-bezier(0.4, 0, 0.1, 1);
163
+ --ease-back: cubic-bezier(0.34, 1.56, 0.64, 1);
164
+ --ease-symmetric: cubic-bezier(0.5, 0, 0.5, 1);
165
+ --ease-linear: linear;
166
+ --ease-spring-rapid: linear(0, 0.0273, 0.0945, 0.184, 0.2834, 0.384, 0.4804, 0.5691, 0.6483, 0.7174, 0.7764, 0.8259, 0.8667, 0.8999, 0.9264, 0.9473, 0.9634, 0.9757, 0.9849, 0.9916, 0.9964, 0.9996, 1.0017, 1.003, 1.0037, 1.0039, 1.0039, 1.0037, 1.0033, 1.003, 1.0026, 1.0022, 1.0018, 1.0015, 1.0012, 1.001, 1.0007, 1.0006, 1.0004, 1.0003, 1);
167
+ --duration-spring-rapid: 318ms;
168
+ --ease-spring-quick: linear(0, 0.024, 0.0825, 0.1604, 0.247, 0.3352, 0.4205, 0.5002, 0.5728, 0.6377, 0.6948, 0.7445, 0.7872, 0.8237, 0.8546, 0.8806, 0.9023, 0.9204, 0.9354, 0.9477, 0.9578, 0.9661, 0.9728, 0.9783, 0.9827, 0.9863, 0.9891, 0.9914, 0.9933, 0.9947, 0.9959, 0.9968, 0.9975, 0.9981, 0.9985, 0.9989, 0.9991, 0.9993, 0.9995, 0.9996, 1);
169
+ --duration-spring-quick: 334ms;
170
+ --ease-spring-small: linear(0, 0.0202, 0.069, 0.1334, 0.2049, 0.278, 0.3493, 0.417, 0.4799, 0.5375, 0.5898, 0.6369, 0.6791, 0.7167, 0.7501, 0.7798, 0.806, 0.8292, 0.8497, 0.8677, 0.8836, 0.8976, 0.91, 0.9208, 0.9304, 0.9388, 0.9462, 0.9527, 0.9584, 0.9634, 0.9679, 0.9717, 0.9752, 0.9782, 0.9808, 0.9831, 0.9852, 0.987, 0.9885, 0.9899, 0.9911, 0.9922, 0.9932, 0.994, 0.9947, 0.9954, 0.9959, 0.9964, 0.9968, 0.9972, 0.9976, 0.9979, 0.9981, 0.9983, 0.9985, 0.9987, 0.9989, 0.999, 0.9991, 1);
171
+ --duration-spring-small: 587ms;
172
+ --ease-spring-medium: linear(0, 0.0121, 0.0444, 0.0919, 0.1501, 0.2152, 0.2844, 0.355, 0.4252, 0.4934, 0.5585, 0.6195, 0.6761, 0.7278, 0.7746, 0.8164, 0.8533, 0.8855, 0.9134, 0.9372, 0.9572, 0.9739, 0.9875, 0.9984, 1.007, 1.0136, 1.0184, 1.0217, 1.0238, 1.0249, 1.0252, 1.0248, 1.0239, 1.0227, 1.0212, 1.0196, 1.0178, 1.016, 1.0143, 1.0126, 1.0109, 1.0094, 1.008, 1.0067, 1.0055, 1.0045, 1.0036, 1.0028, 1.0021, 1.0015, 1.001, 1.0006, 1.0003, 1, 0.9998, 0.9996, 1);
173
+ --duration-spring-medium: 552ms;
174
+ --ease-spring-basic: linear(0, 0.0089, 0.0322, 0.0658, 0.1066, 0.152, 0.2001, 0.2493, 0.2986, 0.3471, 0.3941, 0.4393, 0.4824, 0.5232, 0.5615, 0.5974, 0.6309, 0.662, 0.6909, 0.7175, 0.7421, 0.7648, 0.7855, 0.8046, 0.8221, 0.8381, 0.8527, 0.8661, 0.8783, 0.8894, 0.8995, 0.9088, 0.9172, 0.9248, 0.9318, 0.9381, 0.9438, 0.949, 0.9538, 0.9581, 0.962, 0.9655, 0.9687, 0.9717, 0.9743, 0.9767, 0.9789, 0.9809, 0.9827, 0.9843, 0.9858, 0.9871, 0.9883, 0.9894, 0.9904, 0.9913, 0.9921, 0.9929, 0.9935, 0.9941, 0.9947, 0.9952, 0.9956, 0.9961, 0.9964, 0.9968, 0.9971, 0.9973, 0.9976, 0.9978, 0.998, 0.9982, 0.9984, 0.9985, 0.9987, 0.9988, 0.9989, 1);
175
+ --duration-spring-basic: 761ms;
176
+ --ease-spring-large: linear(0, 0.0047, 0.0179, 0.0383, 0.0648, 0.0961, 0.1315, 0.1699, 0.2106, 0.2529, 0.2963, 0.3401, 0.3838, 0.4272, 0.4699, 0.5115, 0.5518, 0.5906, 0.6278, 0.6633, 0.697, 0.7288, 0.7586, 0.7866, 0.8126, 0.8368, 0.859, 0.8795, 0.8983, 0.9154, 0.9309, 0.9449, 0.9575, 0.9687, 0.9786, 0.9874, 0.995, 1.0017, 1.0074, 1.0122, 1.0163, 1.0196, 1.0223, 1.0244, 1.0261, 1.0272, 1.0279, 1.0283, 1.0284, 1.0282, 1.0277, 1.0271, 1.0263, 1.0254, 1.0243, 1.0232, 1.0221, 1.0208, 1.0196, 1.0184, 1.0171, 1.0159, 1.0147, 1.0135, 1.0124, 1.0113, 1.0103, 1.0093, 1.0083, 1.0074, 1.0066, 1.0058, 1.0051, 1.0044, 1.0038, 1.0033, 1.0027, 1.0023, 1.0018, 1.0015, 1.0011, 1.0008, 1.0005, 1.0003, 1.0001, 0.9999, 0.9997, 0.9996, 1);
177
+ --duration-spring-large: 876ms;
178
+ --ease-spring-bounce: linear(0, 0.0142, 0.0537, 0.1139, 0.1902, 0.2784, 0.3743, 0.4741, 0.5747, 0.673, 0.7667, 0.8538, 0.9329, 1.0028, 1.063, 1.113, 1.153, 1.1831, 1.204, 1.2164, 1.221, 1.2189, 1.211, 1.1984, 1.182, 1.1629, 1.1419, 1.1199, 1.0977, 1.0758, 1.0549, 1.0354, 1.0176, 1.0018, 0.9881, 0.9767, 0.9675, 0.9605, 0.9555, 0.9525, 0.9512, 0.9514, 0.953, 0.9556, 0.9591, 0.9633, 0.9678, 0.9727, 0.9776, 0.9825, 0.9871, 0.9915, 0.9955, 0.9991, 1.0022, 1.0048, 1.0069, 1.0085, 1.0097, 1.0104, 1.0108, 1.0108, 1.0105, 1.0099, 1.0092, 1.0083, 1.0073, 1.0062, 1.0051, 1.0041, 1.003, 1.002, 1.0011, 1.0003, 0.9996, 0.999, 0.9985, 0.9982, 0.9979, 0.9977, 0.9976, 0.9976, 0.9977, 0.9978, 0.9979, 0.9981, 0.9984, 0.9986, 0.9988, 0.9991, 0.9993, 0.9995, 0.9997, 0.9999, 1.0001, 1.0002, 1);
179
+ --duration-spring-bounce: 958ms;
180
+ --ease-spring-slow: linear(0, 0.0033, 0.0122, 0.0257, 0.043, 0.0631, 0.0855, 0.1096, 0.135, 0.1612, 0.188, 0.215, 0.2421, 0.2691, 0.2958, 0.3221, 0.348, 0.3732, 0.3979, 0.4219, 0.4452, 0.4677, 0.4896, 0.5107, 0.5311, 0.5507, 0.5697, 0.5879, 0.6054, 0.6222, 0.6384, 0.654, 0.6689, 0.6832, 0.6969, 0.71, 0.7226, 0.7347, 0.7462, 0.7573, 0.7679, 0.778, 0.7877, 0.797, 0.8059, 0.8144, 0.8225, 0.8303, 0.8378, 0.8449, 0.8517, 0.8582, 0.8645, 0.8704, 0.8761, 0.8816, 0.8868, 0.8918, 0.8965, 0.9011, 0.9054, 0.9096, 0.9136, 0.9174, 0.921, 0.9245, 0.9278, 0.931, 0.934, 0.9369, 0.9397, 0.9424, 0.9449, 0.9473, 0.9497, 0.9519, 0.954, 0.956, 0.958, 0.9598, 0.9616, 0.9633, 0.9649, 0.9664, 0.9679, 0.9693, 0.9707, 0.972, 0.9732, 0.9744, 0.9755, 0.9766, 0.9776, 0.9786, 0.9796, 0.9805, 0.9813, 0.9821, 0.9829, 0.9837, 0.9844, 0.9851, 0.9857, 0.9864, 0.987, 0.9875, 0.9881, 0.9886, 0.9891, 0.9896, 0.9901, 0.9905, 0.9909, 0.9913, 0.9917, 0.9921, 0.9924, 0.9927, 0.9931, 0.9934, 0.9937, 0.9939, 0.9942, 0.9945, 0.9947, 0.9949, 0.9952, 0.9954, 0.9956, 0.9958, 0.996, 0.9961, 0.9963, 0.9965, 0.9966, 0.9968, 0.9969, 0.9971, 0.9972, 0.9973, 0.9974, 0.9975, 0.9976, 0.9978, 0.9979, 0.9979, 0.998, 0.9981, 0.9982, 0.9983, 0.9984, 0.9984, 0.9985, 0.9986, 0.9986, 0.9987, 0.9987, 0.9988, 0.9989, 0.9989, 0.999, 1);
181
+ --duration-spring-slow: 1604ms;
182
+ --motion-press-duration: var(--duration-xs);
183
+ --motion-press-ease: var(--ease-out);
184
+ --motion-press-scale: 0.98;
185
+ --motion-press-dim: 0.12;
186
+ --motion-confirm-weak-ease: var(--ease-spring-small);
187
+ --motion-confirm-weak-duration: var(--duration-spring-small);
188
+ --motion-confirm-strong-ease: var(--ease-spring-bounce);
189
+ --motion-confirm-strong-duration: var(--duration-spring-bounce);
190
+ --motion-reject-duration: var(--duration-l);
191
+ --motion-reject-ease: var(--ease-linear);
192
+ --motion-reject-distance: 2px;
193
+ --motion-enter-duration: var(--duration-m);
194
+ --motion-enter-ease: var(--ease-out);
195
+ --motion-enter-strong-ease: var(--ease-spring-bounce);
196
+ --motion-enter-strong-duration: var(--duration-spring-bounce);
197
+ --motion-enter-scale-from: 0.96;
198
+ --motion-enter-offset: 16px;
199
+ --motion-enter-pop-scale: 0.6;
200
+ --motion-exit-duration: var(--duration-s);
201
+ --motion-exit-ease: var(--ease-in);
202
+ --motion-appear-duration: var(--duration-m);
203
+ --motion-appear-ease: var(--ease-out);
204
+ --motion-reveal-duration: var(--duration-m);
205
+ --motion-reveal-ease: var(--ease-out);
206
+ --motion-reveal-gap: 80ms;
207
+ --motion-progress-duration: var(--duration-m);
208
+ --motion-progress-spring-ease: var(--ease-spring-basic);
209
+ --motion-progress-spring-duration: var(--duration-spring-basic);
210
+ --motion-expand-duration: var(--duration-m);
211
+ --motion-expand-ease: var(--ease-out);
212
+ --motion-expand-rotate: 180deg;
213
+ --motion-dismiss-on-scroll-duration: var(--duration-m);
214
+ --motion-dismiss-on-scroll-ease: var(--ease-out);
215
+ --motion-dismiss-on-scroll-threshold: 1px;
216
+ --motion-attention-shine: 2500ms;
217
+ --motion-attention-blink: 300ms;
218
+ --motion-attention-float-distance: 3px;
219
+ --motion-busy-ring: 1200ms;
220
+ --motion-busy-ring-ease: var(--ease-symmetric);
221
+ --motion-busy-shimmer: 1500ms;
222
+ --motion-busy-dots: 600ms;
155
223
  /* 한 줄에 나란히 놓이는 컨트롤: button, drop-down, text-field */
156
224
  --control-height-xs: 28px;
157
225
  --control-height-s: 36px;
@@ -216,6 +284,27 @@
216
284
  --bottom-nav-badge-count: 18px;
217
285
  --glass-blur: 18px;
218
286
 
287
+ /* 목록 행: list-row. 한 줄 이름에 보조 글이 붙어도 이 높이부터 */
288
+ --list-row-height: 56px;
289
+
290
+ /* 손가락 터치 영역. 보이는 크기가 작은 버튼(아이콘 버튼·빼기·보내기)이 이만큼 눌린다 */
291
+ --touch-target: 44px;
292
+
293
+ /* 사진·영상 썸네일: media-thumbnail. 말풍선 안에서는 쓰는 쪽이 width 로 정하고 이 폭을 넘지 않는다 */
294
+ --media-thumbnail-size-s: 64px;
295
+ --media-thumbnail-size-m: 96px;
296
+ --media-thumbnail-max-width: 240px;
297
+ --media-overlay-button-size: 36px;
298
+ --media-remove-size: 20px;
299
+ --media-invalid-border: 2px;
300
+
301
+ /* 메시지 입력: message-composer. 보내기 원과 입력 칸 최대 높이(16/22 다섯 줄 + 위아래 안쪽) */
302
+ --composer-send-size: 36px;
303
+ --composer-max-height: 132px;
304
+
305
+ /* 빈 화면: empty-state 아이콘 원 */
306
+ --empty-state-icon-box: 64px;
307
+
219
308
  /* 진행 단계 바 */
220
309
  --step-bar-height: 4px;
221
310
 
@@ -255,7 +344,8 @@
255
344
  --z-modal: 9999;
256
345
  --z-toast: 10000;
257
346
 
258
- /* 모션 */
347
+ /* 옛 모션 변수 */
348
+ /* 컴포넌트 작업(KAN-9516~9528)이 --motion-{토큰}-* 로 바꾸면 지운다. 새 코드는 쓰지 않는다. 새 토큰은 tokens/motion.json */
259
349
  --motion-fast: 160ms;
260
350
  --motion-base: 300ms;
261
351
  --motion-ease: ease;
@@ -379,8 +469,33 @@
379
469
  --button-size-l: 54;
380
470
  --button-size-xl: 60;
381
471
  }
472
+ /* tokens/motion.json 에서 생성됨 — 손으로 고치지 않는다. `yarn tokens` */
473
+ /* 시간을 0 으로 만들면 의미 토큰이 var() 로 따라온다. 끝 상태는 그대로 보인다 */
474
+ @media (prefers-reduced-motion: reduce) {
475
+ :root {
476
+ --duration-xs: 0ms;
477
+ --duration-s: 0ms;
478
+ --duration-m: 0ms;
479
+ --duration-l: 0ms;
480
+ --duration-xl: 0ms;
481
+ --duration-spring-rapid: 0ms;
482
+ --duration-spring-quick: 0ms;
483
+ --duration-spring-small: 0ms;
484
+ --duration-spring-medium: 0ms;
485
+ --duration-spring-basic: 0ms;
486
+ --duration-spring-large: 0ms;
487
+ --duration-spring-bounce: 0ms;
488
+ --duration-spring-slow: 0ms;
489
+ --motion-reveal-gap: 0ms;
490
+ --motion-attention-shine: 0ms;
491
+ --motion-attention-blink: 0ms;
492
+ --motion-busy-ring: 0ms;
493
+ --motion-busy-shimmer: 0ms;
494
+ --motion-busy-dots: 0ms;
495
+ }
496
+ }
382
497
  /*
383
- * 크기·두께·레이어·모션. figma.json 에서 만들지 않고 손으로 유지한다(font-face.css 와 같다).
498
+ * 크기·두께·레이어·옛 모션. figma.json 에서 만들지 않고 손으로 유지한다(font-face.css 와 같다).
384
499
  * 디자이너가 숫자를 바꾸는 곳은 여기다. 컴포넌트 CSS 는 숫자를 쓰지 않고 이 변수를 쓴다(빌드가 검사).
385
500
  * 크기 이름은 xs · s · m · l · xl 다섯 개다. 컴포넌트는 그중 지원하는 것만 고른다.
386
501
  */
@@ -585,6 +700,167 @@ p {
585
700
  .nuri-overflow-y-auto {
586
701
  overflow-y: auto;
587
702
  }
703
+ /* 공용 키프레임. 컴포넌트 CSS 보다 앞에 둔다 */
704
+ /*
705
+ * 공용 키프레임. 컴포넌트 CSS 는 @keyframes 를 두지 않고 이 이름을 쓴다(빌드 R18).
706
+ * 시간·곡선은 쓰는 쪽 animation 에서 --motion-{토큰}-* 로 준다. 여기는 모양만 정한다.
707
+ * 쓰는 쪽이 두는 입력 변수: --motion-slide-offset(transform 값), --motion-swipe-from, --motion-swipe-to.
708
+ */
709
+ @keyframes motion-enter-fade {
710
+ from {
711
+ opacity: 0;
712
+ }
713
+ to {
714
+ opacity: 1;
715
+ }
716
+ }
717
+ @keyframes motion-exit-fade {
718
+ from {
719
+ opacity: 1;
720
+ }
721
+ to {
722
+ opacity: 0;
723
+ }
724
+ }
725
+ @keyframes motion-enter-scale {
726
+ from {
727
+ opacity: 0;
728
+ transform: scale(var(--motion-enter-scale-from));
729
+ }
730
+ to {
731
+ opacity: 1;
732
+ transform: none;
733
+ }
734
+ }
735
+ @keyframes motion-exit-scale {
736
+ from {
737
+ opacity: 1;
738
+ transform: none;
739
+ }
740
+ to {
741
+ opacity: 0;
742
+ transform: scale(var(--motion-enter-scale-from));
743
+ }
744
+ }
745
+ @keyframes motion-enter-slide {
746
+ from {
747
+ transform: var(--motion-slide-offset);
748
+ }
749
+ to {
750
+ transform: none;
751
+ }
752
+ }
753
+ @keyframes motion-exit-slide {
754
+ from {
755
+ transform: none;
756
+ }
757
+ to {
758
+ transform: var(--motion-slide-offset);
759
+ }
760
+ }
761
+ @keyframes motion-exit-swipe {
762
+ from {
763
+ transform: translateX(var(--motion-swipe-from));
764
+ }
765
+ to {
766
+ transform: translateX(var(--motion-swipe-to));
767
+ }
768
+ }
769
+ @keyframes motion-reject-wiggle {
770
+ 0%,
771
+ 100% {
772
+ transform: none;
773
+ }
774
+ 25% {
775
+ transform: translateX(var(--motion-reject-distance));
776
+ }
777
+ 50% {
778
+ transform: translateX(calc(var(--motion-reject-distance) * -1));
779
+ }
780
+ 75% {
781
+ transform: translateX(calc(var(--motion-reject-distance) / 2));
782
+ }
783
+ }
784
+ @keyframes motion-reject-shake {
785
+ 0%,
786
+ 100% {
787
+ transform: none;
788
+ }
789
+ 15%,
790
+ 45%,
791
+ 75% {
792
+ transform: translateX(calc(var(--motion-reject-distance) * -2));
793
+ }
794
+ 30%,
795
+ 60% {
796
+ transform: translateX(calc(var(--motion-reject-distance) * 2));
797
+ }
798
+ 90% {
799
+ transform: translateX(var(--motion-reject-distance));
800
+ }
801
+ }
802
+ @keyframes motion-attention-shine {
803
+ from {
804
+ transform: translateX(-100%);
805
+ }
806
+ to {
807
+ transform: translateX(100%);
808
+ }
809
+ }
810
+ @keyframes motion-attention-blink {
811
+ 0%,
812
+ 100% {
813
+ opacity: 1;
814
+ }
815
+ 50% {
816
+ opacity: 0;
817
+ }
818
+ }
819
+ @keyframes motion-busy-dots {
820
+ 0%,
821
+ 80%,
822
+ 100% {
823
+ opacity: 0;
824
+ }
825
+ 40% {
826
+ opacity: 1;
827
+ }
828
+ }
829
+ @keyframes motion-busy-ring {
830
+ from {
831
+ transform: rotate(0deg);
832
+ }
833
+ to {
834
+ transform: rotate(360deg);
835
+ }
836
+ }
837
+ @keyframes motion-busy-shimmer {
838
+ from {
839
+ transform: translateX(-100%);
840
+ }
841
+ to {
842
+ transform: translateX(100%);
843
+ }
844
+ }
845
+ @keyframes motion-enter-pop {
846
+ from {
847
+ opacity: 0;
848
+ transform: var(--motion-slide-offset) scale(var(--motion-enter-pop-scale));
849
+ }
850
+ to {
851
+ opacity: 1;
852
+ transform: none;
853
+ }
854
+ }
855
+ @keyframes motion-attention-float {
856
+ 0%,
857
+ 100% {
858
+ transform: none;
859
+ }
860
+ 50% {
861
+ transform: translateY(calc(var(--motion-attention-float-distance) * -1));
862
+ }
863
+ }
588
864
  /* Component */
589
865
  .base-avatar {
590
866
  border-radius: 50%;
@@ -728,6 +1004,10 @@ p {
728
1004
  display: flex;
729
1005
  align-items: center;
730
1006
  justify-content: center;
1007
+ /* width="fit-content" 일 때 글자가 테두리에 붙지 않게 양옆을 남긴다 */
1008
+ padding: 0 var(--spacing-12);
1009
+ box-sizing: border-box;
1010
+ white-space: nowrap;
731
1011
 
732
1012
  border-width: var(--hairline);
733
1013
  border-style: solid;
@@ -742,6 +1022,9 @@ p {
742
1022
  align-items: center;
743
1023
  justify-content: center;
744
1024
  gap: var(--spacing-4);
1025
+ padding: 0 var(--spacing-12);
1026
+ box-sizing: border-box;
1027
+ white-space: nowrap;
745
1028
 
746
1029
  border-width: var(--hairline);
747
1030
  border-style: solid;
@@ -783,12 +1066,47 @@ p {
783
1066
  background-color: var(--color-accent-red-50);
784
1067
  border-color: transparent;
785
1068
  }
1069
+ /* Toast 의 success·warning 과 같은 계열. 글자는 800 단계라 50 바탕에서 대비 6.8:1 */
1070
+ .variant-success:is(.option,.icon-text,.icon-text-option) {
1071
+ color: var(--color-accent-green-800);
1072
+ background-color: var(--color-accent-green-50);
1073
+ border-color: transparent;
1074
+ }
1075
+ .variant-warning:is(.option,.icon-text,.icon-text-option) {
1076
+ color: var(--color-accent-orange-800);
1077
+ background-color: var(--color-accent-orange-50);
1078
+ border-color: transparent;
1079
+ }
786
1080
  /* icon-text-option 은 테두리가 없는 모양이라 두께·스타일까지 여기서 준다. */
787
1081
  .variant-outlined:is(.option,.icon-text,.icon-text-option) {
788
1082
  color: var(--color-black);
789
1083
  background-color: var(--color-white);
790
1084
  border: var(--hairline) solid var(--color-grey-scale-300);
791
1085
  }
1086
+ /* 스크롤하는 조상의 맨 아래에 붙는다. 모션은 없다 — 주 행동·입력을 담아 스크롤로 숨기지 않는다 */
1087
+ .base-bottom-bar {
1088
+ position: sticky;
1089
+ bottom: 0;
1090
+ z-index: var(--z-sticky);
1091
+ display: flex;
1092
+ }
1093
+ .base-bottom-bar > * {
1094
+ flex: 1;
1095
+ min-width: 0;
1096
+ }
1097
+ /***************************************************************************************************
1098
+ * *
1099
+ * bottom-bar *
1100
+ * *
1101
+ ***************************************************************************************************/
1102
+ .bottom-bar {
1103
+ gap: var(--spacing-8);
1104
+ padding: var(--spacing-12) var(--spacing-16);
1105
+ /* 기기 홈 막대만큼 아래를 더 비운다. env() 의 대체값 0 은 단위가 있어야 calc 가 성립한다 */
1106
+ padding-bottom: calc(var(--spacing-12) + env(safe-area-inset-bottom, 0px)); /* raw-px */
1107
+ border-top: var(--hairline) solid var(--color-grey-scale-200);
1108
+ background-color: var(--color-white);
1109
+ }
792
1110
  .base-bottom-nav:where(.root) {
793
1111
  position: fixed;
794
1112
  left: var(--spacing-16);
@@ -1164,12 +1482,12 @@ p {
1164
1482
  /* 나타날 때 꼬리에서 튀어나온 뒤 세 번 둥실 움직이고 멈춘다 (ADR 0007). 계속 움직이면 거슬린다.
1165
1483
  둥실 지연 = 팝 지연 + 팝 시간. 팝 쪽을 바꾸면 둥실 지연도 같이 바꾼다 */
1166
1484
  --callout-origin: var(--spacing-16) 100%;
1167
- --callout-enter: translateY(var(--spacing-4));
1485
+ --motion-slide-offset: translateY(var(--spacing-4));
1168
1486
 
1169
1487
  transform-origin: var(--callout-origin);
1170
1488
  animation:
1171
- callout-pop var(--motion-slow) var(--motion-spring) var(--motion-fast) both,
1172
- callout-float calc(var(--motion-base) * 8) var(--motion-ease)
1489
+ motion-enter-pop var(--motion-slow) var(--motion-spring) var(--motion-fast) both,
1490
+ motion-attention-float calc(var(--motion-base) * 8) var(--motion-ease)
1173
1491
  calc(var(--motion-fast) + var(--motion-slow)) 3;
1174
1492
  }
1175
1493
  .base-callout.tail-bottom-end {
@@ -1177,7 +1495,7 @@ p {
1177
1495
  }
1178
1496
  .base-callout:is(.tail-top-start,.tail-top-end) {
1179
1497
  --callout-origin: var(--spacing-16) 0;
1180
- --callout-enter: translateY(calc(var(--spacing-4) * -1));
1498
+ --motion-slide-offset: translateY(calc(var(--spacing-4) * -1));
1181
1499
  }
1182
1500
  .base-callout.tail-top-end {
1183
1501
  --callout-origin: calc(100% - var(--spacing-16)) 0;
@@ -1208,32 +1526,47 @@ p {
1208
1526
  .base-callout:is(.tail-bottom-end,.tail-top-end)::after {
1209
1527
  right: var(--spacing-12);
1210
1528
  }
1211
- @keyframes callout-pop {
1212
- from {
1213
- opacity: 0;
1214
- transform: var(--callout-enter) scale(0.6);
1215
- }
1216
-
1217
- to {
1218
- opacity: 1;
1219
- transform: none;
1220
- }
1221
- }
1222
- @keyframes callout-float {
1223
- 0%,
1224
- 100% {
1225
- transform: none;
1226
- }
1227
-
1228
- 50% {
1229
- transform: translateY(calc(var(--spacing-6) / -2));
1230
- }
1231
- }
1232
1529
  @media (prefers-reduced-motion: reduce) {
1233
1530
  .base-callout {
1234
1531
  animation: none;
1235
1532
  }
1236
1533
  }
1534
+ .base-card {
1535
+ display: flex;
1536
+ flex-direction: column;
1537
+ }
1538
+ /***************************************************************************************************
1539
+ * *
1540
+ * card *
1541
+ * *
1542
+ ***************************************************************************************************/
1543
+ .card {
1544
+ gap: var(--spacing-12);
1545
+ padding: var(--spacing-16);
1546
+ border: var(--hairline) solid var(--color-grey-scale-200);
1547
+ border-radius: var(--radius-16);
1548
+ background-color: var(--color-white);
1549
+ }
1550
+ .card.variant-tinted {
1551
+ border-color: var(--color-purple-100);
1552
+ background-color: var(--color-purple-50);
1553
+ }
1554
+ /* 누르는 카드(링크·버튼)만 눌림 모션을 받는다. 보기만 하는 카드는 움직이지 않는다 */
1555
+ .card:is(a[href],button) {
1556
+ width: 100%;
1557
+ color: inherit;
1558
+ text-align: start;
1559
+ text-decoration: none;
1560
+ cursor: pointer;
1561
+ transition: transform var(--motion-press-duration) var(--motion-press-ease);
1562
+ }
1563
+ .card:is(a[href],button):active {
1564
+ transform: scale(var(--motion-press-scale));
1565
+ }
1566
+ .card:is(a[href],button):focus-visible {
1567
+ outline: var(--hairline) solid var(--color-purple-600);
1568
+ outline-offset: var(--spacing-2);
1569
+ }
1237
1570
  .base-check-box.label {
1238
1571
  font-size: var(--font-size-sm);
1239
1572
  font-weight: var(--font-weight-medium);
@@ -1444,6 +1777,53 @@ p {
1444
1777
  margin-top: var(--spacing-2);
1445
1778
  margin-bottom: var(--spacing-4);
1446
1779
  }
1780
+ .base-description-list {
1781
+ display: flex;
1782
+ flex-direction: column;
1783
+ margin: 0;
1784
+ }
1785
+ /***************************************************************************************************
1786
+ * *
1787
+ * description-list *
1788
+ * *
1789
+ ***************************************************************************************************/
1790
+ .description-list {
1791
+ gap: var(--spacing-8);
1792
+ }
1793
+ .description-list-item {
1794
+ display: flex;
1795
+ justify-content: space-between;
1796
+ gap: var(--spacing-12);
1797
+ }
1798
+ .description-list-item > dt {
1799
+ color: var(--color-grey-scale-500);
1800
+ font-size: var(--font-size-sm);
1801
+ line-height: var(--line-height-sm);
1802
+ }
1803
+ .description-list-item > dd {
1804
+ margin: 0;
1805
+ color: var(--color-grey-scale-900);
1806
+ font-size: var(--font-size-sm);
1807
+ line-height: var(--line-height-sm);
1808
+ font-weight: var(--font-weight-medium);
1809
+ text-align: end;
1810
+ }
1811
+ /* 합계 줄. 구분선 아래 값이 한 단계 크고 굵다 */
1812
+ .description-list-item.total {
1813
+ align-items: baseline;
1814
+ margin-top: var(--spacing-4);
1815
+ padding-top: var(--spacing-12);
1816
+ border-top: var(--hairline) solid var(--color-grey-scale-200);
1817
+ }
1818
+ .description-list-item.total > dt {
1819
+ color: var(--color-grey-scale-900);
1820
+ font-weight: var(--font-weight-semibold);
1821
+ }
1822
+ .description-list-item.total > dd {
1823
+ font-size: var(--font-size-base);
1824
+ line-height: var(--line-height-base);
1825
+ font-weight: var(--font-weight-bold);
1826
+ }
1447
1827
  /* Title·Close 같은 조각도 base-drawer 를 달므로 상자 스타일은 overlay·content 에만 건다. */
1448
1828
  .base-drawer:where(.overlay) {
1449
1829
  position: fixed;
@@ -1455,10 +1835,10 @@ p {
1455
1835
  z-index: var(--z-overlay);
1456
1836
  }
1457
1837
  .base-drawer[data-state='open']:where(.overlay) {
1458
- animation: drawer-overlay-in var(--motion-base) var(--motion-ease);
1838
+ animation: motion-enter-fade var(--motion-base) var(--motion-ease);
1459
1839
  }
1460
1840
  .base-drawer[data-state='closed']:where(.overlay) {
1461
- animation: drawer-overlay-out var(--motion-base) var(--motion-ease);
1841
+ animation: motion-exit-fade var(--motion-base) var(--motion-ease);
1462
1842
  }
1463
1843
  .base-drawer:where(.content) {
1464
1844
  position: fixed;
@@ -1493,39 +1873,7 @@ p {
1493
1873
  * drawer *
1494
1874
  * *
1495
1875
  ***************************************************************************************************/
1496
- /* Radix 는 애니메이션이 끝나야 닫힌 패널을 지우므로 transition 이 아니라 keyframes 로 들어오고 나간다. */
1497
- @keyframes drawer-overlay-in {
1498
- from {
1499
- opacity: 0;
1500
- }
1501
- to {
1502
- opacity: 1;
1503
- }
1504
- }
1505
- @keyframes drawer-overlay-out {
1506
- from {
1507
- opacity: 1;
1508
- }
1509
- to {
1510
- opacity: 0;
1511
- }
1512
- }
1513
- @keyframes drawer-slide-in {
1514
- from {
1515
- transform: var(--drawer-offset);
1516
- }
1517
- to {
1518
- transform: translate(0, 0);
1519
- }
1520
- }
1521
- @keyframes drawer-slide-out {
1522
- from {
1523
- transform: translate(0, 0);
1524
- }
1525
- to {
1526
- transform: var(--drawer-offset);
1527
- }
1528
- }
1876
+ /* Radix 는 애니메이션이 끝나야 닫힌 패널을 지우므로 transition 이 아니라 keyframes 로 들어오고 나간다(styles/motion.css). */
1529
1877
  .drawer {
1530
1878
  gap: var(--spacing-8);
1531
1879
  padding: var(--spacing-24);
@@ -1549,7 +1897,7 @@ p {
1549
1897
  bottom: 0;
1550
1898
  width: var(--drawer-width);
1551
1899
  max-width: 100%;
1552
- --drawer-offset: translateX(100%);
1900
+ --motion-slide-offset: translateX(100%);
1553
1901
  }
1554
1902
  .drawer.side-left {
1555
1903
  top: 0;
@@ -1557,7 +1905,7 @@ p {
1557
1905
  bottom: 0;
1558
1906
  width: var(--drawer-width);
1559
1907
  max-width: 100%;
1560
- --drawer-offset: translateX(-100%);
1908
+ --motion-slide-offset: translateX(-100%);
1561
1909
  }
1562
1910
  .drawer.side-bottom {
1563
1911
  left: 0;
@@ -1567,13 +1915,13 @@ p {
1567
1915
  max-height: 100%;
1568
1916
  border-top-left-radius: var(--radius-16);
1569
1917
  border-top-right-radius: var(--radius-16);
1570
- --drawer-offset: translateY(100%);
1918
+ --motion-slide-offset: translateY(100%);
1571
1919
  }
1572
1920
  .drawer[data-state='open'] {
1573
- animation: drawer-slide-in var(--motion-base) var(--motion-ease);
1921
+ animation: motion-enter-slide var(--motion-base) var(--motion-ease);
1574
1922
  }
1575
1923
  .drawer[data-state='closed'] {
1576
- animation: drawer-slide-out var(--motion-base) var(--motion-ease);
1924
+ animation: motion-exit-slide var(--motion-base) var(--motion-ease);
1577
1925
  }
1578
1926
  .drawer-header {
1579
1927
  display: flex;
@@ -2133,13 +2481,62 @@ p {
2133
2481
  .base-text.color-accent-orange-900[class*='color-'] {
2134
2482
  color: var(--color-accent-orange-900);
2135
2483
  }
2136
- .base-icon {
2137
- flex-shrink: 0;
2138
- vertical-align: middle;
2484
+ /* 빈 목록·오류·찾을 수 없음·준비 중 화면. 가운데 세로로 쌓는다 */
2485
+ .base-empty-state {
2486
+ display: flex;
2487
+ flex-direction: column;
2488
+ align-items: center;
2489
+ text-align: center;
2139
2490
  }
2140
- .icon.size-xs {
2141
- width: var(--icon-size-xs);
2142
- height: var(--icon-size-xs);
2491
+ /***************************************************************************************************
2492
+ * *
2493
+ * empty-state *
2494
+ * *
2495
+ ***************************************************************************************************/
2496
+ .empty-state {
2497
+ gap: var(--spacing-8);
2498
+ padding: var(--spacing-24) var(--spacing-16);
2499
+
2500
+ /* variant 는 아이콘 원 색만 바꾼다 */
2501
+ }
2502
+ .empty-state.variant-neutral .empty-state-icon {
2503
+ background-color: var(--color-grey-scale-100);
2504
+ color: var(--color-grey-scale-600);
2505
+ }
2506
+ .empty-state-icon {
2507
+ display: inline-flex;
2508
+ align-items: center;
2509
+ justify-content: center;
2510
+ width: var(--empty-state-icon-box);
2511
+ height: var(--empty-state-icon-box);
2512
+ margin-bottom: var(--spacing-8);
2513
+ border-radius: var(--radius-full);
2514
+ background-color: var(--color-purple-50);
2515
+ color: var(--color-purple-900);
2516
+ }
2517
+ .empty-state-title {
2518
+ font-size: var(--font-size-base);
2519
+ line-height: var(--line-height-base);
2520
+ font-weight: var(--font-weight-semibold);
2521
+ color: var(--color-grey-scale-900);
2522
+ word-break: break-word;
2523
+ }
2524
+ .empty-state-description {
2525
+ font-size: var(--font-size-sm);
2526
+ line-height: var(--line-height-sm);
2527
+ color: var(--color-grey-scale-500);
2528
+ word-break: break-word;
2529
+ }
2530
+ .empty-state-action {
2531
+ margin-top: var(--spacing-8);
2532
+ }
2533
+ .base-icon {
2534
+ flex-shrink: 0;
2535
+ vertical-align: middle;
2536
+ }
2537
+ .icon.size-xs {
2538
+ width: var(--icon-size-xs);
2539
+ height: var(--icon-size-xs);
2143
2540
  }
2144
2541
  .icon.size-s {
2145
2542
  width: var(--icon-size-s);
@@ -2157,6 +2554,115 @@ p {
2157
2554
  width: var(--icon-size-xl);
2158
2555
  height: var(--icon-size-xl);
2159
2556
  }
2557
+ .base-list:where(.group) {
2558
+ display: flex;
2559
+ flex-direction: column;
2560
+ }
2561
+ /* 행은 div·button·링크 어느 것이든 같은 모양이다 */
2562
+ .base-list:where(.row) {
2563
+ position: relative;
2564
+ display: flex;
2565
+ align-items: center;
2566
+ box-sizing: border-box;
2567
+ width: 100%;
2568
+ border: 0;
2569
+ background-color: transparent;
2570
+ color: inherit;
2571
+ text-align: start;
2572
+ text-decoration: none;
2573
+ }
2574
+ /***************************************************************************************************
2575
+ * *
2576
+ * list-group *
2577
+ * *
2578
+ ***************************************************************************************************/
2579
+ .list-group {
2580
+ gap: var(--spacing-8);
2581
+ }
2582
+ .list-group-label {
2583
+ padding: 0 var(--spacing-4);
2584
+ color: var(--color-grey-scale-500);
2585
+ font-size: var(--font-size-sm);
2586
+ line-height: var(--line-height-sm);
2587
+ font-weight: var(--font-weight-semibold);
2588
+ }
2589
+ .list-group-surface {
2590
+ display: flex;
2591
+ flex-direction: column;
2592
+ overflow: hidden;
2593
+ border: var(--hairline) solid var(--color-grey-scale-200);
2594
+ border-radius: var(--radius-16);
2595
+ background-color: var(--color-white);
2596
+ }
2597
+ /***************************************************************************************************
2598
+ * *
2599
+ * list-row *
2600
+ * *
2601
+ ***************************************************************************************************/
2602
+ .list-row {
2603
+ min-height: var(--list-row-height);
2604
+ gap: var(--spacing-12);
2605
+ padding: var(--spacing-10) var(--spacing-16);
2606
+
2607
+ /* 누르는 행(링크·버튼)은 줄어들지 않고 배경이 어두워진다. 폭이 꽉 찬 면이라 scale 이 어색하다 */
2608
+ }
2609
+ .list-row:is(a[href],button) {
2610
+ cursor: pointer;
2611
+ transition: background-color var(--motion-press-duration) var(--motion-press-ease);
2612
+ }
2613
+ .list-row:is(a[href],button):active {
2614
+ background-color: var(--color-grey-scale-100);
2615
+ }
2616
+ /* 면이 overflow: hidden 이라 테두리 안쪽에 그린다 */
2617
+ .list-row:is(a[href],button):focus-visible {
2618
+ outline: var(--hairline) solid var(--color-purple-600);
2619
+ outline-offset: calc(var(--spacing-2) * -1);
2620
+ }
2621
+ /* 행 사이 구분선. 왼쪽은 안쪽 여백만큼 띄운다 */
2622
+ .list-row + .list-row::before {
2623
+ content: '';
2624
+ position: absolute;
2625
+ top: 0;
2626
+ right: 0;
2627
+ left: var(--spacing-16);
2628
+ height: var(--hairline);
2629
+ background-color: var(--color-grey-scale-100);
2630
+ }
2631
+ .list-row-leading {
2632
+ display: flex;
2633
+ flex: none;
2634
+ color: var(--color-grey-scale-700);
2635
+ }
2636
+ .list-row-text {
2637
+ display: flex;
2638
+ flex: 1;
2639
+ flex-direction: column;
2640
+ gap: var(--spacing-2);
2641
+ min-width: 0;
2642
+ }
2643
+ .list-row-label {
2644
+ color: var(--color-grey-scale-900);
2645
+ font-size: var(--font-size-base);
2646
+ line-height: var(--line-height-base);
2647
+ font-weight: var(--font-weight-medium);
2648
+ }
2649
+ .list-row-description {
2650
+ color: var(--color-grey-scale-500);
2651
+ font-size: var(--font-size-sm);
2652
+ line-height: var(--line-height-sm);
2653
+ }
2654
+ .list-row-trailing {
2655
+ display: flex;
2656
+ flex: none;
2657
+ align-items: center;
2658
+ color: var(--color-grey-scale-500);
2659
+ font-size: var(--font-size-sm);
2660
+ line-height: var(--line-height-sm);
2661
+ }
2662
+ .list-row-chevron {
2663
+ flex: none;
2664
+ color: var(--color-grey-scale-400);
2665
+ }
2160
2666
  .base-inline-loading {
2161
2667
  display: flex;
2162
2668
  width: 100%;
@@ -2220,7 +2726,7 @@ p {
2220
2726
  height: 100%;
2221
2727
  border: var(--spinner-stroke) solid;
2222
2728
  border-radius: 50%;
2223
- animation: lds-ring var(--spinner-duration) var(--spinner-ease) infinite;
2729
+ animation: motion-busy-ring var(--spinner-duration) var(--spinner-ease) infinite;
2224
2730
  border-color: currentColor transparent transparent transparent;
2225
2731
  }
2226
2732
  .loading-indicator-single-ring:nth-child(1) {
@@ -2235,13 +2741,361 @@ p {
2235
2741
  .loading-indicator-single-ring:nth-child(4) {
2236
2742
  animation-delay: 0s;
2237
2743
  }
2238
- @keyframes lds-ring {
2239
- 0% {
2240
- transform: rotate(0deg);
2744
+ /* Title·Close 같은 조각도 base-media 를 달므로 상자 스타일은 overlay·content 에만 건다. */
2745
+ .base-media:where(.overlay) {
2746
+ position: fixed;
2747
+ inset: 0;
2748
+ z-index: var(--z-overlay);
2749
+ background-color: color-mix(in srgb, var(--color-black) 90%, transparent);
2241
2750
  }
2242
- 100% {
2243
- transform: rotate(360deg);
2751
+ .base-media[data-state='open']:where(.overlay) {
2752
+ animation: motion-enter-fade var(--motion-enter-duration) var(--motion-enter-ease);
2753
+ }
2754
+ .base-media[data-state='closed']:where(.overlay) {
2755
+ animation: motion-exit-fade var(--motion-exit-duration) var(--motion-exit-ease);
2756
+ }
2757
+ .base-media:where(.content) {
2758
+ position: fixed;
2759
+ inset: 0;
2760
+ z-index: var(--z-modal);
2761
+ display: flex;
2762
+ align-items: center;
2763
+ justify-content: center;
2764
+ }
2765
+ .base-media:where(.close) {
2766
+ display: inline-flex;
2767
+ align-items: center;
2768
+ justify-content: center;
2769
+ padding: 0;
2770
+ border: none;
2771
+ background-color: transparent;
2772
+ cursor: var(--cursor-button);
2773
+ }
2774
+ /***************************************************************************************************
2775
+ * *
2776
+ * media-viewer *
2777
+ * *
2778
+ ***************************************************************************************************/
2779
+ /* Radix 는 애니메이션이 끝나야 닫힌 Content 를 지우므로 keyframes 로 들어오고 나간다(styles/motion.css). */
2780
+ .media-viewer[data-state='open'] {
2781
+ animation: motion-enter-scale var(--motion-enter-duration) var(--motion-enter-ease);
2782
+ }
2783
+ .media-viewer[data-state='closed'] {
2784
+ animation: motion-exit-scale var(--motion-exit-duration) var(--motion-exit-ease);
2785
+ }
2786
+ .media-viewer-media {
2787
+ display: block;
2788
+ max-width: 100%;
2789
+ max-height: 100%;
2790
+ -o-object-fit: contain;
2791
+ object-fit: contain;
2792
+ }
2793
+ /* 노치 아래로 숨지 않게 안전 영역만큼 내린다 */
2794
+ .media-viewer-close {
2795
+ position: absolute;
2796
+ top: calc(env(safe-area-inset-top) + var(--spacing-8));
2797
+ right: calc(env(safe-area-inset-right) + var(--spacing-8));
2798
+ width: var(--touch-target);
2799
+ height: var(--touch-target);
2800
+ color: var(--color-white);
2801
+ }
2802
+ /***************************************************************************************************
2803
+ * *
2804
+ * media-thumbnail *
2805
+ * *
2806
+ ***************************************************************************************************/
2807
+ .media-thumbnail {
2808
+ position: relative;
2809
+ flex-shrink: 0;
2810
+ }
2811
+ .media-thumbnail.size-s {
2812
+ width: var(--media-thumbnail-size-s);
2813
+ height: var(--media-thumbnail-size-s);
2814
+ }
2815
+ .media-thumbnail.size-m {
2816
+ width: var(--media-thumbnail-size-m);
2817
+ height: var(--media-thumbnail-size-m);
2818
+ }
2819
+ /* 말풍선 안. 폭을 채우되 넘지 않는다 */
2820
+ .media-thumbnail.size-l {
2821
+ width: 100%;
2822
+ max-width: var(--media-thumbnail-max-width);
2823
+ aspect-ratio: 4 / 3;
2824
+ }
2825
+ /* base-button(border·radius·focus 를 지운다)보다 우선해야 해서 .media-thumbnail 아래에 둔다 */
2826
+ .media-thumbnail .media-thumbnail-open {
2827
+ position: relative;
2828
+ width: 100%;
2829
+ height: 100%;
2830
+ padding: 0;
2831
+ overflow: hidden;
2832
+ box-sizing: border-box;
2833
+ border-radius: var(--radius-6);
2834
+ background-color: var(--color-grey-scale-200);
2835
+ transition: transform var(--motion-press-duration) var(--motion-press-ease);
2836
+ }
2837
+ :is(.media-thumbnail .media-thumbnail-open):active:not([data-disabled]) {
2838
+ transform: scale(var(--motion-press-scale));
2839
+ }
2840
+ [data-invalid]:is(.media-thumbnail .media-thumbnail-open) {
2841
+ border: var(--media-invalid-border) solid var(--color-accent-red-500);
2842
+ }
2843
+ :is(.media-thumbnail .media-thumbnail-open):focus-visible {
2844
+ outline: var(--hairline) solid var(--color-purple-600);
2845
+ outline-offset: var(--spacing-2);
2846
+ }
2847
+ .media-thumbnail .media-thumbnail-remove {
2848
+ position: absolute;
2849
+ top: var(--spacing-4);
2850
+ right: var(--spacing-4);
2851
+ width: var(--media-remove-size);
2852
+ height: var(--media-remove-size);
2853
+ padding: 0;
2854
+ border-radius: var(--radius-full);
2855
+ background-color: color-mix(in srgb, var(--color-grey-scale-900) 80%, transparent);
2856
+ color: var(--color-white);
2857
+
2858
+ /* 20 원을 터치 44 로 넓힌다 */
2859
+ }
2860
+ :is(.media-thumbnail .media-thumbnail-remove)::after {
2861
+ content: '';
2862
+ position: absolute;
2863
+ top: 50%;
2864
+ left: 50%;
2865
+ width: var(--touch-target);
2866
+ height: var(--touch-target);
2867
+ transform: translate(-50%, -50%);
2868
+ }
2869
+ :is(.media-thumbnail .media-thumbnail-remove):focus-visible {
2870
+ outline: var(--hairline) solid var(--color-purple-600);
2871
+ outline-offset: var(--spacing-2);
2872
+ }
2873
+ .media-thumbnail-media {
2874
+ display: block;
2875
+ width: 100%;
2876
+ height: 100%;
2877
+ -o-object-fit: cover;
2878
+ object-fit: cover;
2879
+ }
2880
+ .media-thumbnail-play {
2881
+ position: absolute;
2882
+ top: 50%;
2883
+ left: 50%;
2884
+ display: flex;
2885
+ align-items: center;
2886
+ justify-content: center;
2887
+ width: var(--media-overlay-button-size);
2888
+ height: var(--media-overlay-button-size);
2889
+ border-radius: var(--radius-full);
2890
+ background-color: color-mix(in srgb, var(--color-black) 50%, transparent);
2891
+ color: var(--color-white);
2892
+ transform: translate(-50%, -50%);
2893
+ pointer-events: none;
2894
+ }
2895
+ .media-thumbnail-loading {
2896
+ position: absolute;
2897
+ inset: 0;
2898
+ display: flex;
2899
+ align-items: center;
2900
+ justify-content: center;
2901
+ background-color: color-mix(in srgb, var(--color-white) 60%, transparent);
2902
+ }
2903
+ /* 흰 막 위라 기본 링 색(grey-200)은 안 보인다 */
2904
+ .media-thumbnail-spinner {
2905
+ color: var(--color-grey-scale-600);
2906
+ }
2907
+ .base-message-composer:where(.root) {
2908
+ display: flex;
2909
+ flex-direction: column;
2910
+ }
2911
+ .base-message-composer:where(.textarea) {
2912
+ box-sizing: border-box;
2913
+ margin: 0;
2914
+ font-family: inherit;
2915
+ resize: none;
2916
+ }
2917
+ .base-message-composer[data-disabled]:where(.textarea) {
2918
+ cursor: var(--cursor-disabled);
2919
+ }
2920
+ .base-message-composer:where(.send) {
2921
+ display: grid;
2922
+ place-items: center;
2923
+ flex: none;
2924
+ padding: 0;
2925
+ border: none;
2926
+ background: transparent;
2927
+ cursor: pointer;
2928
+ -webkit-tap-highlight-color: transparent;
2929
+ }
2930
+ /***************************************************************************************************
2931
+ * *
2932
+ * message-composer *
2933
+ * *
2934
+ ***************************************************************************************************/
2935
+ .message-composer {
2936
+ gap: var(--spacing-8);
2937
+ }
2938
+ .message-composer-attachments {
2939
+ display: flex;
2940
+ gap: var(--spacing-8);
2941
+ overflow-x: auto;
2942
+ }
2943
+ /* 입력 칸이 늘어나도 아이콘·보내기 버튼은 아래 줄에 붙는다 */
2944
+ .message-composer-row {
2945
+ display: flex;
2946
+ align-items: flex-end;
2947
+ gap: var(--spacing-4);
2948
+ }
2949
+ /* 16px 글자 — iOS 가 입력 칸에 포커스할 때 화면을 확대하지 않는다. 높이는 글자만큼 늘고 max-height 에서 스크롤한다 */
2950
+ .message-composer-textarea {
2951
+ flex: 1;
2952
+ min-width: 0;
2953
+ min-height: var(--touch-target);
2954
+ max-height: var(--composer-max-height);
2955
+ field-sizing: content;
2956
+ padding: var(--spacing-10) var(--spacing-16);
2957
+ border: var(--hairline) solid var(--color-grey-scale-300);
2958
+ border-radius: var(--radius-20);
2959
+ outline: none;
2960
+ background-color: var(--color-white);
2961
+ color: var(--color-grey-scale-900);
2962
+ font-size: var(--font-size-base);
2963
+ line-height: var(--line-height-base);
2964
+ }
2965
+ .message-composer-textarea::-moz-placeholder {
2966
+ color: var(--color-grey-scale-400);
2967
+ }
2968
+ .message-composer-textarea::placeholder {
2969
+ color: var(--color-grey-scale-400);
2970
+ }
2971
+ .message-composer-textarea:focus {
2972
+ border-color: var(--color-purple-900);
2973
+ }
2974
+ .message-composer-textarea[data-disabled] {
2975
+ background-color: var(--color-grey-scale-100);
2976
+ }
2977
+ /* 누르는 곳은 --touch-target, 보이는 원은 --composer-send-size */
2978
+ .message-composer-send {
2979
+ width: var(--touch-target);
2980
+ height: var(--touch-target);
2981
+ transition: transform var(--motion-press-duration) var(--motion-press-ease);
2982
+ }
2983
+ .message-composer-send:not([aria-disabled='true'],[aria-busy='true']):active {
2984
+ transform: scale(var(--motion-press-scale));
2985
+ }
2986
+ .message-composer-send[aria-disabled='true'] .message-composer-send-circle {
2987
+ background-color: var(--color-grey-scale-200);
2988
+ }
2989
+ .message-composer-send[data-rejecting] .message-composer-send-circle {
2990
+ animation: motion-reject-wiggle var(--motion-reject-duration) var(--motion-reject-ease);
2991
+ }
2992
+ .message-composer-send-circle {
2993
+ display: grid;
2994
+ place-items: center;
2995
+ width: var(--composer-send-size);
2996
+ height: var(--composer-send-size);
2997
+ border-radius: var(--radius-full);
2998
+ background-color: var(--color-purple-900);
2999
+ color: var(--color-white);
3000
+ }
3001
+ .message-composer-spinner {
3002
+ color: var(--color-white);
3003
+ }
3004
+ .base-message:where(.root) {
3005
+ display: flex;
3006
+ flex-direction: column;
3007
+ margin: 0;
3008
+ padding: 0;
3009
+ list-style: none;
3010
+ }
3011
+ .base-message:where(.item) {
3012
+ display: flex;
3013
+ flex-direction: column;
3014
+ }
3015
+ .base-message:where(.bubble) {
3016
+ display: flex;
3017
+ flex-direction: column;
3018
+ min-width: 0;
3019
+ }
3020
+ /***************************************************************************************************
3021
+ * *
3022
+ * message-thread · message · message-event *
3023
+ * *
3024
+ ***************************************************************************************************/
3025
+ .message-thread {
3026
+ gap: var(--spacing-12);
3027
+ }
3028
+ .message {
3029
+ gap: var(--spacing-4);
3030
+ align-items: flex-start;
3031
+ }
3032
+ .message.side-end {
3033
+ align-items: flex-end;
3034
+ }
3035
+ .message.side-end .message-row {
3036
+ flex-direction: row-reverse;
3037
+ }
3038
+ .message.side-end .message-bubble {
3039
+ border-radius: var(--radius-16) var(--radius-16) var(--radius-4) var(--radius-16);
3040
+ background-color: var(--color-purple-900);
3041
+ color: var(--color-white);
2244
3042
  }
3043
+ /* 방금 보내거나 받은 메시지만 움직인다. 처음 그린 대화는 motion 이 없어 가만히 있다 */
3044
+ .message:is(.motion-weak,.motion-strong) {
3045
+ --motion-slide-offset: translateY(var(--motion-enter-offset));
3046
+ }
3047
+ .message.motion-weak {
3048
+ animation:
3049
+ motion-enter-fade var(--motion-enter-duration) var(--motion-enter-ease),
3050
+ motion-enter-slide var(--motion-enter-duration) var(--motion-enter-ease);
3051
+ }
3052
+ .message.motion-strong {
3053
+ animation:
3054
+ motion-enter-fade var(--motion-enter-strong-duration) var(--motion-enter-strong-ease),
3055
+ motion-enter-slide var(--motion-enter-strong-duration) var(--motion-enter-strong-ease);
3056
+ }
3057
+ .message-name {
3058
+ font-size: var(--font-size-xsm);
3059
+ line-height: var(--line-height-xsm);
3060
+ font-weight: var(--font-weight-semibold);
3061
+ color: var(--color-grey-scale-700);
3062
+ }
3063
+ .message-row {
3064
+ display: flex;
3065
+ align-items: flex-end;
3066
+ gap: var(--spacing-6);
3067
+ max-width: 80%;
3068
+ }
3069
+ /* 보낸 쪽 아래 모서리만 덜 둥글다 */
3070
+ .message-bubble {
3071
+ gap: var(--spacing-6);
3072
+ padding: var(--spacing-10) var(--spacing-14);
3073
+ border-radius: var(--radius-16) var(--radius-16) var(--radius-16) var(--radius-4);
3074
+ background-color: var(--color-grey-scale-100);
3075
+ color: var(--color-grey-scale-900);
3076
+ font-size: var(--font-size-base);
3077
+ line-height: var(--line-height-base);
3078
+ white-space: pre-wrap;
3079
+ overflow-wrap: anywhere;
3080
+ }
3081
+ .message-bubble.media-only {
3082
+ padding: var(--spacing-6);
3083
+ }
3084
+ .message-time {
3085
+ flex: none;
3086
+ font-size: var(--font-size-xsm);
3087
+ line-height: var(--line-height-xsm);
3088
+ color: var(--color-grey-scale-500);
3089
+ }
3090
+ .message-event {
3091
+ align-self: center;
3092
+ padding: var(--spacing-2) var(--spacing-10);
3093
+ border-radius: var(--radius-full);
3094
+ background-color: var(--color-grey-scale-100);
3095
+ color: var(--color-grey-scale-600);
3096
+ font-size: var(--font-size-xsm);
3097
+ line-height: var(--line-height-xsm);
3098
+ font-weight: var(--font-weight-medium);
2245
3099
  }
2246
3100
  /* Title·Close 같은 조각도 base-modal 을 달므로 상자 스타일은 container·overlay 에만 건다. */
2247
3101
  .base-modal:where(.modal-container, .modal-overlay) {
@@ -2286,6 +3140,11 @@ button.modal-with-double-button-close-button {
2286
3140
  background-color: transparent;
2287
3141
  color: var(--color-grey-scale-600);
2288
3142
  }
3143
+ .modal-with-double-button-container {
3144
+ /* 폰 폭(390)에서 고정 폭이 화면을 넘지 않게 Modal 과 같이 양옆을 남긴다 */
3145
+ max-width: calc(100% - var(--spacing-30) * 2);
3146
+ box-sizing: border-box;
3147
+ }
2289
3148
  .modal-with-double-button-container.size-s {
2290
3149
  width: var(--modal-width-s);
2291
3150
  }
@@ -2400,6 +3259,69 @@ button.modal-with-double-button-close-button {
2400
3259
  align-items: center;
2401
3260
  gap: var(--spacing-10);
2402
3261
  }
3262
+ /* 카드·시트 안에 머무는 안내. 스스로 사라지는 알림은 Toast 다 */
3263
+ .base-notice {
3264
+ display: flex;
3265
+ align-items: flex-start;
3266
+ gap: var(--spacing-8);
3267
+ }
3268
+ /***************************************************************************************************
3269
+ * *
3270
+ * notice *
3271
+ * *
3272
+ ***************************************************************************************************/
3273
+ /* variant 가 바탕색과 글자·아이콘 색을 정한다 */
3274
+ .notice {
3275
+ padding: var(--spacing-12) var(--spacing-14);
3276
+ border-radius: var(--radius-6);
3277
+ background-color: var(--color-grey-scale-100);
3278
+ color: var(--color-grey-scale-700);
3279
+ font-size: var(--font-size-sm);
3280
+ line-height: var(--line-height-sm);
3281
+ }
3282
+ .notice.variant-success {
3283
+ background-color: var(--color-accent-green-50);
3284
+ color: var(--color-accent-green-800);
3285
+ }
3286
+ .notice.variant-warning {
3287
+ background-color: var(--color-accent-orange-50);
3288
+ color: var(--color-accent-orange-800);
3289
+ }
3290
+ .notice.variant-danger {
3291
+ background-color: var(--color-accent-red-50);
3292
+ color: var(--color-accent-red-700);
3293
+ }
3294
+ /* 조건부로 나타나는 안내만 motion 을 준다. 사라질 때는 움직이지 않는다 */
3295
+ .notice.motion-weak {
3296
+ animation: motion-enter-fade var(--motion-enter-duration) var(--motion-enter-ease);
3297
+ }
3298
+ .notice.motion-strong {
3299
+ animation: motion-enter-fade var(--motion-enter-strong-duration) var(--motion-enter-strong-ease);
3300
+ }
3301
+ /* 아이콘(18)을 첫 줄 글(18)에 맞춘다 */
3302
+ .notice-icon {
3303
+ flex: none;
3304
+ }
3305
+ .notice-body {
3306
+ display: flex;
3307
+ flex: 1;
3308
+ flex-direction: column;
3309
+ gap: var(--spacing-10);
3310
+ min-width: 0;
3311
+ }
3312
+ .notice-text {
3313
+ display: flex;
3314
+ flex-direction: column;
3315
+ gap: var(--spacing-2);
3316
+ word-break: break-word;
3317
+ }
3318
+ .notice-title {
3319
+ font-weight: var(--font-weight-semibold);
3320
+ }
3321
+ /* action 은 CTAButton(xs·neutral) 이라 모양은 버튼 CSS 가 정한다. 폭은 글자만큼 */
3322
+ .notice-action {
3323
+ align-self: flex-start;
3324
+ }
2403
3325
  .base-pagination:where(.item) {
2404
3326
  display: inline-flex;
2405
3327
  align-items: center;
@@ -2486,6 +3408,18 @@ button.modal-with-double-button-close-button {
2486
3408
  .base-radio:where(.button) {
2487
3409
  padding: 0;
2488
3410
  }
3411
+ .base-radio:where(.chip) {
3412
+ position: relative;
3413
+ display: inline-flex;
3414
+ align-items: center;
3415
+
3416
+ /* 36 높이를 터치 44 로 넓힌다 */
3417
+ }
3418
+ .base-radio:where(.chip)::after {
3419
+ content: '';
3420
+ position: absolute;
3421
+ inset: calc(-1 * var(--spacing-4)) 0;
3422
+ }
2489
3423
  /***************************************************************************************************
2490
3424
  * *
2491
3425
  * radio *
@@ -2533,6 +3467,46 @@ button.modal-with-double-button-close-button {
2533
3467
  .radio.radio-button[data-disabled] .radio-indicator {
2534
3468
  background-color: var(--color-grey-scale-400);
2535
3469
  }
3470
+ /* chip: 줄을 바꿔 가로로 늘어놓는다 */
3471
+ .radio.radio-group.variant-chip {
3472
+ flex-direction: row;
3473
+ flex-wrap: wrap;
3474
+ gap: var(--spacing-8);
3475
+ }
3476
+ /* 모양은 ChipGroup 칩과 같다. .base-button 의 border·radius·focus 초기화를 이기려고 .radio.radio-chip 으로 건다 */
3477
+ .radio.radio-chip {
3478
+ height: var(--control-height-s);
3479
+ padding: 0 var(--spacing-14);
3480
+ border: var(--hairline) solid var(--color-grey-scale-300);
3481
+ border-radius: var(--radius-full);
3482
+ background-color: var(--color-grey-scale-50);
3483
+ color: var(--color-grey-scale-600);
3484
+ font-size: var(--font-size-sm);
3485
+ font-weight: var(--font-weight-medium);
3486
+ transition:
3487
+ transform var(--motion-press-duration) var(--motion-press-ease),
3488
+ background-color var(--motion-confirm-weak-duration) var(--motion-confirm-weak-ease),
3489
+ color var(--motion-confirm-weak-duration) var(--motion-confirm-weak-ease),
3490
+ border-color var(--motion-confirm-weak-duration) var(--motion-confirm-weak-ease);
3491
+ }
3492
+ .radio.radio-chip:active:not([data-disabled]) {
3493
+ transform: scale(var(--motion-press-scale));
3494
+ }
3495
+ .radio.radio-chip[data-state='checked'] {
3496
+ border-color: var(--color-purple-900);
3497
+ background-color: var(--color-purple-900);
3498
+ color: var(--color-grey-scale-50);
3499
+ font-weight: var(--font-weight-semibold);
3500
+ }
3501
+ .radio.radio-chip[data-disabled] {
3502
+ border-color: var(--color-grey-scale-200);
3503
+ color: var(--color-grey-scale-300);
3504
+ cursor: var(--cursor-disabled);
3505
+ }
3506
+ .radio.radio-chip:focus-visible {
3507
+ outline: var(--hairline) solid var(--color-purple-600);
3508
+ outline-offset: var(--spacing-2);
3509
+ }
2536
3510
  .base-skeleton {
2537
3511
  display: block;
2538
3512
  box-sizing: border-box;
@@ -2587,7 +3561,7 @@ button.modal-with-double-button-close-button {
2587
3561
  transparent 100%
2588
3562
  );
2589
3563
  transform: translateX(-100%);
2590
- animation: skeleton-shimmer var(--skeleton-duration) var(--motion-ease) infinite;
3564
+ animation: motion-busy-shimmer var(--skeleton-duration) var(--motion-ease) infinite;
2591
3565
  }
2592
3566
  @media (prefers-reduced-motion: reduce) {
2593
3567
  .skeleton.animated:not(.multiline)::after,
@@ -2595,14 +3569,6 @@ button.modal-with-double-button-close-button {
2595
3569
  animation: none;
2596
3570
  }
2597
3571
  }
2598
- @keyframes skeleton-shimmer {
2599
- 0% {
2600
- transform: translateX(-100%);
2601
- }
2602
- 100% {
2603
- transform: translateX(100%);
2604
- }
2605
- }
2606
3572
  .base-step-progress:where(.root) {
2607
3573
  display: grid;
2608
3574
  grid-template-columns: repeat(var(--step-count), minmax(0, 1fr));
@@ -2877,6 +3843,67 @@ button.modal-with-double-button-close-button {
2877
3843
  background-color: var(--color-purple-50);
2878
3844
  z-index: var(--z-base);
2879
3845
  }
3846
+ .base-tag {
3847
+ display: inline-flex;
3848
+ align-items: center;
3849
+
3850
+ /* 지우기 버튼. 글자 옆 작은 X 지만 누르는 자리는 터치 44 로 넓힌다 */
3851
+ }
3852
+ .base-tag:where(.remove) {
3853
+ position: relative;
3854
+ flex: none;
3855
+ display: inline-flex;
3856
+ align-items: center;
3857
+ justify-content: center;
3858
+ padding: 0;
3859
+ border: none;
3860
+ background: transparent;
3861
+ cursor: var(--cursor-button);
3862
+ }
3863
+ .base-tag:where(.remove)::after {
3864
+ content: '';
3865
+ position: absolute;
3866
+ top: 50%;
3867
+ left: 50%;
3868
+ width: var(--touch-target);
3869
+ height: var(--touch-target);
3870
+ transform: translate(-50%, -50%);
3871
+ }
3872
+ /***************************************************************************************************
3873
+ * *
3874
+ * tag *
3875
+ * *
3876
+ ***************************************************************************************************/
3877
+ /* CTAButton secondary·Callout 과 같은 연보라 */
3878
+ .tag {
3879
+ height: var(--label-height-s);
3880
+ padding: 0 var(--spacing-12);
3881
+ border-radius: var(--radius-full);
3882
+ background-color: var(--color-purple-50);
3883
+ color: var(--color-purple-900);
3884
+ font-size: var(--font-size-sm);
3885
+ line-height: var(--line-height-sm);
3886
+ font-weight: var(--font-weight-semibold);
3887
+ white-space: nowrap;
3888
+
3889
+ /* X 버튼 안에 여백이 있어 오른쪽을 줄인다 */
3890
+ }
3891
+ .tag.removable {
3892
+ padding-right: var(--spacing-4);
3893
+ }
3894
+ .tag-remove {
3895
+ width: var(--label-height-xs);
3896
+ height: var(--label-height-xs);
3897
+ border-radius: var(--radius-full);
3898
+ color: var(--color-purple-600);
3899
+ transition: transform var(--motion-press-duration) var(--motion-press-ease);
3900
+ }
3901
+ .tag-remove:active {
3902
+ transform: scale(var(--motion-press-scale));
3903
+ }
3904
+ .tag-remove:focus-visible {
3905
+ outline: var(--hairline) solid var(--color-purple-600);
3906
+ }
2880
3907
  .base-text-area:where(.textarea) {
2881
3908
  display: block;
2882
3909
  width: 100%;
@@ -3045,10 +4072,12 @@ button.modal-with-double-button-close-button {
3045
4072
  list-style: none;
3046
4073
  }
3047
4074
  .base-toast[data-state='open']:where(:not(.viewport,.title,.description,.action,.close)) {
3048
- animation: toast-in var(--motion-base) var(--motion-ease);
4075
+ animation:
4076
+ motion-enter-slide var(--motion-base) var(--motion-ease),
4077
+ motion-enter-fade var(--motion-base) var(--motion-ease);
3049
4078
  }
3050
4079
  .base-toast[data-state='closed']:where(:not(.viewport,.title,.description,.action,.close)) {
3051
- animation: toast-out var(--motion-fast) var(--motion-ease) forwards;
4080
+ animation: motion-exit-fade var(--motion-fast) var(--motion-ease) forwards;
3052
4081
  }
3053
4082
  .base-toast[data-swipe='move']:where(:not(.viewport,.title,.description,.action,.close)) {
3054
4083
  transform: translateX(var(--radix-toast-swipe-move-x));
@@ -3058,7 +4087,9 @@ button.modal-with-double-button-close-button {
3058
4087
  transition: transform var(--motion-fast) var(--motion-ease);
3059
4088
  }
3060
4089
  .base-toast[data-swipe='end']:where(:not(.viewport,.title,.description,.action,.close)) {
3061
- animation: toast-swipe-out var(--motion-fast) var(--motion-ease) forwards;
4090
+ --motion-swipe-from: var(--radix-toast-swipe-end-x);
4091
+ --motion-swipe-to: calc(100% + var(--spacing-24));
4092
+ animation: motion-exit-swipe var(--motion-fast) var(--motion-ease) forwards;
3062
4093
  }
3063
4094
  .base-toast:where(.viewport) {
3064
4095
  position: fixed;
@@ -3073,12 +4104,12 @@ button.modal-with-double-button-close-button {
3073
4104
  list-style: none;
3074
4105
  outline: none;
3075
4106
  /* 아래에서 올라오는 자리를 기본으로 두고 position 클래스가 덮는다 */
3076
- --toast-enter: translateY(var(--spacing-16));
4107
+ --motion-slide-offset: translateY(var(--spacing-16));
3077
4108
  }
3078
4109
  .base-toast.position-top-right:where(.viewport) {
3079
4110
  top: 0;
3080
4111
  right: 0;
3081
- --toast-enter: translateY(calc(-1 * var(--spacing-16)));
4112
+ --motion-slide-offset: translateY(calc(-1 * var(--spacing-16)));
3082
4113
  }
3083
4114
  .base-toast.position-bottom-right:where(.viewport) {
3084
4115
  right: 0;
@@ -3099,35 +4130,6 @@ button.modal-with-double-button-close-button {
3099
4130
  background: transparent;
3100
4131
  cursor: var(--cursor-button);
3101
4132
  }
3102
- @keyframes toast-in {
3103
- from {
3104
- opacity: 0;
3105
- transform: var(--toast-enter);
3106
- }
3107
-
3108
- to {
3109
- opacity: 1;
3110
- transform: none;
3111
- }
3112
- }
3113
- @keyframes toast-out {
3114
- from {
3115
- opacity: 1;
3116
- }
3117
-
3118
- to {
3119
- opacity: 0;
3120
- }
3121
- }
3122
- @keyframes toast-swipe-out {
3123
- from {
3124
- transform: translateX(var(--radix-toast-swipe-end-x));
3125
- }
3126
-
3127
- to {
3128
- transform: translateX(calc(100% + var(--spacing-24)));
3129
- }
3130
- }
3131
4133
  @media (prefers-reduced-motion: reduce) {
3132
4134
  .base-toast:where(:not(.viewport, .title, .description, .action, .close)) {
3133
4135
  animation: none;