@icen.ai/ui 0.3.0 → 0.5.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 (78) hide show
  1. package/dist/behaviors/back-top.mjs +84 -0
  2. package/dist/behaviors/charts.mjs +188 -0
  3. package/dist/behaviors/command-palette.mjs +177 -0
  4. package/dist/behaviors/datatable.mjs +244 -24
  5. package/dist/behaviors/date-picker.mjs +228 -0
  6. package/dist/behaviors/input.mjs +170 -1
  7. package/dist/behaviors/notification.mjs +754 -0
  8. package/dist/behaviors/select.mjs +151 -22
  9. package/dist/behaviors/split-pane.mjs +102 -0
  10. package/dist/behaviors/upload.mjs +165 -14
  11. package/dist/components/back-top.css +88 -0
  12. package/dist/components/back-top.mjs +3 -0
  13. package/dist/components/badge.css +131 -0
  14. package/dist/components/badge.mjs +2 -0
  15. package/dist/components/breadcrumb.css +57 -0
  16. package/dist/components/card.css +68 -0
  17. package/dist/components/chart-area.css +15 -0
  18. package/dist/components/chart-area.mjs +4 -0
  19. package/dist/components/chart-bar.css +79 -0
  20. package/dist/components/chart-bar.mjs +4 -0
  21. package/dist/components/chart-gauge.css +38 -0
  22. package/dist/components/chart-gauge.mjs +4 -0
  23. package/dist/components/chart-heatmap.css +52 -0
  24. package/dist/components/chart-heatmap.mjs +4 -0
  25. package/dist/components/chart-line.css +15 -0
  26. package/dist/components/chart-line.mjs +4 -0
  27. package/dist/components/chart-pie.css +26 -0
  28. package/dist/components/chart-pie.mjs +4 -0
  29. package/dist/components/chart-radar.css +19 -0
  30. package/dist/components/chart-radar.mjs +4 -0
  31. package/dist/components/chart-sparkline.css +14 -0
  32. package/dist/components/chart-sparkline.mjs +4 -0
  33. package/dist/components/chart-stack.css +30 -0
  34. package/dist/components/chart-stack.mjs +4 -0
  35. package/dist/components/charts.css +7 -207
  36. package/dist/components/charts.mjs +9 -0
  37. package/dist/components/command-palette.css +246 -0
  38. package/dist/components/command-palette.mjs +3 -0
  39. package/dist/components/content.css +214 -0
  40. package/dist/components/copy.css +93 -0
  41. package/dist/components/copy.mjs +3 -0
  42. package/dist/components/datatable.css +86 -0
  43. package/dist/components/date-picker.css +244 -0
  44. package/dist/components/date-picker.mjs +3 -0
  45. package/dist/components/empty.css +73 -3
  46. package/dist/components/feedback.css +239 -1
  47. package/dist/components/form.css +119 -0
  48. package/dist/components/form.mjs +2 -0
  49. package/dist/components/input.css +24 -1
  50. package/dist/components/media.css +144 -1
  51. package/dist/components/nav.css +99 -0
  52. package/dist/components/notification.css +387 -0
  53. package/dist/components/notification.mjs +3 -0
  54. package/dist/components/panel.css +100 -5
  55. package/dist/components/pill.css +70 -0
  56. package/dist/components/scroll-area.css +112 -0
  57. package/dist/components/scroll-area.mjs +2 -0
  58. package/dist/components/segmented.css +75 -0
  59. package/dist/components/select.css +97 -2
  60. package/dist/components/sidebar.css +118 -0
  61. package/dist/components/slider.css +113 -0
  62. package/dist/components/split-pane.css +112 -0
  63. package/dist/components/split-pane.mjs +3 -0
  64. package/dist/components/stat.css +129 -6
  65. package/dist/components/steps.css +97 -0
  66. package/dist/components/switch.css +142 -0
  67. package/dist/components/table.css +86 -3
  68. package/dist/components/tabs.css +122 -1
  69. package/dist/components/tag.css +164 -0
  70. package/dist/components/tag.mjs +2 -0
  71. package/dist/components/toast.css +114 -0
  72. package/dist/components/toolbar.css +101 -0
  73. package/dist/components/toolbar.mjs +2 -0
  74. package/dist/components/upload.css +61 -4
  75. package/dist/index.mjs +2039 -62
  76. package/dist/registry.json +200 -24
  77. package/dist/ui.css +4865 -622
  78. package/package.json +1 -1
@@ -0,0 +1,93 @@
1
+ /*
2
+ * @icen.ai/ui — Component: copy(复制按钮,与 behaviors/copy 配套)
3
+ *
4
+ * 完整契约:
5
+ * 文本变体(成功后文本 1.4s 内变「已复制」再还原):
6
+ * <button class="copy-btn" data-copy="复制的文本">复制</button>
7
+ * <button class="copy-btn copy-btn--ghost" data-copy="…">复制链接</button>
8
+ *
9
+ * 图标变体(带 data-copy-icon,仅加 .done 类;由 CSS 显隐子 svg):
10
+ * <button class="copy-btn copy-btn--icon" data-copy="…" data-copy-icon aria-label="复制">
11
+ * <svg class="copy-icon-clipboard">…</svg>
12
+ * <svg class="copy-icon-check" hidden>…</svg>
13
+ * </button>
14
+ *
15
+ * behavior 自动处理:
16
+ * - 文本变体:成功后 textContent = '已复制' + 加 .done 类,1.4s 后还原
17
+ * - 图标变体(带 data-copy-icon):仅加 .done 类(不改文本),由 CSS 切换 svg 显隐
18
+ *
19
+ * 与 toast.ok 区别:copy 是"原地反馈"(按钮自身闪烁),toast 是"全局通知"。
20
+ */
21
+
22
+ .copy-btn {
23
+ display: inline-flex;
24
+ align-items: center;
25
+ justify-content: center;
26
+ gap: var(--density-space-xs);
27
+ height: var(--density-input-height-sm);
28
+ padding: 0 var(--density-space-sm);
29
+ background: var(--token-control);
30
+ color: var(--token-text-muted);
31
+ border: 1px solid var(--token-outline-soft);
32
+ border-radius: var(--radius-sm);
33
+ font: inherit;
34
+ font-size: var(--density-font-size-sm);
35
+ cursor: pointer;
36
+ -webkit-tap-highlight-color: transparent;
37
+ transition:
38
+ color var(--icen-tx-fast),
39
+ border-color var(--icen-tx-fast),
40
+ background-color var(--icen-tx-fast);
41
+ }
42
+ .copy-btn:hover {
43
+ color: var(--token-text);
44
+ border-color: var(--icen-hover-border);
45
+ }
46
+ .copy-btn:focus-visible {
47
+ outline: none;
48
+ box-shadow: var(--icen-ring);
49
+ }
50
+ .copy-btn:disabled,
51
+ .copy-btn[aria-disabled='true'] {
52
+ opacity: var(--icen-disabled-opacity);
53
+ cursor: not-allowed;
54
+ pointer-events: none;
55
+ }
56
+
57
+ /* ghost 变体:透明底,纯描边 / hover 才显底 */
58
+ .copy-btn--ghost {
59
+ background: transparent;
60
+ border-color: var(--token-line-soft);
61
+ }
62
+ .copy-btn--ghost:hover {
63
+ background: var(--token-fill-1);
64
+ border-color: var(--token-accent);
65
+ }
66
+
67
+ /* ── 成功闪烁态(behavior 1.4s 后移除)── */
68
+ .copy-btn.done {
69
+ color: var(--token-success);
70
+ border-color: color-mix(in srgb, var(--token-success) 40%, transparent);
71
+ background: color-mix(in srgb, var(--token-success) 10%, transparent);
72
+ }
73
+
74
+ /* ── 图标变体:方形按钮,仅放 svg ── */
75
+ .copy-btn--icon {
76
+ width: 28px;
77
+ height: 28px;
78
+ padding: 0;
79
+ }
80
+ .copy-btn--icon svg {
81
+ width: 14px;
82
+ height: 14px;
83
+ flex-shrink: 0;
84
+ }
85
+
86
+ /* .done 时显隐切换:剪贴板 → 对勾 */
87
+ .copy-btn[data-copy-icon] .copy-icon-check { display: none; }
88
+ .copy-btn[data-copy-icon].done .copy-icon-clipboard { display: none; }
89
+ .copy-btn[data-copy-icon].done .copy-icon-check { display: inline-block; }
90
+
91
+ @media (prefers-reduced-motion: reduce) {
92
+ .copy-btn { transition: none; }
93
+ }
@@ -0,0 +1,3 @@
1
+ // @icen.ai/ui — kit: copy(CSS + behavior 一行入口;构建产物,勿手改)
2
+ import './copy.css';
3
+ export * from '../behaviors/copy.mjs';
@@ -341,3 +341,89 @@
341
341
  @media (prefers-reduced-motion: reduce) {
342
342
  .dt-filter-panel { animation: none; }
343
343
  }
344
+
345
+ /* ── 紧凑密度(32px 行高) ── */
346
+ .datatable--compact .dt-cell { min-height: 32px; padding: 0 10px; }
347
+ .datatable--compact .dt-cell--head { min-height: 32px; font-size: var(--density-font-size-xs); }
348
+
349
+ /* ── 工具栏右侧操作按钮组 ── */
350
+ .dt-toolbar-actions {
351
+ margin-left: auto;
352
+ display: flex;
353
+ align-items: center;
354
+ gap: 4px;
355
+ }
356
+ .dt-tool-btn {
357
+ display: inline-flex;
358
+ align-items: center;
359
+ justify-content: center;
360
+ width: 28px;
361
+ height: 28px;
362
+ flex-shrink: 0;
363
+ border: 1px solid var(--token-line-soft);
364
+ border-radius: var(--radius-sm);
365
+ background: transparent;
366
+ color: var(--token-text-faint);
367
+ cursor: pointer;
368
+ transition: color var(--icen-tx-fast), border-color var(--icen-tx-fast), background var(--icen-tx-fast);
369
+ }
370
+ .dt-tool-btn:hover {
371
+ color: var(--token-text);
372
+ border-color: var(--token-accent);
373
+ background: var(--token-fill-1);
374
+ }
375
+
376
+ /* ── 列宽拖拽手柄 ── */
377
+ .dt-resize {
378
+ position: absolute;
379
+ top: 0;
380
+ right: 0;
381
+ bottom: 0;
382
+ width: 6px;
383
+ cursor: col-resize;
384
+ user-select: none;
385
+ touch-action: none;
386
+ z-index: 1;
387
+ }
388
+ .dt-resize::after {
389
+ content: '';
390
+ position: absolute;
391
+ top: 25%;
392
+ bottom: 25%;
393
+ right: 2px;
394
+ width: 2px;
395
+ border-radius: 1px;
396
+ background: transparent;
397
+ transition: background var(--icen-tx-fast);
398
+ }
399
+ .dt-resize:hover::after,
400
+ .dt-resize:active::after {
401
+ background: var(--token-accent);
402
+ }
403
+ .dt-cell--head { /* 确保相对定位供 resize 绝对定位 */ }
404
+
405
+ /* ── 冻结列(左固定 sticky) ── */
406
+ .datatable--frozen .dt-cell--frozen {
407
+ position: sticky;
408
+ z-index: var(--z-raised);
409
+ background: var(--token-surface);
410
+ }
411
+ .datatable--frozen .dt-row--head .dt-cell--frozen {
412
+ z-index: calc(var(--z-raised) + 1);
413
+ background: var(--token-bg-soft);
414
+ }
415
+ .datatable--frozen .dt-row.is-selected .dt-cell--frozen {
416
+ background: var(--token-accent-subtle);
417
+ }
418
+ .datatable--frozen .dt-body .dt-row:hover .dt-cell--frozen {
419
+ background: var(--token-fill-1);
420
+ }
421
+ .datatable--frozen .dt-row.is-selected:hover .dt-cell--frozen {
422
+ background: color-mix(in srgb, var(--token-accent-subtle) 82%, var(--token-fill-1));
423
+ }
424
+
425
+ /* ── 行状态类(rowClassName 产出) ── */
426
+ .dt-row--danger { background: color-mix(in srgb, var(--token-error) 6%, transparent) !important; }
427
+ .dt-row--danger:hover { background: color-mix(in srgb, var(--token-error) 10%, transparent) !important; }
428
+ .dt-row--warning { background: color-mix(in srgb, var(--token-warning) 6%, transparent) !important; }
429
+ .dt-row--success { background: color-mix(in srgb, var(--token-success) 6%, transparent) !important; }
@@ -0,0 +1,244 @@
1
+ /*
2
+ * @icen.ai/ui — Component: date-picker(日期选择,与 behaviors/date-picker 配套)
3
+ *
4
+ * 契约:
5
+ * <div class="date-picker" data-date-picker data-date-picker-input="id">
6
+ * <button class="date-picker-trigger" type="button" aria-haspopup="dialog">
7
+ * <span class="date-picker-value">2026-07-29</span>
8
+ * <svg class="date-picker-icon">…日历 svg…</svg>
9
+ * </button>
10
+ * <input type="hidden" id="id" name="date" value="2026-07-29" />
11
+ * </div>
12
+ *
13
+ * 或直接挂到普通 input(behavior 自动包裹触发器外观):
14
+ * <input class="date-picker-input" data-date-picker value="2026-07-29" />
15
+ *
16
+ * behavior 渲染面板(portal 到 body):
17
+ * <div class="date-picker-panel" role="dialog">
18
+ * <div class="date-picker-head">
19
+ * <button class="date-picker-nav" data-prev>‹</button>
20
+ * <span class="date-picker-title">2026 年 7 月</span>
21
+ * <button class="date-picker-nav" data-next>›</button>
22
+ * </div>
23
+ * <div class="date-picker-weekdays">
24
+ * <span>一</span><span>二</span><span>三</span><span>四</span><span>五</span><span>六</span><span>日</span>
25
+ * </div>
26
+ * <div class="date-picker-grid">
27
+ * <button class="date-picker-cell [.is-outside] [.is-today] [.is-selected] [.is-disabled]">15</button>
28
+ * …
29
+ * </div>
30
+ * <div class="date-picker-foot">
31
+ * <button class="date-picker-today">今天</button>
32
+ * <button class="date-picker-clear">清除</button>
33
+ * </div>
34
+ * </div>
35
+ *
36
+ * 可选 data-*:
37
+ * data-date-picker-format="yyyy-mm-dd" 显示格式
38
+ * data-date-picker-min="2026-01-01" 最小可选日期
39
+ * data-date-picker-max="2026-12-31" 最大可选日期
40
+ * data-date-picker-week-start="0" 周首日(0=周日,1=周一,默认 1)
41
+ */
42
+
43
+ .date-picker {
44
+ display: inline-block;
45
+ position: relative;
46
+ }
47
+
48
+ .date-picker-trigger {
49
+ display: inline-flex;
50
+ align-items: center;
51
+ gap: var(--density-space-xs);
52
+ width: 100%;
53
+ height: var(--density-input-height-md);
54
+ padding: 0 var(--density-space-sm);
55
+ background: var(--token-control);
56
+ border: 1px solid var(--token-outline-soft);
57
+ border-radius: var(--radius-md);
58
+ box-shadow: var(--token-shadow-inset);
59
+ color: var(--token-text);
60
+ font: inherit;
61
+ font-size: var(--density-font-size-md);
62
+ cursor: pointer;
63
+ -webkit-tap-highlight-color: transparent;
64
+ transition: border-color var(--icen-tx-fast), box-shadow var(--icen-tx-fast);
65
+ }
66
+ .date-picker-trigger:hover { border-color: var(--icen-hover-border); }
67
+ .date-picker-trigger.is-open {
68
+ border-color: var(--icen-control-focus-border);
69
+ box-shadow: var(--icen-control-focus-shadow);
70
+ }
71
+ .date-picker-trigger:focus-visible {
72
+ outline: none;
73
+ border-color: var(--icen-control-focus-border);
74
+ box-shadow: var(--icen-control-focus-shadow);
75
+ }
76
+ .date-picker-value {
77
+ flex: 1;
78
+ min-width: 0;
79
+ text-align: left;
80
+ overflow: hidden;
81
+ text-overflow: ellipsis;
82
+ white-space: nowrap;
83
+ font-variant-numeric: tabular-nums;
84
+ }
85
+ .date-picker-value.is-empty { color: color-mix(in srgb, var(--token-text-faint) 60%, transparent); }
86
+ .date-picker-icon { display: inline-flex; color: var(--token-text-faint); flex-shrink: 0; }
87
+ .date-picker-icon svg { width: 14px; height: 14px; }
88
+
89
+ /* 尺寸 */
90
+ .date-picker-trigger.date-picker-trigger--sm { height: var(--density-input-height-sm); font-size: var(--density-font-size-sm); }
91
+ .date-picker-trigger.date-picker-trigger--lg { height: var(--density-input-height-lg); font-size: var(--density-font-size-lg); }
92
+
93
+ /* ── 面板 ── */
94
+ .date-picker-panel {
95
+ position: fixed;
96
+ z-index: var(--z-popover);
97
+ width: 280px;
98
+ padding: var(--density-space-sm);
99
+ background: var(--token-card-strong);
100
+ border: 1px solid var(--token-outline);
101
+ border-radius: var(--radius-md);
102
+ box-shadow: var(--token-shadow-overlay);
103
+ font-size: var(--density-font-size-sm);
104
+ animation: icen-dp-in var(--icen-tx-fast);
105
+ }
106
+ @keyframes icen-dp-in {
107
+ from { opacity: 0; transform: scale(0.96) translateY(-4px); }
108
+ to { opacity: 1; transform: scale(1) translateY(0); }
109
+ }
110
+
111
+ .date-picker-head {
112
+ display: flex;
113
+ align-items: center;
114
+ justify-content: space-between;
115
+ margin-bottom: var(--density-space-sm);
116
+ }
117
+ .date-picker-title {
118
+ font-weight: 600;
119
+ color: var(--token-text);
120
+ font-variant-numeric: tabular-nums;
121
+ flex: 1;
122
+ text-align: center;
123
+ }
124
+ .date-picker-nav {
125
+ display: inline-flex;
126
+ align-items: center;
127
+ justify-content: center;
128
+ width: 24px;
129
+ height: 24px;
130
+ padding: 0;
131
+ border: 0;
132
+ border-radius: var(--radius-xs);
133
+ background: transparent;
134
+ color: var(--token-text-muted);
135
+ font-size: var(--density-font-size-md);
136
+ cursor: pointer;
137
+ -webkit-tap-highlight-color: transparent;
138
+ transition: color var(--icen-tx-fast), background-color var(--icen-tx-fast);
139
+ }
140
+ .date-picker-nav:hover { color: var(--token-text); background: var(--token-fill-1); }
141
+ .date-picker-nav:focus-visible {
142
+ outline: none;
143
+ box-shadow: var(--icen-ring);
144
+ }
145
+
146
+ .date-picker-weekdays {
147
+ display: grid;
148
+ grid-template-columns: repeat(7, 1fr);
149
+ gap: 2px;
150
+ margin-bottom: 4px;
151
+ }
152
+ .date-picker-weekdays > span {
153
+ display: flex;
154
+ align-items: center;
155
+ justify-content: center;
156
+ height: 24px;
157
+ font-size: var(--density-font-size-xs);
158
+ color: var(--token-text-faint);
159
+ font-weight: 500;
160
+ }
161
+
162
+ .date-picker-grid {
163
+ display: grid;
164
+ grid-template-columns: repeat(7, 1fr);
165
+ gap: 2px;
166
+ }
167
+ .date-picker-cell {
168
+ display: flex;
169
+ align-items: center;
170
+ justify-content: center;
171
+ height: 30px;
172
+ padding: 0;
173
+ border: 0;
174
+ border-radius: var(--radius-sm);
175
+ background: transparent;
176
+ color: var(--token-text);
177
+ font: inherit;
178
+ font-size: var(--density-font-size-sm);
179
+ font-variant-numeric: tabular-nums;
180
+ cursor: pointer;
181
+ -webkit-tap-highlight-color: transparent;
182
+ transition: background-color var(--icen-tx-fast), color var(--icen-tx-fast);
183
+ }
184
+ .date-picker-cell:hover { background: var(--token-fill-1); }
185
+ .date-picker-cell.is-today {
186
+ border: 1px solid var(--token-accent);
187
+ color: var(--token-accent);
188
+ font-weight: 600;
189
+ }
190
+ .date-picker-cell.is-selected {
191
+ background: var(--token-accent);
192
+ color: var(--token-on-accent, #fff);
193
+ font-weight: 600;
194
+ }
195
+ .date-picker-cell.is-selected.is-today { border-color: var(--token-on-accent, #fff); }
196
+ .date-picker-cell.is-outside {
197
+ color: var(--token-text-faint);
198
+ opacity: 0.55;
199
+ }
200
+ .date-picker-cell.is-disabled {
201
+ color: var(--token-text-faint);
202
+ opacity: 0.35;
203
+ cursor: not-allowed;
204
+ pointer-events: none;
205
+ }
206
+ .date-picker-cell:focus-visible {
207
+ outline: none;
208
+ box-shadow: var(--icen-ring);
209
+ }
210
+
211
+ .date-picker-foot {
212
+ display: flex;
213
+ justify-content: space-between;
214
+ gap: var(--density-space-xs);
215
+ margin-top: var(--density-space-sm);
216
+ padding-top: var(--density-space-sm);
217
+ border-top: 1px solid var(--token-line-soft);
218
+ }
219
+ .date-picker-today,
220
+ .date-picker-clear {
221
+ padding: 4px var(--density-space-sm);
222
+ border: 0;
223
+ border-radius: var(--radius-xs);
224
+ background: transparent;
225
+ color: var(--token-accent);
226
+ font: inherit;
227
+ font-size: var(--density-font-size-xs);
228
+ cursor: pointer;
229
+ -webkit-tap-highlight-color: transparent;
230
+ transition: background-color var(--icen-tx-fast);
231
+ }
232
+ .date-picker-today:hover,
233
+ .date-picker-clear:hover { background: var(--token-fill-1); }
234
+ .date-picker-today:focus-visible,
235
+ .date-picker-clear:focus-visible {
236
+ outline: none;
237
+ box-shadow: var(--icen-ring);
238
+ }
239
+ .date-picker-clear { color: var(--token-text-muted); }
240
+
241
+ @media (prefers-reduced-motion: reduce) {
242
+ .date-picker-panel { animation: none; }
243
+ .date-picker-cell { transition: none; }
244
+ }
@@ -0,0 +1,3 @@
1
+ // @icen.ai/ui — kit: date-picker(CSS + behavior 一行入口;构建产物,勿手改)
2
+ import './date-picker.css';
3
+ export * from '../behaviors/date-picker.mjs';
@@ -1,16 +1,25 @@
1
1
  /*
2
- * @icen.ai/ui — Component: empty(空状态 / 无数据 / 404 占位)
2
+ * @icen.ai/ui — Component: empty(空状态 / 无数据 / 404 / 错误 / 维护中)
3
3
  *
4
4
  * 完整契约:
5
- * <div class="empty [.empty--sm|.empty--lg]">
5
+ * <div class="empty [.empty--sm|.empty--lg]
6
+ * [.empty--error|.empty--404|.empty--search|.empty--maintenance|.empty--network]">
6
7
  * <div class="empty-icon">…svg…</div> ← 可选,缺省显示默认占位
7
8
  * <h3 class="empty-title">暂无数据</h3>
8
9
  * <p class="empty-desc">描述文案(可选)</p>
9
10
  * <div class="empty-action"> ← 可选:操作按钮位
10
11
  * <button class="btn btn-primary">…</button>
12
+ * <button class="btn">…</button>
11
13
  * </div>
12
14
  * </div>
13
15
  *
16
+ * 类型预设(改图标底色与基调):
17
+ * .empty--error 错误(红底图标)
18
+ * .empty--404 404(中性大字)
19
+ * .empty--search 搜索无结果
20
+ * .empty--maintenance 维护中(warning 底)
21
+ * .empty--network 网络错误(info 底)
22
+ *
14
23
  * 规格源自 opengal src/presentation/ui/data/display.tsx 的 EmptyState。
15
24
  */
16
25
 
@@ -25,6 +34,7 @@
25
34
  .empty--sm { padding: var(--density-space-md); }
26
35
  .empty--lg { padding: 64px var(--density-space-md); }
27
36
 
37
+ /* ── 图标 ── */
28
38
  .empty-icon {
29
39
  width: 48px;
30
40
  height: 48px;
@@ -44,6 +54,28 @@
44
54
  .empty--lg .empty-icon svg { width: 32px; height: 32px; }
45
55
  .empty--sm .empty-icon svg { width: 18px; height: 18px; }
46
56
 
57
+ /* ── 类型预设 ── */
58
+ .empty--error .empty-icon {
59
+ background: color-mix(in srgb, var(--token-error) 10%, transparent);
60
+ border-color: color-mix(in srgb, var(--token-error) 20%, transparent);
61
+ color: var(--token-error);
62
+ }
63
+ .empty--maintenance .empty-icon {
64
+ background: color-mix(in srgb, var(--token-warning) 10%, transparent);
65
+ border-color: color-mix(in srgb, var(--token-warning) 20%, transparent);
66
+ color: var(--token-warning);
67
+ }
68
+ .empty--network .empty-icon {
69
+ background: color-mix(in srgb, var(--token-info) 10%, transparent);
70
+ border-color: color-mix(in srgb, var(--token-info) 20%, transparent);
71
+ color: var(--token-info);
72
+ }
73
+ .empty--search .empty-icon {
74
+ background: var(--token-fill-1);
75
+ color: var(--token-text-muted);
76
+ }
77
+
78
+ /* ── 标题与描述 ── */
47
79
  .empty-title {
48
80
  font-size: var(--density-font-size-md);
49
81
  font-weight: 500;
@@ -51,15 +83,53 @@
51
83
  margin: 0;
52
84
  line-height: 1.4;
53
85
  }
86
+ .empty--error .empty-title { color: var(--token-error); }
54
87
  .empty-desc {
55
88
  font-size: var(--density-font-size-sm);
56
89
  color: var(--token-text-muted);
57
90
  margin: 4px 0 0;
58
- max-width: 240px;
91
+ max-width: 280px;
59
92
  line-height: 1.5;
60
93
  }
94
+
95
+ /* ── 操作区 ── */
61
96
  .empty-action {
62
97
  margin-top: var(--density-space-md);
63
98
  display: flex;
64
99
  gap: var(--density-space-xs);
100
+ flex-wrap: wrap;
101
+ justify-content: center;
102
+ }
103
+
104
+ /* ── 404 大数字 ── */
105
+ .empty--404 .empty-title {
106
+ font-size: 72px;
107
+ font-weight: 800;
108
+ font-family: var(--font-heading);
109
+ line-height: 1;
110
+ letter-spacing: -0.02em;
111
+ color: var(--token-text-faint);
112
+ margin-bottom: var(--density-space-sm);
113
+ }
114
+ .empty--404 .empty-icon { display: none; }
115
+
116
+ /* ── 插画背景(使用方可用渐变背景替代图标) ── */
117
+ .empty--illustrated {
118
+ background: radial-gradient(ellipse at center,
119
+ color-mix(in srgb, var(--token-accent) 6%, transparent) 0%,
120
+ transparent 70%);
121
+ border-radius: var(--radius-lg);
122
+ }
123
+
124
+ /* ── 内嵌模式(empty 嵌入卡片/面板内时无 padding) ── */
125
+ .empty--inline {
126
+ padding: var(--density-space-md) var(--density-space-sm);
127
+ }
128
+
129
+ @media (max-width: 480px) {
130
+ .empty--404 .empty-title { font-size: 56px; }
131
+ }
132
+
133
+ @media (prefers-reduced-motion: reduce) {
134
+ .empty { animation: none; }
65
135
  }