starmusic-pc
1/* ==========================================================================
2 星音乐 PC 版 — 设计令牌
3 --------------------------------------------------------------------------
4 主题色由 JS 注入到 .app-root 的内联样式(见 common/theme.js → buildThemeStyle):
5 --theme-color / --theme-color-rgb / --theme-color-dark / --theme-color-soft
6 未注入时回退默认主题(星野绿 #31c27c)。
7 ========================================================================== */
8
9:root {
10 color-scheme: light;
11
12 /* ---- 字体 ---- */
13 --font-sans: 'PingFang SC', 'HarmonyOS Sans SC', 'Microsoft YaHei UI', 'Microsoft YaHei',
14 -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans SC', Roboto, Helvetica, Arial, sans-serif;
15 --font-mono: 'JetBrains Mono', 'Cascadia Mono', Consolas, 'Courier New', monospace;
16
17 /* ---- 中性色 ---- */
18 --text-1: #1c1f23;
19 --text-2: #464b53;
20 --text-3: #6b7280;
21 --text-4: #9aa1ab;
22 --text-5: #c2c7cf;
23
24 --surface-0: #f4f6f8;
25 --surface-1: #ffffff;
26 --surface-2: #fafbfc;
27 --surface-3: #f1f3f6;
28 --surface-4: #e8ebef;
29 --surface-glass: rgba(255, 255, 255, 0.72);
30 --surface-glass-strong: rgba(255, 255, 255, 0.9);
31 /* 浮层专用不透明底色(下拉建议 / 弹窗 / 上下文菜单 / 播放条弹出面板)。
32 这些层浮在其它内容之上,必须完全不透:背景图模式下 .bg-on 会把 --surface-1
33 改成半透明,浮层若沿用它,下面的列表文字会透上来和浮层内容叠在一起。 */
34 --surface-pop: #ffffff;
35 --surface-overlay: rgba(18, 22, 28, 0.42);
36
37 --line-1: #ebedf0;
38 --line-2: #dfe3e8;
39 --line-3: #cbd1d9;
40
41 /* ---- 语义色 ---- */
42 --color-success: #22c55e;
43 --color-warning: #f59e0b;
44 --color-danger: #f43f5e;
45 --color-info: #3b82f6;
46 --color-heart: #ff4d6a;
47
48 /* ---- 主题色(回退值) ---- */
49 --theme-color: #31c27c;
50 --theme-color-rgb: 49, 194, 124;
51 --theme-color-dark: #1ba56a;
52 --theme-color-soft: #e8f8f0;
53
54 /* ---- 圆角 ---- */
55 --r-xs: 6px;
56 --r-sm: 9px;
57 --r-md: 13px;
58 --r-lg: 18px;
59 --r-xl: 24px;
60 --r-2xl: 32px;
61 --r-full: 999px;
62
63 /* ---- 阴影 ---- */
64 --sh-xs: 0 1px 2px rgba(23, 28, 36, 0.05);
65 --sh-sm: 0 2px 8px rgba(23, 28, 36, 0.06), 0 1px 2px rgba(23, 28, 36, 0.04);
66 --sh-md: 0 6px 20px rgba(23, 28, 36, 0.08), 0 2px 6px rgba(23, 28, 36, 0.04);
67 --sh-lg: 0 14px 40px rgba(23, 28, 36, 0.12), 0 4px 12px rgba(23, 28, 36, 0.06);
68 --sh-xl: 0 28px 70px rgba(23, 28, 36, 0.2), 0 8px 24px rgba(23, 28, 36, 0.1);
69 --sh-accent: 0 10px 28px rgba(var(--theme-color-rgb), 0.36);
70 --sh-inset-top: inset 0 1px 0 rgba(255, 255, 255, 0.7);
71
72 /* ---- 毛玻璃 ---- */
73 --blur-sm: blur(10px) saturate(160%);
74 --blur-md: blur(20px) saturate(180%);
75 --blur-lg: blur(40px) saturate(180%);
76
77 /* ---- 动效 ---- */
78 --t-fast: 130ms;
79 --t-base: 220ms;
80 --t-slow: 360ms;
81 --t-slower: 560ms;
82 --ease-out: cubic-bezier(0.22, 0.61, 0.36, 1);
83 --ease-in-out: cubic-bezier(0.42, 0, 0.58, 1);
84 --ease-spring: cubic-bezier(0.34, 1.42, 0.64, 1);
85 --ease-overshoot: cubic-bezier(0.22, 1.3, 0.42, 1);
86
87 /* ---- 布局尺寸 ---- */
88 --h-titlebar: 44px;
89 --w-sidebar: 236px;
90 --w-queue: 340px;
91 --h-player: 92px;
92 --z-dropdown: 60;
93 --z-player: 80;
94 --z-overlay: 100;
95 --z-modal: 140;
96 --z-toast: 200;
97}
98
99/* 背景图开启时,内容层半透明(变量由 common/background.js 的 buildBgVars 注入) */
100.bg-on {
101 --surface-1: var(--card-bg, rgba(255, 255, 255, 0.72));
102 --surface-2: var(--card-bg, rgba(255, 255, 255, 0.72));
103 --surface-glass: var(--card-bg, rgba(255, 255, 255, 0.72));
104 --surface-glass-strong: var(--card-bg-strong, rgba(255, 255, 255, 0.92));
105 --line-1: rgba(255, 255, 255, 0.4);
106 --line-2: rgba(255, 255, 255, 0.5);
107}
108
109/* ==========================================================================
110 基础重置
111 ========================================================================== */
112
113*,
114*::before,
115*::after {
116 box-sizing: border-box;
117}
118
119html,
120body {
121 margin: 0;
122 padding: 0;
123 height: 100%;
124 overflow: hidden;
125 font-family: var(--font-sans);
126 font-size: 14px;
127 line-height: 1.55;
128 color: var(--text-1);
129 background: var(--surface-0);
130 -webkit-font-smoothing: antialiased;
131 -moz-osx-font-smoothing: grayscale;
132 text-rendering: optimizeLegibility;
133}
134
135#app {
136 height: 100%;
137}
138
139body {
140 /* 桌面应用:禁用文本选择与拖拽,输入框单独放开 */
141 user-select: none;
142 -webkit-user-select: none;
143 cursor: default;
144}
145
146input,
147textarea,
148[contenteditable='true'] {
149 user-select: text;
150 -webkit-user-select: text;
151 font-family: inherit;
152}
153
154img {
155 -webkit-user-drag: none;
156 user-select: none;
157}
158
159button {
160 font-family: inherit;
161 border: none;
162 background: none;
163 padding: 0;
164 cursor: pointer;
165 color: inherit;
166}
167
168a {
169 color: var(--theme-color-dark);
170 text-decoration: none;
171}
172
173h1,
174h2,
175h3,
176h4,
177h5,
178h6,
179p {
180 margin: 0;
181 font-weight: inherit;
182}
183
184ul,
185ol {
186 margin: 0;
187 padding: 0;
188 list-style: none;
189}
190
191/* ---- 滚动条 ---- */
192::-webkit-scrollbar {
193 width: 10px;
194 height: 10px;
195}
196
197::-webkit-scrollbar-track {
198 background: transparent;
199}
200
201::-webkit-scrollbar-thumb {
202 background: rgba(120, 130, 145, 0.28);
203 border: 3px solid transparent;
204 border-radius: var(--r-full);
205 background-clip: content-box;
206}
207
208::-webkit-scrollbar-thumb:hover {
209 background: rgba(120, 130, 145, 0.5);
210 background-clip: content-box;
211}
212
213::-webkit-scrollbar-corner {
214 background: transparent;
215}
216
217/* ---- 焦点可见性 ---- */
218:focus {
219 outline: none;
220}
221
222:focus-visible {
223 outline: 2px solid rgba(var(--theme-color-rgb), 0.7);
224 outline-offset: 2px;
225 border-radius: var(--r-xs);
226}
227
228/* ==========================================================================
229 通用原子类
230 ========================================================================== */
231
232.scroll-y {
233 overflow-y: auto;
234 overscroll-behavior: contain;
235}
236
237.row {
238 display: flex;
239 align-items: center;
240}
241
242.col {
243 display: flex;
244 flex-direction: column;
245}
246
247.grow {
248 flex: 1;
249 min-width: 0;
250}
251
252.ellipsis {
253 overflow: hidden;
254 white-space: nowrap;
255 text-overflow: ellipsis;
256}
257
258.ellipsis-2 {
259 display: -webkit-box;
260 -webkit-line-clamp: 2;
261 -webkit-box-orient: vertical;
262 overflow: hidden;
263}
264
265.no-drag {
266 --wails-draggable: no-drag;
267}
268
269.press {
270 transition: transform var(--t-fast) var(--ease-out), background-color var(--t-fast) var(--ease-out),
271 box-shadow var(--t-fast) var(--ease-out);
272}
273
274.press:active {
275 transform: scale(0.96);
276}
277
278.press-soft:active {
279 transform: scale(0.985);
280}
281
282/* ==========================================================================
283 关键帧动画
284 ========================================================================== */
285
286/* 渐入上移 */
287@keyframes kFadeUp {
288 from {
289 opacity: 0;
290 transform: translateY(14px);
291 }
292 to {
293 opacity: 1;
294 transform: translateY(0);
295 }
296}
297
298/* 渐入 */
299@keyframes kFadeIn {
300 from {
301 opacity: 0;
302 }
303 to {
304 opacity: 1;
305 }
306}
307
308/* 轻微放大渐入 */
309@keyframes kScaleIn {
310 from {
311 opacity: 0;
312 transform: scale(0.94);
313 }
314 to {
315 opacity: 1;
316 transform: scale(1);
317 }
318}
319
320/* 弹窗缩放(带轻微过冲) */
321@keyframes kPopIn {
322 0% {
323 opacity: 0;
324 transform: scale(0.86) translateY(10px);
325 }
326 100% {
327 opacity: 1;
328 transform: scale(1) translateY(0);
329 }
330}
331
332/* 自下方滑入 */
333@keyframes kSlideUp {
334 from {
335 opacity: 0;
336 transform: translateY(24px);
337 }
338 to {
339 opacity: 1;
340 transform: translateY(0);
341 }
342}
343
344/* 自右侧滑入 */
345@keyframes kSlideInRight {
346 from {
347 opacity: 0;
348 transform: translateX(26px);
349 }
350 to {
351 opacity: 1;
352 transform: translateX(0);
353 }
354}
355
356/* 自左侧滑入 */
357@keyframes kSlideInLeft {
358 from {
359 opacity: 0;
360 transform: translateX(-26px);
361 }
362 to {
363 opacity: 1;
364 transform: translateX(0);
365 }
366}
367
368/* 遮罩渐显 */
369@keyframes kMaskIn {
370 from {
371 opacity: 0;
372 }
373 to {
374 opacity: 1;
375 }
376}
377
378/* 弹性回弹 */
379@keyframes kBounce {
380 0% {
381 transform: scale(1);
382 }
383 38% {
384 transform: scale(0.86);
385 }
386 68% {
387 transform: scale(1.06);
388 }
389 100% {
390 transform: scale(1);
391 }
392}
393
394/* 唱片旋转 */
395@keyframes kSpin {
396 from {
397 transform: rotate(0deg);
398 }
399 to {
400 transform: rotate(360deg);
401 }
402}
403
404/* 呼吸(封面轻微起伏,营造"正在播放"的生命感) */
405@keyframes kBreath {
406 0%,
407 100% {
408 transform: scale(1);
409 }
410 50% {
411 transform: scale(1.035);
412 }
413}
414
415/* 缓慢浮动(装饰元素) */
416@keyframes kFloat {
417 0%,
418 100% {
419 transform: translateY(0) rotate(0deg);
420 }
421 50% {
422 transform: translateY(-12px) rotate(3deg);
423 }
424}
425
426/* 脉冲光环(播放按钮) */
427@keyframes kPulseRing {
428 0% {
429 transform: scale(0.9);
430 opacity: 0.55;
431 }
432 70% {
433 transform: scale(1.45);
434 opacity: 0;
435 }
436 100% {
437 transform: scale(1.45);
438 opacity: 0;
439 }
440}
441
442/* 骨架屏微光 */
443@keyframes kShimmer {
444 0% {
445 background-position: -220% 0;
446 }
447 100% {
448 background-position: 220% 0;
449 }
450}
451
452/* 呼吸发光 */
453@keyframes kGlow {
454 0%,
455 100% {
456 box-shadow: 0 0 0 0 rgba(var(--theme-color-rgb), 0.35);
457 }
458 50% {
459 box-shadow: 0 0 22px 6px rgba(var(--theme-color-rgb), 0.22);
460 }
461}
462
463/* 星空闪烁 */
464@keyframes kTwinkle {
465 0%,
466 100% {
467 opacity: 0.25;
468 transform: scale(0.8);
469 }
470 50% {
471 opacity: 1;
472 transform: scale(1.15);
473 }
474}
475
476/* 流星划过 */
477@keyframes kShooting {
478 0% {
479 transform: translate3d(0, 0, 0) rotate(28deg);
480 opacity: 0;
481 }
482 12% {
483 opacity: 1;
484 }
485 100% {
486 transform: translate3d(-520px, 300px, 0) rotate(28deg);
487 opacity: 0;
488 }
489}
490
491/* 声波条(迷你均衡器) */
492@keyframes kEq {
493 0%,
494 100% {
495 transform: scaleY(0.32);
496 }
497 50% {
498 transform: scaleY(1);
499 }
500}
501
502/* 旋转加载 */
503@keyframes kSpinner {
504 to {
505 transform: rotate(360deg);
506 }
507}
508
509/* 进度条流光 */
510@keyframes kFlow {
511 0% {
512 transform: translateX(-100%);
513 }
514 100% {
515 transform: translateX(100%);
516 }
517}
518
519/* 抖动(错误反馈) */
520@keyframes kShake {
521 0%,
522 100% {
523 transform: translateX(0);
524 }
525 20% {
526 transform: translateX(-6px);
527 }
528 40% {
529 transform: translateX(6px);
530 }
531 60% {
532 transform: translateX(-4px);
533 }
534 80% {
535 transform: translateX(4px);
536 }
537}
538
539/* 彩蛋粒子:消散上升 */
540@keyframes kEggFloatUp {
541 0% {
542 opacity: 0;
543 transform: translate3d(0, 30px, 0) scale(0.5) rotate(0deg);
544 }
545 15% {
546 opacity: 1;
547 }
548 100% {
549 opacity: 0;
550 transform: translate3d(var(--dx, 0px), -240px, 0) scale(1.15) rotate(var(--rot, 25deg));
551 }
552}
553
554/* 彩蛋粒子:原地闪烁 */
555@keyframes kEggTwinkle {
556 0%,
557 100% {
558 opacity: 0.15;
559 transform: scale(0.6);
560 }
561 50% {
562 opacity: 1;
563 transform: scale(1.2);
564 }
565}
566
567/* 彩蛋粒子:流星 */
568@keyframes kEggMeteor {
569 0% {
570 opacity: 0;
571 transform: translate3d(60px, -60px, 0) rotate(28deg) scale(0.6);
572 }
573 15% {
574 opacity: 1;
575 }
576 100% {
577 opacity: 0;
578 transform: translate3d(-160px, 160px, 0) rotate(28deg) scale(1);
579 }
580}
581
582/* 跳跃弧线(关于页小游戏) */
583@keyframes kJumpArc {
584 0% {
585 transform: translate3d(0, 0, 0) scale(1);
586 }
587 50% {
588 transform: translate3d(var(--jx, 0px), -70px, 0) scale(0.92);
589 }
590 100% {
591 transform: translate3d(var(--jx2, 0px), 0, 0) scale(1);
592 }
593}
594
595/* 消息中心滑入 */
596@keyframes kToastIn {
597 from {
598 opacity: 0;
599 transform: translateX(28px) scale(0.96);
600 }
601 to {
602 opacity: 1;
603 transform: translateX(0) scale(1);
604 }
605}
606
607@keyframes kToastOut {
608 from {
609 opacity: 1;
610 transform: translateX(0) scale(1);
611 }
612 to {
613 opacity: 0;
614 transform: translateX(20px) scale(0.95);
615 }
616}
617
618/* ==========================================================================
619 动画工具类
620 ========================================================================== */
621
622.a-fade-up {
623 animation: kFadeUp var(--t-slow) var(--ease-out) both;
624 animation-delay: var(--delay, 0ms);
625}
626
627.a-fade-in {
628 animation: kFadeIn var(--t-base) ease both;
629 animation-delay: var(--delay, 0ms);
630}
631
632.a-scale-in {
633 animation: kScaleIn var(--t-slow) var(--ease-out) both;
634 animation-delay: var(--delay, 0ms);
635}
636
637.a-pop-in {
638 animation: kPopIn var(--t-slow) var(--ease-overshoot) both;
639}
640
641.a-slide-up {
642 animation: kSlideUp var(--t-slow) var(--ease-out) both;
643}
644
645.a-slide-right {
646 animation: kSlideInRight var(--t-slow) var(--ease-out) both;
647}
648
649.a-spin {
650 animation: kSpin 20s linear infinite;
651}
652
653.a-breath {
654 animation: kBreath 5.6s var(--ease-in-out) infinite;
655}
656
657.a-float {
658 animation: kFloat 6s var(--ease-in-out) infinite;
659}
660
661.a-glow {
662 animation: kGlow 3.4s var(--ease-in-out) infinite;
663}
664
665.a-shake {
666 animation: kShake 420ms var(--ease-out);
667}
668
669.a-bounce {
670 animation: kBounce 360ms var(--ease-spring);
671}
672
673/* 骨架屏 */
674.skeleton {
675 background: linear-gradient(
676 100deg,
677 var(--surface-3) 22%,
678 var(--surface-4) 42%,
679 var(--surface-3) 60%
680 );
681 background-size: 220% 100%;
682 animation: kShimmer 1.5s linear infinite;
683 border-radius: var(--r-sm);
684}
685
686/* ==========================================================================
687 页面过渡(Vue <Transition> 用)
688 ========================================================================== */
689
690.page-enter-active {
691 animation: kSlideInRight var(--t-slow) var(--ease-out);
692}
693
694.page-leave-active {
695 animation: kFadeIn var(--t-fast) reverse both;
696 pointer-events: none;
697}
698
699.overlay-enter-active,
700.overlay-leave-active {
701 transition: opacity var(--t-slow) var(--ease-out);
702}
703
704.overlay-enter-from,
705.overlay-leave-to {
706 opacity: 0;
707}
708
709.queue-enter-active,
710.queue-leave-active {
711 transition: transform var(--t-slow) var(--ease-out), opacity var(--t-base) var(--ease-out);
712}
713
714.queue-enter-from,
715.queue-leave-to {
716 transform: translateX(24px);
717 opacity: 0;
718}
719
720/* 尊重系统的减弱动效偏好 */
721@media (prefers-reduced-motion: reduce) {
722 *,
723 *::before,
724 *::after {
725 animation-duration: 0.001ms !important;
726 animation-iteration-count: 1 !important;
727 transition-duration: 0.001ms !important;
728 scroll-behavior: auto !important;
729 }
730}