@icen.ai/ui 0.4.0 → 0.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 (155) hide show
  1. package/AGENTS.md +256 -0
  2. package/README.md +47 -13
  3. package/dist/base.css +31 -4
  4. package/dist/behaviors/accordion.d.mts +4 -0
  5. package/dist/behaviors/accordion.mjs +4 -3
  6. package/dist/behaviors/ai-chat.d.mts +57 -0
  7. package/dist/behaviors/ai-chat.mjs +557 -0
  8. package/dist/behaviors/ai-composer.d.mts +112 -0
  9. package/dist/behaviors/ai-composer.mjs +2032 -0
  10. package/dist/behaviors/ai-core.d.mts +151 -0
  11. package/dist/behaviors/ai-core.mjs +491 -0
  12. package/dist/behaviors/ai-diff.d.mts +53 -0
  13. package/dist/behaviors/ai-diff.mjs +282 -0
  14. package/dist/behaviors/ai-panel.d.mts +98 -0
  15. package/dist/behaviors/ai-panel.mjs +1012 -0
  16. package/dist/behaviors/ai-provider.d.mts +198 -0
  17. package/dist/behaviors/ai-provider.mjs +1055 -0
  18. package/dist/behaviors/ai-tool.d.mts +18 -0
  19. package/dist/behaviors/ai-tool.mjs +2125 -0
  20. package/dist/behaviors/ai-tools.d.mts +81 -0
  21. package/dist/behaviors/ai-tools.mjs +1658 -0
  22. package/dist/behaviors/back-top.d.mts +20 -0
  23. package/dist/behaviors/back-top.mjs +101 -0
  24. package/dist/behaviors/carousel.d.mts +4 -0
  25. package/dist/behaviors/carousel.mjs +34 -16
  26. package/dist/behaviors/charts.d.mts +250 -0
  27. package/dist/behaviors/charts.mjs +1301 -448
  28. package/dist/behaviors/command-palette.d.mts +8 -0
  29. package/dist/behaviors/command-palette.mjs +189 -0
  30. package/dist/behaviors/context-menu.d.mts +36 -0
  31. package/dist/behaviors/context-menu.mjs +6 -2
  32. package/dist/behaviors/copy.d.mts +4 -0
  33. package/dist/behaviors/copy.mjs +7 -6
  34. package/dist/behaviors/datatable.d.mts +78 -0
  35. package/dist/behaviors/datatable.mjs +54 -11
  36. package/dist/behaviors/date-picker.d.mts +4 -0
  37. package/dist/behaviors/date-picker.mjs +296 -0
  38. package/dist/behaviors/dropdown.d.mts +4 -0
  39. package/dist/behaviors/dropdown.mjs +7 -3
  40. package/dist/behaviors/input.d.mts +4 -0
  41. package/dist/behaviors/input.mjs +184 -8
  42. package/dist/behaviors/modal.d.mts +11 -0
  43. package/dist/behaviors/modal.mjs +11 -6
  44. package/dist/behaviors/nav.d.mts +7 -0
  45. package/dist/behaviors/nav.mjs +25 -9
  46. package/dist/behaviors/notification.d.mts +172 -0
  47. package/dist/behaviors/notification.mjs +908 -0
  48. package/dist/behaviors/popover.d.mts +46 -0
  49. package/dist/behaviors/popover.mjs +2 -1
  50. package/dist/behaviors/select.d.mts +4 -0
  51. package/dist/behaviors/select.mjs +268 -34
  52. package/dist/behaviors/sidebar.d.mts +4 -0
  53. package/dist/behaviors/sidebar.mjs +4 -3
  54. package/dist/behaviors/slider.d.mts +4 -0
  55. package/dist/behaviors/slider.mjs +4 -3
  56. package/dist/behaviors/split-pane.d.mts +4 -0
  57. package/dist/behaviors/split-pane.mjs +102 -0
  58. package/dist/behaviors/tabs.d.mts +8 -0
  59. package/dist/behaviors/tabs.mjs +44 -19
  60. package/dist/behaviors/tag-input.d.mts +4 -0
  61. package/dist/behaviors/tag-input.mjs +12 -3
  62. package/dist/behaviors/theme.d.mts +25 -0
  63. package/dist/behaviors/theme.mjs +3 -1
  64. package/dist/behaviors/toast.d.mts +26 -0
  65. package/dist/behaviors/toast.mjs +24 -18
  66. package/dist/behaviors/tree.d.mts +4 -0
  67. package/dist/behaviors/tree.mjs +4 -3
  68. package/dist/behaviors/upload.d.mts +4 -0
  69. package/dist/behaviors/upload.mjs +205 -17
  70. package/dist/components/ai-chat.css +1037 -0
  71. package/dist/components/ai-chat.mjs +3 -0
  72. package/dist/components/ai-composer.mjs +3 -0
  73. package/dist/components/ai-context.mjs +2 -0
  74. package/dist/components/ai-diff.css +311 -0
  75. package/dist/components/ai-diff.mjs +3 -0
  76. package/dist/components/ai-files.mjs +3 -0
  77. package/dist/components/ai-message.mjs +3 -0
  78. package/dist/components/ai-panel.css +682 -0
  79. package/dist/components/ai-reasoning.mjs +3 -0
  80. package/dist/components/ai-subagent.mjs +2 -0
  81. package/dist/components/ai-todo.mjs +2 -0
  82. package/dist/components/ai-tool-call.mjs +2 -0
  83. package/dist/components/ai-tool.css +532 -0
  84. package/dist/components/ai-usage.mjs +3 -0
  85. package/dist/components/back-top.css +88 -0
  86. package/dist/components/back-top.mjs +3 -0
  87. package/dist/components/badge.css +127 -0
  88. package/dist/components/badge.mjs +2 -0
  89. package/dist/components/breadcrumb.css +53 -0
  90. package/dist/components/btn.css +4 -7
  91. package/dist/components/card.css +66 -1
  92. package/dist/components/chart-bar.css +10 -0
  93. package/dist/components/chart-calendar.mjs +5 -0
  94. package/dist/components/chart-gauge.css +1 -1
  95. package/dist/components/chart-heatmap.css +40 -0
  96. package/dist/components/chart-scatter.css +24 -0
  97. package/dist/components/chart-scatter.mjs +4 -0
  98. package/dist/components/charts.css +86 -1
  99. package/dist/components/charts.mjs +1 -0
  100. package/dist/components/command-palette.css +246 -0
  101. package/dist/components/command-palette.mjs +3 -0
  102. package/dist/components/content.css +221 -8
  103. package/dist/components/copy.css +93 -0
  104. package/dist/components/copy.mjs +3 -0
  105. package/dist/components/datatable.css +4 -8
  106. package/dist/components/date-picker.css +240 -0
  107. package/dist/components/date-picker.mjs +3 -0
  108. package/dist/components/empty.css +69 -3
  109. package/dist/components/feedback.css +241 -10
  110. package/dist/components/form.css +129 -11
  111. package/dist/components/form.mjs +2 -0
  112. package/dist/components/input.css +25 -2
  113. package/dist/components/layout.css +36 -36
  114. package/dist/components/media.css +153 -6
  115. package/dist/components/menu.css +2 -2
  116. package/dist/components/modal.css +1 -1
  117. package/dist/components/nav.css +106 -2
  118. package/dist/components/notification.css +387 -0
  119. package/dist/components/notification.mjs +3 -0
  120. package/dist/components/pagination.css +1 -1
  121. package/dist/components/panel.css +104 -9
  122. package/dist/components/pill.css +82 -12
  123. package/dist/components/scroll-area.css +112 -0
  124. package/dist/components/scroll-area.mjs +2 -0
  125. package/dist/components/segmented.css +80 -5
  126. package/dist/components/segmented.mjs +1 -0
  127. package/dist/components/select.css +104 -17
  128. package/dist/components/sidebar.css +119 -1
  129. package/dist/components/slider.css +113 -0
  130. package/dist/components/split-pane.css +111 -0
  131. package/dist/components/split-pane.mjs +3 -0
  132. package/dist/components/stat.css +131 -12
  133. package/dist/components/steps.css +98 -1
  134. package/dist/components/switch.css +145 -7
  135. package/dist/components/table.css +111 -29
  136. package/dist/components/tabs.css +132 -22
  137. package/dist/components/tag-input.css +4 -4
  138. package/dist/components/tag.css +164 -0
  139. package/dist/components/tag.mjs +2 -0
  140. package/dist/components/toast.css +122 -5
  141. package/dist/components/toolbar.css +101 -0
  142. package/dist/components/toolbar.mjs +2 -0
  143. package/dist/components/tooltip.css +2 -2
  144. package/dist/components/upload.css +61 -4
  145. package/dist/index.d.mts +37 -0
  146. package/dist/index.mjs +9409 -929
  147. package/dist/registry.json +176 -4
  148. package/dist/retro-effects.css +21 -17
  149. package/dist/tokens/colors.css +4 -1
  150. package/dist/tokens/retro-effects.css +21 -17
  151. package/dist/tokens/style-profiles.css +21 -0
  152. package/dist/tokens.css +25 -1
  153. package/dist/ui.css +9616 -2839
  154. package/docs/spec/ai-native.md +659 -0
  155. package/package.json +15 -5
@@ -202,6 +202,119 @@
202
202
  background: color-mix(in srgb, var(--token-text) 12%, transparent);
203
203
  }
204
204
 
205
+ /* ── 带标签的刻度(marks)── */
206
+ .slider-marks {
207
+ position: relative;
208
+ height: 20px;
209
+ margin-top: 2px;
210
+ font-size: var(--density-font-size-xs);
211
+ color: var(--token-text-faint);
212
+ font-variant-numeric: tabular-nums;
213
+ }
214
+ .slider-mark {
215
+ position: absolute;
216
+ transform: translateX(-50%);
217
+ white-space: nowrap;
218
+ cursor: pointer;
219
+ transition: color var(--icen-tx-fast);
220
+ }
221
+ .slider-mark:hover {
222
+ color: var(--token-accent);
223
+ }
224
+ .slider-mark.is-active {
225
+ color: var(--token-accent);
226
+ font-weight: 500;
227
+ }
228
+ /* 刻度上的大点(配合 marks 使用) */
229
+ .slider-mark::before {
230
+ content: '';
231
+ position: absolute;
232
+ left: 50%;
233
+ top: -8px;
234
+ width: 3px;
235
+ height: 3px;
236
+ border-radius: var(--radius-full);
237
+ background: var(--token-text-faint);
238
+ transform: translateX(-50%);
239
+ transition: background-color var(--icen-tx-fast);
240
+ }
241
+ .slider-mark:hover::before,
242
+ .slider-mark.is-active::before {
243
+ background: var(--token-accent);
244
+ }
245
+
246
+ /* ── 值提示气泡(拖拽时出现)── */
247
+ .slider-tooltip {
248
+ position: absolute;
249
+ bottom: calc(50% + var(--slider-thumb-size));
250
+ transform: translateX(-50%);
251
+ padding: 2px var(--density-space-xs);
252
+ background: var(--token-text);
253
+ color: var(--token-card-strong);
254
+ font-size: var(--density-font-size-xs);
255
+ font-variant-numeric: tabular-nums;
256
+ border-radius: var(--radius-xs);
257
+ white-space: nowrap;
258
+ pointer-events: none;
259
+ opacity: 0;
260
+ transition: opacity var(--icen-tx-fast);
261
+ z-index: 3;
262
+ }
263
+ .slider-tooltip::after {
264
+ content: '';
265
+ position: absolute;
266
+ top: 100%;
267
+ left: 50%;
268
+ transform: translateX(-50%);
269
+ border: 4px solid transparent;
270
+ border-top-color: var(--token-text);
271
+ }
272
+ .slider:active .slider-tooltip,
273
+ .slider:focus-within .slider-tooltip {
274
+ opacity: 1;
275
+ }
276
+ .slider--vertical .slider-tooltip {
277
+ bottom: auto;
278
+ left: calc(50% + var(--slider-thumb-size));
279
+ top: 50%;
280
+ transform: translate(0, -50%);
281
+ }
282
+ .slider--vertical .slider-tooltip::after {
283
+ top: 50%;
284
+ left: auto;
285
+ right: 100%;
286
+ transform: translateY(-50%);
287
+ border-top-color: transparent;
288
+ border-right-color: var(--token-text);
289
+ }
290
+
291
+ /* ── 滑块拖拽态(active 时放大) ── */
292
+ .slider:active .slider-thumb {
293
+ transform: translate(-50%, -50%) scale(1.25);
294
+ }
295
+ .slider--vertical:active .slider-thumb {
296
+ transform: translate(-50%, 50%) scale(1.25);
297
+ }
298
+
299
+ /* ── 填充段带渐变(高级视觉) ── */
300
+ .slider-fill--gradient,
301
+ .slider-range--gradient {
302
+ background: linear-gradient(90deg,
303
+ color-mix(in srgb, var(--token-accent) 60%, transparent),
304
+ var(--token-accent));
305
+ }
306
+
307
+ /* ── 禁用态填充色变灰 ── */
308
+ .slider.is-disabled .slider-fill,
309
+ .slider.is-disabled .slider-range {
310
+ background: var(--token-fill-2);
311
+ }
312
+ .slider.is-disabled .slider-thumb {
313
+ border-color: var(--token-text-faint);
314
+ }
315
+
205
316
  @media (prefers-reduced-motion: reduce) {
206
317
  .slider-fill, .slider-range, .slider-thumb { transition: none; }
318
+ .slider-tooltip { transition: none; }
319
+ .slider-mark, .slider-mark::before { transition: none; }
207
320
  }
@@ -0,0 +1,111 @@
1
+ /*
2
+ * @icen.ai/ui — Component: split-pane(分栏拖拽容器,与 behaviors/split-pane 配套)
3
+ *
4
+ * 契约:
5
+ * <div class="split-pane [--vertical]" data-split-pane style="--split: 50%">
6
+ * <div class="split-pane-first">左侧(或上侧)内容</div>
7
+ * <div class="split-pane-divider" role="separator" aria-orientation="vertical" aria-valuenow="50"></div>
8
+ * <div class="split-pane-second">右侧(或下侧)内容</div>
9
+ * </div>
10
+ *
11
+ * behavior 接管 .split-pane-divider 的 pointer 拖拽,按比例更新 --split 变量
12
+ * (范围 10% – 90%);双击分隔条复位到 50%。
13
+ *
14
+ * 常用于:编辑器左右分栏 / 后台主从布局 / 邮件列表+详情。
15
+ */
16
+
17
+ .split-pane {
18
+ display: flex;
19
+ align-items: stretch;
20
+ width: 100%;
21
+ height: 100%;
22
+ min-height: 200px;
23
+ overflow: hidden;
24
+ background: var(--token-surface);
25
+ border: 1px solid var(--token-line-soft);
26
+ border-radius: var(--radius-md);
27
+ }
28
+
29
+ .split-pane-first {
30
+ flex: 0 0 var(--split, 50%);
31
+ min-width: 0;
32
+ overflow: auto;
33
+ overscroll-behavior: contain;
34
+ }
35
+ .split-pane-second {
36
+ flex: 1 1 auto;
37
+ min-width: 0;
38
+ overflow: auto;
39
+ overscroll-behavior: contain;
40
+ }
41
+
42
+ /* ── 分隔条(默认纵向 col-resize) ── */
43
+ .split-pane-divider {
44
+ flex: 0 0 6px;
45
+ position: relative;
46
+ background: var(--token-line-soft);
47
+ cursor: col-resize;
48
+ user-select: none;
49
+ touch-action: none;
50
+ transition: background var(--icen-tx-fast);
51
+ }
52
+ .split-pane-divider::after {
53
+ content: '';
54
+ position: absolute;
55
+ top: 50%;
56
+ left: 50%;
57
+ width: 2px;
58
+ height: 28px;
59
+ border-radius: 1px;
60
+ background: var(--token-text-faint);
61
+ opacity: 0.35;
62
+ transform: translate(-50%, -50%);
63
+ transition: opacity var(--icen-tx-fast), background-color var(--icen-tx-fast);
64
+ }
65
+ .split-pane-divider:hover::after,
66
+ .split-pane-divider.is-dragging::after {
67
+ opacity: 1;
68
+ background: var(--token-accent);
69
+ }
70
+ .split-pane-divider:hover,
71
+ .split-pane-divider.is-dragging {
72
+ background: color-mix(in srgb, var(--token-accent) 14%, var(--token-line-soft));
73
+ }
74
+ .split-pane-divider:focus-visible {
75
+ outline: none;
76
+ background: color-mix(in srgb, var(--token-accent) 18%, var(--token-line-soft));
77
+ }
78
+
79
+ /* ── 纵向布局(上下分栏) ── */
80
+ .split-pane--vertical {
81
+ flex-direction: column;
82
+ }
83
+ .split-pane--vertical .split-pane-first {
84
+ flex: 0 0 var(--split, 50%);
85
+ min-height: 0;
86
+ }
87
+ .split-pane--vertical .split-pane-second {
88
+ flex-basis: auto;
89
+ min-height: 0;
90
+ }
91
+ .split-pane--vertical .split-pane-divider {
92
+ cursor: row-resize;
93
+ flex-basis: 6px;
94
+ height: 6px;
95
+ width: auto;
96
+ }
97
+ .split-pane--vertical .split-pane-divider::after {
98
+ width: 28px;
99
+ height: 2px;
100
+ }
101
+
102
+ /* ── 嵌套分栏不应再有外边框 ── */
103
+ .split-pane .split-pane {
104
+ border: 0;
105
+ border-radius: 0;
106
+ }
107
+
108
+ @media (prefers-reduced-motion: reduce) {
109
+ .split-pane-divider { transition: none; }
110
+ .split-pane-divider::after { transition: none; }
111
+ }
@@ -0,0 +1,3 @@
1
+ // @icen.ai/ui — kit: split-pane(CSS + behavior 一行入口;构建产物,勿手改)
2
+ import './split-pane.css';
3
+ export * from '../behaviors/split-pane.mjs';
@@ -3,18 +3,31 @@
3
3
  *
4
4
  * 完整契约:
5
5
  * <div class="stat-grid [stat-grid--2|stat-grid--3|stat-grid--4]">
6
- * <div class="stat-card [.stat-card--interactive]">
6
+ * <div class="stat-card [.stat-card--interactive] [.stat-card--horizontal]">
7
7
  * <div class="stat-head">
8
8
  * <span class="stat-label">用户总数</span>
9
- * <span class="stat-icon">…svg…</span> ← 可选
9
+ * <span class="stat-icon [.icon--success|.icon--warning|.icon--error|.icon--info]>…svg…</span>
10
10
  * </div>
11
- * <div class="stat-num [.accent|.warn|.bad|.info]">0</div>
12
- * <div class="stat-delta [.up|.down|.flat]"> ← 可选趋势
13
- * <svg>…arrow…</svg> +12.4%
14
- * <span class="stat-period">比上周</span>
11
+ * <div class="stat-body">
12
+ * <div class="stat-num [--accent|--warning|--error|--info]">0</div>
13
+ * <div class="stat-delta [.up|.down|.flat]"> ← 可选趋势
14
+ * <svg>…arrow…</svg> +12.4%
15
+ * <span class="stat-period">比上周</span>
16
+ * </div>
17
+ * </div>
18
+ * <div class="stat-spark">…svg mini chart…</div> ← 可选:迷你折线/柱状
19
+ * <div class="stat-progress"> ← 可选:进度环
20
+ * <svg class="stat-ring" viewBox="0 0 36 36">
21
+ * <circle class="stat-ring-track" cx="18" cy="18" r="15.5" />
22
+ * <circle class="stat-ring-fill" cx="18" cy="18" r="15.5" style="stroke-dasharray:75,100" />
23
+ * </svg>
15
24
  * </div>
16
25
  * </div>
17
26
  * </div>
27
+ *
28
+ * .stat-card--horizontal:横向布局(数字在右,标签在左),适合 dashboard 顶栏。
29
+ * .stat-spark:内嵌迷你折线/柱状图(消费方可引用 chart-sparkline.css 或自绘 SVG)。
30
+ * .stat-ring:进度环(SVG stroke-dasharray 控制百分比,0-100)。
18
31
  */
19
32
 
20
33
  .stat-grid {
@@ -72,20 +85,31 @@
72
85
  background: var(--token-accent-subtle);
73
86
  color: var(--token-accent);
74
87
  }
88
+ .stat-icon.icon--success { background: color-mix(in srgb, var(--token-success) 14%, transparent); color: var(--token-success); }
89
+ .stat-icon.icon--warning { background: color-mix(in srgb, var(--token-warning) 14%, transparent); color: var(--token-warning); }
90
+ .stat-icon.icon--error { background: color-mix(in srgb, var(--token-error) 14%, transparent); color: var(--token-error); }
91
+ .stat-icon.icon--info { background: color-mix(in srgb, var(--token-info) 14%, transparent); color: var(--token-info); }
75
92
  .stat-icon svg { width: 14px; height: 14px; }
76
93
 
94
+ /* ── 内容体(数字 + 趋势) ── */
95
+ .stat-body {
96
+ display: flex;
97
+ flex-direction: column;
98
+ gap: 2px;
99
+ }
100
+
77
101
  .stat-num {
78
- font-size: 24px;
102
+ font-size: var(--density-font-size-2xl);
79
103
  font-weight: 700;
80
104
  line-height: 1.2;
81
105
  font-variant-numeric: tabular-nums lining-nums;
82
106
  color: var(--token-text);
83
107
  word-break: break-all;
84
108
  }
85
- .stat-num.accent { color: var(--token-accent); }
86
- .stat-num.warn { color: var(--token-warning); }
87
- .stat-num.bad { color: var(--token-error); }
88
- .stat-num.info { color: var(--token-info); }
109
+ .stat-num--accent { color: var(--token-accent); }
110
+ .stat-num--warning { color: var(--token-warning); }
111
+ .stat-num--error { color: var(--token-error); }
112
+ .stat-num--info { color: var(--token-info); }
89
113
 
90
114
  .stat-delta {
91
115
  display: inline-flex;
@@ -110,4 +134,99 @@
110
134
  gap: 2px;
111
135
  }
112
136
  .stat-card--sm .stat-num { font-size: var(--density-font-size-lg); }
113
- .stat-card--sm .stat-label { font-size: 10px; }
137
+ .stat-card--sm .stat-label { font-size: var(--density-font-size-2xs); }
138
+
139
+ /* ── 横向布局(dashboard 顶栏:标签左 + 数字右) ── */
140
+ .stat-card--horizontal {
141
+ flex-direction: row;
142
+ align-items: center;
143
+ justify-content: space-between;
144
+ gap: var(--density-space-md);
145
+ }
146
+ .stat-card--horizontal .stat-head {
147
+ flex-direction: column;
148
+ align-items: flex-start;
149
+ gap: 2px;
150
+ flex: 1;
151
+ min-width: 0;
152
+ }
153
+ .stat-card--horizontal .stat-body {
154
+ text-align: right;
155
+ flex-shrink: 0;
156
+ }
157
+ .stat-card--horizontal .stat-delta {
158
+ justify-content: flex-end;
159
+ }
160
+
161
+ /* ── 迷你图槽(stat-spark)── */
162
+ .stat-spark {
163
+ margin-top: var(--density-space-xs);
164
+ height: 28px;
165
+ width: 100%;
166
+ }
167
+ .stat-spark svg {
168
+ width: 100%;
169
+ height: 100%;
170
+ display: block;
171
+ }
172
+ .stat-card--horizontal .stat-spark {
173
+ margin-top: 0;
174
+ width: 80px;
175
+ flex-shrink: 0;
176
+ }
177
+
178
+ /* ── 进度环 ── */
179
+ .stat-progress {
180
+ display: flex;
181
+ align-items: center;
182
+ justify-content: center;
183
+ margin-top: var(--density-space-xs);
184
+ }
185
+ .stat-card--horizontal .stat-progress {
186
+ margin-top: 0;
187
+ flex-shrink: 0;
188
+ }
189
+ .stat-ring {
190
+ width: 48px;
191
+ height: 48px;
192
+ transform: rotate(-90deg);
193
+ }
194
+ .stat-ring-track {
195
+ fill: none;
196
+ stroke: var(--token-fill-2);
197
+ stroke-width: 3;
198
+ }
199
+ .stat-ring-fill {
200
+ fill: none;
201
+ stroke: var(--token-accent);
202
+ stroke-width: 3;
203
+ stroke-linecap: round;
204
+ stroke-dasharray: 0, 100;
205
+ transition: stroke-dasharray var(--icen-tx-slow) var(--ease-out);
206
+ }
207
+ .stat-card .stat-ring-fill { stroke: var(--token-accent); }
208
+
209
+ /* ── 数字带前缀/后缀 ── */
210
+ .stat-num-prefix,
211
+ .stat-num-suffix {
212
+ font-size: 0.6em;
213
+ font-weight: 500;
214
+ color: var(--token-text-faint);
215
+ margin: 0 1px;
216
+ }
217
+
218
+ /* ── 加载态骨架(脉冲 keyframes 见 base.css 的 icen-skeleton-pulse) ── */
219
+ .stat-card.is-loading .stat-num,
220
+ .stat-card.is-loading .stat-delta {
221
+ color: transparent;
222
+ background: var(--token-fill-2);
223
+ border-radius: var(--radius-xs);
224
+ animation: icen-skeleton-pulse var(--stat-skeleton-duration, 1.5s) ease-in-out infinite;
225
+ }
226
+ .stat-card.is-loading .stat-num { height: 28px; }
227
+ .stat-card.is-loading .stat-delta { height: 14px; width: 60%; }
228
+ @media (prefers-reduced-motion: reduce) {
229
+ .stat-card.is-loading .stat-num,
230
+ .stat-card.is-loading .stat-delta { animation: none; }
231
+ .stat-ring-fill { transition: none; }
232
+ }
@@ -61,7 +61,7 @@
61
61
  }
62
62
  .step--finish .step-dot {
63
63
  background: var(--token-accent);
64
- color: var(--token-on-accent, #fff);
64
+ color: var(--token-on-accent);
65
65
  }
66
66
  .step--process .step-dot {
67
67
  background: var(--token-accent-subtle);
@@ -133,3 +133,100 @@
133
133
  @media (prefers-reduced-motion: reduce) {
134
134
  .step-dot, .step-line { transition: none; }
135
135
  }
136
+
137
+ /* ── 纯点变体(无数字/无标签,紧凑进度指示) ── */
138
+ .steps--dots .step-dot {
139
+ width: 10px;
140
+ height: 10px;
141
+ padding: 0;
142
+ font-size: 0;
143
+ }
144
+ .steps--dots .step-line { top: 20px; }
145
+ .steps--dots .step-title,
146
+ .steps--dots .step-desc { display: none; }
147
+ .steps--dots .step { flex: none; }
148
+ .steps--dots { gap: var(--density-space-xs); }
149
+
150
+ /* ── 自定义图标槽(step-dot 内放 svg) ── */
151
+ .step-dot-icon {
152
+ width: 16px;
153
+ height: 16px;
154
+ }
155
+ .step--finish .step-dot-icon { color: var(--token-on-accent); }
156
+ .step--process .step-dot-icon { color: var(--token-accent); }
157
+ .step--wait .step-dot-icon { color: var(--token-text-faint); }
158
+ .step--error .step-dot-icon { color: var(--token-error); }
159
+
160
+ /* ── 可点击步骤 ── */
161
+ .step--clickable {
162
+ cursor: pointer;
163
+ transition: opacity var(--icen-tx-fast);
164
+ }
165
+ .step--clickable:hover .step-dot {
166
+ transform: scale(1.08);
167
+ box-shadow: var(--icen-ring-soft);
168
+ }
169
+ .step--clickable:hover .step-title {
170
+ color: var(--token-accent);
171
+ }
172
+
173
+ /* ── 进度条变体(步骤间的填充进度,非离散连线) ── */
174
+ .steps--progress .step-line {
175
+ height: 3px;
176
+ border-radius: var(--radius-full);
177
+ background: var(--token-fill-2);
178
+ overflow: hidden;
179
+ }
180
+ .steps--progress .step-line::after {
181
+ content: '';
182
+ position: absolute;
183
+ inset: 0;
184
+ background: var(--token-accent);
185
+ transform-origin: left;
186
+ transform: scaleX(0);
187
+ transition: transform var(--icen-tx-slow) var(--ease-out);
188
+ }
189
+ .steps--progress .step--finish .step-line::after { transform: scaleX(1); }
190
+
191
+ /* ── 垂直模式的进度条变体 ── */
192
+ .steps--vertical.steps--progress .step-line::after {
193
+ transform-origin: top;
194
+ transform: scaleY(0);
195
+ }
196
+ .steps--vertical.steps--progress .step--finish .step-line::after {
197
+ transform: scaleY(1);
198
+ }
199
+
200
+ /* ── 步骤描述富化(可选 icon + text) ── */
201
+ .step-extra {
202
+ display: flex;
203
+ align-items: center;
204
+ gap: 4px;
205
+ margin-top: 4px;
206
+ font-size: var(--density-font-size-xs);
207
+ color: var(--token-text-faint);
208
+ justify-content: center;
209
+ }
210
+ .step--error .step-extra { color: var(--token-error); }
211
+
212
+ /* ── 数字步骤的连接箭头(可选替代纯线条) ── */
213
+ .steps--connected .step-line {
214
+ height: 0;
215
+ background: transparent;
216
+ }
217
+ .steps--connected .step-line::before {
218
+ content: '';
219
+ position: absolute;
220
+ inset: 0;
221
+ border-top: 1px dashed var(--token-outline-soft);
222
+ }
223
+ .steps--connected .step--finish .step-line::before {
224
+ border-top-style: solid;
225
+ border-top-color: var(--token-accent);
226
+ }
227
+
228
+ @media (max-width: 640px) {
229
+ /* 小屏:横向 steps 变为可横向滚动 */
230
+ .steps { overflow-x: auto; flex-wrap: nowrap; }
231
+ .step { min-width: 80px; }
232
+ }
@@ -59,8 +59,8 @@
59
59
  width: var(--switch-thumb);
60
60
  height: var(--switch-thumb);
61
61
  border-radius: var(--radius-full);
62
- background: var(--token-on-accent, #fff);
63
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
62
+ background: var(--token-on-accent);
63
+ box-shadow: 0 1px 2px color-mix(in srgb, var(--token-text) 15%, transparent);
64
64
  transition: transform var(--icen-tx-bounce);
65
65
  }
66
66
  .switch[aria-checked='true'] .switch-thumb {
@@ -71,10 +71,6 @@
71
71
  .switch--sm { --switch-w: 36px; --switch-h: 20px; --switch-thumb: 16px; }
72
72
  .switch--lg { --switch-w: 56px; --switch-h: 28px; --switch-thumb: 24px; }
73
73
 
74
- @media (prefers-reduced-motion: reduce) {
75
- .switch-thumb { transition: none; }
76
- }
77
-
78
74
  /* ── 勾选框(✓ 与半选横条均 ::before 自绘) ── */
79
75
  .checkbox {
80
76
  --checkbox-size: 16px;
@@ -104,7 +100,7 @@
104
100
  .checkbox[aria-checked='mixed'] {
105
101
  background: var(--token-accent);
106
102
  border-color: var(--token-accent);
107
- color: var(--token-on-accent, #fff);
103
+ color: var(--token-on-accent);
108
104
  }
109
105
  .checkbox::before {
110
106
  content: '';
@@ -210,3 +206,145 @@
210
206
  .radio-item[aria-checked='true'] .radio-dot::after {
211
207
  transform: translate(-50%, -50%) scale(1);
212
208
  }
209
+
210
+ /* ── 开关色彩变体 ── */
211
+ .switch--success[aria-checked='true'] { background: var(--token-success); }
212
+ .switch--warning[aria-checked='true'] { background: var(--token-warning); }
213
+ .switch--danger[aria-checked='true'] { background: var(--token-error); }
214
+
215
+ /* ── 开关带文字(on/off 文字嵌入) ── */
216
+ .switch--text {
217
+ --switch-w: 52px;
218
+ }
219
+ .switch--text .switch-thumb {
220
+ display: flex;
221
+ align-items: center;
222
+ justify-content: center;
223
+ font-size: var(--density-font-size-2xs);
224
+ font-weight: 700;
225
+ color: transparent;
226
+ }
227
+ .switch--text[aria-checked='true']::before {
228
+ content: 'ON';
229
+ position: absolute;
230
+ left: 6px;
231
+ top: 50%;
232
+ transform: translateY(-50%);
233
+ font-size: var(--density-font-size-2xs);
234
+ font-weight: 700;
235
+ color: var(--token-on-accent);
236
+ }
237
+ .switch--text[aria-checked='false']::before {
238
+ content: 'OFF';
239
+ position: absolute;
240
+ right: 6px;
241
+ top: 50%;
242
+ transform: translateY(-50%);
243
+ font-size: var(--density-font-size-2xs);
244
+ font-weight: 700;
245
+ color: var(--token-text-faint);
246
+ }
247
+
248
+ /* ── 带标签的开关行 ── */
249
+ .switch-row {
250
+ display: flex;
251
+ align-items: center;
252
+ justify-content: space-between;
253
+ gap: var(--density-space-sm);
254
+ padding: var(--density-space-sm) 0;
255
+ }
256
+ .switch-row-label {
257
+ display: flex;
258
+ flex-direction: column;
259
+ gap: 1px;
260
+ flex: 1;
261
+ min-width: 0;
262
+ }
263
+ .switch-row-title {
264
+ font-size: var(--density-font-size-md);
265
+ color: var(--token-text);
266
+ }
267
+ .switch-row-desc {
268
+ font-size: var(--density-font-size-xs);
269
+ color: var(--token-text-faint);
270
+ }
271
+
272
+ /* ── 勾选行(checkbox + label 组合) ── */
273
+ .checkbox-row {
274
+ display: flex;
275
+ align-items: flex-start;
276
+ gap: var(--density-space-sm);
277
+ cursor: pointer;
278
+ }
279
+ .checkbox-row-label {
280
+ display: flex;
281
+ flex-direction: column;
282
+ gap: 1px;
283
+ flex: 1;
284
+ min-width: 0;
285
+ }
286
+
287
+ /* ── 单选卡片(富化版 radio-item,带描述与图标) ── */
288
+ .radio-card {
289
+ display: flex;
290
+ flex-direction: column;
291
+ gap: var(--density-space-xs);
292
+ padding: var(--density-space-md);
293
+ border: 1px solid var(--token-outline-soft);
294
+ border-radius: var(--radius-md);
295
+ background: var(--token-surface);
296
+ cursor: pointer;
297
+ transition:
298
+ border-color var(--icen-tx-fast),
299
+ box-shadow var(--icen-tx-fast),
300
+ background-color var(--icen-tx-fast);
301
+ }
302
+ .radio-card:hover {
303
+ border-color: var(--icen-hover-border);
304
+ }
305
+ .radio-card-head {
306
+ display: flex;
307
+ align-items: center;
308
+ justify-content: space-between;
309
+ gap: var(--density-space-sm);
310
+ }
311
+ .radio-card.is-selected,
312
+ .radio-card[aria-checked='true'] {
313
+ border-color: var(--token-accent);
314
+ background: var(--token-accent-subtle);
315
+ box-shadow: var(--icen-ring-soft);
316
+ }
317
+ .radio-card:focus-visible {
318
+ outline: none;
319
+ box-shadow: var(--icen-ring);
320
+ }
321
+ .radio-card-title {
322
+ font-size: var(--density-font-size-md);
323
+ font-weight: 500;
324
+ color: var(--token-text);
325
+ }
326
+ .radio-card-desc {
327
+ font-size: var(--density-font-size-sm);
328
+ color: var(--token-text-muted);
329
+ line-height: 1.5;
330
+ }
331
+
332
+ /* 单选卡片网格 */
333
+ .radio-card-grid {
334
+ display: grid;
335
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
336
+ gap: var(--density-space-sm);
337
+ }
338
+
339
+ /* ── 复选框组(多列网格) ── */
340
+ .checkbox-grid {
341
+ display: grid;
342
+ grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
343
+ gap: var(--density-space-sm);
344
+ }
345
+
346
+ @media (prefers-reduced-motion: reduce) {
347
+ .switch-thumb { transition: none; }
348
+ .radio-dot::after { transition: none; }
349
+ .radio-card { transition: none; }
350
+ }