@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
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,32 @@
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
+ /* 아이콘 버튼: icon-button. 보이는 원의 지름. 누르는 자리는 --touch-target 이상 */
306
+ --icon-button-size-s: 36px;
307
+ --icon-button-size-m: 44px;
308
+ --icon-button-size-l: 56px;
309
+
310
+ /* 빈 화면: empty-state 아이콘 원 */
311
+ --empty-state-icon-box: 64px;
312
+
219
313
  /* 진행 단계 바 */
220
314
  --step-bar-height: 4px;
221
315
 
@@ -255,7 +349,8 @@
255
349
  --z-modal: 9999;
256
350
  --z-toast: 10000;
257
351
 
258
- /* 모션 */
352
+ /* 옛 모션 변수 */
353
+ /* 컴포넌트 작업(KAN-9516~9528)이 --motion-{토큰}-* 로 바꾸면 지운다. 새 코드는 쓰지 않는다. 새 토큰은 tokens/motion.json */
259
354
  --motion-fast: 160ms;
260
355
  --motion-base: 300ms;
261
356
  --motion-ease: ease;
@@ -379,8 +474,33 @@
379
474
  --button-size-l: 54;
380
475
  --button-size-xl: 60;
381
476
  }
477
+ /* tokens/motion.json 에서 생성됨 — 손으로 고치지 않는다. `yarn tokens` */
478
+ /* 시간을 0 으로 만들면 의미 토큰이 var() 로 따라온다. 끝 상태는 그대로 보인다 */
479
+ @media (prefers-reduced-motion: reduce) {
480
+ :root {
481
+ --duration-xs: 0ms;
482
+ --duration-s: 0ms;
483
+ --duration-m: 0ms;
484
+ --duration-l: 0ms;
485
+ --duration-xl: 0ms;
486
+ --duration-spring-rapid: 0ms;
487
+ --duration-spring-quick: 0ms;
488
+ --duration-spring-small: 0ms;
489
+ --duration-spring-medium: 0ms;
490
+ --duration-spring-basic: 0ms;
491
+ --duration-spring-large: 0ms;
492
+ --duration-spring-bounce: 0ms;
493
+ --duration-spring-slow: 0ms;
494
+ --motion-reveal-gap: 0ms;
495
+ --motion-attention-shine: 0ms;
496
+ --motion-attention-blink: 0ms;
497
+ --motion-busy-ring: 0ms;
498
+ --motion-busy-shimmer: 0ms;
499
+ --motion-busy-dots: 0ms;
500
+ }
501
+ }
382
502
  /*
383
- * 크기·두께·레이어·모션. figma.json 에서 만들지 않고 손으로 유지한다(font-face.css 와 같다).
503
+ * 크기·두께·레이어·옛 모션. figma.json 에서 만들지 않고 손으로 유지한다(font-face.css 와 같다).
384
504
  * 디자이너가 숫자를 바꾸는 곳은 여기다. 컴포넌트 CSS 는 숫자를 쓰지 않고 이 변수를 쓴다(빌드가 검사).
385
505
  * 크기 이름은 xs · s · m · l · xl 다섯 개다. 컴포넌트는 그중 지원하는 것만 고른다.
386
506
  */
@@ -585,6 +705,167 @@ p {
585
705
  .nuri-overflow-y-auto {
586
706
  overflow-y: auto;
587
707
  }
708
+ /* 공용 키프레임. 컴포넌트 CSS 보다 앞에 둔다 */
709
+ /*
710
+ * 공용 키프레임. 컴포넌트 CSS 는 @keyframes 를 두지 않고 이 이름을 쓴다(빌드 R18).
711
+ * 시간·곡선은 쓰는 쪽 animation 에서 --motion-{토큰}-* 로 준다. 여기는 모양만 정한다.
712
+ * 쓰는 쪽이 두는 입력 변수: --motion-slide-offset(transform 값), --motion-swipe-from, --motion-swipe-to.
713
+ */
714
+ @keyframes motion-enter-fade {
715
+ from {
716
+ opacity: 0;
717
+ }
718
+ to {
719
+ opacity: 1;
720
+ }
721
+ }
722
+ @keyframes motion-exit-fade {
723
+ from {
724
+ opacity: 1;
725
+ }
726
+ to {
727
+ opacity: 0;
728
+ }
729
+ }
730
+ @keyframes motion-enter-scale {
731
+ from {
732
+ opacity: 0;
733
+ transform: scale(var(--motion-enter-scale-from));
734
+ }
735
+ to {
736
+ opacity: 1;
737
+ transform: none;
738
+ }
739
+ }
740
+ @keyframes motion-exit-scale {
741
+ from {
742
+ opacity: 1;
743
+ transform: none;
744
+ }
745
+ to {
746
+ opacity: 0;
747
+ transform: scale(var(--motion-enter-scale-from));
748
+ }
749
+ }
750
+ @keyframes motion-enter-slide {
751
+ from {
752
+ transform: var(--motion-slide-offset);
753
+ }
754
+ to {
755
+ transform: none;
756
+ }
757
+ }
758
+ @keyframes motion-exit-slide {
759
+ from {
760
+ transform: none;
761
+ }
762
+ to {
763
+ transform: var(--motion-slide-offset);
764
+ }
765
+ }
766
+ @keyframes motion-exit-swipe {
767
+ from {
768
+ transform: translateX(var(--motion-swipe-from));
769
+ }
770
+ to {
771
+ transform: translateX(var(--motion-swipe-to));
772
+ }
773
+ }
774
+ @keyframes motion-reject-wiggle {
775
+ 0%,
776
+ 100% {
777
+ transform: none;
778
+ }
779
+ 25% {
780
+ transform: translateX(var(--motion-reject-distance));
781
+ }
782
+ 50% {
783
+ transform: translateX(calc(var(--motion-reject-distance) * -1));
784
+ }
785
+ 75% {
786
+ transform: translateX(calc(var(--motion-reject-distance) / 2));
787
+ }
788
+ }
789
+ @keyframes motion-reject-shake {
790
+ 0%,
791
+ 100% {
792
+ transform: none;
793
+ }
794
+ 15%,
795
+ 45%,
796
+ 75% {
797
+ transform: translateX(calc(var(--motion-reject-distance) * -2));
798
+ }
799
+ 30%,
800
+ 60% {
801
+ transform: translateX(calc(var(--motion-reject-distance) * 2));
802
+ }
803
+ 90% {
804
+ transform: translateX(var(--motion-reject-distance));
805
+ }
806
+ }
807
+ @keyframes motion-attention-shine {
808
+ from {
809
+ transform: translateX(-100%);
810
+ }
811
+ to {
812
+ transform: translateX(100%);
813
+ }
814
+ }
815
+ @keyframes motion-attention-blink {
816
+ 0%,
817
+ 100% {
818
+ opacity: 1;
819
+ }
820
+ 50% {
821
+ opacity: 0;
822
+ }
823
+ }
824
+ @keyframes motion-busy-dots {
825
+ 0%,
826
+ 80%,
827
+ 100% {
828
+ opacity: 0;
829
+ }
830
+ 40% {
831
+ opacity: 1;
832
+ }
833
+ }
834
+ @keyframes motion-busy-ring {
835
+ from {
836
+ transform: rotate(0deg);
837
+ }
838
+ to {
839
+ transform: rotate(360deg);
840
+ }
841
+ }
842
+ @keyframes motion-busy-shimmer {
843
+ from {
844
+ transform: translateX(-100%);
845
+ }
846
+ to {
847
+ transform: translateX(100%);
848
+ }
849
+ }
850
+ @keyframes motion-enter-pop {
851
+ from {
852
+ opacity: 0;
853
+ transform: var(--motion-slide-offset) scale(var(--motion-enter-pop-scale));
854
+ }
855
+ to {
856
+ opacity: 1;
857
+ transform: none;
858
+ }
859
+ }
860
+ @keyframes motion-attention-float {
861
+ 0%,
862
+ 100% {
863
+ transform: none;
864
+ }
865
+ 50% {
866
+ transform: translateY(calc(var(--motion-attention-float-distance) * -1));
867
+ }
868
+ }
588
869
  /* Component */
589
870
  .base-avatar {
590
871
  border-radius: 50%;
@@ -728,6 +1009,10 @@ p {
728
1009
  display: flex;
729
1010
  align-items: center;
730
1011
  justify-content: center;
1012
+ /* width="fit-content" 일 때 글자가 테두리에 붙지 않게 양옆을 남긴다 */
1013
+ padding: 0 var(--spacing-12);
1014
+ box-sizing: border-box;
1015
+ white-space: nowrap;
731
1016
 
732
1017
  border-width: var(--hairline);
733
1018
  border-style: solid;
@@ -742,6 +1027,9 @@ p {
742
1027
  align-items: center;
743
1028
  justify-content: center;
744
1029
  gap: var(--spacing-4);
1030
+ padding: 0 var(--spacing-12);
1031
+ box-sizing: border-box;
1032
+ white-space: nowrap;
745
1033
 
746
1034
  border-width: var(--hairline);
747
1035
  border-style: solid;
@@ -783,12 +1071,47 @@ p {
783
1071
  background-color: var(--color-accent-red-50);
784
1072
  border-color: transparent;
785
1073
  }
1074
+ /* Toast 의 success·warning 과 같은 계열. 글자는 800 단계라 50 바탕에서 대비 6.8:1 */
1075
+ .variant-success:is(.option,.icon-text,.icon-text-option) {
1076
+ color: var(--color-accent-green-800);
1077
+ background-color: var(--color-accent-green-50);
1078
+ border-color: transparent;
1079
+ }
1080
+ .variant-warning:is(.option,.icon-text,.icon-text-option) {
1081
+ color: var(--color-accent-orange-800);
1082
+ background-color: var(--color-accent-orange-50);
1083
+ border-color: transparent;
1084
+ }
786
1085
  /* icon-text-option 은 테두리가 없는 모양이라 두께·스타일까지 여기서 준다. */
787
1086
  .variant-outlined:is(.option,.icon-text,.icon-text-option) {
788
1087
  color: var(--color-black);
789
1088
  background-color: var(--color-white);
790
1089
  border: var(--hairline) solid var(--color-grey-scale-300);
791
1090
  }
1091
+ /* 스크롤하는 조상의 맨 아래에 붙는다. 모션은 없다 — 주 행동·입력을 담아 스크롤로 숨기지 않는다 */
1092
+ .base-bottom-bar {
1093
+ position: sticky;
1094
+ bottom: 0;
1095
+ z-index: var(--z-sticky);
1096
+ display: flex;
1097
+ }
1098
+ .base-bottom-bar > * {
1099
+ flex: 1;
1100
+ min-width: 0;
1101
+ }
1102
+ /***************************************************************************************************
1103
+ * *
1104
+ * bottom-bar *
1105
+ * *
1106
+ ***************************************************************************************************/
1107
+ .bottom-bar {
1108
+ gap: var(--spacing-8);
1109
+ padding: var(--spacing-12) var(--spacing-16);
1110
+ /* 기기 홈 막대만큼 아래를 더 비운다. env() 의 대체값 0 은 단위가 있어야 calc 가 성립한다 */
1111
+ padding-bottom: calc(var(--spacing-12) + env(safe-area-inset-bottom, 0px)); /* raw-px */
1112
+ border-top: var(--hairline) solid var(--color-grey-scale-200);
1113
+ background-color: var(--color-white);
1114
+ }
792
1115
  .base-bottom-nav:where(.root) {
793
1116
  position: fixed;
794
1117
  left: var(--spacing-16);
@@ -1164,12 +1487,12 @@ p {
1164
1487
  /* 나타날 때 꼬리에서 튀어나온 뒤 세 번 둥실 움직이고 멈춘다 (ADR 0007). 계속 움직이면 거슬린다.
1165
1488
  둥실 지연 = 팝 지연 + 팝 시간. 팝 쪽을 바꾸면 둥실 지연도 같이 바꾼다 */
1166
1489
  --callout-origin: var(--spacing-16) 100%;
1167
- --callout-enter: translateY(var(--spacing-4));
1490
+ --motion-slide-offset: translateY(var(--spacing-4));
1168
1491
 
1169
1492
  transform-origin: var(--callout-origin);
1170
1493
  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)
1494
+ motion-enter-pop var(--motion-slow) var(--motion-spring) var(--motion-fast) both,
1495
+ motion-attention-float calc(var(--motion-base) * 8) var(--motion-ease)
1173
1496
  calc(var(--motion-fast) + var(--motion-slow)) 3;
1174
1497
  }
1175
1498
  .base-callout.tail-bottom-end {
@@ -1177,7 +1500,7 @@ p {
1177
1500
  }
1178
1501
  .base-callout:is(.tail-top-start,.tail-top-end) {
1179
1502
  --callout-origin: var(--spacing-16) 0;
1180
- --callout-enter: translateY(calc(var(--spacing-4) * -1));
1503
+ --motion-slide-offset: translateY(calc(var(--spacing-4) * -1));
1181
1504
  }
1182
1505
  .base-callout.tail-top-end {
1183
1506
  --callout-origin: calc(100% - var(--spacing-16)) 0;
@@ -1208,32 +1531,47 @@ p {
1208
1531
  .base-callout:is(.tail-bottom-end,.tail-top-end)::after {
1209
1532
  right: var(--spacing-12);
1210
1533
  }
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
1534
  @media (prefers-reduced-motion: reduce) {
1233
1535
  .base-callout {
1234
1536
  animation: none;
1235
1537
  }
1236
1538
  }
1539
+ .base-card {
1540
+ display: flex;
1541
+ flex-direction: column;
1542
+ }
1543
+ /***************************************************************************************************
1544
+ * *
1545
+ * card *
1546
+ * *
1547
+ ***************************************************************************************************/
1548
+ .card {
1549
+ gap: var(--spacing-12);
1550
+ padding: var(--spacing-16);
1551
+ border: var(--hairline) solid var(--color-grey-scale-200);
1552
+ border-radius: var(--radius-16);
1553
+ background-color: var(--color-white);
1554
+ }
1555
+ .card.variant-tinted {
1556
+ border-color: var(--color-purple-100);
1557
+ background-color: var(--color-purple-50);
1558
+ }
1559
+ /* 누르는 카드(링크·버튼)만 눌림 모션을 받는다. 보기만 하는 카드는 움직이지 않는다 */
1560
+ .card:is(a[href],button) {
1561
+ width: 100%;
1562
+ color: inherit;
1563
+ text-align: start;
1564
+ text-decoration: none;
1565
+ cursor: pointer;
1566
+ transition: transform var(--motion-press-duration) var(--motion-press-ease);
1567
+ }
1568
+ .card:is(a[href],button):active {
1569
+ transform: scale(var(--motion-press-scale));
1570
+ }
1571
+ .card:is(a[href],button):focus-visible {
1572
+ outline: var(--hairline) solid var(--color-purple-600);
1573
+ outline-offset: var(--spacing-2);
1574
+ }
1237
1575
  .base-check-box.label {
1238
1576
  font-size: var(--font-size-sm);
1239
1577
  font-weight: var(--font-weight-medium);
@@ -1444,6 +1782,53 @@ p {
1444
1782
  margin-top: var(--spacing-2);
1445
1783
  margin-bottom: var(--spacing-4);
1446
1784
  }
1785
+ .base-description-list {
1786
+ display: flex;
1787
+ flex-direction: column;
1788
+ margin: 0;
1789
+ }
1790
+ /***************************************************************************************************
1791
+ * *
1792
+ * description-list *
1793
+ * *
1794
+ ***************************************************************************************************/
1795
+ .description-list {
1796
+ gap: var(--spacing-8);
1797
+ }
1798
+ .description-list-item {
1799
+ display: flex;
1800
+ justify-content: space-between;
1801
+ gap: var(--spacing-12);
1802
+ }
1803
+ .description-list-item > dt {
1804
+ color: var(--color-grey-scale-500);
1805
+ font-size: var(--font-size-sm);
1806
+ line-height: var(--line-height-sm);
1807
+ }
1808
+ .description-list-item > dd {
1809
+ margin: 0;
1810
+ color: var(--color-grey-scale-900);
1811
+ font-size: var(--font-size-sm);
1812
+ line-height: var(--line-height-sm);
1813
+ font-weight: var(--font-weight-medium);
1814
+ text-align: end;
1815
+ }
1816
+ /* 합계 줄. 구분선 아래 값이 한 단계 크고 굵다 */
1817
+ .description-list-item.total {
1818
+ align-items: baseline;
1819
+ margin-top: var(--spacing-4);
1820
+ padding-top: var(--spacing-12);
1821
+ border-top: var(--hairline) solid var(--color-grey-scale-200);
1822
+ }
1823
+ .description-list-item.total > dt {
1824
+ color: var(--color-grey-scale-900);
1825
+ font-weight: var(--font-weight-semibold);
1826
+ }
1827
+ .description-list-item.total > dd {
1828
+ font-size: var(--font-size-base);
1829
+ line-height: var(--line-height-base);
1830
+ font-weight: var(--font-weight-bold);
1831
+ }
1447
1832
  /* Title·Close 같은 조각도 base-drawer 를 달므로 상자 스타일은 overlay·content 에만 건다. */
1448
1833
  .base-drawer:where(.overlay) {
1449
1834
  position: fixed;
@@ -1455,10 +1840,10 @@ p {
1455
1840
  z-index: var(--z-overlay);
1456
1841
  }
1457
1842
  .base-drawer[data-state='open']:where(.overlay) {
1458
- animation: drawer-overlay-in var(--motion-base) var(--motion-ease);
1843
+ animation: motion-enter-fade var(--motion-base) var(--motion-ease);
1459
1844
  }
1460
1845
  .base-drawer[data-state='closed']:where(.overlay) {
1461
- animation: drawer-overlay-out var(--motion-base) var(--motion-ease);
1846
+ animation: motion-exit-fade var(--motion-base) var(--motion-ease);
1462
1847
  }
1463
1848
  .base-drawer:where(.content) {
1464
1849
  position: fixed;
@@ -1493,39 +1878,7 @@ p {
1493
1878
  * drawer *
1494
1879
  * *
1495
1880
  ***************************************************************************************************/
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
- }
1881
+ /* Radix 는 애니메이션이 끝나야 닫힌 패널을 지우므로 transition 이 아니라 keyframes 로 들어오고 나간다(styles/motion.css). */
1529
1882
  .drawer {
1530
1883
  gap: var(--spacing-8);
1531
1884
  padding: var(--spacing-24);
@@ -1549,7 +1902,7 @@ p {
1549
1902
  bottom: 0;
1550
1903
  width: var(--drawer-width);
1551
1904
  max-width: 100%;
1552
- --drawer-offset: translateX(100%);
1905
+ --motion-slide-offset: translateX(100%);
1553
1906
  }
1554
1907
  .drawer.side-left {
1555
1908
  top: 0;
@@ -1557,7 +1910,7 @@ p {
1557
1910
  bottom: 0;
1558
1911
  width: var(--drawer-width);
1559
1912
  max-width: 100%;
1560
- --drawer-offset: translateX(-100%);
1913
+ --motion-slide-offset: translateX(-100%);
1561
1914
  }
1562
1915
  .drawer.side-bottom {
1563
1916
  left: 0;
@@ -1567,13 +1920,13 @@ p {
1567
1920
  max-height: 100%;
1568
1921
  border-top-left-radius: var(--radius-16);
1569
1922
  border-top-right-radius: var(--radius-16);
1570
- --drawer-offset: translateY(100%);
1923
+ --motion-slide-offset: translateY(100%);
1571
1924
  }
1572
1925
  .drawer[data-state='open'] {
1573
- animation: drawer-slide-in var(--motion-base) var(--motion-ease);
1926
+ animation: motion-enter-slide var(--motion-base) var(--motion-ease);
1574
1927
  }
1575
1928
  .drawer[data-state='closed'] {
1576
- animation: drawer-slide-out var(--motion-base) var(--motion-ease);
1929
+ animation: motion-exit-slide var(--motion-base) var(--motion-ease);
1577
1930
  }
1578
1931
  .drawer-header {
1579
1932
  display: flex;
@@ -2133,21 +2486,70 @@ p {
2133
2486
  .base-text.color-accent-orange-900[class*='color-'] {
2134
2487
  color: var(--color-accent-orange-900);
2135
2488
  }
2136
- .base-icon {
2137
- flex-shrink: 0;
2138
- vertical-align: middle;
2489
+ /* 빈 목록·오류·찾을 수 없음·준비 중 화면. 가운데 세로로 쌓는다 */
2490
+ .base-empty-state {
2491
+ display: flex;
2492
+ flex-direction: column;
2493
+ align-items: center;
2494
+ text-align: center;
2139
2495
  }
2140
- .icon.size-xs {
2141
- width: var(--icon-size-xs);
2142
- height: var(--icon-size-xs);
2143
- }
2144
- .icon.size-s {
2145
- width: var(--icon-size-s);
2146
- height: var(--icon-size-s);
2147
- }
2148
- .icon.size-m {
2149
- width: var(--icon-size-m);
2150
- height: var(--icon-size-m);
2496
+ /***************************************************************************************************
2497
+ * *
2498
+ * empty-state *
2499
+ * *
2500
+ ***************************************************************************************************/
2501
+ .empty-state {
2502
+ gap: var(--spacing-8);
2503
+ padding: var(--spacing-24) var(--spacing-16);
2504
+
2505
+ /* variant 는 아이콘 원 색만 바꾼다 */
2506
+ }
2507
+ .empty-state.variant-neutral .empty-state-icon {
2508
+ background-color: var(--color-grey-scale-100);
2509
+ color: var(--color-grey-scale-600);
2510
+ }
2511
+ .empty-state-icon {
2512
+ display: inline-flex;
2513
+ align-items: center;
2514
+ justify-content: center;
2515
+ width: var(--empty-state-icon-box);
2516
+ height: var(--empty-state-icon-box);
2517
+ margin-bottom: var(--spacing-8);
2518
+ border-radius: var(--radius-full);
2519
+ background-color: var(--color-purple-50);
2520
+ color: var(--color-purple-900);
2521
+ }
2522
+ .empty-state-title {
2523
+ font-size: var(--font-size-base);
2524
+ line-height: var(--line-height-base);
2525
+ font-weight: var(--font-weight-semibold);
2526
+ color: var(--color-grey-scale-900);
2527
+ word-break: break-word;
2528
+ }
2529
+ .empty-state-description {
2530
+ font-size: var(--font-size-sm);
2531
+ line-height: var(--line-height-sm);
2532
+ color: var(--color-grey-scale-500);
2533
+ word-break: break-word;
2534
+ }
2535
+ .empty-state-action {
2536
+ margin-top: var(--spacing-8);
2537
+ }
2538
+ .base-icon {
2539
+ flex-shrink: 0;
2540
+ vertical-align: middle;
2541
+ }
2542
+ .icon.size-xs {
2543
+ width: var(--icon-size-xs);
2544
+ height: var(--icon-size-xs);
2545
+ }
2546
+ .icon.size-s {
2547
+ width: var(--icon-size-s);
2548
+ height: var(--icon-size-s);
2549
+ }
2550
+ .icon.size-m {
2551
+ width: var(--icon-size-m);
2552
+ height: var(--icon-size-m);
2151
2553
  }
2152
2554
  .icon.size-l {
2153
2555
  width: var(--icon-size-l);
@@ -2157,6 +2559,193 @@ p {
2157
2559
  width: var(--icon-size-xl);
2158
2560
  height: var(--icon-size-xl);
2159
2561
  }
2562
+ .base-icon-button {
2563
+ position: relative;
2564
+ flex: none;
2565
+ display: inline-flex;
2566
+ align-items: center;
2567
+ justify-content: center;
2568
+ padding: 0;
2569
+ border: none;
2570
+ cursor: var(--cursor-button);
2571
+
2572
+ /* 보이는 크기가 작아도(s 36) 누르는 자리는 터치 44 */
2573
+ }
2574
+ .base-icon-button::after {
2575
+ content: '';
2576
+ position: absolute;
2577
+ top: 50%;
2578
+ left: 50%;
2579
+ width: var(--touch-target);
2580
+ height: var(--touch-target);
2581
+ transform: translate(-50%, -50%);
2582
+ }
2583
+ .base-icon-button[data-disabled] {
2584
+ cursor: var(--cursor-disabled);
2585
+ }
2586
+ /***************************************************************************************************
2587
+ * *
2588
+ * icon-button *
2589
+ * *
2590
+ ***************************************************************************************************/
2591
+ .icon-button {
2592
+ border-radius: var(--radius-full);
2593
+ transition:
2594
+ transform var(--motion-press-duration) var(--motion-press-ease),
2595
+ background-color var(--motion-press-duration) var(--motion-press-ease);
2596
+ }
2597
+ .icon-button:active:not([data-disabled]) {
2598
+ transform: scale(var(--motion-press-scale));
2599
+ }
2600
+ .icon-button:focus-visible {
2601
+ outline: var(--hairline) solid var(--color-purple-600);
2602
+ outline-offset: var(--spacing-2);
2603
+ }
2604
+ .icon-button.size-s {
2605
+ width: var(--icon-button-size-s);
2606
+ height: var(--icon-button-size-s);
2607
+ }
2608
+ .icon-button.size-m {
2609
+ width: var(--icon-button-size-m);
2610
+ height: var(--icon-button-size-m);
2611
+ }
2612
+ .icon-button.size-l {
2613
+ width: var(--icon-button-size-l);
2614
+ height: var(--icon-button-size-l);
2615
+ }
2616
+ /* 바탕 없이 아이콘만. 누르는 동안 회색 원이 보인다 */
2617
+ .icon-button.variant-ghost {
2618
+ background-color: transparent;
2619
+ color: var(--color-grey-scale-700);
2620
+ }
2621
+ .icon-button.variant-ghost:active:not([data-disabled]) {
2622
+ background-color: var(--color-grey-scale-100);
2623
+ }
2624
+ /* CTAButton secondary 와 같은 연보라 */
2625
+ .icon-button.variant-secondary {
2626
+ background-color: var(--color-purple-50);
2627
+ color: var(--color-purple-900);
2628
+ }
2629
+ .icon-button.variant-primary {
2630
+ background-color: var(--color-purple-900);
2631
+ color: var(--color-white);
2632
+ }
2633
+ .icon-button[data-disabled] {
2634
+ color: var(--color-grey-scale-300);
2635
+ }
2636
+ .icon-button[data-disabled]:is(.variant-secondary,.variant-primary) {
2637
+ background-color: var(--color-grey-scale-200);
2638
+ color: var(--color-white);
2639
+ }
2640
+ .base-list:where(.group) {
2641
+ display: flex;
2642
+ flex-direction: column;
2643
+ }
2644
+ /* 행은 div·button·링크 어느 것이든 같은 모양이다 */
2645
+ .base-list:where(.row) {
2646
+ position: relative;
2647
+ display: flex;
2648
+ align-items: center;
2649
+ box-sizing: border-box;
2650
+ width: 100%;
2651
+ border: 0;
2652
+ background-color: transparent;
2653
+ color: inherit;
2654
+ text-align: start;
2655
+ text-decoration: none;
2656
+ }
2657
+ /***************************************************************************************************
2658
+ * *
2659
+ * list-group *
2660
+ * *
2661
+ ***************************************************************************************************/
2662
+ .list-group {
2663
+ gap: var(--spacing-8);
2664
+ }
2665
+ .list-group-label {
2666
+ padding: 0 var(--spacing-4);
2667
+ color: var(--color-grey-scale-500);
2668
+ font-size: var(--font-size-sm);
2669
+ line-height: var(--line-height-sm);
2670
+ font-weight: var(--font-weight-semibold);
2671
+ }
2672
+ .list-group-surface {
2673
+ display: flex;
2674
+ flex-direction: column;
2675
+ overflow: hidden;
2676
+ border: var(--hairline) solid var(--color-grey-scale-200);
2677
+ border-radius: var(--radius-16);
2678
+ background-color: var(--color-white);
2679
+ }
2680
+ /***************************************************************************************************
2681
+ * *
2682
+ * list-row *
2683
+ * *
2684
+ ***************************************************************************************************/
2685
+ .list-row {
2686
+ min-height: var(--list-row-height);
2687
+ gap: var(--spacing-12);
2688
+ padding: var(--spacing-10) var(--spacing-16);
2689
+
2690
+ /* 누르는 행(링크·버튼)은 줄어들지 않고 배경이 어두워진다. 폭이 꽉 찬 면이라 scale 이 어색하다 */
2691
+ }
2692
+ .list-row:is(a[href],button) {
2693
+ cursor: pointer;
2694
+ transition: background-color var(--motion-press-duration) var(--motion-press-ease);
2695
+ }
2696
+ .list-row:is(a[href],button):active {
2697
+ background-color: var(--color-grey-scale-100);
2698
+ }
2699
+ /* 면이 overflow: hidden 이라 테두리 안쪽에 그린다 */
2700
+ .list-row:is(a[href],button):focus-visible {
2701
+ outline: var(--hairline) solid var(--color-purple-600);
2702
+ outline-offset: calc(var(--spacing-2) * -1);
2703
+ }
2704
+ /* 행 사이 구분선. 왼쪽은 안쪽 여백만큼 띄운다 */
2705
+ .list-row + .list-row::before {
2706
+ content: '';
2707
+ position: absolute;
2708
+ top: 0;
2709
+ right: 0;
2710
+ left: var(--spacing-16);
2711
+ height: var(--hairline);
2712
+ background-color: var(--color-grey-scale-100);
2713
+ }
2714
+ .list-row-leading {
2715
+ display: flex;
2716
+ flex: none;
2717
+ color: var(--color-grey-scale-700);
2718
+ }
2719
+ .list-row-text {
2720
+ display: flex;
2721
+ flex: 1;
2722
+ flex-direction: column;
2723
+ gap: var(--spacing-2);
2724
+ min-width: 0;
2725
+ }
2726
+ .list-row-label {
2727
+ color: var(--color-grey-scale-900);
2728
+ font-size: var(--font-size-base);
2729
+ line-height: var(--line-height-base);
2730
+ font-weight: var(--font-weight-medium);
2731
+ }
2732
+ .list-row-description {
2733
+ color: var(--color-grey-scale-500);
2734
+ font-size: var(--font-size-sm);
2735
+ line-height: var(--line-height-sm);
2736
+ }
2737
+ .list-row-trailing {
2738
+ display: flex;
2739
+ flex: none;
2740
+ align-items: center;
2741
+ color: var(--color-grey-scale-500);
2742
+ font-size: var(--font-size-sm);
2743
+ line-height: var(--line-height-sm);
2744
+ }
2745
+ .list-row-chevron {
2746
+ flex: none;
2747
+ color: var(--color-grey-scale-400);
2748
+ }
2160
2749
  .base-inline-loading {
2161
2750
  display: flex;
2162
2751
  width: 100%;
@@ -2220,7 +2809,7 @@ p {
2220
2809
  height: 100%;
2221
2810
  border: var(--spinner-stroke) solid;
2222
2811
  border-radius: 50%;
2223
- animation: lds-ring var(--spinner-duration) var(--spinner-ease) infinite;
2812
+ animation: motion-busy-ring var(--spinner-duration) var(--spinner-ease) infinite;
2224
2813
  border-color: currentColor transparent transparent transparent;
2225
2814
  }
2226
2815
  .loading-indicator-single-ring:nth-child(1) {
@@ -2235,13 +2824,361 @@ p {
2235
2824
  .loading-indicator-single-ring:nth-child(4) {
2236
2825
  animation-delay: 0s;
2237
2826
  }
2238
- @keyframes lds-ring {
2239
- 0% {
2240
- transform: rotate(0deg);
2827
+ /* Title·Close 같은 조각도 base-media 를 달므로 상자 스타일은 overlay·content 에만 건다. */
2828
+ .base-media:where(.overlay) {
2829
+ position: fixed;
2830
+ inset: 0;
2831
+ z-index: var(--z-overlay);
2832
+ background-color: color-mix(in srgb, var(--color-black) 90%, transparent);
2241
2833
  }
2242
- 100% {
2243
- transform: rotate(360deg);
2834
+ .base-media[data-state='open']:where(.overlay) {
2835
+ animation: motion-enter-fade var(--motion-enter-duration) var(--motion-enter-ease);
2836
+ }
2837
+ .base-media[data-state='closed']:where(.overlay) {
2838
+ animation: motion-exit-fade var(--motion-exit-duration) var(--motion-exit-ease);
2839
+ }
2840
+ .base-media:where(.content) {
2841
+ position: fixed;
2842
+ inset: 0;
2843
+ z-index: var(--z-modal);
2844
+ display: flex;
2845
+ align-items: center;
2846
+ justify-content: center;
2847
+ }
2848
+ .base-media:where(.close) {
2849
+ display: inline-flex;
2850
+ align-items: center;
2851
+ justify-content: center;
2852
+ padding: 0;
2853
+ border: none;
2854
+ background-color: transparent;
2855
+ cursor: var(--cursor-button);
2856
+ }
2857
+ /***************************************************************************************************
2858
+ * *
2859
+ * media-viewer *
2860
+ * *
2861
+ ***************************************************************************************************/
2862
+ /* Radix 는 애니메이션이 끝나야 닫힌 Content 를 지우므로 keyframes 로 들어오고 나간다(styles/motion.css). */
2863
+ .media-viewer[data-state='open'] {
2864
+ animation: motion-enter-scale var(--motion-enter-duration) var(--motion-enter-ease);
2865
+ }
2866
+ .media-viewer[data-state='closed'] {
2867
+ animation: motion-exit-scale var(--motion-exit-duration) var(--motion-exit-ease);
2868
+ }
2869
+ .media-viewer-media {
2870
+ display: block;
2871
+ max-width: 100%;
2872
+ max-height: 100%;
2873
+ -o-object-fit: contain;
2874
+ object-fit: contain;
2875
+ }
2876
+ /* 노치 아래로 숨지 않게 안전 영역만큼 내린다 */
2877
+ .media-viewer-close {
2878
+ position: absolute;
2879
+ top: calc(env(safe-area-inset-top) + var(--spacing-8));
2880
+ right: calc(env(safe-area-inset-right) + var(--spacing-8));
2881
+ width: var(--touch-target);
2882
+ height: var(--touch-target);
2883
+ color: var(--color-white);
2884
+ }
2885
+ /***************************************************************************************************
2886
+ * *
2887
+ * media-thumbnail *
2888
+ * *
2889
+ ***************************************************************************************************/
2890
+ .media-thumbnail {
2891
+ position: relative;
2892
+ flex-shrink: 0;
2893
+ }
2894
+ .media-thumbnail.size-s {
2895
+ width: var(--media-thumbnail-size-s);
2896
+ height: var(--media-thumbnail-size-s);
2897
+ }
2898
+ .media-thumbnail.size-m {
2899
+ width: var(--media-thumbnail-size-m);
2900
+ height: var(--media-thumbnail-size-m);
2901
+ }
2902
+ /* 말풍선 안. 폭을 채우되 넘지 않는다 */
2903
+ .media-thumbnail.size-l {
2904
+ width: 100%;
2905
+ max-width: var(--media-thumbnail-max-width);
2906
+ aspect-ratio: 4 / 3;
2907
+ }
2908
+ /* base-button(border·radius·focus 를 지운다)보다 우선해야 해서 .media-thumbnail 아래에 둔다 */
2909
+ .media-thumbnail .media-thumbnail-open {
2910
+ position: relative;
2911
+ width: 100%;
2912
+ height: 100%;
2913
+ padding: 0;
2914
+ overflow: hidden;
2915
+ box-sizing: border-box;
2916
+ border-radius: var(--radius-6);
2917
+ background-color: var(--color-grey-scale-200);
2918
+ transition: transform var(--motion-press-duration) var(--motion-press-ease);
2919
+ }
2920
+ :is(.media-thumbnail .media-thumbnail-open):active:not([data-disabled]) {
2921
+ transform: scale(var(--motion-press-scale));
2922
+ }
2923
+ [data-invalid]:is(.media-thumbnail .media-thumbnail-open) {
2924
+ border: var(--media-invalid-border) solid var(--color-accent-red-500);
2925
+ }
2926
+ :is(.media-thumbnail .media-thumbnail-open):focus-visible {
2927
+ outline: var(--hairline) solid var(--color-purple-600);
2928
+ outline-offset: var(--spacing-2);
2929
+ }
2930
+ .media-thumbnail .media-thumbnail-remove {
2931
+ position: absolute;
2932
+ top: var(--spacing-4);
2933
+ right: var(--spacing-4);
2934
+ width: var(--media-remove-size);
2935
+ height: var(--media-remove-size);
2936
+ padding: 0;
2937
+ border-radius: var(--radius-full);
2938
+ background-color: color-mix(in srgb, var(--color-grey-scale-900) 80%, transparent);
2939
+ color: var(--color-white);
2940
+
2941
+ /* 20 원을 터치 44 로 넓힌다 */
2942
+ }
2943
+ :is(.media-thumbnail .media-thumbnail-remove)::after {
2944
+ content: '';
2945
+ position: absolute;
2946
+ top: 50%;
2947
+ left: 50%;
2948
+ width: var(--touch-target);
2949
+ height: var(--touch-target);
2950
+ transform: translate(-50%, -50%);
2951
+ }
2952
+ :is(.media-thumbnail .media-thumbnail-remove):focus-visible {
2953
+ outline: var(--hairline) solid var(--color-purple-600);
2954
+ outline-offset: var(--spacing-2);
2955
+ }
2956
+ .media-thumbnail-media {
2957
+ display: block;
2958
+ width: 100%;
2959
+ height: 100%;
2960
+ -o-object-fit: cover;
2961
+ object-fit: cover;
2962
+ }
2963
+ .media-thumbnail-play {
2964
+ position: absolute;
2965
+ top: 50%;
2966
+ left: 50%;
2967
+ display: flex;
2968
+ align-items: center;
2969
+ justify-content: center;
2970
+ width: var(--media-overlay-button-size);
2971
+ height: var(--media-overlay-button-size);
2972
+ border-radius: var(--radius-full);
2973
+ background-color: color-mix(in srgb, var(--color-black) 50%, transparent);
2974
+ color: var(--color-white);
2975
+ transform: translate(-50%, -50%);
2976
+ pointer-events: none;
2977
+ }
2978
+ .media-thumbnail-loading {
2979
+ position: absolute;
2980
+ inset: 0;
2981
+ display: flex;
2982
+ align-items: center;
2983
+ justify-content: center;
2984
+ background-color: color-mix(in srgb, var(--color-white) 60%, transparent);
2985
+ }
2986
+ /* 흰 막 위라 기본 링 색(grey-200)은 안 보인다 */
2987
+ .media-thumbnail-spinner {
2988
+ color: var(--color-grey-scale-600);
2989
+ }
2990
+ .base-message-composer:where(.root) {
2991
+ display: flex;
2992
+ flex-direction: column;
2993
+ }
2994
+ .base-message-composer:where(.textarea) {
2995
+ box-sizing: border-box;
2996
+ margin: 0;
2997
+ font-family: inherit;
2998
+ resize: none;
2999
+ }
3000
+ .base-message-composer[data-disabled]:where(.textarea) {
3001
+ cursor: var(--cursor-disabled);
3002
+ }
3003
+ .base-message-composer:where(.send) {
3004
+ display: grid;
3005
+ place-items: center;
3006
+ flex: none;
3007
+ padding: 0;
3008
+ border: none;
3009
+ background: transparent;
3010
+ cursor: pointer;
3011
+ -webkit-tap-highlight-color: transparent;
3012
+ }
3013
+ /***************************************************************************************************
3014
+ * *
3015
+ * message-composer *
3016
+ * *
3017
+ ***************************************************************************************************/
3018
+ .message-composer {
3019
+ gap: var(--spacing-8);
3020
+ }
3021
+ .message-composer-attachments {
3022
+ display: flex;
3023
+ gap: var(--spacing-8);
3024
+ overflow-x: auto;
3025
+ }
3026
+ /* 입력 칸이 늘어나도 아이콘·보내기 버튼은 아래 줄에 붙는다 */
3027
+ .message-composer-row {
3028
+ display: flex;
3029
+ align-items: flex-end;
3030
+ gap: var(--spacing-4);
3031
+ }
3032
+ /* 16px 글자 — iOS 가 입력 칸에 포커스할 때 화면을 확대하지 않는다. 높이는 글자만큼 늘고 max-height 에서 스크롤한다 */
3033
+ .message-composer-textarea {
3034
+ flex: 1;
3035
+ min-width: 0;
3036
+ min-height: var(--touch-target);
3037
+ max-height: var(--composer-max-height);
3038
+ field-sizing: content;
3039
+ padding: var(--spacing-10) var(--spacing-16);
3040
+ border: var(--hairline) solid var(--color-grey-scale-300);
3041
+ border-radius: var(--radius-20);
3042
+ outline: none;
3043
+ background-color: var(--color-white);
3044
+ color: var(--color-grey-scale-900);
3045
+ font-size: var(--font-size-base);
3046
+ line-height: var(--line-height-base);
3047
+ }
3048
+ .message-composer-textarea::-moz-placeholder {
3049
+ color: var(--color-grey-scale-400);
3050
+ }
3051
+ .message-composer-textarea::placeholder {
3052
+ color: var(--color-grey-scale-400);
3053
+ }
3054
+ .message-composer-textarea:focus {
3055
+ border-color: var(--color-purple-900);
3056
+ }
3057
+ .message-composer-textarea[data-disabled] {
3058
+ background-color: var(--color-grey-scale-100);
2244
3059
  }
3060
+ /* 누르는 곳은 --touch-target, 보이는 원은 --composer-send-size */
3061
+ .message-composer-send {
3062
+ width: var(--touch-target);
3063
+ height: var(--touch-target);
3064
+ transition: transform var(--motion-press-duration) var(--motion-press-ease);
3065
+ }
3066
+ .message-composer-send:not([aria-disabled='true'],[aria-busy='true']):active {
3067
+ transform: scale(var(--motion-press-scale));
3068
+ }
3069
+ .message-composer-send[aria-disabled='true'] .message-composer-send-circle {
3070
+ background-color: var(--color-grey-scale-200);
3071
+ }
3072
+ .message-composer-send[data-rejecting] .message-composer-send-circle {
3073
+ animation: motion-reject-wiggle var(--motion-reject-duration) var(--motion-reject-ease);
3074
+ }
3075
+ .message-composer-send-circle {
3076
+ display: grid;
3077
+ place-items: center;
3078
+ width: var(--composer-send-size);
3079
+ height: var(--composer-send-size);
3080
+ border-radius: var(--radius-full);
3081
+ background-color: var(--color-purple-900);
3082
+ color: var(--color-white);
3083
+ }
3084
+ .message-composer-spinner {
3085
+ color: var(--color-white);
3086
+ }
3087
+ .base-message:where(.root) {
3088
+ display: flex;
3089
+ flex-direction: column;
3090
+ margin: 0;
3091
+ padding: 0;
3092
+ list-style: none;
3093
+ }
3094
+ .base-message:where(.item) {
3095
+ display: flex;
3096
+ flex-direction: column;
3097
+ }
3098
+ .base-message:where(.bubble) {
3099
+ display: flex;
3100
+ flex-direction: column;
3101
+ min-width: 0;
3102
+ }
3103
+ /***************************************************************************************************
3104
+ * *
3105
+ * message-thread · message · message-event *
3106
+ * *
3107
+ ***************************************************************************************************/
3108
+ .message-thread {
3109
+ gap: var(--spacing-12);
3110
+ }
3111
+ .message {
3112
+ gap: var(--spacing-4);
3113
+ align-items: flex-start;
3114
+ }
3115
+ .message.side-end {
3116
+ align-items: flex-end;
3117
+ }
3118
+ .message.side-end .message-row {
3119
+ flex-direction: row-reverse;
3120
+ }
3121
+ .message.side-end .message-bubble {
3122
+ border-radius: var(--radius-16) var(--radius-16) var(--radius-4) var(--radius-16);
3123
+ background-color: var(--color-purple-900);
3124
+ color: var(--color-white);
3125
+ }
3126
+ /* 방금 보내거나 받은 메시지만 움직인다. 처음 그린 대화는 motion 이 없어 가만히 있다 */
3127
+ .message:is(.motion-weak,.motion-strong) {
3128
+ --motion-slide-offset: translateY(var(--motion-enter-offset));
3129
+ }
3130
+ .message.motion-weak {
3131
+ animation:
3132
+ motion-enter-fade var(--motion-enter-duration) var(--motion-enter-ease),
3133
+ motion-enter-slide var(--motion-enter-duration) var(--motion-enter-ease);
3134
+ }
3135
+ .message.motion-strong {
3136
+ animation:
3137
+ motion-enter-fade var(--motion-enter-strong-duration) var(--motion-enter-strong-ease),
3138
+ motion-enter-slide var(--motion-enter-strong-duration) var(--motion-enter-strong-ease);
3139
+ }
3140
+ .message-name {
3141
+ font-size: var(--font-size-xsm);
3142
+ line-height: var(--line-height-xsm);
3143
+ font-weight: var(--font-weight-semibold);
3144
+ color: var(--color-grey-scale-700);
3145
+ }
3146
+ .message-row {
3147
+ display: flex;
3148
+ align-items: flex-end;
3149
+ gap: var(--spacing-6);
3150
+ max-width: 80%;
3151
+ }
3152
+ /* 보낸 쪽 아래 모서리만 덜 둥글다 */
3153
+ .message-bubble {
3154
+ gap: var(--spacing-6);
3155
+ padding: var(--spacing-10) var(--spacing-14);
3156
+ border-radius: var(--radius-16) var(--radius-16) var(--radius-16) var(--radius-4);
3157
+ background-color: var(--color-grey-scale-100);
3158
+ color: var(--color-grey-scale-900);
3159
+ font-size: var(--font-size-base);
3160
+ line-height: var(--line-height-base);
3161
+ white-space: pre-wrap;
3162
+ overflow-wrap: anywhere;
3163
+ }
3164
+ .message-bubble.media-only {
3165
+ padding: var(--spacing-6);
3166
+ }
3167
+ .message-time {
3168
+ flex: none;
3169
+ font-size: var(--font-size-xsm);
3170
+ line-height: var(--line-height-xsm);
3171
+ color: var(--color-grey-scale-500);
3172
+ }
3173
+ .message-event {
3174
+ align-self: center;
3175
+ padding: var(--spacing-2) var(--spacing-10);
3176
+ border-radius: var(--radius-full);
3177
+ background-color: var(--color-grey-scale-100);
3178
+ color: var(--color-grey-scale-600);
3179
+ font-size: var(--font-size-xsm);
3180
+ line-height: var(--line-height-xsm);
3181
+ font-weight: var(--font-weight-medium);
2245
3182
  }
2246
3183
  /* Title·Close 같은 조각도 base-modal 을 달므로 상자 스타일은 container·overlay 에만 건다. */
2247
3184
  .base-modal:where(.modal-container, .modal-overlay) {
@@ -2286,6 +3223,11 @@ button.modal-with-double-button-close-button {
2286
3223
  background-color: transparent;
2287
3224
  color: var(--color-grey-scale-600);
2288
3225
  }
3226
+ .modal-with-double-button-container {
3227
+ /* 폰 폭(390)에서 고정 폭이 화면을 넘지 않게 Modal 과 같이 양옆을 남긴다 */
3228
+ max-width: calc(100% - var(--spacing-30) * 2);
3229
+ box-sizing: border-box;
3230
+ }
2289
3231
  .modal-with-double-button-container.size-s {
2290
3232
  width: var(--modal-width-s);
2291
3233
  }
@@ -2400,6 +3342,69 @@ button.modal-with-double-button-close-button {
2400
3342
  align-items: center;
2401
3343
  gap: var(--spacing-10);
2402
3344
  }
3345
+ /* 카드·시트 안에 머무는 안내. 스스로 사라지는 알림은 Toast 다 */
3346
+ .base-notice {
3347
+ display: flex;
3348
+ align-items: flex-start;
3349
+ gap: var(--spacing-8);
3350
+ }
3351
+ /***************************************************************************************************
3352
+ * *
3353
+ * notice *
3354
+ * *
3355
+ ***************************************************************************************************/
3356
+ /* variant 가 바탕색과 글자·아이콘 색을 정한다 */
3357
+ .notice {
3358
+ padding: var(--spacing-12) var(--spacing-14);
3359
+ border-radius: var(--radius-6);
3360
+ background-color: var(--color-grey-scale-100);
3361
+ color: var(--color-grey-scale-700);
3362
+ font-size: var(--font-size-sm);
3363
+ line-height: var(--line-height-sm);
3364
+ }
3365
+ .notice.variant-success {
3366
+ background-color: var(--color-accent-green-50);
3367
+ color: var(--color-accent-green-800);
3368
+ }
3369
+ .notice.variant-warning {
3370
+ background-color: var(--color-accent-orange-50);
3371
+ color: var(--color-accent-orange-800);
3372
+ }
3373
+ .notice.variant-danger {
3374
+ background-color: var(--color-accent-red-50);
3375
+ color: var(--color-accent-red-700);
3376
+ }
3377
+ /* 조건부로 나타나는 안내만 motion 을 준다. 사라질 때는 움직이지 않는다 */
3378
+ .notice.motion-weak {
3379
+ animation: motion-enter-fade var(--motion-enter-duration) var(--motion-enter-ease);
3380
+ }
3381
+ .notice.motion-strong {
3382
+ animation: motion-enter-fade var(--motion-enter-strong-duration) var(--motion-enter-strong-ease);
3383
+ }
3384
+ /* 아이콘(18)을 첫 줄 글(18)에 맞춘다 */
3385
+ .notice-icon {
3386
+ flex: none;
3387
+ }
3388
+ .notice-body {
3389
+ display: flex;
3390
+ flex: 1;
3391
+ flex-direction: column;
3392
+ gap: var(--spacing-10);
3393
+ min-width: 0;
3394
+ }
3395
+ .notice-text {
3396
+ display: flex;
3397
+ flex-direction: column;
3398
+ gap: var(--spacing-2);
3399
+ word-break: break-word;
3400
+ }
3401
+ .notice-title {
3402
+ font-weight: var(--font-weight-semibold);
3403
+ }
3404
+ /* action 은 CTAButton(xs·neutral) 이라 모양은 버튼 CSS 가 정한다. 폭은 글자만큼 */
3405
+ .notice-action {
3406
+ align-self: flex-start;
3407
+ }
2403
3408
  .base-pagination:where(.item) {
2404
3409
  display: inline-flex;
2405
3410
  align-items: center;
@@ -2486,6 +3491,18 @@ button.modal-with-double-button-close-button {
2486
3491
  .base-radio:where(.button) {
2487
3492
  padding: 0;
2488
3493
  }
3494
+ .base-radio:where(.chip) {
3495
+ position: relative;
3496
+ display: inline-flex;
3497
+ align-items: center;
3498
+
3499
+ /* 36 높이를 터치 44 로 넓힌다 */
3500
+ }
3501
+ .base-radio:where(.chip)::after {
3502
+ content: '';
3503
+ position: absolute;
3504
+ inset: calc(-1 * var(--spacing-4)) 0;
3505
+ }
2489
3506
  /***************************************************************************************************
2490
3507
  * *
2491
3508
  * radio *
@@ -2533,6 +3550,46 @@ button.modal-with-double-button-close-button {
2533
3550
  .radio.radio-button[data-disabled] .radio-indicator {
2534
3551
  background-color: var(--color-grey-scale-400);
2535
3552
  }
3553
+ /* chip: 줄을 바꿔 가로로 늘어놓는다 */
3554
+ .radio.radio-group.variant-chip {
3555
+ flex-direction: row;
3556
+ flex-wrap: wrap;
3557
+ gap: var(--spacing-8);
3558
+ }
3559
+ /* 모양은 ChipGroup 칩과 같다. .base-button 의 border·radius·focus 초기화를 이기려고 .radio.radio-chip 으로 건다 */
3560
+ .radio.radio-chip {
3561
+ height: var(--control-height-s);
3562
+ padding: 0 var(--spacing-14);
3563
+ border: var(--hairline) solid var(--color-grey-scale-300);
3564
+ border-radius: var(--radius-full);
3565
+ background-color: var(--color-grey-scale-50);
3566
+ color: var(--color-grey-scale-600);
3567
+ font-size: var(--font-size-sm);
3568
+ font-weight: var(--font-weight-medium);
3569
+ transition:
3570
+ transform var(--motion-press-duration) var(--motion-press-ease),
3571
+ background-color var(--motion-confirm-weak-duration) var(--motion-confirm-weak-ease),
3572
+ color var(--motion-confirm-weak-duration) var(--motion-confirm-weak-ease),
3573
+ border-color var(--motion-confirm-weak-duration) var(--motion-confirm-weak-ease);
3574
+ }
3575
+ .radio.radio-chip:active:not([data-disabled]) {
3576
+ transform: scale(var(--motion-press-scale));
3577
+ }
3578
+ .radio.radio-chip[data-state='checked'] {
3579
+ border-color: var(--color-purple-900);
3580
+ background-color: var(--color-purple-900);
3581
+ color: var(--color-grey-scale-50);
3582
+ font-weight: var(--font-weight-semibold);
3583
+ }
3584
+ .radio.radio-chip[data-disabled] {
3585
+ border-color: var(--color-grey-scale-200);
3586
+ color: var(--color-grey-scale-300);
3587
+ cursor: var(--cursor-disabled);
3588
+ }
3589
+ .radio.radio-chip:focus-visible {
3590
+ outline: var(--hairline) solid var(--color-purple-600);
3591
+ outline-offset: var(--spacing-2);
3592
+ }
2536
3593
  .base-skeleton {
2537
3594
  display: block;
2538
3595
  box-sizing: border-box;
@@ -2587,7 +3644,7 @@ button.modal-with-double-button-close-button {
2587
3644
  transparent 100%
2588
3645
  );
2589
3646
  transform: translateX(-100%);
2590
- animation: skeleton-shimmer var(--skeleton-duration) var(--motion-ease) infinite;
3647
+ animation: motion-busy-shimmer var(--skeleton-duration) var(--motion-ease) infinite;
2591
3648
  }
2592
3649
  @media (prefers-reduced-motion: reduce) {
2593
3650
  .skeleton.animated:not(.multiline)::after,
@@ -2595,14 +3652,6 @@ button.modal-with-double-button-close-button {
2595
3652
  animation: none;
2596
3653
  }
2597
3654
  }
2598
- @keyframes skeleton-shimmer {
2599
- 0% {
2600
- transform: translateX(-100%);
2601
- }
2602
- 100% {
2603
- transform: translateX(100%);
2604
- }
2605
- }
2606
3655
  .base-step-progress:where(.root) {
2607
3656
  display: grid;
2608
3657
  grid-template-columns: repeat(var(--step-count), minmax(0, 1fr));
@@ -2877,6 +3926,67 @@ button.modal-with-double-button-close-button {
2877
3926
  background-color: var(--color-purple-50);
2878
3927
  z-index: var(--z-base);
2879
3928
  }
3929
+ .base-tag {
3930
+ display: inline-flex;
3931
+ align-items: center;
3932
+
3933
+ /* 지우기 버튼. 글자 옆 작은 X 지만 누르는 자리는 터치 44 로 넓힌다 */
3934
+ }
3935
+ .base-tag:where(.remove) {
3936
+ position: relative;
3937
+ flex: none;
3938
+ display: inline-flex;
3939
+ align-items: center;
3940
+ justify-content: center;
3941
+ padding: 0;
3942
+ border: none;
3943
+ background: transparent;
3944
+ cursor: var(--cursor-button);
3945
+ }
3946
+ .base-tag:where(.remove)::after {
3947
+ content: '';
3948
+ position: absolute;
3949
+ top: 50%;
3950
+ left: 50%;
3951
+ width: var(--touch-target);
3952
+ height: var(--touch-target);
3953
+ transform: translate(-50%, -50%);
3954
+ }
3955
+ /***************************************************************************************************
3956
+ * *
3957
+ * tag *
3958
+ * *
3959
+ ***************************************************************************************************/
3960
+ /* CTAButton secondary·Callout 과 같은 연보라 */
3961
+ .tag {
3962
+ height: var(--label-height-s);
3963
+ padding: 0 var(--spacing-12);
3964
+ border-radius: var(--radius-full);
3965
+ background-color: var(--color-purple-50);
3966
+ color: var(--color-purple-900);
3967
+ font-size: var(--font-size-sm);
3968
+ line-height: var(--line-height-sm);
3969
+ font-weight: var(--font-weight-semibold);
3970
+ white-space: nowrap;
3971
+
3972
+ /* X 버튼 안에 여백이 있어 오른쪽을 줄인다 */
3973
+ }
3974
+ .tag.removable {
3975
+ padding-right: var(--spacing-4);
3976
+ }
3977
+ .tag-remove {
3978
+ width: var(--label-height-xs);
3979
+ height: var(--label-height-xs);
3980
+ border-radius: var(--radius-full);
3981
+ color: var(--color-purple-600);
3982
+ transition: transform var(--motion-press-duration) var(--motion-press-ease);
3983
+ }
3984
+ .tag-remove:active {
3985
+ transform: scale(var(--motion-press-scale));
3986
+ }
3987
+ .tag-remove:focus-visible {
3988
+ outline: var(--hairline) solid var(--color-purple-600);
3989
+ }
2880
3990
  .base-text-area:where(.textarea) {
2881
3991
  display: block;
2882
3992
  width: 100%;
@@ -3045,10 +4155,12 @@ button.modal-with-double-button-close-button {
3045
4155
  list-style: none;
3046
4156
  }
3047
4157
  .base-toast[data-state='open']:where(:not(.viewport,.title,.description,.action,.close)) {
3048
- animation: toast-in var(--motion-base) var(--motion-ease);
4158
+ animation:
4159
+ motion-enter-slide var(--motion-base) var(--motion-ease),
4160
+ motion-enter-fade var(--motion-base) var(--motion-ease);
3049
4161
  }
3050
4162
  .base-toast[data-state='closed']:where(:not(.viewport,.title,.description,.action,.close)) {
3051
- animation: toast-out var(--motion-fast) var(--motion-ease) forwards;
4163
+ animation: motion-exit-fade var(--motion-fast) var(--motion-ease) forwards;
3052
4164
  }
3053
4165
  .base-toast[data-swipe='move']:where(:not(.viewport,.title,.description,.action,.close)) {
3054
4166
  transform: translateX(var(--radix-toast-swipe-move-x));
@@ -3058,7 +4170,9 @@ button.modal-with-double-button-close-button {
3058
4170
  transition: transform var(--motion-fast) var(--motion-ease);
3059
4171
  }
3060
4172
  .base-toast[data-swipe='end']:where(:not(.viewport,.title,.description,.action,.close)) {
3061
- animation: toast-swipe-out var(--motion-fast) var(--motion-ease) forwards;
4173
+ --motion-swipe-from: var(--radix-toast-swipe-end-x);
4174
+ --motion-swipe-to: calc(100% + var(--spacing-24));
4175
+ animation: motion-exit-swipe var(--motion-fast) var(--motion-ease) forwards;
3062
4176
  }
3063
4177
  .base-toast:where(.viewport) {
3064
4178
  position: fixed;
@@ -3073,12 +4187,12 @@ button.modal-with-double-button-close-button {
3073
4187
  list-style: none;
3074
4188
  outline: none;
3075
4189
  /* 아래에서 올라오는 자리를 기본으로 두고 position 클래스가 덮는다 */
3076
- --toast-enter: translateY(var(--spacing-16));
4190
+ --motion-slide-offset: translateY(var(--spacing-16));
3077
4191
  }
3078
4192
  .base-toast.position-top-right:where(.viewport) {
3079
4193
  top: 0;
3080
4194
  right: 0;
3081
- --toast-enter: translateY(calc(-1 * var(--spacing-16)));
4195
+ --motion-slide-offset: translateY(calc(-1 * var(--spacing-16)));
3082
4196
  }
3083
4197
  .base-toast.position-bottom-right:where(.viewport) {
3084
4198
  right: 0;
@@ -3099,35 +4213,6 @@ button.modal-with-double-button-close-button {
3099
4213
  background: transparent;
3100
4214
  cursor: var(--cursor-button);
3101
4215
  }
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
4216
  @media (prefers-reduced-motion: reduce) {
3132
4217
  .base-toast:where(:not(.viewport, .title, .description, .action, .close)) {
3133
4218
  animation: none;