@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
@@ -15,6 +15,8 @@
15
15
  box-shadow: var(--token-shadow-inset);
16
16
  border: 1px solid transparent;
17
17
  font-size: var(--density-font-size-sm);
18
+ position: relative;
19
+ overflow: hidden;
18
20
  }
19
21
  .alert--info { background: var(--token-info-muted); border-color: color-mix(in srgb, var(--token-info) 18%, transparent); }
20
22
  .alert--success { background: var(--token-success-muted); border-color: color-mix(in srgb, var(--token-success) 18%, transparent); }
@@ -209,9 +211,245 @@
209
211
  50% { opacity: 0.5; }
210
212
  }
211
213
 
214
+ /* ── alert 增强 ── */
215
+
216
+ /* 可操作横幅(含按钮) */
217
+ .alert-actions {
218
+ display: flex;
219
+ gap: var(--density-space-xs);
220
+ margin-top: var(--density-space-xs);
221
+ }
222
+ .alert--banner {
223
+ border-radius: 0;
224
+ border-left: none;
225
+ border-right: none;
226
+ border-top: none;
227
+ }
228
+ .alert--inline {
229
+ display: inline-flex;
230
+ padding: 2px var(--density-space-sm);
231
+ font-size: var(--density-font-size-xs);
232
+ }
233
+ .alert--inline .alert-close { width: 16px; height: 16px; }
234
+
235
+ /* ── result 增强 ── */
236
+ .result-list {
237
+ margin: var(--density-space-sm) 0 0;
238
+ padding: var(--density-space-sm);
239
+ background: var(--token-bg-soft);
240
+ border-radius: var(--radius-md);
241
+ font-size: var(--density-font-size-sm);
242
+ color: var(--token-text-muted);
243
+ text-align: left;
244
+ max-width: 400px;
245
+ max-height: 200px;
246
+ overflow-y: auto;
247
+ }
248
+ .result-list-item {
249
+ display: flex;
250
+ align-items: flex-start;
251
+ gap: var(--density-space-xs);
252
+ padding: 2px 0;
253
+ font-variant-numeric: tabular-nums;
254
+ word-break: break-all;
255
+ }
256
+
257
+ /* ── progress 环形 ── */
258
+ .progress-ring-wrap {
259
+ display: inline-flex;
260
+ align-items: center;
261
+ justify-content: center;
262
+ position: relative;
263
+ }
264
+ .progress-ring {
265
+ transform: rotate(-90deg);
266
+ }
267
+ .progress-ring-track {
268
+ fill: none;
269
+ stroke: var(--token-fill-2);
270
+ stroke-width: 4;
271
+ }
272
+ .progress-ring-fill {
273
+ fill: none;
274
+ stroke: var(--token-accent);
275
+ stroke-width: 4;
276
+ stroke-linecap: round;
277
+ transition: stroke-dashoffset var(--icen-tx-slow) var(--ease-out);
278
+ }
279
+ .progress-ring-text {
280
+ position: absolute;
281
+ font-size: var(--density-font-size-sm);
282
+ font-weight: 600;
283
+ font-variant-numeric: tabular-nums;
284
+ color: var(--token-text);
285
+ }
286
+ .progress-ring--sm .progress-ring-track,
287
+ .progress-ring--sm .progress-ring-fill { stroke-width: 3; }
288
+ .progress-ring--lg .progress-ring-track,
289
+ .progress-ring--lg .progress-ring-fill { stroke-width: 6; }
290
+ .progress-ring--success .progress-ring-fill { stroke: var(--token-success); }
291
+ .progress-ring--warning .progress-ring-fill { stroke: var(--token-warning); }
292
+ .progress-ring--error .progress-ring-fill { stroke: var(--token-error); }
293
+
294
+ /* progress 条纹动画 */
295
+ .progress--striped .progress-bar {
296
+ background-image: linear-gradient(
297
+ 45deg,
298
+ color-mix(in srgb, var(--token-card-strong) 20%, transparent) 25%,
299
+ transparent 25%,
300
+ transparent 50%,
301
+ color-mix(in srgb, var(--token-card-strong) 20%, transparent) 50%,
302
+ color-mix(in srgb, var(--token-card-strong) 20%, transparent) 75%,
303
+ transparent 75%,
304
+ transparent
305
+ );
306
+ background-size: 20px 20px;
307
+ animation: progress-stripe 1s linear infinite;
308
+ }
309
+ @keyframes progress-stripe {
310
+ from { background-position: 0 0; }
311
+ to { background-position: 20px 0; }
312
+ }
313
+
314
+ /* progress 渐变 */
315
+ .progress--gradient .progress-bar {
316
+ background: linear-gradient(90deg,
317
+ var(--token-accent),
318
+ color-mix(in srgb, var(--token-accent) 60%, var(--token-info)));
319
+ }
320
+
321
+ /* ── spinner 变体 ── */
322
+
323
+ /* 点阵 spinner */
324
+ .spinner--dots {
325
+ width: auto;
326
+ height: auto;
327
+ border: none;
328
+ display: inline-flex;
329
+ gap: 3px;
330
+ background: none;
331
+ animation: none;
332
+ }
333
+ .spinner--dots span {
334
+ display: block;
335
+ width: 6px;
336
+ height: 6px;
337
+ border-radius: var(--radius-full);
338
+ background: var(--token-accent);
339
+ animation: spinner-dot 1.4s ease-in-out infinite;
340
+ }
341
+ .spinner--dots span:nth-child(2) { animation-delay: 0.2s; }
342
+ .spinner--dots span:nth-child(3) { animation-delay: 0.4s; }
343
+ @keyframes spinner-dot {
344
+ 0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); }
345
+ 40% { opacity: 1; transform: scale(1); }
346
+ }
347
+
348
+ /* 条形 spinner */
349
+ .spinner--bars {
350
+ width: auto;
351
+ height: auto;
352
+ border: none;
353
+ display: inline-flex;
354
+ align-items: flex-end;
355
+ gap: 2px;
356
+ background: none;
357
+ animation: none;
358
+ }
359
+ .spinner--bars span {
360
+ display: block;
361
+ width: 3px;
362
+ height: 100%;
363
+ background: var(--token-accent);
364
+ border-radius: 1px;
365
+ animation: spinner-bar 1s ease-in-out infinite;
366
+ }
367
+ .spinner--bars span:nth-child(2) { animation-delay: 0.1s; }
368
+ .spinner--bars span:nth-child(3) { animation-delay: 0.2s; }
369
+ .spinner--bars span:nth-child(4) { animation-delay: 0.3s; }
370
+ @keyframes spinner-bar {
371
+ 0%, 100% { transform: scaleY(0.4); }
372
+ 50% { transform: scaleY(1); }
373
+ }
374
+
375
+ /* 全屏遮罩 spinner */
376
+ .spinner-overlay {
377
+ position: fixed;
378
+ inset: 0;
379
+ z-index: var(--z-overlay, 9999);
380
+ display: flex;
381
+ align-items: center;
382
+ justify-content: center;
383
+ background: color-mix(in srgb, var(--token-card-strong) 70%, transparent);
384
+ backdrop-filter: blur(2px);
385
+ -webkit-backdrop-filter: blur(2px);
386
+ flex-direction: column;
387
+ gap: var(--density-space-md);
388
+ }
389
+ .spinner-overlay-text {
390
+ font-size: var(--density-font-size-sm);
391
+ color: var(--token-text-muted);
392
+ }
393
+
394
+ /* ── skeleton 复合体 ── */
395
+
396
+ /* 骨架文本(多行模拟) */
397
+ .skeleton-text-group {
398
+ display: flex;
399
+ flex-direction: column;
400
+ gap: var(--density-space-xs);
401
+ flex: 1;
402
+ }
403
+
404
+ /* 头像 + 文本行组合(评论/消息占位) */
405
+ .skeleton-media {
406
+ display: flex;
407
+ align-items: center;
408
+ gap: var(--density-space-sm);
409
+ }
410
+
411
+ /* 表格骨架 */
412
+ .skeleton-table {
413
+ display: flex;
414
+ flex-direction: column;
415
+ gap: var(--density-space-sm);
416
+ }
417
+ .skeleton-table-row {
418
+ display: flex;
419
+ gap: var(--density-space-sm);
420
+ }
421
+ .skeleton-table-row > * {
422
+ height: 12px;
423
+ border-radius: var(--radius-xs);
424
+ background: color-mix(in srgb, var(--token-text) 8%, transparent);
425
+ animation: icen-skeleton-pulse 2s ease-in-out infinite;
426
+ }
427
+
428
+ /* 骨架波纹变体(shimmer,区别于 pulse) */
429
+ .skeleton--shimmer {
430
+ background: linear-gradient(
431
+ 90deg,
432
+ color-mix(in srgb, var(--token-text) 6%, transparent) 0%,
433
+ color-mix(in srgb, var(--token-text) 12%, transparent) 50%,
434
+ color-mix(in srgb, var(--token-text) 6%, transparent) 100%
435
+ );
436
+ background-size: 200% 100%;
437
+ animation: skeleton-shimmer 1.5s ease-in-out infinite;
438
+ }
439
+ @keyframes skeleton-shimmer {
440
+ from { background-position: 200% 0; }
441
+ to { background-position: -200% 0; }
442
+ }
443
+
212
444
  @media (prefers-reduced-motion: reduce) {
213
445
  .progress-bar { transition: none; }
214
446
  .progress--indeterminate .progress-bar { animation: none; width: 100% !important; }
447
+ .progress--striped .progress-bar { animation: none; }
448
+ .progress-ring-fill { transition: none; }
215
449
  .spinner { animation-duration: 2s; }
216
- .skeleton { animation: none; opacity: 0.6; }
450
+ .spinner--dots span,
451
+ .spinner--bars span { animation: none; }
452
+ .skeleton,
453
+ .skeleton-table-row > * { animation: none; opacity: 0.6; }
454
+ .skeleton--shimmer { animation: none; }
217
455
  }
@@ -114,6 +114,125 @@
114
114
  }
115
115
  .form-field[data-invalid='true'] .form-label { color: var(--token-error); }
116
116
 
117
+ /* ── 表单增强 ── */
118
+
119
+ /* 水平布局(label 与控件同行) */
120
+ .form-field--horizontal {
121
+ display: flex;
122
+ flex-direction: row;
123
+ align-items: flex-start;
124
+ gap: var(--density-space-md);
125
+ }
126
+ .form-field--horizontal .form-label {
127
+ flex-shrink: 0;
128
+ width: 120px;
129
+ text-align: right;
130
+ padding-top: calc((var(--density-input-height-md) - 1lh) / 2);
131
+ }
132
+ .form-field--horizontal .form-field-control {
133
+ flex: 1;
134
+ min-width: 0;
135
+ }
136
+
137
+ /* label 带帮助提示图标 */
138
+ .form-label-help {
139
+ display: inline-flex;
140
+ align-items: center;
141
+ margin-left: 4px;
142
+ width: 14px;
143
+ height: 14px;
144
+ color: var(--token-text-faint);
145
+ cursor: help;
146
+ }
147
+ .form-label-help svg { width: 12px; height: 12px; }
148
+
149
+ /* label 带可选标记 */
150
+ .form-label-optional {
151
+ font-size: 10px;
152
+ font-weight: 400;
153
+ color: var(--token-text-faint);
154
+ margin-left: 4px;
155
+ }
156
+
157
+ /* 表单区块标题 */
158
+ .form-section {
159
+ padding: var(--density-space-sm) 0;
160
+ }
161
+ .form-section-title {
162
+ font-size: var(--density-font-size-md);
163
+ font-weight: 600;
164
+ color: var(--token-text);
165
+ margin: 0 0 var(--density-space-sm);
166
+ padding-bottom: var(--density-space-xs);
167
+ border-bottom: 1px solid var(--token-outline-soft);
168
+ }
169
+ .form-section-desc {
170
+ font-size: var(--density-font-size-sm);
171
+ color: var(--token-text-muted);
172
+ margin: calc(-1 * var(--density-space-xs)) 0 var(--density-space-sm);
173
+ }
174
+
175
+ /* 表单操作区(底部按钮行) */
176
+ .form-actions {
177
+ display: flex;
178
+ align-items: center;
179
+ gap: var(--density-space-sm);
180
+ padding-top: var(--density-space-sm);
181
+ border-top: 1px solid var(--token-outline-soft);
182
+ }
183
+ .form-actions--right { justify-content: flex-end; }
184
+ .form-actions--center { justify-content: center; }
185
+ .form-actions--split { justify-content: space-between; }
186
+
187
+ /* 行内表单(紧凑,单行多字段) */
188
+ .form--inline {
189
+ flex-direction: row;
190
+ align-items: flex-end;
191
+ gap: var(--density-space-sm);
192
+ flex-wrap: wrap;
193
+ }
194
+ .form--inline .form-field {
195
+ flex: 1;
196
+ min-width: 160px;
197
+ }
198
+
199
+ /* 字段网格(多列布局) */
200
+ .form-grid {
201
+ display: grid;
202
+ grid-template-columns: repeat(var(--form-cols, 2), 1fr);
203
+ gap: var(--density-space-md);
204
+ }
205
+ .form-grid .form-field--full { grid-column: 1 / -1; }
206
+
207
+ /* 字段成功态 */
208
+ .form-field[data-valid='true'] .form-label::after {
209
+ content: '✓';
210
+ margin-left: 4px;
211
+ color: var(--token-success);
212
+ font-weight: 700;
213
+ }
214
+
215
+ /* 表单加载遮罩 */
216
+ .form-loading {
217
+ position: relative;
218
+ pointer-events: none;
219
+ opacity: 0.6;
220
+ }
221
+ .form-loading::after {
222
+ content: '';
223
+ position: absolute;
224
+ top: 50%;
225
+ left: 50%;
226
+ width: 24px;
227
+ height: 24px;
228
+ margin: -12px 0 0 -12px;
229
+ border: 2px solid color-mix(in srgb, var(--token-accent) 30%, transparent);
230
+ border-top-color: var(--token-accent);
231
+ border-radius: 50%;
232
+ animation: form-loading-spin 0.8s linear infinite;
233
+ }
234
+ @keyframes form-loading-spin { to { transform: rotate(360deg); } }
235
+
117
236
  /* ── 页面级横幅:操作结果 / 引导提示 ── */
118
237
  .banner-ok,
119
238
  .banner-err {
@@ -0,0 +1,2 @@
1
+ // @icen.ai/ui — kit: form(CSS + behavior 一行入口;构建产物,勿手改)
2
+ import './form.css';
@@ -188,7 +188,19 @@
188
188
  color: color-mix(in srgb, var(--token-text-faint) 60%, transparent);
189
189
  }
190
190
 
191
- /* 字符计数(behavior 可挂到 .textarea-wrap 内尾部) */
191
+ /* 字符计数(behavior 可挂到 .input-wrap 或 .textarea-wrap 内尾部) */
192
+ .input-wrap { position: relative; }
193
+ .input-wrap--has-counter { padding-right: calc(var(--density-space-sm) + 48px); }
194
+ .input-wrap--has-counter .input-counter {
195
+ position: absolute;
196
+ right: var(--density-space-sm);
197
+ top: 50%;
198
+ transform: translateY(-50%);
199
+ font-size: var(--density-font-size-xs);
200
+ color: var(--token-text-faint);
201
+ font-variant-numeric: tabular-nums;
202
+ pointer-events: none;
203
+ }
192
204
  .input-counter {
193
205
  position: absolute;
194
206
  right: var(--density-space-sm);
@@ -197,6 +209,17 @@
197
209
  color: var(--token-text-faint);
198
210
  font-variant-numeric: tabular-nums;
199
211
  pointer-events: none;
212
+ transition: color var(--icen-tx-fast);
200
213
  }
214
+ .input-counter.is-warn { color: var(--token-warning); }
215
+ .input-counter.is-error { color: var(--token-error); font-weight: 600; }
216
+
201
217
  .textarea-wrap { position: relative; }
202
218
  .textarea-wrap .textarea { padding-bottom: 24px; }
219
+
220
+ /* 有计数器时的 textarea 底部留空 */
221
+ .textarea-wrap .input-counter {
222
+ position: absolute;
223
+ right: var(--density-space-sm);
224
+ bottom: var(--density-space-xs);
225
+ }
@@ -59,6 +59,42 @@
59
59
  .avatar-status--busy { background: var(--token-error); }
60
60
  .avatar-status--away { background: var(--token-warning); }
61
61
 
62
+ /* 头像色彩变体(文字头像底色) */
63
+ .avatar--info { background: color-mix(in srgb, var(--token-info) 15%, transparent); color: var(--token-info); }
64
+ .avatar--success { background: color-mix(in srgb, var(--token-success) 15%, transparent); color: var(--token-success); }
65
+ .avatar--warning { background: color-mix(in srgb, var(--token-warning) 15%, transparent); color: var(--token-warning); }
66
+ .avatar--error { background: color-mix(in srgb, var(--token-error) 15%, transparent); color: var(--token-error); }
67
+
68
+ /* 渐变底头像 */
69
+ .avatar--gradient {
70
+ background: linear-gradient(135deg, var(--token-accent), var(--token-accent-deep, var(--token-accent)));
71
+ color: var(--token-on-accent, #fff);
72
+ }
73
+
74
+ /* 方形头像 */
75
+ .avatar--square { border-radius: var(--radius-md); }
76
+
77
+ /* 头像通知环(红圈外框) */
78
+ .avatar--ring {
79
+ box-shadow: 0 0 0 2px var(--token-bg), 0 0 0 4px var(--token-error);
80
+ }
81
+
82
+ /* 头像组溢出计数 */
83
+ .avatar-group-count {
84
+ display: inline-flex;
85
+ align-items: center;
86
+ justify-content: center;
87
+ width: 28px;
88
+ height: 28px;
89
+ font-size: 10px;
90
+ font-weight: 600;
91
+ color: var(--token-text-muted);
92
+ background: var(--token-fill-2);
93
+ border: 2px solid var(--token-bg);
94
+ border-radius: var(--radius-full);
95
+ margin-left: -8px;
96
+ }
97
+
62
98
  /* ══════════════ media-card · 封面卡 ══════════════ */
63
99
  .media-card {
64
100
  position: relative;
@@ -164,6 +200,53 @@
164
200
  font-variant-numeric: tabular-nums;
165
201
  }
166
202
 
203
+ /* 只读模式(不可交互) */
204
+ .rating--readonly .rating-star {
205
+ cursor: default;
206
+ }
207
+ .rating--readonly .rating-star:hover {
208
+ transform: none;
209
+ }
210
+
211
+ /* 清除评分钮 */
212
+ .rating-clear {
213
+ display: inline-flex;
214
+ align-items: center;
215
+ justify-content: center;
216
+ width: 20px;
217
+ height: 20px;
218
+ margin-left: 4px;
219
+ padding: 0;
220
+ border: 0;
221
+ border-radius: var(--radius-xs);
222
+ background: transparent;
223
+ color: var(--token-text-faint);
224
+ cursor: pointer;
225
+ transition: color var(--icen-tx-fast), background-color var(--icen-tx-fast);
226
+ }
227
+ .rating-clear:hover {
228
+ color: var(--token-text);
229
+ background: var(--token-fill-1);
230
+ }
231
+ .rating-clear svg { width: 12px; height: 12px; }
232
+
233
+ /* 评分数(评价人数) */
234
+ .rating-count {
235
+ margin-left: var(--density-space-xs);
236
+ font-size: var(--density-font-size-xs);
237
+ color: var(--token-text-faint);
238
+ font-variant-numeric: tabular-nums;
239
+ }
240
+
241
+ /* 评分 + 文字摘要(如「4.5 · 128 条评价」) */
242
+ .rating-summary {
243
+ display: inline-flex;
244
+ align-items: center;
245
+ gap: var(--density-space-xs);
246
+ font-size: var(--density-font-size-sm);
247
+ color: var(--token-text-muted);
248
+ }
249
+
167
250
  /* ══════════════ carousel · 轮播(behaviors/carousel 配套) ══════════════
168
251
  * DOM 契约:
169
252
  * <div class="carousel" data-carousel>
@@ -274,7 +357,67 @@
274
357
  transition: width var(--icen-tx-slow);
275
358
  }
276
359
 
360
+ /* 缩略图导航条 */
361
+ .carousel-thumbs {
362
+ display: flex;
363
+ gap: var(--density-space-xs);
364
+ margin-top: var(--density-space-sm);
365
+ padding: var(--density-space-xs);
366
+ overflow-x: auto;
367
+ scrollbar-width: none;
368
+ }
369
+ .carousel-thumbs::-webkit-scrollbar { display: none; }
370
+ .carousel-thumb {
371
+ flex-shrink: 0;
372
+ width: 56px;
373
+ height: 40px;
374
+ border-radius: var(--radius-sm);
375
+ overflow: hidden;
376
+ border: 2px solid transparent;
377
+ cursor: pointer;
378
+ opacity: 0.6;
379
+ transition: opacity var(--icen-tx-fast), border-color var(--icen-tx-fast);
380
+ }
381
+ .carousel-thumb:hover { opacity: 1; }
382
+ .carousel-thumb.is-active,
383
+ .carousel-thumb[aria-current='true'] {
384
+ opacity: 1;
385
+ border-color: var(--token-accent);
386
+ }
387
+ .carousel-thumb img,
388
+ .carousel-thumb video {
389
+ width: 100%;
390
+ height: 100%;
391
+ object-fit: cover;
392
+ }
393
+
394
+ /* 轮播索引指示器(1 / 5) */
395
+ .carousel-index {
396
+ position: absolute;
397
+ top: var(--density-space-sm);
398
+ right: var(--density-space-sm);
399
+ padding: 2px var(--density-space-sm);
400
+ font-size: var(--density-font-size-xs);
401
+ font-variant-numeric: tabular-nums;
402
+ color: var(--token-bg);
403
+ background: color-mix(in srgb, var(--token-text) 60%, transparent);
404
+ border-radius: var(--radius-full);
405
+ backdrop-filter: blur(4px);
406
+ -webkit-backdrop-filter: blur(4px);
407
+ z-index: 2;
408
+ }
409
+
410
+ /* 渐入动画 */
411
+ .carousel-slide {
412
+ animation: carousel-slide-fade var(--icen-tx-normal) var(--ease-out);
413
+ }
414
+ @keyframes carousel-slide-fade {
415
+ from { opacity: 0.6; }
416
+ to { opacity: 1; }
417
+ }
418
+
277
419
  @media (prefers-reduced-motion: reduce) {
278
420
  .carousel-track { transition: none; }
279
- .media-card, .rating-star, .carousel-arrow { transition: none; }
421
+ .carousel-slide { animation: none; }
422
+ .media-card, .rating-star, .carousel-arrow, .carousel-thumb { transition: none; }
280
423
  }
@@ -159,3 +159,102 @@
159
159
  @media (prefers-reduced-motion: reduce) {
160
160
  .nav, .nav-btn { transition: none; }
161
161
  }
162
+
163
+ /* ── 增强 ── */
164
+
165
+ /* 导航搜索槽 */
166
+ .nav-search {
167
+ display: flex;
168
+ align-items: center;
169
+ gap: var(--density-space-xs);
170
+ padding: 0 var(--density-space-sm);
171
+ height: 32px;
172
+ min-width: 180px;
173
+ background: var(--token-control);
174
+ border: 1px solid var(--token-outline-soft);
175
+ border-radius: var(--radius-full);
176
+ color: var(--token-text-faint);
177
+ font-size: var(--density-font-size-sm);
178
+ cursor: pointer;
179
+ transition: border-color var(--icen-tx-fast);
180
+ }
181
+ .nav-search:hover { border-color: var(--token-accent); }
182
+ .nav-search svg { width: 14px; height: 14px; flex-shrink: 0; }
183
+ .nav-search-kbd {
184
+ margin-left: auto;
185
+ padding: 1px 5px;
186
+ font-size: 10px;
187
+ font-family: var(--font-mono);
188
+ color: var(--token-text-faint);
189
+ background: var(--token-fill-2);
190
+ border-radius: var(--radius-xs);
191
+ }
192
+
193
+ /* 导航按钮上的通知徽标 */
194
+ .nav-btn-badge {
195
+ position: absolute;
196
+ top: 2px;
197
+ right: 2px;
198
+ min-width: 8px;
199
+ height: 8px;
200
+ padding: 0;
201
+ background: var(--token-error);
202
+ border: 1.5px solid var(--token-surface-glass);
203
+ border-radius: var(--radius-full);
204
+ pointer-events: none;
205
+ }
206
+ .nav-btn { position: relative; }
207
+ .nav-btn-badge--count {
208
+ min-width: 16px;
209
+ height: 16px;
210
+ padding: 0 4px;
211
+ font-size: 9px;
212
+ font-weight: 700;
213
+ color: var(--token-on-accent, #fff);
214
+ display: inline-flex;
215
+ align-items: center;
216
+ justify-content: center;
217
+ line-height: 1;
218
+ }
219
+
220
+ /* 链接式导航项(a 标签替代 button) */
221
+ .nav-link {
222
+ display: inline-flex;
223
+ align-items: center;
224
+ gap: var(--density-space-xs);
225
+ padding: 6px var(--density-space-sm);
226
+ font-size: var(--density-font-size-md);
227
+ color: var(--token-text-muted);
228
+ text-decoration: none;
229
+ border-radius: var(--radius-full);
230
+ transition: color var(--icen-tx-fast), background-color var(--icen-tx-fast);
231
+ }
232
+ .nav-link:hover {
233
+ color: var(--token-text);
234
+ background: var(--token-control-hover);
235
+ text-decoration: none;
236
+ }
237
+ .nav-link.is-active,
238
+ .nav-link[aria-current='page'] {
239
+ color: var(--token-accent);
240
+ background: var(--token-accent-subtle);
241
+ }
242
+
243
+ /* 导航用户区(头像 + 下拉触发器) */
244
+ .nav-user {
245
+ display: inline-flex;
246
+ align-items: center;
247
+ gap: var(--density-space-xs);
248
+ padding: 3px var(--density-space-xs) 3px 3px;
249
+ border-radius: var(--radius-full);
250
+ border: 1px solid var(--token-outline-soft);
251
+ background: var(--token-surface);
252
+ cursor: pointer;
253
+ transition: border-color var(--icen-tx-fast);
254
+ }
255
+ .nav-user:hover { border-color: var(--token-accent); }
256
+ .nav-user-name {
257
+ font-size: var(--density-font-size-sm);
258
+ color: var(--token-text);
259
+ }
260
+ .nav-user svg { width: 12px; height: 12px; color: var(--token-text-faint); }