From aaacbddfa48f331f08778e828a1f64f842763571 Mon Sep 17 00:00:00 2001
From: Abner <22141172+Silentely@users.noreply.github.com>
Date: Sat, 11 Jul 2026 12:46:13 +0800
Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20style:=20=E4=BC=98=E5=8C=96?=
=?UTF-8?q?=E5=89=8D=E7=AB=AF=20UI=20=E6=B5=81=E7=95=85=E5=BA=A6=E3=80=81?=
=?UTF-8?q?=E8=A7=86=E8=A7=89=E5=B1=82=E6=AC=A1=E4=B8=8E=E6=9A=97=E8=89=B2?=
=?UTF-8?q?=E6=A8=A1=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
统一 motion token,拆分步骤 completed/active 与进度轨,弱化表单卡上浮
与无限呼吸动画;首页改中性 mesh 背景,补齐三页暗色与通知 safe-area。
---
index.html | 197 ++++++++++++++----
src/giffgaff/giffgaff_modular.html | 2 +-
src/giffgaff/js/modules/ui-controller.js | 30 ++-
src/giffgaff/styles/giffgaff-animations.css | 95 ++++-----
src/giffgaff/styles/giffgaff-base.css | 129 +++++++++++-
src/giffgaff/styles/giffgaff-components.css | 196 ++++++++++++++---
src/giffgaff/styles/giffgaff-service-time.css | 120 +++++++++--
src/simyo/js/modules/ui-controller.js | 30 ++-
src/simyo/simyo_modular.html | 2 +-
src/simyo/styles/simyo-animations.css | 17 +-
src/simyo/styles/simyo-base.css | 146 ++++++++++++-
src/simyo/styles/simyo-components.css | 151 +++++++++++---
src/styles/animations.css | 139 ++++++------
src/styles/design-system.css | 113 +++++++---
src/styles/mobile-responsive.css | 3 +-
src/styles/notification.css | 74 ++++---
16 files changed, 1109 insertions(+), 335 deletions(-)
diff --git a/index.html b/index.html
index 5b14121..0edfe94 100644
--- a/index.html
+++ b/index.html
@@ -2,7 +2,8 @@
diff --git a/src/giffgaff/giffgaff_modular.html b/src/giffgaff/giffgaff_modular.html
index 4226f8c..b553395 100644
--- a/src/giffgaff/giffgaff_modular.html
+++ b/src/giffgaff/giffgaff_modular.html
@@ -2,7 +2,7 @@
-
+
diff --git a/src/giffgaff/js/modules/ui-controller.js b/src/giffgaff/js/modules/ui-controller.js
index 86bc484..b3f8148 100644
--- a/src/giffgaff/js/modules/ui-controller.js
+++ b/src/giffgaff/js/modules/ui-controller.js
@@ -97,18 +97,40 @@ export class UIController {
* 更新步骤指示器
*/
updateSteps(currentStep) {
- this.elements.steps.forEach((step, index) => {
+ const steps = this.elements.steps;
+ const total = steps.length;
+
+ steps.forEach((step, index) => {
const stepNo = index + 1;
- if (stepNo <= currentStep) {
+ step.classList.remove('active', 'completed');
+
+ if (stepNo < currentStep) {
+ step.classList.add('completed');
+ step.style.cursor = 'pointer';
+ step.title = tl('点击返回此步骤');
+ step.setAttribute('aria-current', 'false');
+ } else if (stepNo === currentStep) {
step.classList.add('active');
step.style.cursor = 'pointer';
- step.title = stepNo < currentStep ? tl('点击返回此步骤') : '';
+ step.title = '';
+ step.setAttribute('aria-current', 'step');
} else {
- step.classList.remove('active');
step.style.cursor = 'not-allowed';
step.title = tl('请按顺序完成前序步骤');
+ step.setAttribute('aria-current', 'false');
}
});
+
+ // 进度轨:已完成区间占比(1 步时为 0,末步为 1)
+ const indicator = steps[0] && steps[0].closest
+ ? steps[0].closest('.step-indicator')
+ : null;
+ if (indicator && total > 1) {
+ const progress = Math.max(0, Math.min(1, (currentStep - 1) / (total - 1)));
+ indicator.style.setProperty('--step-progress', String(progress));
+ } else if (indicator) {
+ indicator.style.setProperty('--step-progress', currentStep > 0 ? '1' : '0');
+ }
}
/**
diff --git a/src/giffgaff/styles/giffgaff-animations.css b/src/giffgaff/styles/giffgaff-animations.css
index 2065bcc..3413f0b 100644
--- a/src/giffgaff/styles/giffgaff-animations.css
+++ b/src/giffgaff/styles/giffgaff-animations.css
@@ -45,96 +45,68 @@
60% { transform: translateY(-3px); }
}
-/* ===== 服务时间动画 ===== */
+/* ===== 服务时间动画(仅 opacity / 阴影,禁止 scale 以降低干扰与耗电) ===== */
@keyframes breathing-light {
0%, 100% {
- box-shadow: 0 0 5px rgba(220, 38, 38, 0.3);
- transform: scale(1);
+ opacity: 1;
+ box-shadow: 0 0 0 0 rgba(220, 38, 38, 0.25);
}
50% {
- box-shadow: 0 0 20px rgba(220, 38, 38, 0.6);
- transform: scale(1.05);
+ opacity: 0.92;
+ box-shadow: 0 0 0 4px rgba(220, 38, 38, 0.12);
}
}
@keyframes breathing-light-orange {
0%, 100% {
- box-shadow: 0 0 5px rgba(245, 158, 11, 0.3);
- transform: scale(1);
+ opacity: 1;
+ box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.25);
}
50% {
- box-shadow: 0 0 20px rgba(245, 158, 11, 0.6);
- transform: scale(1.05);
+ opacity: 0.92;
+ box-shadow: 0 0 0 4px rgba(245, 158, 11, 0.12);
}
}
@keyframes breathing-light-green {
- 0%, 100% {
- box-shadow: 0 0 5px rgba(22, 163, 74, 0.3);
- transform: scale(1);
- }
- 50% {
- box-shadow: 0 0 20px rgba(22, 163, 74, 0.6);
- transform: scale(1.05);
- }
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.88; }
}
@keyframes breathing-light-purple {
- 0%, 100% {
- box-shadow: 0 0 5px rgba(99, 102, 241, 0.3);
- transform: scale(1);
- }
- 50% {
- box-shadow: 0 0 20px rgba(99, 102, 241, 0.6);
- transform: scale(1.05);
- }
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.88; }
}
+/* 警告:轻微 opacity,不再缩放 */
@keyframes warning-pulse {
- 0%, 100% {
- transform: scale(1);
- }
- 50% {
- transform: scale(1.05);
- }
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.85; }
}
@keyframes success-bounce {
- 0%, 100% {
- transform: scale(1);
- }
- 25% {
- transform: scale(1.02);
- }
- 75% {
- transform: scale(1.02);
- }
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.9; }
}
@keyframes pulse-glow {
0%, 100% {
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
50% {
- box-shadow: 0 4px 16px rgba(99, 102, 241, 0.3);
+ box-shadow: 0 4px 14px rgba(99, 102, 241, 0.18);
}
}
+/* 时间数字更新:一次轻触反馈,由 JS 触发类名时使用 */
@keyframes time-update {
- 0% {
- transform: scale(1);
- }
- 50% {
- transform: scale(1.1);
- }
- 100% {
- transform: scale(1);
- }
+ 0% { opacity: 0.7; }
+ 100% { opacity: 1; }
}
@keyframes time-breathe {
- 0%, 100% { transform: translateZ(0) scale(1); }
- 50% { transform: translateZ(0) scale(1.02); }
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.92; }
}
@keyframes modalSlideIn {
@@ -144,5 +116,20 @@
/* ===== 服务时间图标过渡 ===== */
#serviceTimeIcon {
- transition: all 0.3s ease;
+ transition:
+ color var(--transition-base, 240ms ease),
+ transform var(--transition-base, 240ms ease),
+ opacity var(--transition-base, 240ms ease);
+}
+
+/* 步骤内容进入(与 design-system / animations 对齐的本地兜底) */
+@keyframes section-enter {
+ from {
+ opacity: 0;
+ transform: translateY(12px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
}
\ No newline at end of file
diff --git a/src/giffgaff/styles/giffgaff-base.css b/src/giffgaff/styles/giffgaff-base.css
index 1499653..d0a472f 100644
--- a/src/giffgaff/styles/giffgaff-base.css
+++ b/src/giffgaff/styles/giffgaff-base.css
@@ -29,7 +29,7 @@ body {
color: #212529;
min-height: 100vh;
padding: 20px;
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ font-family: var(--font-sans, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif);
margin: 0;
}
@@ -61,7 +61,10 @@ textarea, input {
background: rgba(255, 255, 255, 0.9);
color: #212529;
font-size: 16px;
- transition: all 0.3s ease;
+ transition:
+ border-color var(--transition-base, 240ms ease),
+ box-shadow var(--transition-base, 240ms ease),
+ background-color var(--transition-base, 240ms ease);
box-sizing: border-box;
}
@@ -77,21 +80,24 @@ textarea:focus, input:focus {
background: rgba(255, 255, 255, 1);
}
-/* 卡片样式 */
+/* 卡片样式:内容卡仅微调阴影,避免填表时大幅上浮 */
.card {
- background: rgba(255, 255, 255, 0.9);
+ background: rgba(255, 255, 255, 0.92);
border-radius: 16px;
border: 1px solid rgba(255, 204, 0, 0.3);
- box-shadow: 0 4px 25px rgba(0, 0, 0, 0.1);
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
margin-bottom: 30px;
overflow: hidden;
- transition: all 0.3s ease;
+ transition:
+ box-shadow var(--transition-base, 240ms ease),
+ border-color var(--transition-base, 240ms ease);
}
-.card:hover {
- transform: translateY(-5px);
- box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15);
- border: 1px solid rgba(255, 204, 0, 0.5);
+@media (hover: hover) {
+ .card:hover {
+ box-shadow: 0 6px 24px rgba(0, 0, 0, 0.1);
+ border-color: rgba(255, 204, 0, 0.45);
+ }
}
.card-header {
@@ -132,4 +138,107 @@ textarea:focus, input:focus {
display: flex;
align-items: center;
gap: 8px;
+}
+
+/* ===== 暗色模式 ===== */
+@media (prefers-color-scheme: dark) {
+ :root {
+ --secondary: #1e1e1e;
+ --light: #1a1a1a;
+ --dark: #e9ecef;
+ --gray: #adb5bd;
+ --st-muted: #94a3b8;
+ --st-strong: #f1f5f9;
+ --st-card-shadow: 0 8px 28px rgba(0, 0, 0, 0.4);
+ --st-soft-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
+ --st-border: 1px solid rgba(255, 255, 255, 0.08);
+ }
+
+ body {
+ background: linear-gradient(135deg, #1a1610 0%, #12100c 45%, #0c0b09 100%);
+ color: #e9ecef;
+ }
+
+ textarea,
+ input {
+ background: rgba(30, 30, 30, 0.92);
+ color: #e9ecef;
+ border-color: rgba(255, 204, 0, 0.28);
+ }
+
+ textarea:focus,
+ input:focus {
+ background: rgba(36, 36, 36, 1);
+ border-color: var(--primary);
+ box-shadow: 0 0 0 4px rgba(255, 204, 0, 0.15);
+ }
+
+ .card {
+ background: rgba(30, 30, 30, 0.92);
+ border-color: rgba(255, 204, 0, 0.22);
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.35);
+ }
+
+ @media (hover: hover) {
+ .card:hover {
+ box-shadow: 0 6px 24px rgba(0, 0, 0, 0.45);
+ border-color: rgba(255, 204, 0, 0.38);
+ }
+ }
+
+ .card-header {
+ background: rgba(255, 204, 0, 0.1);
+ color: #f8f9fa;
+ border-bottom-color: rgba(255, 204, 0, 0.18);
+ }
+
+ .status-panel {
+ background: linear-gradient(135deg, rgba(36, 32, 20, 0.95) 0%, rgba(28, 26, 18, 0.95) 100%);
+ border-color: rgba(255, 255, 255, 0.08);
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
+ }
+
+ .status-panel h3 {
+ color: #e9ecef;
+ }
+
+ /* Bootstrap 工具类在暗色下的可读性 */
+ .text-dark {
+ color: #e9ecef !important;
+ }
+
+ .bg-light {
+ background-color: #2a2a2a !important;
+ color: #e9ecef;
+ }
+
+ .bg-warning.text-dark,
+ .card-header.bg-warning {
+ color: #111 !important;
+ }
+
+ .alert-danger {
+ background-color: rgba(127, 29, 29, 0.35) !important;
+ border-color: rgba(248, 113, 113, 0.35) !important;
+ color: #fecaca !important;
+ }
+
+ .alert-warning {
+ background-color: rgba(120, 53, 15, 0.35) !important;
+ border-color: rgba(251, 191, 36, 0.35) !important;
+ color: #fde68a !important;
+ }
+
+ .alert-info {
+ background-color: rgba(30, 58, 138, 0.35) !important;
+ border-color: rgba(96, 165, 250, 0.35) !important;
+ color: #bfdbfe !important;
+ }
+
+ pre.bg-light,
+ .border.bg-light {
+ background-color: #242424 !important;
+ border-color: rgba(255, 255, 255, 0.1) !important;
+ color: #e2e8f0;
+ }
}
\ No newline at end of file
diff --git a/src/giffgaff/styles/giffgaff-components.css b/src/giffgaff/styles/giffgaff-components.css
index 0fa21a9..87783b6 100644
--- a/src/giffgaff/styles/giffgaff-components.css
+++ b/src/giffgaff/styles/giffgaff-components.css
@@ -50,7 +50,11 @@
align-items: center;
justify-content: center;
border-radius: 30px;
- transition: all 0.3s;
+ transition:
+ transform var(--transition-fast, 160ms ease),
+ box-shadow var(--transition-fast, 160ms ease),
+ border-color var(--transition-base, 240ms ease),
+ background var(--transition-base, 240ms ease);
gap: 8px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
text-decoration: none;
@@ -59,11 +63,13 @@
flex-shrink: 0;
}
-.login-btn:hover {
- background: linear-gradient(to right, #ffffff, #ffcc00);
- transform: translateY(-2px);
- box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15);
- border-color: rgba(255, 204, 0, 0.5);
+@media (hover: hover) {
+ .login-btn:hover {
+ background: linear-gradient(to right, #ffffff, #ffcc00);
+ transform: translateY(-2px);
+ box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15);
+ border-color: rgba(255, 204, 0, 0.5);
+ }
}
.github-btn {
@@ -77,7 +83,12 @@
align-items: center;
justify-content: center;
border-radius: 30px;
- transition: all 0.3s;
+ transition:
+ transform var(--transition-fast, 160ms ease),
+ box-shadow var(--transition-fast, 160ms ease),
+ border-color var(--transition-base, 240ms ease),
+ background var(--transition-base, 240ms ease),
+ color var(--transition-fast, 160ms ease);
gap: 8px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
text-decoration: none;
@@ -87,12 +98,14 @@
flex-shrink: 0;
}
-.github-btn:hover {
- background: linear-gradient(to right, #666666, #333333);
- transform: translateY(-2px);
- box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15);
- border-color: rgba(51, 51, 51, 0.5);
- color: #ffffff;
+@media (hover: hover) {
+ .github-btn:hover {
+ background: linear-gradient(to right, #666666, #333333);
+ transform: translateY(-2px);
+ box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15);
+ border-color: rgba(51, 51, 51, 0.5);
+ color: #ffffff;
+ }
}
.btn-group {
@@ -108,7 +121,11 @@
font-size: 17px;
font-weight: 600;
cursor: pointer;
- transition: all 0.3s;
+ transition:
+ transform var(--transition-fast, 160ms ease),
+ box-shadow var(--transition-fast, 160ms ease),
+ background var(--transition-base, 240ms ease),
+ opacity var(--transition-fast, 160ms ease);
display: flex;
align-items: center;
justify-content: center;
@@ -121,10 +138,12 @@
color: #212529;
}
-.btn-primary:hover {
- background: linear-gradient(to right, #ffffff, #ffcc00);
- transform: translateY(-3px);
- box-shadow: 0 8px 15px rgba(0, 0, 0, 0.15);
+@media (hover: hover) {
+ .btn-primary:hover {
+ background: linear-gradient(to right, #ffffff, #ffcc00);
+ transform: translateY(-2px);
+ box-shadow: 0 8px 15px rgba(0, 0, 0, 0.12);
+ }
}
.btn:disabled {
@@ -142,15 +161,20 @@
border-radius: 6px;
font-size: 14px;
cursor: pointer;
- transition: all 0.3s ease;
+ transition:
+ transform var(--transition-fast, 160ms ease),
+ box-shadow var(--transition-fast, 160ms ease),
+ background var(--transition-base, 240ms ease);
display: flex;
align-items: center;
gap: 6px;
}
-.clear-session-btn:hover {
- background: linear-gradient(135deg, #c82333 0%, #a71e2a 100%);
- transform: translateY(-1px);
+@media (hover: hover) {
+ .clear-session-btn:hover {
+ background: linear-gradient(135deg, #c82333 0%, #a71e2a 100%);
+ transform: translateY(-1px);
+ }
}
/* ===== 页面标题 ===== */
@@ -189,7 +213,7 @@
content: counter(list-item) ".\00A0\00A0";
}
-/* ===== 步骤指示器 ===== */
+/* ===== 步骤指示器(进度轨见 design-system) ===== */
.step-indicator {
display: flex;
justify-content: space-between;
@@ -211,6 +235,7 @@
z-index: 2;
width: 120px;
position: relative;
+ transition: opacity var(--transition-fast, 160ms ease);
}
.step-number {
@@ -227,20 +252,45 @@
color: #212529;
border: 2px solid rgba(255, 193, 7, 0.55);
box-shadow: inset 0 0 0 2px rgba(255, 193, 7, 0.18);
+ transition:
+ background var(--transition-base, 240ms ease),
+ border-color var(--transition-base, 240ms ease),
+ box-shadow var(--transition-base, 240ms ease),
+ color var(--transition-base, 240ms ease),
+ transform var(--transition-fast, 160ms ease);
+}
+
+.step.completed .step-number {
+ background: linear-gradient(135deg, #f0c000, #e6b800);
+ color: #212529;
+ border-color: rgba(230, 184, 0, 0.9);
+ box-shadow: 0 4px 12px rgba(255, 193, 7, 0.35);
+}
+
+.step.completed .step-text {
+ color: #854d0e;
+ font-weight: 600;
}
.step.active .step-number {
background: linear-gradient(135deg, #ffcc00, #ffd84d);
- color: white;
+ color: #212529;
box-shadow: 0 6px 16px rgba(255, 193, 7, 0.45);
- border-color: rgba(255, 193, 7, 0.8);
+ border-color: rgba(255, 193, 7, 0.85);
+ transform: scale(1.06);
+}
+
+.step.active .step-text {
+ color: #713f12;
+ font-weight: 700;
}
.step-text {
font-size: 14px;
text-align: center;
- color: #212529;
+ color: #64748b;
font-weight: 500;
+ transition: color var(--transition-fast, 160ms ease);
}
/* ===== 状态显示 ===== */
@@ -378,7 +428,8 @@
margin: 20px 0;
padding: 16px 20px;
box-shadow: 0 4px 15px rgba(255, 204, 0, 0.3);
- animation: subtle-pulse 3s ease-in-out infinite;
+ /* 静态展示,避免持续脉冲 */
+ animation: none;
}
.promo-content {
@@ -392,7 +443,7 @@
.promo-icon {
color: #212529;
font-size: 20px;
- animation: bounce 2s ease-in-out infinite;
+ animation: none;
}
.promo-text {
@@ -406,7 +457,7 @@
color: #212529;
text-decoration: underline;
font-weight: 600;
- transition: all 0.3s ease;
+ transition: color var(--transition-fast, 160ms ease), text-shadow var(--transition-fast, 160ms ease);
}
.promo-link:hover {
@@ -415,14 +466,14 @@
text-shadow: 0 0 8px rgba(0, 0, 0, 0.2);
}
-/* ===== 章节控制 ===== */
+/* ===== 章节控制:进入时淡入上移 ===== */
.section {
display: none;
- animation: slideIn 0.5s ease;
}
.section.active {
display: block;
+ animation: section-enter 0.35s var(--ease-out, cubic-bezier(.2, .8, .2, 1)) both;
}
.verification-section {
@@ -432,7 +483,14 @@
.verification-section.active {
display: block;
- animation: fadeIn 0.5s ease;
+ animation: section-enter 0.35s var(--ease-out, cubic-bezier(.2, .8, .2, 1)) both;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .section.active,
+ .verification-section.active {
+ animation: none !important;
+ }
}
/* ===== 表单标签 ===== */
@@ -612,3 +670,77 @@
display: block;
animation: fadeIn 0.4s ease;
}
+
+/* ===== 暗色模式:组件层 ===== */
+@media (prefers-color-scheme: dark) {
+ .page-title h1 {
+ color: #f8fafc;
+ }
+
+ .page-title p {
+ color: rgba(233, 236, 239, 0.72);
+ }
+
+ .step-indicator {
+ background: linear-gradient(135deg, #1e1e1e, #252525);
+ border-color: rgba(255, 255, 255, 0.08);
+ box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
+ }
+
+ .step-number {
+ background: #2a2618;
+ color: #f8fafc;
+ border-color: rgba(255, 193, 7, 0.45);
+ box-shadow: inset 0 0 0 2px rgba(255, 193, 7, 0.12);
+ }
+
+ .step-text {
+ color: #94a3b8;
+ }
+
+ .step.completed .step-text {
+ color: #fde68a;
+ }
+
+ .step.active .step-text {
+ color: #fef3c7;
+ }
+
+ .status {
+ background: rgba(30, 30, 30, 0.92);
+ color: #e9ecef;
+ }
+
+ .status-item {
+ background: rgba(42, 42, 42, 0.9);
+ border-color: rgba(255, 255, 255, 0.08);
+ }
+
+ .status-label {
+ color: #adb5bd;
+ }
+
+ .status-value {
+ color: #d0d0d0;
+ }
+
+ .form-label {
+ color: #e9ecef;
+ }
+
+ .new-user-notice {
+ background: linear-gradient(135deg, rgba(127, 29, 29, 0.35), rgba(69, 10, 10, 0.45));
+ border-color: rgba(248, 113, 113, 0.35);
+ color: #fecaca;
+ }
+
+ .promo-banner {
+ box-shadow: 0 4px 18px rgba(255, 204, 0, 0.18);
+ animation: none;
+ }
+
+ .login-btn {
+ color: #111;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.35);
+ }
+}
diff --git a/src/giffgaff/styles/giffgaff-service-time.css b/src/giffgaff/styles/giffgaff-service-time.css
index d2fc4f2..7bd3288 100644
--- a/src/giffgaff/styles/giffgaff-service-time.css
+++ b/src/giffgaff/styles/giffgaff-service-time.css
@@ -117,32 +117,35 @@
}
#serviceTimeIcon.warning {
- background: #e6f6ee;
- color: #10b981;
- box-shadow: inset 0 0 0 2px rgba(16,185,129,0.15);
- animation: breathing-light-green 2s ease-in-out infinite;
+ background: #fef3c7;
+ color: #d97706;
+ box-shadow: inset 0 0 0 2px rgba(245,158,11,0.2);
+ /* 警告态保留极轻 opacity 脉动,成功态静态 */
+ animation: warning-pulse 2.8s ease-in-out infinite;
}
#serviceTimeIcon.success {
background: #e6f6ee;
color: #10b981;
box-shadow: inset 0 0 0 2px rgba(16,185,129,0.15);
- animation: breathing-light-green 2s ease-in-out infinite;
+ animation: none;
}
-#serviceTimeIcon:hover {
- transform: scale(1.1);
+@media (hover: hover) {
+ #serviceTimeIcon:hover {
+ transform: scale(1.04);
+ }
}
.alert-warning #serviceTimeIcon {
- animation: warning-pulse 2s ease-in-out infinite;
+ animation: warning-pulse 2.8s ease-in-out infinite;
background: #f8ecbb;
color: #f59d06;
box-shadow: inset 0 0 0 2px rgba(245,158,11,0.15);
}
.alert-success #serviceTimeIcon {
- animation: breathing-light-green 2s ease-in-out infinite;
+ animation: none;
background: #e0f6eb;
color: #10b981;
box-shadow: inset 0 0 0 2px rgba(16,185,129,0.15);
@@ -157,7 +160,11 @@
line-height: 1.2;
white-space: nowrap;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
- transition: all 0.3s ease;
+ transition:
+ transform var(--transition-fast, 160ms ease),
+ box-shadow var(--transition-base, 240ms ease),
+ background-color var(--transition-base, 240ms ease),
+ border-color var(--transition-base, 240ms ease);
z-index: 10;
flex-shrink: 0;
text-align: center;
@@ -167,7 +174,7 @@
background: #f0fdf4;
color: #16a34a;
border: 2px solid #16a34a;
- animation: breathing-light-green 2s ease-in-out infinite;
+ animation: none;
}
.service-time-action-badge.warning {
@@ -176,7 +183,8 @@
border: 2px solid #dc2626;
font-weight: 800;
text-shadow: none;
- animation: breathing-light 2s ease-in-out infinite;
+ /* 仅警告徽章保留轻量脉动 */
+ animation: breathing-light 3s ease-in-out infinite;
}
.service-time-action-badge:hover {
@@ -199,7 +207,8 @@
position: relative;
overflow: hidden;
isolation: isolate;
- animation: pulse-glow 2s ease-in-out infinite;
+ /* 时间卡静态展示,避免持续 glow 动画 */
+ animation: none;
}
.current-time-card::after {
@@ -219,7 +228,7 @@
display: grid;
place-items: center;
box-shadow: inset 0 0 0 1px rgba(99,102,241,0.22);
- animation: breathing-light-purple 2s ease-in-out infinite;
+ animation: none;
}
.time-card-icon i {
@@ -247,11 +256,92 @@
letter-spacing: 1.2px;
color: #0b1324;
text-shadow: 0 1px 0 rgba(255,255,255,0.6);
- animation: time-breathe 3s ease-in-out infinite;
+ /* 静态可读,避免持续呼吸动画 */
+ animation: none;
}
.time-card-hint {
font-size: 12px;
color: #6b7280;
margin-top: 6px;
+}
+
+/* 服务时间:暗色模式 */
+@media (prefers-color-scheme: dark) {
+ .service-time-grid {
+ background: linear-gradient(135deg, #2a2418 0%, #1f1b14 100%);
+ border-color: rgba(255, 193, 7, 0.22);
+ box-shadow: 0 12px 30px rgba(0, 0, 0, 0.4);
+ }
+
+ .service-time-left {
+ background: linear-gradient(135deg, #322b1c 60%, #241f16 60%);
+ border-color: rgba(255, 193, 7, 0.2);
+ box-shadow: 0 12px 30px rgba(0, 0, 0, 0.35);
+ }
+
+ .service-time-title {
+ color: #f8fafc;
+ }
+
+ .service-time-message {
+ color: #cbd5e1;
+ }
+
+ .current-time-card {
+ background: linear-gradient(135deg, #2a2418 60%, #322b1c 60%);
+ box-shadow: 0 8px 28px rgba(0, 0, 0, 0.4);
+ }
+
+ .current-time-card::after {
+ background: linear-gradient(180deg, rgba(255,255,255,0.06), rgba(255,255,255,0));
+ }
+
+ .time-card-value {
+ color: #f8fafc;
+ text-shadow: none;
+ }
+
+ .time-card-hint {
+ color: #94a3b8;
+ }
+
+ #serviceTimeIcon {
+ background: rgba(16, 185, 129, 0.15);
+ color: #34d399;
+ box-shadow: inset 0 0 0 2px rgba(16, 185, 129, 0.25);
+ }
+
+ .alert-warning #serviceTimeIcon,
+ #serviceTimeIcon.warning {
+ background: rgba(245, 158, 11, 0.18);
+ color: #fbbf24;
+ box-shadow: inset 0 0 0 2px rgba(245, 158, 11, 0.28);
+ }
+
+ .service-time-action-badge.success {
+ background: rgba(22, 163, 74, 0.15);
+ color: #4ade80;
+ border-color: #16a34a;
+ }
+
+ .service-time-action-badge.warning {
+ background: rgba(220, 38, 38, 0.15);
+ color: #f87171;
+ border-color: #dc2626;
+ }
+
+ .time-card-icon {
+ background: linear-gradient(135deg, #1e1b2e, #252038);
+ box-shadow: inset 0 0 0 1px rgba(129, 140, 248, 0.28);
+ }
+}
+
+/* 尊重减少动态偏好:去掉剩余状态脉动 */
+@media (prefers-reduced-motion: reduce) {
+ #serviceTimeIcon.warning,
+ .alert-warning #serviceTimeIcon,
+ .service-time-action-badge.warning {
+ animation: none !important;
+ }
}
\ No newline at end of file
diff --git a/src/simyo/js/modules/ui-controller.js b/src/simyo/js/modules/ui-controller.js
index af44ba5..3f6c2e8 100644
--- a/src/simyo/js/modules/ui-controller.js
+++ b/src/simyo/js/modules/ui-controller.js
@@ -85,18 +85,40 @@ export class UIController {
* 更新步骤指示器
*/
updateSteps(currentStep) {
- this.elements.steps.forEach((step, index) => {
+ const steps = this.elements.steps;
+ const total = steps.length;
+
+ steps.forEach((step, index) => {
const stepNo = index + 1;
- if (stepNo <= currentStep) {
+ step.classList.remove('active', 'completed');
+
+ if (stepNo < currentStep) {
+ step.classList.add('completed');
+ step.style.cursor = 'pointer';
+ step.title = tl('点击返回此步骤');
+ step.setAttribute('aria-current', 'false');
+ } else if (stepNo === currentStep) {
step.classList.add('active');
step.style.cursor = 'pointer';
- step.title = stepNo < currentStep ? tl('点击返回此步骤') : '';
+ step.title = '';
+ step.setAttribute('aria-current', 'step');
} else {
- step.classList.remove('active');
step.style.cursor = 'not-allowed';
step.title = tl('请按顺序完成前序步骤');
+ step.setAttribute('aria-current', 'false');
}
});
+
+ // 进度轨:已完成区间占比(1 步时为 0,末步为 1)
+ const indicator = steps[0] && steps[0].closest
+ ? steps[0].closest('.step-indicator')
+ : null;
+ if (indicator && total > 1) {
+ const progress = Math.max(0, Math.min(1, (currentStep - 1) / (total - 1)));
+ indicator.style.setProperty('--step-progress', String(progress));
+ } else if (indicator) {
+ indicator.style.setProperty('--step-progress', currentStep > 0 ? '1' : '0');
+ }
}
/**
diff --git a/src/simyo/simyo_modular.html b/src/simyo/simyo_modular.html
index 5d11a2c..5766422 100644
--- a/src/simyo/simyo_modular.html
+++ b/src/simyo/simyo_modular.html
@@ -2,7 +2,7 @@
-
+
diff --git a/src/simyo/styles/simyo-animations.css b/src/simyo/styles/simyo-animations.css
index 39b0be6..0abfc9a 100644
--- a/src/simyo/styles/simyo-animations.css
+++ b/src/simyo/styles/simyo-animations.css
@@ -54,8 +54,9 @@
.reveal {
opacity: 0;
transform: translateY(12px);
- transition: opacity 0.5s ease, transform 0.5s ease;
- will-change: opacity, transform;
+ transition:
+ opacity var(--transition-base, 240ms ease),
+ transform var(--transition-base, 240ms ease);
}
.reveal.show {
@@ -63,6 +64,18 @@
transform: translateY(0);
}
+/* 步骤内容进入 */
+@keyframes section-enter {
+ from {
+ opacity: 0;
+ transform: translateY(12px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
/* 降低动效:尊重用户的减少动态偏好 */
@media (prefers-reduced-motion: reduce) {
.reveal {
diff --git a/src/simyo/styles/simyo-base.css b/src/simyo/styles/simyo-base.css
index 44241e9..134ecea 100644
--- a/src/simyo/styles/simyo-base.css
+++ b/src/simyo/styles/simyo-base.css
@@ -19,7 +19,7 @@ body {
color: #212529;
min-height: 100vh;
padding: 20px;
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ font-family: var(--font-sans, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif);
margin: 0;
}
@@ -83,7 +83,7 @@ body {
margin-top: 8px;
}
-/* 步骤指示器 */
+/* 步骤指示器(进度轨见 design-system .step-indicator::before/::after) */
.step-indicator {
display: flex;
justify-content: space-between;
@@ -105,6 +105,7 @@ body {
z-index: 2;
width: 120px;
position: relative;
+ transition: opacity var(--transition-fast, 160ms ease);
}
.step-number {
@@ -121,20 +122,45 @@ body {
color: #212529;
border: 2px solid rgba(255, 107, 0, 0.55);
box-shadow: inset 0 0 0 2px rgba(255, 107, 0, 0.18);
+ transition:
+ background var(--transition-base, 240ms ease),
+ border-color var(--transition-base, 240ms ease),
+ box-shadow var(--transition-base, 240ms ease),
+ color var(--transition-base, 240ms ease),
+ transform var(--transition-fast, 160ms ease);
+}
+
+.step.completed .step-number {
+ background: linear-gradient(135deg, #ff8a2b, #ff6b00);
+ color: #fff;
+ border-color: rgba(255, 107, 0, 0.9);
+ box-shadow: 0 4px 12px rgba(255, 107, 0, 0.3);
+}
+
+.step.completed .step-text {
+ color: #c2410c;
+ font-weight: 600;
}
.step.active .step-number {
background: linear-gradient(135deg, #ff7a1a, #ffb066);
- color: white;
+ color: #fff;
+ border-color: rgba(255, 107, 0, 0.85);
box-shadow: 0 6px 16px rgba(255, 107, 0, 0.45);
- border-color: rgba(255, 107, 0, 0.8);
+ transform: scale(1.06);
+}
+
+.step.active .step-text {
+ color: #9a3412;
+ font-weight: 700;
}
.step-text {
font-size: 14px;
text-align: center;
- color: #212529;
+ color: #64748b;
font-weight: 500;
+ transition: color var(--transition-fast, 160ms ease);
}
/* 表单元素 */
@@ -154,7 +180,10 @@ textarea, input {
background: rgba(255, 255, 255, 0.9);
color: #212529;
font-size: 16px;
- transition: all 0.3s ease;
+ transition:
+ border-color var(--transition-base, 240ms ease),
+ box-shadow var(--transition-base, 240ms ease),
+ background-color var(--transition-base, 240ms ease);
box-sizing: border-box;
}
@@ -184,13 +213,14 @@ textarea:focus-visible {
outline-offset: 2px;
}
-/* 区块显示控制 */
+/* 区块显示控制:进入时淡入上移 */
.section {
display: none;
}
.section.active {
display: block;
+ animation: section-enter 0.35s var(--ease-out, cubic-bezier(.2, .8, .2, 1)) both;
}
#resultContainer {
@@ -199,6 +229,14 @@ textarea:focus-visible {
#resultContainer.active {
display: block;
+ animation: section-enter 0.35s var(--ease-out, cubic-bezier(.2, .8, .2, 1)) both;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .section.active,
+ #resultContainer.active {
+ animation: none !important;
+ }
}
/* 加载动画 */
@@ -239,6 +277,100 @@ textarea:focus-visible {
opacity: 1;
}
+/* ===== 暗色模式 ===== */
+@media (prefers-color-scheme: dark) {
+ :root {
+ --secondary: #2a1f16;
+ --light: #1a1a1a;
+ --dark: #e9ecef;
+ --gray: #adb5bd;
+ }
+
+ body {
+ background: linear-gradient(135deg, #1a1410 0%, #12100e 50%, #0c0b0a 100%);
+ color: #e9ecef;
+ }
+
+ .app-title {
+ background: linear-gradient(to right, #ff8a3d, #6eb0ff);
+ background-clip: text;
+ -webkit-background-clip: text;
+ }
+
+ .page-title h1 {
+ color: #f1f5f9;
+ }
+
+ .page-title p {
+ color: rgba(233, 236, 239, 0.72);
+ }
+
+ .step-indicator {
+ background: linear-gradient(135deg, #1e1e1e, #252525);
+ border-color: rgba(255, 255, 255, 0.08);
+ box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
+ }
+
+ .step-number {
+ background: #2a2118;
+ color: #f8fafc;
+ border-color: rgba(255, 107, 0, 0.45);
+ box-shadow: inset 0 0 0 2px rgba(255, 107, 0, 0.12);
+ }
+
+ .step-text {
+ color: #94a3b8;
+ }
+
+ .step.completed .step-text {
+ color: #fdba74;
+ }
+
+ .step.active .step-text {
+ color: #ffedd5;
+ }
+
+ textarea,
+ input {
+ background: rgba(30, 30, 30, 0.92);
+ color: #e9ecef;
+ border-color: rgba(255, 107, 0, 0.28);
+ }
+
+ textarea:focus,
+ input:focus {
+ background: rgba(36, 36, 36, 1);
+ box-shadow: 0 0 0 4px rgba(255, 107, 0, 0.18);
+ }
+
+ .text-dark {
+ color: #e9ecef !important;
+ }
+
+ .bg-light {
+ background-color: #2a2a2a !important;
+ color: #e9ecef;
+ }
+
+ .alert-danger {
+ background-color: rgba(127, 29, 29, 0.35) !important;
+ border-color: rgba(248, 113, 113, 0.35) !important;
+ color: #fecaca !important;
+ }
+
+ .alert-warning {
+ background-color: rgba(120, 53, 15, 0.35) !important;
+ border-color: rgba(251, 191, 36, 0.35) !important;
+ color: #fde68a !important;
+ }
+
+ .alert-info {
+ background-color: rgba(30, 58, 138, 0.35) !important;
+ border-color: rgba(96, 165, 250, 0.35) !important;
+ color: #bfdbfe !important;
+ }
+}
+
/* Toast通知样式 */
.toast-notification {
position: fixed;
diff --git a/src/simyo/styles/simyo-components.css b/src/simyo/styles/simyo-components.css
index a0daa9d..df31a7b 100644
--- a/src/simyo/styles/simyo-components.css
+++ b/src/simyo/styles/simyo-components.css
@@ -3,21 +3,24 @@
* 定义卡片、按钮、状态面板等组件样式
*/
-/* 卡片样式 */
+/* 卡片样式:内容卡仅微调阴影,避免填表时大幅上浮 */
.card {
- background: rgba(255, 255, 255, 0.9);
+ background: rgba(255, 255, 255, 0.92);
border-radius: 16px;
border: 1px solid rgba(255, 107, 0, 0.3);
- box-shadow: 0 4px 25px rgba(0, 0, 0, 0.1);
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
margin-bottom: 30px;
overflow: hidden;
- transition: all 0.3s ease;
+ transition:
+ box-shadow var(--transition-base, 240ms ease),
+ border-color var(--transition-base, 240ms ease);
}
-.card:hover {
- transform: translateY(-5px);
- box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15);
- border: 1px solid rgba(255, 107, 0, 0.5);
+@media (hover: hover) {
+ .card:hover {
+ box-shadow: 0 6px 24px rgba(0, 0, 0, 0.1);
+ border-color: rgba(255, 107, 0, 0.45);
+ }
}
.card-header {
@@ -55,7 +58,11 @@
font-size: 17px;
font-weight: 600;
cursor: pointer;
- transition: all 0.3s;
+ transition:
+ transform var(--transition-fast, 160ms ease),
+ box-shadow var(--transition-fast, 160ms ease),
+ background var(--transition-base, 240ms ease),
+ opacity var(--transition-fast, 160ms ease);
display: flex;
align-items: center;
justify-content: center;
@@ -68,10 +75,12 @@
color: #212529;
}
-.btn-primary:hover {
- background: linear-gradient(to right, #ffffff, #ff6b00);
- transform: translateY(-3px);
- box-shadow: 0 8px 15px rgba(0, 0, 0, 0.15);
+@media (hover: hover) {
+ .btn-primary:hover {
+ background: linear-gradient(to right, #ffffff, #ff6b00);
+ transform: translateY(-2px);
+ box-shadow: 0 8px 15px rgba(0, 0, 0, 0.12);
+ }
}
.btn:disabled {
@@ -92,7 +101,11 @@
align-items: center;
justify-content: center;
border-radius: 30px;
- transition: all 0.3s;
+ transition:
+ transform var(--transition-fast, 160ms ease),
+ box-shadow var(--transition-fast, 160ms ease),
+ border-color var(--transition-base, 240ms ease),
+ background var(--transition-base, 240ms ease);
gap: 8px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
text-decoration: none;
@@ -101,11 +114,13 @@
flex-shrink: 0;
}
-.help-btn:hover {
- background: linear-gradient(to right, #ffffff, #ff6b00);
- transform: translateY(-2px);
- box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15);
- border-color: rgba(255, 107, 0, 0.5);
+@media (hover: hover) {
+ .help-btn:hover {
+ background: linear-gradient(to right, #ffffff, #ff6b00);
+ transform: translateY(-2px);
+ box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15);
+ border-color: rgba(255, 107, 0, 0.5);
+ }
}
.github-btn {
@@ -119,7 +134,12 @@
align-items: center;
justify-content: center;
border-radius: 30px;
- transition: all 0.3s;
+ transition:
+ transform var(--transition-fast, 160ms ease),
+ box-shadow var(--transition-fast, 160ms ease),
+ border-color var(--transition-base, 240ms ease),
+ background var(--transition-base, 240ms ease),
+ color var(--transition-fast, 160ms ease);
gap: 8px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
text-decoration: none;
@@ -129,12 +149,14 @@
flex-shrink: 0;
}
-.github-btn:hover {
- background: linear-gradient(to right, #666666, #333333);
- transform: translateY(-2px);
- box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15);
- border-color: rgba(51, 51, 51, 0.5);
- color: #ffffff;
+@media (hover: hover) {
+ .github-btn:hover {
+ background: linear-gradient(to right, #666666, #333333);
+ transform: translateY(-2px);
+ box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15);
+ border-color: rgba(51, 51, 51, 0.5);
+ color: #ffffff;
+ }
}
/* 状态显示 */
@@ -259,15 +281,19 @@
border-radius: 6px;
font-size: 14px;
cursor: pointer;
- transition: all 0.3s ease;
+ transition:
+ transform var(--transition-fast, 160ms ease),
+ background var(--transition-base, 240ms ease);
display: flex;
align-items: center;
gap: 6px;
}
-.clear-session-btn:hover {
- background: linear-gradient(135deg, #c82333 0%, #a71e2a 100%);
- transform: translateY(-1px);
+@media (hover: hover) {
+ .clear-session-btn:hover {
+ background: linear-gradient(135deg, #c82333 0%, #a71e2a 100%);
+ transform: translateY(-1px);
+ }
}
/* 信息卡片 */
@@ -298,7 +324,7 @@
margin: 20px 0;
padding: 16px 20px;
box-shadow: 0 4px 15px rgba(255, 107, 0, 0.3);
- animation: subtle-pulse 3s ease-in-out infinite;
+ animation: none;
}
.promo-content {
@@ -312,7 +338,7 @@
.promo-icon {
color: white;
font-size: 20px;
- animation: bounce 2s ease-in-out infinite;
+ animation: none;
}
.promo-text {
@@ -326,7 +352,7 @@
color: white;
text-decoration: underline;
font-weight: 600;
- transition: all 0.3s ease;
+ transition: color var(--transition-fast, 160ms ease), text-shadow var(--transition-fast, 160ms ease);
}
.promo-link:hover {
@@ -344,4 +370,63 @@
.qrcode-container:hover .tooltip {
opacity: 1;
+}
+
+/* ===== 暗色模式:组件层 ===== */
+@media (prefers-color-scheme: dark) {
+ .card {
+ background: rgba(30, 30, 30, 0.92);
+ border-color: rgba(255, 107, 0, 0.28);
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.35);
+ }
+
+ @media (hover: hover) {
+ .card:hover {
+ box-shadow: 0 6px 24px rgba(0, 0, 0, 0.45);
+ border-color: rgba(255, 107, 0, 0.42);
+ }
+ }
+
+ .card-header {
+ background: rgba(255, 107, 0, 0.12);
+ color: #f8f9fa;
+ border-bottom-color: rgba(255, 107, 0, 0.2);
+ }
+
+ .status {
+ background: rgba(30, 30, 30, 0.92);
+ color: #e9ecef;
+ }
+
+ .status-item {
+ background: rgba(42, 42, 42, 0.9);
+ border-color: rgba(255, 255, 255, 0.08);
+ }
+
+ .status-label {
+ color: #adb5bd;
+ }
+
+ .status-value {
+ color: #d0d0d0;
+ }
+
+ .form-label {
+ color: #e9ecef;
+ }
+
+ .info-card {
+ background: linear-gradient(135deg, #2a1f16 0%, #3d2818 100%);
+ color: #f8fafc;
+ border-color: rgba(255, 107, 0, 0.3);
+ }
+
+ .promo-banner {
+ animation: none;
+ box-shadow: 0 4px 18px rgba(255, 107, 0, 0.22);
+ }
+
+ .promo-icon {
+ animation: none;
+ }
}
\ No newline at end of file
diff --git a/src/styles/animations.css b/src/styles/animations.css
index 9cb35fc..b392738 100644
--- a/src/styles/animations.css
+++ b/src/styles/animations.css
@@ -1,70 +1,48 @@
-/* 微交互动画和触摸反馈 */
+/* 微交互动画与触摸反馈
+ 依赖 design-system.css 的 motion tokens,禁止在此重新定义 --transition-* 时长格式
+*/
-/* 基础动画变量 */
+/* 本地缓动别名(与 design-system 对齐,作兜底) */
:root {
- --transition-fast: 0.15s ease-out;
- --transition-normal: 0.3s ease-out;
- --transition-slow: 0.5s ease-out;
--bounce-ease: cubic-bezier(0.68, -0.55, 0.265, 1.55);
- --smooth-ease: cubic-bezier(0.4, 0, 0.2, 1);
+ --smooth-ease: var(--ease-out, cubic-bezier(0.22, 0.61, 0.36, 1));
}
-/* 按钮点击反馈动画 */
+/* 按钮点击反馈 */
.btn {
position: relative;
overflow: hidden;
- transition: all var(--transition-normal) var(--smooth-ease);
- transform: translateZ(0);
- will-change: transform;
+ transition:
+ transform var(--transition-fast, 120ms var(--smooth-ease)),
+ box-shadow var(--transition-fast, 120ms var(--smooth-ease)),
+ background-color var(--transition-base, 200ms var(--smooth-ease)),
+ border-color var(--transition-base, 200ms var(--smooth-ease)),
+ color var(--transition-fast, 120ms var(--smooth-ease)),
+ opacity var(--transition-fast, 120ms var(--smooth-ease));
}
.btn:active {
- transform: scale(0.95) translateZ(0);
+ transform: scale(0.98);
}
-.btn::before {
- content: '';
- position: absolute;
- top: 50%;
- left: 50%;
- width: 0;
- height: 0;
- background: rgba(255, 255, 255, 0.3);
- border-radius: 50%;
- transform: translate(-50%, -50%);
- transition: width 0.6s, height 0.6s;
-}
-
-.btn:active::before {
- width: 300px;
- height: 300px;
-}
-
-/* 卡片悬停效果 */
+/* 卡片:仅声明过渡,上浮由可导航卡片自行定义 */
.card {
- transition: all var(--transition-normal) var(--smooth-ease);
- transform: translateZ(0);
- will-change: transform, box-shadow;
+ transition:
+ transform var(--transition-base, 200ms var(--smooth-ease)),
+ box-shadow var(--transition-base, 200ms var(--smooth-ease)),
+ border-color var(--transition-base, 200ms var(--smooth-ease));
}
-.card:hover {
- transform: translateY(-2px) translateZ(0);
- box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
-}
-
-.card:active {
- transform: translateY(0) translateZ(0);
-}
-
-/* 输入框焦点动画 */
+/* 输入框焦点:ring 即可,避免 scale 造成布局抖动 */
.form-control {
- transition: all var(--transition-normal) var(--smooth-ease);
- transform: translateZ(0);
+ transition:
+ border-color var(--transition-base, 200ms var(--smooth-ease)),
+ box-shadow var(--transition-base, 200ms var(--smooth-ease)),
+ background-color var(--transition-base, 200ms var(--smooth-ease));
}
.form-control:focus {
- transform: scale(1.02) translateZ(0);
- box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
+ box-shadow: 0 0 0 3px var(--ring-color, rgba(0, 123, 255, 0.15));
}
/* 加载状态指示器 */
@@ -95,7 +73,7 @@
/* 淡入动画 */
.fade-in {
- animation: fadeIn 0.5s ease-out;
+ animation: fadeIn 0.35s var(--smooth-ease);
}
@keyframes fadeIn {
@@ -103,14 +81,26 @@
to { opacity: 1; transform: translateY(0); }
}
+/* 步骤内容区进入动画 */
+@keyframes section-enter {
+ from {
+ opacity: 0;
+ transform: translateY(12px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
/* 滑入动画 */
.slide-in {
- animation: slideIn 0.3s ease-out;
+ animation: slideIn 0.3s var(--smooth-ease);
}
@keyframes slideIn {
- from { transform: translateX(-100%); }
- to { transform: translateX(0); }
+ from { transform: translateX(-100%); opacity: 0; }
+ to { transform: translateX(0); opacity: 1; }
}
/* 弹跳动画 */
@@ -131,18 +121,9 @@
-webkit-tap-highlight-color: transparent;
touch-action: manipulation;
}
-
+
.btn:active {
transform: scale(0.98);
- background-color: rgba(0, 0, 0, 0.1);
- }
-
- .card:active {
- transform: scale(0.98);
- }
-
- .form-control:focus {
- transform: none;
}
}
@@ -177,7 +158,9 @@
z-index: 9999;
opacity: 0;
visibility: hidden;
- transition: all var(--transition-normal);
+ transition:
+ opacity var(--transition-base, 200ms var(--smooth-ease)),
+ visibility var(--transition-base, 200ms var(--smooth-ease));
}
.loading-overlay.show {
@@ -188,7 +171,7 @@
/* 进度条动画 */
.progress-bar {
height: 4px;
- background: linear-gradient(90deg, #007bff, #28a745);
+ background: linear-gradient(90deg, var(--primary, #007bff), #28a745);
border-radius: 2px;
overflow: hidden;
position: relative;
@@ -210,10 +193,12 @@
100% { left: 100%; }
}
-/* 工具提示动画优化 */
+/* 工具提示动画 */
.tooltip {
- transition: opacity var(--transition-fast), transform var(--transition-fast);
- transform: scale(0.9);
+ transition:
+ opacity var(--transition-fast, 120ms var(--smooth-ease)),
+ transform var(--transition-fast, 120ms var(--smooth-ease));
+ transform: scale(0.96);
transform-origin: center bottom;
}
@@ -223,30 +208,34 @@
/* 状态切换动画 */
.status-transition {
- transition: all var(--transition-normal) var(--smooth-ease);
+ transition:
+ color var(--transition-base, 200ms var(--smooth-ease)),
+ transform var(--transition-base, 200ms var(--smooth-ease));
}
.status-transition.connected {
color: #28a745;
- transform: scale(1.05);
}
.status-transition.disconnected {
color: #dc3545;
- transform: scale(0.95);
}
-
/* 骨架屏动画 */
@keyframes skeleton-loading {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
-/* 步骤脉冲环动画 */
+/* 步骤脉冲环(颜色跟随品牌 --primary) */
@keyframes pulse-ring {
- 0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(13, 110, 253, 0.7); }
- 70% { transform: scale(1); box-shadow: 0 0 0 10px rgba(13, 110, 253, 0); }
- 100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(13, 110, 253, 0); }
+ 0% {
+ box-shadow: 0 0 0 0 color-mix(in srgb, var(--primary, #0d6efd) 55%, transparent);
+ }
+ 70% {
+ box-shadow: 0 0 0 10px color-mix(in srgb, var(--primary, #0d6efd) 0%, transparent);
+ }
+ 100% {
+ box-shadow: 0 0 0 0 color-mix(in srgb, var(--primary, #0d6efd) 0%, transparent);
+ }
}
-
\ No newline at end of file
diff --git a/src/styles/design-system.css b/src/styles/design-system.css
index a9c42e0..79f5a6b 100644
--- a/src/styles/design-system.css
+++ b/src/styles/design-system.css
@@ -1,9 +1,8 @@
/* Design System: tokens, utilities, base components
Scope: shared across brand pages. Brands override --primary/--secondary in page-level :root.
+ 字体使用系统栈,避免阻塞式 Google Fonts @import。
*/
-@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
-
/* 按钮组与打赏按钮样式 */
.header-actions {
display: flex;
@@ -22,10 +21,9 @@
justify-content: center;
border-radius: 999px;
gap: 8px;
- transition: transform var(--transition-fast), box-shadow var(--transition-fast), background var(--transition-fast);
+ transition: transform var(--transition-fast), box-shadow var(--transition-fast), background-color var(--transition-fast);
text-decoration: none !important;
box-shadow: var(--shadow-sm);
- will-change: transform;
border: 1px solid rgba(255,255,255,0.1);
}
@@ -90,12 +88,13 @@
--shadow-md: 0 4px 18px rgba(0,0,0,0.12);
--shadow-lg: 0 8px 28px rgba(0,0,0,0.18);
- /* Motion */
- --ease-out: cubic-bezier(.22,.61,.36,1);
- --ease-in: cubic-bezier(.55,.085,.68,.53);
- --transition-fast: 120ms var(--ease-out);
- --transition-base: 200ms var(--ease-out);
- --transition-slow: 320ms var(--ease-out);
+ /* Motion:全站唯一时长/缓动源,品牌页与 animations.css 禁止覆盖为其它格式 */
+ --ease-out: cubic-bezier(.2, .8, .2, 1);
+ --ease-in: cubic-bezier(.55, .085, .68, .53);
+ --ease-standard: var(--ease-out);
+ --transition-fast: 160ms var(--ease-out);
+ --transition-base: 240ms var(--ease-out);
+ --transition-slow: 360ms var(--ease-out);
/* Focus ring */
--ring-color: rgba(76,154,255,.35);
@@ -110,8 +109,8 @@
--space-6: 24px;
--space-8: 32px;
- /* Typography */
- --font-sans: 'Inter', system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
+ /* Typography:系统字体栈,含中文回退 */
+ --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
}
/* Dark Mode Support */
@@ -161,10 +160,22 @@
color: var(--text);
}
+ /* body 背景由各品牌页 / 首页自行定义,避免全局硬覆盖品牌色渐变 */
body {
- background: linear-gradient(135deg, #1a1a1a 0%, #000000 100%);
color: var(--text);
}
+
+ .card-header-soft {
+ color: var(--text);
+ }
+
+ .status-label {
+ color: var(--muted);
+ }
+
+ .loading-overlay {
+ background: rgba(0, 0, 0, 0.6);
+ }
}
/* 2) Accessibility and base resets */
@@ -265,9 +276,8 @@ html[data-locale="en"] .lang-en { display: inline !important; }
/* Reveal-on-scroll animation */
.reveal {
opacity: 0;
- transform: translateY(16px);
+ transform: translateY(12px);
transition: opacity var(--transition-base), transform var(--transition-base);
- will-change: opacity, transform;
}
.reveal.show {
opacity: 1;
@@ -290,10 +300,6 @@ html[data-locale="en"] .lang-en { display: inline !important; }
* { scroll-behavior: auto !important; }
}
-/* Fine-tune reveal transition to be lightweight and smooth */
-.reveal { will-change: opacity, transform; }
-.reveal.show { transition: transform var(--transition-base), opacity var(--transition-base); }
-
/* 4) Components */
.card-elevated {
background: var(--surface-glass);
@@ -461,17 +467,17 @@ html[data-locale="en"] .lang-en { display: inline !important; }
}
.tooltip.show { opacity: 1; }
-/* Promo banner */
+/* Promo banner:静态展示,避免持续 scale 动画 */
.promo-banner {
border-radius: var(--radius-md);
margin: 20px 0;
padding: 16px 20px;
box-shadow: 0 4px 15px color-mix(in srgb, var(--primary) 30%, transparent);
- animation: subtle-pulse 3s ease-in-out infinite;
+ animation: none;
}
@keyframes subtle-pulse {
- 0%,100% { transform: scale(1); }
- 50% { transform: scale(1.02); }
+ 0%,100% { opacity: 1; }
+ 50% { opacity: 0.92; }
}
/* Reveal helpers for QR preview hovers may reuse .tooltip from above */
@@ -528,10 +534,65 @@ html[data-locale="en"] .lang-en { display: inline !important; }
}
}
-/* Step Indicator Pulse */
+/* Step Indicator:当前步脉冲(颜色跟随 --primary) */
.step.active .step-number {
- box-shadow: 0 0 0 0 rgba(13, 110, 253, 0.7);
- animation: pulse-ring 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+ animation: pulse-ring 2s var(--ease-out) infinite;
+}
+
+/* 步骤指示器进度轨(--step-progress: 0~1,由 JS 写入) */
+.step-indicator {
+ --step-progress: 0;
+}
+
+.step-indicator::before,
+.step-indicator::after {
+ content: '';
+ position: absolute;
+ top: calc(16px + 28px);
+ left: 10%;
+ right: 10%;
+ height: 3px;
+ border-radius: 999px;
+ z-index: 1;
+ pointer-events: none;
+}
+
+.step-indicator::before {
+ background: color-mix(in srgb, var(--muted, #6c757d) 22%, transparent);
+}
+
+.step-indicator::after {
+ right: auto;
+ width: calc((100% - 20%) * var(--step-progress, 0));
+ background: linear-gradient(
+ 90deg,
+ color-mix(in srgb, var(--primary) 75%, transparent),
+ var(--primary)
+ );
+ transition: width var(--transition-slow);
+}
+
+@media (max-width: 768px) {
+ .step-indicator::before,
+ .step-indicator::after {
+ top: calc(16px + 20px);
+ }
+}
+
+@media (max-width: 480px) {
+ .step-indicator::before,
+ .step-indicator::after {
+ top: calc(16px + 17px);
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .step.active .step-number {
+ animation: none !important;
+ }
+ .step-indicator::after {
+ transition: none !important;
+ }
}
/* Keyboard focus visibility for custom buttons built on
*/
diff --git a/src/styles/mobile-responsive.css b/src/styles/mobile-responsive.css
index 493c26d..eb6ad9f 100644
--- a/src/styles/mobile-responsive.css
+++ b/src/styles/mobile-responsive.css
@@ -363,10 +363,9 @@ a:not(.btn) {
性能优化
======================================== */
-/* GPU加速 */
+/* GPU 加速:仅在动画中临时使用,避免常驻 will-change */
.gpu-accelerated {
transform: translateZ(0);
- will-change: transform;
}
/* 减少重绘 */
diff --git a/src/styles/notification.css b/src/styles/notification.css
index a0e5370..8fd0ce3 100644
--- a/src/styles/notification.css
+++ b/src/styles/notification.css
@@ -2,8 +2,8 @@
.notification-container {
position: fixed;
- top: 20px;
- right: 20px;
+ top: max(16px, calc(env(safe-area-inset-top, 0px) + 12px));
+ right: max(16px, calc(env(safe-area-inset-right, 0px) + 12px));
z-index: 10000;
display: flex;
flex-direction: column;
@@ -25,9 +25,8 @@
pointer-events: auto;
transform: translateX(calc(100% + 20px));
opacity: 0;
- transition: transform var(--transition-base, 200ms) var(--ease-out, cubic-bezier(.22,.61,.36,1)),
- opacity var(--transition-base, 200ms) var(--ease-out, cubic-bezier(.22,.61,.36,1));
- will-change: transform, opacity;
+ transition: transform var(--transition-base, 240ms var(--ease-out, cubic-bezier(.2,.8,.2,1))),
+ opacity var(--transition-base, 240ms var(--ease-out, cubic-bezier(.2,.8,.2,1)));
}
.notification.show {
@@ -64,10 +63,12 @@
background: none;
border: none;
padding: 4px;
+ min-width: 32px;
+ min-height: 32px;
cursor: pointer;
color: inherit;
opacity: 0.7;
- transition: opacity var(--transition-fast, 120ms);
+ transition: opacity var(--transition-fast, 160ms);
flex-shrink: 0;
border-radius: 4px;
}
@@ -102,17 +103,26 @@
color: white;
}
-/* 响应式设计 */
+/* 响应式 + 安全区 */
@media (max-width: 768px) {
.notification-container {
- top: 10px;
- right: 10px;
- left: 10px;
+ top: max(10px, calc(env(safe-area-inset-top, 0px) + 8px));
+ right: max(10px, calc(env(safe-area-inset-right, 0px) + 8px));
+ left: max(10px, calc(env(safe-area-inset-left, 0px) + 8px));
max-width: none;
}
.notification {
padding: 14px 16px;
+ transform: translateY(calc(-100% - 16px));
+ }
+
+ .notification.show {
+ transform: translateY(0);
+ }
+
+ .notification.hide {
+ transform: translateY(calc(-100% - 16px));
}
.notification-message {
@@ -136,11 +146,11 @@
}
}
-/* 网络状态指示器样式 (Ported from index.html) */
+/* 网络状态指示器 */
.network-status {
position: fixed;
- top: 20px;
- right: 20px;
+ top: max(16px, calc(env(safe-area-inset-top, 0px) + 12px));
+ right: max(16px, calc(env(safe-area-inset-right, 0px) + 12px));
padding: 12px 20px;
border-radius: 8px;
color: white;
@@ -148,7 +158,7 @@
z-index: 10000;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
transform: translateX(100%);
- transition: transform 0.3s ease;
+ transition: transform var(--transition-base, 240ms ease);
}
.network-status.success {
@@ -169,21 +179,15 @@
.update-notification {
position: fixed;
- bottom: 20px;
- right: 20px;
+ bottom: max(16px, calc(env(safe-area-inset-bottom, 0px) + 12px));
+ right: max(16px, calc(env(safe-area-inset-right, 0px) + 12px));
background: linear-gradient(135deg, #6366f1, #8b5cf6);
color: white;
padding: 16px 20px;
border-radius: 12px;
box-shadow: 0 8px 25px rgba(99, 102, 241, 0.3);
z-index: 10000;
- max-width: 300px;
-}
-
-.notification-content {
- display: flex;
- align-items: center;
- justify-content: space-between;
+ max-width: min(300px, calc(100vw - 32px - env(safe-area-inset-left, 0px) - env(safe-area-inset-right, 0px)));
}
/* 加载状态样式 */
@@ -201,7 +205,7 @@
align-items: center;
opacity: 0;
pointer-events: none;
- transition: opacity 0.3s ease;
+ transition: opacity var(--transition-base, 240ms ease);
}
.loading-overlay.show {
@@ -237,3 +241,25 @@
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
+
+/* 暗色模式 */
+@media (prefers-color-scheme: dark) {
+ .loading-overlay {
+ background: rgba(0, 0, 0, 0.65);
+ }
+
+ .loading-content {
+ background: #1e1e1e;
+ color: #e9ecef;
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.45);
+ }
+
+ .loading-content p {
+ color: #adb5bd;
+ }
+
+ .loading-spinner {
+ border-color: #333;
+ border-top-color: var(--primary, #8b5cf6);
+ }
+}