@icen.ai/ui 0.2.1 → 0.4.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 (121) hide show
  1. package/README.md +29 -16
  2. package/dist/base.css +389 -48
  3. package/dist/behaviors/accordion.mjs +20 -0
  4. package/dist/behaviors/carousel.mjs +30 -1
  5. package/dist/behaviors/charts.mjs +356 -0
  6. package/dist/behaviors/copy.mjs +4 -3
  7. package/dist/behaviors/datatable.mjs +968 -0
  8. package/dist/behaviors/input.mjs +20 -1
  9. package/dist/behaviors/modal.mjs +14 -0
  10. package/dist/behaviors/nav.mjs +31 -4
  11. package/dist/behaviors/sidebar.mjs +12 -0
  12. package/dist/behaviors/slider.mjs +35 -3
  13. package/dist/behaviors/tabs.mjs +100 -9
  14. package/dist/behaviors/tag-input.mjs +11 -0
  15. package/dist/behaviors/theme.mjs +26 -0
  16. package/dist/behaviors/toast.mjs +83 -15
  17. package/dist/behaviors/tree.mjs +57 -1
  18. package/dist/behaviors/upload.mjs +2 -3
  19. package/dist/cli.mjs +74 -0
  20. package/dist/components/accordion.mjs +3 -0
  21. package/dist/components/alert.mjs +2 -0
  22. package/dist/components/avatar.mjs +2 -0
  23. package/dist/components/breadcrumb.css +91 -18
  24. package/dist/components/breadcrumb.mjs +2 -0
  25. package/dist/components/btn.css +174 -14
  26. package/dist/components/btn.mjs +2 -0
  27. package/dist/components/card.css +118 -18
  28. package/dist/components/card.mjs +2 -0
  29. package/dist/components/carousel.mjs +3 -0
  30. package/dist/components/chart-area.css +15 -0
  31. package/dist/components/chart-area.mjs +4 -0
  32. package/dist/components/chart-bar.css +79 -0
  33. package/dist/components/chart-bar.mjs +4 -0
  34. package/dist/components/chart-gauge.css +38 -0
  35. package/dist/components/chart-gauge.mjs +4 -0
  36. package/dist/components/chart-heatmap.css +52 -0
  37. package/dist/components/chart-heatmap.mjs +4 -0
  38. package/dist/components/chart-line.css +15 -0
  39. package/dist/components/chart-line.mjs +4 -0
  40. package/dist/components/chart-pie.css +26 -0
  41. package/dist/components/chart-pie.mjs +4 -0
  42. package/dist/components/chart-radar.css +19 -0
  43. package/dist/components/chart-radar.mjs +4 -0
  44. package/dist/components/chart-sparkline.css +14 -0
  45. package/dist/components/chart-sparkline.mjs +4 -0
  46. package/dist/components/chart-stack.css +30 -0
  47. package/dist/components/chart-stack.mjs +4 -0
  48. package/dist/components/charts.css +66 -74
  49. package/dist/components/charts.mjs +12 -0
  50. package/dist/components/content.css +256 -74
  51. package/dist/components/context-menu.mjs +3 -0
  52. package/dist/components/datatable.css +429 -0
  53. package/dist/components/datatable.mjs +4 -0
  54. package/dist/components/desc.mjs +2 -0
  55. package/dist/components/dropdown.mjs +3 -0
  56. package/dist/components/empty.css +50 -10
  57. package/dist/components/empty.mjs +2 -0
  58. package/dist/components/feedback.css +158 -47
  59. package/dist/components/form.css +139 -22
  60. package/dist/components/input.css +165 -45
  61. package/dist/components/input.mjs +3 -0
  62. package/dist/components/kbd.mjs +2 -0
  63. package/dist/components/layout.mjs +2 -0
  64. package/dist/components/list.mjs +2 -0
  65. package/dist/components/media-card.mjs +2 -0
  66. package/dist/components/media.css +187 -40
  67. package/dist/components/menu.css +159 -32
  68. package/dist/components/modal.css +140 -34
  69. package/dist/components/modal.mjs +3 -0
  70. package/dist/components/nav.css +134 -32
  71. package/dist/components/nav.mjs +3 -0
  72. package/dist/components/pagination.css +105 -19
  73. package/dist/components/pagination.mjs +2 -0
  74. package/dist/components/panel.css +61 -6
  75. package/dist/components/panel.mjs +2 -0
  76. package/dist/components/pill.css +74 -14
  77. package/dist/components/pill.mjs +2 -0
  78. package/dist/components/popover.css +109 -7
  79. package/dist/components/popover.mjs +3 -0
  80. package/dist/components/progress.mjs +2 -0
  81. package/dist/components/rating.mjs +2 -0
  82. package/dist/components/result.mjs +2 -0
  83. package/dist/components/segmented.css +199 -50
  84. package/dist/components/segmented.mjs +2 -0
  85. package/dist/components/select.css +140 -39
  86. package/dist/components/select.mjs +3 -0
  87. package/dist/components/sidebar.css +162 -34
  88. package/dist/components/sidebar.mjs +3 -0
  89. package/dist/components/skeleton.mjs +2 -0
  90. package/dist/components/slider.css +169 -31
  91. package/dist/components/slider.mjs +3 -0
  92. package/dist/components/spinner.mjs +2 -0
  93. package/dist/components/stat.css +103 -8
  94. package/dist/components/stat.mjs +2 -0
  95. package/dist/components/steps.css +105 -31
  96. package/dist/components/steps.mjs +2 -0
  97. package/dist/components/switch.css +171 -54
  98. package/dist/components/switch.mjs +2 -0
  99. package/dist/components/table.css +114 -12
  100. package/dist/components/table.mjs +2 -0
  101. package/dist/components/tabs.css +104 -17
  102. package/dist/components/tabs.mjs +3 -0
  103. package/dist/components/tag-input.css +119 -23
  104. package/dist/components/tag-input.mjs +3 -0
  105. package/dist/components/timeline.mjs +2 -0
  106. package/dist/components/toast.css +85 -12
  107. package/dist/components/toast.mjs +3 -0
  108. package/dist/components/tooltip.css +86 -26
  109. package/dist/components/tooltip.mjs +2 -0
  110. package/dist/components/tree.mjs +3 -0
  111. package/dist/components/upload.css +144 -17
  112. package/dist/components/upload.mjs +3 -0
  113. package/dist/index.mjs +811 -42
  114. package/dist/registry.json +310 -2
  115. package/dist/retro-effects.css +264 -0
  116. package/dist/tokens/colors.css +18 -90
  117. package/dist/tokens/retro-effects.css +264 -0
  118. package/dist/tokens/style-profiles.css +47 -47
  119. package/dist/tokens.css +65 -137
  120. package/dist/ui.css +5069 -1026
  121. package/package.json +9 -1
@@ -1,18 +1,31 @@
1
1
  {
2
2
  "name": "@icen.ai/ui",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "tokens": [
5
5
  "colors.css",
6
6
  "style-profiles.css",
7
7
  "typography.css"
8
8
  ],
9
+ "tokensExtras": [
10
+ "retro-effects.css"
11
+ ],
9
12
  "base": "base.css",
10
13
  "components": [
11
14
  "breadcrumb",
12
15
  "btn",
13
16
  "card",
17
+ "chart-area",
18
+ "chart-bar",
19
+ "chart-gauge",
20
+ "chart-heatmap",
21
+ "chart-line",
22
+ "chart-pie",
23
+ "chart-radar",
24
+ "chart-sparkline",
25
+ "chart-stack",
14
26
  "charts",
15
27
  "content",
28
+ "datatable",
16
29
  "empty",
17
30
  "feedback",
18
31
  "form",
@@ -46,6 +59,7 @@
46
59
  "charts",
47
60
  "context-menu",
48
61
  "copy",
62
+ "datatable",
49
63
  "dropdown",
50
64
  "input",
51
65
  "modal",
@@ -60,5 +74,299 @@
60
74
  "toast",
61
75
  "tree",
62
76
  "upload"
63
- ]
77
+ ],
78
+ "slugs": {
79
+ "btn": {
80
+ "css": "btn.css"
81
+ },
82
+ "panel": {
83
+ "css": "panel.css"
84
+ },
85
+ "pill": {
86
+ "css": "pill.css"
87
+ },
88
+ "tabs": {
89
+ "css": "tabs.css",
90
+ "behavior": "tabs",
91
+ "init": "initTabs"
92
+ },
93
+ "stat": {
94
+ "css": "stat.css"
95
+ },
96
+ "toast": {
97
+ "css": "toast.css",
98
+ "behavior": "toast",
99
+ "exports": [
100
+ "toast"
101
+ ]
102
+ },
103
+ "input": {
104
+ "css": "input.css",
105
+ "behavior": "input",
106
+ "init": "initInput"
107
+ },
108
+ "select": {
109
+ "css": "select.css",
110
+ "behavior": "select",
111
+ "init": "initSelect"
112
+ },
113
+ "slider": {
114
+ "css": "slider.css",
115
+ "behavior": "slider",
116
+ "init": "initSlider"
117
+ },
118
+ "tag-input": {
119
+ "css": "tag-input.css",
120
+ "behavior": "tag-input",
121
+ "init": "initTagInput"
122
+ },
123
+ "switch": {
124
+ "css": "switch.css"
125
+ },
126
+ "segmented": {
127
+ "css": "segmented.css"
128
+ },
129
+ "upload": {
130
+ "css": "upload.css",
131
+ "behavior": "upload",
132
+ "init": "initUpload"
133
+ },
134
+ "modal": {
135
+ "css": "modal.css",
136
+ "behavior": "modal",
137
+ "init": "initModal"
138
+ },
139
+ "dropdown": {
140
+ "css": "menu.css",
141
+ "behavior": "dropdown",
142
+ "init": "initDropdown"
143
+ },
144
+ "context-menu": {
145
+ "css": "menu.css",
146
+ "behavior": "context-menu",
147
+ "init": "initContextMenu"
148
+ },
149
+ "popover": {
150
+ "css": "popover.css",
151
+ "behavior": "popover",
152
+ "exports": [
153
+ "openPopover",
154
+ "closePopover",
155
+ "computePopoverLayout"
156
+ ]
157
+ },
158
+ "tooltip": {
159
+ "css": "tooltip.css"
160
+ },
161
+ "card": {
162
+ "css": "card.css"
163
+ },
164
+ "empty": {
165
+ "css": "empty.css"
166
+ },
167
+ "avatar": {
168
+ "css": "media.css"
169
+ },
170
+ "media-card": {
171
+ "css": "media.css"
172
+ },
173
+ "rating": {
174
+ "css": "media.css"
175
+ },
176
+ "kbd": {
177
+ "css": "content.css"
178
+ },
179
+ "list": {
180
+ "css": "content.css"
181
+ },
182
+ "accordion": {
183
+ "css": "content.css",
184
+ "behavior": "accordion",
185
+ "init": "initAccordion"
186
+ },
187
+ "timeline": {
188
+ "css": "content.css"
189
+ },
190
+ "desc": {
191
+ "css": "content.css"
192
+ },
193
+ "tree": {
194
+ "css": "content.css",
195
+ "behavior": "tree",
196
+ "init": "initTree"
197
+ },
198
+ "carousel": {
199
+ "css": "media.css",
200
+ "behavior": "carousel",
201
+ "init": "initCarousel"
202
+ },
203
+ "table": {
204
+ "css": "table.css"
205
+ },
206
+ "datatable": {
207
+ "css": "datatable.css",
208
+ "extraCss": [
209
+ "menu.css"
210
+ ],
211
+ "behavior": "datatable",
212
+ "exports": [
213
+ "createTable"
214
+ ]
215
+ },
216
+ "charts": {
217
+ "css": "charts.css",
218
+ "extraCss": [
219
+ "chart-line.css",
220
+ "chart-bar.css",
221
+ "chart-pie.css",
222
+ "chart-radar.css",
223
+ "chart-heatmap.css",
224
+ "chart-area.css",
225
+ "chart-stack.css",
226
+ "chart-gauge.css",
227
+ "chart-sparkline.css"
228
+ ],
229
+ "behavior": "charts",
230
+ "exports": [
231
+ "renderVBar",
232
+ "renderHBar",
233
+ "renderStack",
234
+ "renderDonut",
235
+ "renderLine",
236
+ "renderArea",
237
+ "renderRadar",
238
+ "renderHeatmap",
239
+ "renderSparkline",
240
+ "renderGauge"
241
+ ]
242
+ },
243
+ "chart-line": {
244
+ "css": "chart-line.css",
245
+ "extraCss": [
246
+ "charts.css"
247
+ ],
248
+ "behavior": "charts",
249
+ "exports": [
250
+ "renderLine"
251
+ ]
252
+ },
253
+ "chart-bar": {
254
+ "css": "chart-bar.css",
255
+ "extraCss": [
256
+ "charts.css"
257
+ ],
258
+ "behavior": "charts",
259
+ "exports": [
260
+ "renderVBar",
261
+ "renderHBar"
262
+ ]
263
+ },
264
+ "chart-pie": {
265
+ "css": "chart-pie.css",
266
+ "extraCss": [
267
+ "charts.css"
268
+ ],
269
+ "behavior": "charts",
270
+ "exports": [
271
+ "renderDonut"
272
+ ]
273
+ },
274
+ "chart-radar": {
275
+ "css": "chart-radar.css",
276
+ "extraCss": [
277
+ "charts.css"
278
+ ],
279
+ "behavior": "charts",
280
+ "exports": [
281
+ "renderRadar"
282
+ ]
283
+ },
284
+ "chart-heatmap": {
285
+ "css": "chart-heatmap.css",
286
+ "extraCss": [
287
+ "charts.css"
288
+ ],
289
+ "behavior": "charts",
290
+ "exports": [
291
+ "renderHeatmap"
292
+ ]
293
+ },
294
+ "chart-area": {
295
+ "css": "chart-area.css",
296
+ "extraCss": [
297
+ "charts.css"
298
+ ],
299
+ "behavior": "charts",
300
+ "exports": [
301
+ "renderArea"
302
+ ]
303
+ },
304
+ "chart-stack": {
305
+ "css": "chart-stack.css",
306
+ "extraCss": [
307
+ "charts.css"
308
+ ],
309
+ "behavior": "charts",
310
+ "exports": [
311
+ "renderStack"
312
+ ]
313
+ },
314
+ "chart-gauge": {
315
+ "css": "chart-gauge.css",
316
+ "extraCss": [
317
+ "charts.css"
318
+ ],
319
+ "behavior": "charts",
320
+ "exports": [
321
+ "renderGauge"
322
+ ]
323
+ },
324
+ "chart-sparkline": {
325
+ "css": "chart-sparkline.css",
326
+ "extraCss": [
327
+ "charts.css"
328
+ ],
329
+ "behavior": "charts",
330
+ "exports": [
331
+ "renderSparkline"
332
+ ]
333
+ },
334
+ "alert": {
335
+ "css": "feedback.css"
336
+ },
337
+ "result": {
338
+ "css": "feedback.css"
339
+ },
340
+ "progress": {
341
+ "css": "feedback.css"
342
+ },
343
+ "spinner": {
344
+ "css": "feedback.css"
345
+ },
346
+ "skeleton": {
347
+ "css": "feedback.css"
348
+ },
349
+ "nav": {
350
+ "css": "nav.css",
351
+ "behavior": "nav",
352
+ "init": "initNav"
353
+ },
354
+ "sidebar": {
355
+ "css": "sidebar.css",
356
+ "behavior": "sidebar",
357
+ "init": "initSidebar"
358
+ },
359
+ "breadcrumb": {
360
+ "css": "breadcrumb.css"
361
+ },
362
+ "pagination": {
363
+ "css": "pagination.css"
364
+ },
365
+ "steps": {
366
+ "css": "steps.css"
367
+ },
368
+ "layout": {
369
+ "css": "layout.css"
370
+ }
371
+ }
64
372
  }
@@ -0,0 +1,264 @@
1
+ /*
2
+ * @icen.ai/ui — Retro 效果层(按需引入)
3
+ *
4
+ * 与 tokens.css 不同,本文件 **不** 进 tokens.css 拼合,需显式 import:
5
+ * import '@icen.ai/ui/retro-effects.css';
6
+ *
7
+ * 仅在 <html class="style-retro">(或叠加 .dark)激活时生效,对其它风格无副作用:
8
+ * - CRT 扫描线 + 暗角 + 颗粒噪点
9
+ * - 屏幕闪烁 / 抖动 / 游走扫描光
10
+ * - RGB 色散 / 霓虹发光(暗色)
11
+ * - 像素边框 / 像素按钮 / 像素分隔线工具类
12
+ * - 开机自检 / 打字机光标 / 页面闪白动画
13
+ *
14
+ * 所有动画尊重 prefers-reduced-motion:在 reduce 下由 base.css 的全局兜底降为 0.01ms。
15
+ */
16
+
17
+ /* ══════════════════════════════════════════════════════════════
18
+ CRT 显示器效果
19
+ ══════════════════════════════════════════════════════════════ */
20
+
21
+ /* CRT 扫描线 overlay */
22
+ .style-retro body::before {
23
+ content: "";
24
+ position: fixed;
25
+ inset: 0;
26
+ pointer-events: none;
27
+ z-index: 9999;
28
+ opacity: 0.14;
29
+ background:
30
+ repeating-linear-gradient(
31
+ 0deg,
32
+ transparent,
33
+ transparent 2px,
34
+ rgba(0, 0, 0, 0.35) 2px,
35
+ rgba(0, 0, 0, 0.35) 4px
36
+ );
37
+ mix-blend-mode: multiply;
38
+ }
39
+
40
+ .style-retro.dark body::before {
41
+ opacity: 0.22;
42
+ mix-blend-mode: screen;
43
+ }
44
+
45
+ /* CRT 暗角 + 轻微曲面 */
46
+ .style-retro body::after {
47
+ content: "";
48
+ position: fixed;
49
+ inset: 0;
50
+ pointer-events: none;
51
+ z-index: 9998;
52
+ background:
53
+ radial-gradient(
54
+ ellipse at center,
55
+ transparent 55%,
56
+ rgba(0, 0, 0, 0.18) 100%
57
+ );
58
+ opacity: 0.55;
59
+ }
60
+
61
+ /* 屏幕闪烁(备用工具类,需要时挂在元素上) */
62
+ @keyframes retro-flicker {
63
+ 0%, 100% { opacity: 1; }
64
+ 50% { opacity: 0.985; }
65
+ }
66
+
67
+ /* 屏幕轻微抖动(备用工具类) */
68
+ @keyframes retro-jitter {
69
+ 0%, 100% { transform: translate(0, 0); }
70
+ 25% { transform: translate(0.5px, 0); }
71
+ 50% { transform: translate(-0.5px, 0.5px); }
72
+ 75% { transform: translate(0, -0.5px); }
73
+ }
74
+
75
+ /* CRT 颗粒噪点 */
76
+ @keyframes retro-grain {
77
+ 0%, 100% { background-position: 0 0; }
78
+ 10% { background-position: -5% -10%; }
79
+ 20% { background-position: -15% 5%; }
80
+ 30% { background-position: 7% -25%; }
81
+ 40% { background-position: -5% 25%; }
82
+ 50% { background-position: -15% 10%; }
83
+ 60% { background-position: 15% 0%; }
84
+ 70% { background-position: 0% 15%; }
85
+ 80% { background-position: 3% 35%; }
86
+ 90% { background-position: -10% 10%; }
87
+ }
88
+
89
+ .style-retro .retro-crt-grain {
90
+ position: fixed;
91
+ inset: 0;
92
+ pointer-events: none;
93
+ z-index: 9997;
94
+ opacity: 0.06;
95
+ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E");
96
+ animation: retro-grain 0.5s steps(10) infinite;
97
+ }
98
+
99
+ /* ══════════════════════════════════════════════════════════════
100
+ 全局动画关键帧 + 游走扫描光
101
+ ══════════════════════════════════════════════════════════════ */
102
+
103
+ @keyframes retro-blink {
104
+ 0%, 49% { opacity: 1; }
105
+ 50%, 100% { opacity: 0; }
106
+ }
107
+
108
+ @keyframes retro-scanline-slide {
109
+ 0% { transform: translateY(-100%); }
110
+ 100% { transform: translateY(100vh); }
111
+ }
112
+
113
+ /* 游走的扫描线(偶尔闪过)—— 挂到 .style-retro 下的显式节点 */
114
+ .style-retro .retro-scanline-beam,
115
+ .retro-scanline-beam {
116
+ position: fixed;
117
+ left: 0;
118
+ right: 0;
119
+ height: 4px;
120
+ background: linear-gradient(
121
+ 180deg,
122
+ transparent,
123
+ color-mix(in srgb, var(--token-accent) 30%, transparent),
124
+ transparent
125
+ );
126
+ pointer-events: none;
127
+ z-index: 9996;
128
+ opacity: 0.4;
129
+ animation: retro-scanline-slide 4s linear infinite;
130
+ }
131
+
132
+ /* ══════════════════════════════════════════════════════════════
133
+ 开机自检 / 打字机效果
134
+ ══════════════════════════════════════════════════════════════ */
135
+
136
+ @keyframes retro-boot-text {
137
+ 0% { opacity: 0; transform: translateY(4px); }
138
+ 10% { opacity: 1; transform: translateY(0); }
139
+ 90% { opacity: 1; transform: translateY(0); }
140
+ 100% { opacity: 0; transform: translateY(-4px); }
141
+ }
142
+
143
+ .retro-boot-line {
144
+ opacity: 0;
145
+ animation: retro-boot-text 0.3s steps(1) forwards;
146
+ }
147
+
148
+ .retro-boot-line:nth-child(1) { animation-delay: 0.1s; }
149
+ .retro-boot-line:nth-child(2) { animation-delay: 0.25s; }
150
+ .retro-boot-line:nth-child(3) { animation-delay: 0.4s; }
151
+ .retro-boot-line:nth-child(4) { animation-delay: 0.55s; }
152
+ .retro-boot-line:nth-child(5) { animation-delay: 0.7s; }
153
+
154
+ /* 打字机光标 */
155
+ .retro-cursor::after {
156
+ content: "█";
157
+ display: inline-block;
158
+ margin-left: 2px;
159
+ color: var(--token-accent);
160
+ animation: retro-blink 1s step-end infinite;
161
+ }
162
+
163
+ /* ══════════════════════════════════════════════════════════════
164
+ RGB 色散 / 霓虹发光(暗色下尤其出彩)
165
+ ══════════════════════════════════════════════════════════════ */
166
+
167
+ .style-retro.dark .retro-glow {
168
+ text-shadow:
169
+ -1px 0 0 rgba(255, 0, 0, 0.4),
170
+ 1px 0 0 rgba(0, 255, 255, 0.4),
171
+ 0 0 2px var(--token-accent),
172
+ 0 0 8px var(--token-accent);
173
+ }
174
+
175
+ .style-retro.dark .retro-glow-cold {
176
+ text-shadow:
177
+ -1px 0 0 rgba(255, 0, 0, 0.4),
178
+ 1px 0 0 rgba(0, 255, 255, 0.4),
179
+ 0 0 2px var(--token-accent-cold),
180
+ 0 0 8px var(--token-accent-cold);
181
+ }
182
+
183
+ /* 亮色模式下发光退化为单层硬投影,保持像素感而不刺眼 */
184
+ .style-retro:not(.dark) .retro-glow {
185
+ text-shadow:
186
+ 1px 1px 0 color-mix(in srgb, var(--token-accent) 30%, transparent);
187
+ }
188
+
189
+ /* ══════════════════════════════════════════════════════════════
190
+ 像素边框工具类(任意风格下可用,但与 retro 最契合)
191
+ ══════════════════════════════════════════════════════════════ */
192
+
193
+ .retro-pixel-border {
194
+ border: 2px solid var(--token-line);
195
+ box-shadow:
196
+ 2px 2px 0 var(--token-line-strong),
197
+ inset 2px 2px 0 var(--token-surface-strong),
198
+ inset -2px -2px 0 var(--token-line-soft);
199
+ }
200
+
201
+ .retro-pixel-border-accent {
202
+ border: 2px solid var(--token-accent);
203
+ box-shadow:
204
+ 2px 2px 0 var(--token-accent-cold),
205
+ inset 2px 2px 0 color-mix(in srgb, var(--token-accent) 20%, transparent),
206
+ inset -2px -2px 0 color-mix(in srgb, var(--token-accent) 40%, transparent);
207
+ }
208
+
209
+ /* 像素按钮:硬投影 + 按下内移 */
210
+ .retro-pixel-button {
211
+ position: relative;
212
+ border: 2px solid var(--token-line-strong);
213
+ box-shadow:
214
+ inset -2px -2px 0 var(--token-line-soft),
215
+ inset 2px 2px 0 var(--token-surface-strong),
216
+ 2px 2px 0 var(--token-line);
217
+ transition: none;
218
+ }
219
+
220
+ .retro-pixel-button:active {
221
+ transform: translate(2px, 2px);
222
+ box-shadow:
223
+ inset -2px -2px 0 var(--token-line-soft),
224
+ inset 2px 2px 0 var(--token-surface-strong);
225
+ }
226
+
227
+ /* ══════════════════════════════════════════════════════════════
228
+ 选中文字 / 像素分隔线
229
+ ══════════════════════════════════════════════════════════════ */
230
+
231
+ .style-retro ::selection {
232
+ background: var(--token-accent);
233
+ color: var(--token-on-accent);
234
+ }
235
+
236
+ .retro-pixel-rule {
237
+ height: 0;
238
+ border: 0;
239
+ border-top: 2px dashed var(--token-line);
240
+ image-rendering: pixelated;
241
+ }
242
+
243
+ /* ══════════════════════════════════════════════════════════════
244
+ 页面切换 / 闪白效果
245
+ ══════════════════════════════════════════════════════════════ */
246
+
247
+ @keyframes retro-flash {
248
+ 0% { opacity: 0; }
249
+ 10% { opacity: 0.8; }
250
+ 100% { opacity: 0; }
251
+ }
252
+
253
+ .retro-page-flash {
254
+ position: fixed;
255
+ inset: 0;
256
+ pointer-events: none;
257
+ z-index: 10000;
258
+ background: var(--token-bg);
259
+ opacity: 0;
260
+ }
261
+
262
+ .retro-page-flash.active {
263
+ animation: retro-flash 0.2s steps(3) forwards;
264
+ }