thomasm291678
VetCopilot-LCPS: LCPS 2.0 本地推理引擎替换 DeepSeek API
793e953
Raw History Blame Contribute Delete
92.3 kB
@tailwind base;
@tailwind components;
@tailwind utilities;
/* ================================================================
主题系统
[data-theme] 选择器覆盖 Tailwind 工具类
================================================================ */
/* ===== 方案 A:暖绿自然 (Emerald) ===== */
[data-theme="emerald"] .bg-medical-50 { background-color: #ecfdf5; }
[data-theme="emerald"] .bg-medical-100 { background-color: #d1fae5; }
[data-theme="emerald"] .bg-medical-200 { background-color: #a7f3d0; }
[data-theme="emerald"] .bg-medical-500 { background-color: #10b981; }
[data-theme="emerald"] .bg-medical-600 { background-color: #059669; }
[data-theme="emerald"] .bg-medical-700 { background-color: #047857; }
[data-theme="emerald"] .text-medical-600 { color: #059669; }
[data-theme="emerald"] .text-medical-700 { color: #047857; }
[data-theme="emerald"] .border-medical-600 { border-color: #059669; }
[data-theme="emerald"] .ring-medical-500 { --tw-ring-color: #10b981; }
[data-theme="emerald"] .ring-medical-600 { --tw-ring-color: #059669; }
[data-theme="emerald"] .from-medical-600 { --tw-gradient-from: #059669; --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); }
[data-theme="emerald"] .from-medical-700 { --tw-gradient-from: #047857; --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); }
[data-theme="emerald"] .to-medical-700 { --tw-gradient-to: #047857; }
[data-theme="emerald"] .bg-accent-50 { background-color: #fffbeb; }
[data-theme="emerald"] .bg-accent-100 { background-color: #fef3c7; }
[data-theme="emerald"] .bg-accent-500 { background-color: #f59e0b; }
[data-theme="emerald"] .bg-accent-600 { background-color: #d97706; }
[data-theme="emerald"] .text-accent-600 { color: #d97706; }
[data-theme="emerald"] .border-accent-500 { border-color: #f59e0b; }
[data-theme="emerald"] .bg-surface-50 { background-color: #f8faf7; }
[data-theme="emerald"] .bg-surface-100 { background-color: #f0f5f0; }
[data-theme="emerald"] .page-title::before { background: linear-gradient(180deg, #10b981, #059669); }
[data-theme="emerald"] .btn-primary { background-color: #059669; }
[data-theme="emerald"] .btn-primary:hover { background-color: #047857; }
/* ===== 方案 B:蓝绿医疗 (Cyan) ===== */
[data-theme="cyan"] .bg-medical-50 { background-color: #ecfeff; }
[data-theme="cyan"] .bg-medical-100 { background-color: #cffafe; }
[data-theme="cyan"] .bg-medical-200 { background-color: #a5f3fc; }
[data-theme="cyan"] .bg-medical-500 { background-color: #06b6d4; }
[data-theme="cyan"] .bg-medical-600 { background-color: #0891b2; }
[data-theme="cyan"] .bg-medical-700 { background-color: #0e7490; }
[data-theme="cyan"] .text-medical-600 { color: #0891b2; }
[data-theme="cyan"] .text-medical-700 { color: #0e7490; }
[data-theme="cyan"] .border-medical-600 { border-color: #0891b2; }
[data-theme="cyan"] .ring-medical-500 { --tw-ring-color: #06b6d4; }
[data-theme="cyan"] .ring-medical-600 { --tw-ring-color: #0891b2; }
[data-theme="cyan"] .from-medical-600 { --tw-gradient-from: #0891b2; --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); }
[data-theme="cyan"] .from-medical-700 { --tw-gradient-from: #0e7490; --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); }
[data-theme="cyan"] .to-medical-700 { --tw-gradient-to: #0e7490; }
[data-theme="cyan"] .bg-accent-50 { background-color: #f0fdf4; }
[data-theme="cyan"] .bg-accent-100 { background-color: #dcfce7; }
[data-theme="cyan"] .bg-accent-500 { background-color: #22c55e; }
[data-theme="cyan"] .bg-accent-600 { background-color: #16a34a; }
[data-theme="cyan"] .text-accent-600 { color: #16a34a; }
[data-theme="cyan"] .border-accent-500 { border-color: #22c55e; }
[data-theme="cyan"] .bg-surface-50 { background-color: #f8fafa; }
[data-theme="cyan"] .bg-surface-100 { background-color: #f0f5f6; }
[data-theme="cyan"] .page-title::before { background: linear-gradient(180deg, #06b6d4, #0891b2); }
[data-theme="cyan"] .btn-primary { background-color: #0891b2; }
[data-theme="cyan"] .btn-primary:hover { background-color: #0e7490; }
/* ================================================================
暗色模式 CSS 变量基础(为未来暗色模式做准备)
当前所有变量值为亮色默认值,暂不启用暗色模式
================================================================ */
:root {
/* ---- 背景色 ---- */
--color-bg-primary: #ffffff;
--color-bg-secondary: #f8fafc;
--color-surface: #ffffff;
/* ---- 文字色 ---- */
--color-text-primary: #111827;
--color-text-secondary: #6b7280;
/* ---- 边框色 ---- */
--color-border: #e5e7eb;
/* ---- 阴影层级 ---- */
--shadow-sm: 0 1px 2px 0 rgba(0,0,0,0.05);
--shadow-md: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1);
--shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1);
}
/* 在未来启用暗色模式时,只需在 <html> 上添加 data-theme="dark" */
[data-theme="dark"] {
--color-bg-primary: #0f172a;
--color-bg-secondary: #1e293b;
--color-surface: #1e293b;
--color-text-primary: #f1f5f9;
--color-text-secondary: #94a3b8;
--color-border: #334155;
--shadow-sm: 0 1px 2px 0 rgba(0,0,0,0.3);
--shadow-md: 0 4px 6px -1px rgba(0,0,0,0.4), 0 2px 4px -2px rgba(0,0,0,0.3);
--shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.5), 0 4px 6px -4px rgba(0,0,0,0.4);
}
/* ================================================================
Base
================================================================ */
@layer base {
* { box-sizing: border-box; }
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"Helvetica Neue", Arial, "Noto Sans SC", "Microsoft YaHei", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* ===== 自定义选中色 ===== */
::selection { background-color: rgba(59,130,246,0.2); color: #1e3a8a; }
[data-theme="emerald"] ::selection { background-color: rgba(16,185,129,0.2); color: #064e3b; }
[data-theme="cyan"] ::selection { background-color: rgba(6,182,212,0.2); color: #164e63; }
/* ===== 全局自定义滚动条 ===== */
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
background: #d1d5db;
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover { background: #9ca3af; }
/* ===== 全局 textarea 统一样式 ===== */
textarea:not(.input-field) {
@apply w-full px-3 py-2 text-sm border border-gray-300 rounded-lg
focus:outline-none focus:ring-2 focus:ring-medical-500 focus:border-transparent
placeholder:text-gray-400 transition-all duration-200 resize-y;
}
/* ===== 全局 input 统一样式 ===== */
input[type="text"]:not(.input-field):not([class*="border-"]),
input[type="tel"]:not(.input-field):not([class*="border-"]),
input[type="email"]:not(.input-field):not([class*="border-"]),
input[type="number"]:not(.input-field):not([class*="border-"]),
input[type="date"]:not(.input-field):not([class*="border-"]),
input[type="password"]:not(.input-field):not([class*="border-"]) {
@apply w-full px-3 py-2 text-sm border border-gray-300 rounded-lg
focus:outline-none focus:ring-2 focus:ring-medical-500 focus:border-transparent
placeholder:text-gray-400 transition-all duration-200;
}
/* ===== 全局 select 美化 ===== */
select:not(.input-field):not([class*="native"]) {
@apply w-full px-3 py-2 text-sm border border-gray-300 rounded-lg
bg-white text-gray-700
focus:outline-none focus:ring-2 focus:ring-medical-500 focus:border-transparent
transition-all duration-200;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%236b7280' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 0.75rem center;
padding-right: 2.25rem;
}
/* ===== 全局表格行 hover ===== */
table tbody tr { transition: background-color 0.15s ease; }
table tbody tr:hover { background-color: #f9fafb; }
/* ===== 全局链接颜色跟随主题 ===== */
a:not([class*="text-"]) { color: #2563eb; }
[data-theme="emerald"] a:not([class*="text-"]) { color: #059669; }
[data-theme="cyan"] a:not([class*="text-"]) { color: #0891b2; }
/* ===== 自定义 checkbox/radio ===== */
input[type="checkbox"].custom-check {
@apply w-4 h-4 rounded border-gray-300 text-medical-600
focus:ring-medical-500 focus:ring-2 transition-all duration-150;
}
input[type="radio"].custom-radio {
@apply w-4 h-4 border-gray-300 text-medical-600
focus:ring-medical-500 focus:ring-2 transition-all duration-150;
}
/* ===== Reduced Motion:尊重用户操作系统偏好 ===== */
@media (prefers-reduced-motion: reduce) {
/* 全局禁用所有非必要动画和过渡 */
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
/* 骨架屏闪烁 → 静态展示 */
.skeleton,
.skeleton-shimmer,
.skeleton-card,
.skeleton-card-line,
.skeleton-avatar {
animation: none !important;
background-image: none !important;
opacity: 0.5;
}
/* 脉冲动画 → 静态 */
.animate-spin,
.animate-pulse,
.animate-pulse-soft,
.animate-bounce {
animation: none !important;
}
/* 浮动动画 → 静态 */
.animate-float,
.empty-state-icon {
animation: none !important;
}
}
.page-enter { animation: fade-in-up 0.35s ease-out; }
}
/* ================================================================
Components
================================================================ */
@layer components {
/* ===== 按钮 ===== */
.btn-primary {
@apply inline-flex items-center justify-center gap-2 px-5 py-2.5
bg-medical-600 text-white text-sm font-medium rounded-lg
hover:bg-medical-700 active:scale-95
transition-all duration-200
disabled:opacity-50 disabled:cursor-not-allowed disabled:active:scale-100;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04);
}
.btn-primary:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.08), 0 2px 4px rgba(0,0,0,0.04); }
.btn-secondary {
@apply inline-flex items-center justify-center gap-2 px-5 py-2.5
bg-white text-gray-700 text-sm font-medium rounded-lg
border border-gray-300 hover:bg-gray-50 active:bg-gray-100 active:scale-95
transition-all duration-200
disabled:opacity-50 disabled:cursor-not-allowed disabled:active:scale-100;
}
.btn-danger {
@apply inline-flex items-center justify-center gap-2 px-5 py-2.5
bg-white text-red-600 text-sm font-medium rounded-lg
border border-red-300 hover:bg-red-50 active:bg-red-100 active:scale-95
transition-all duration-200
disabled:opacity-50 disabled:cursor-not-allowed disabled:active:scale-100;
}
.btn-ghost {
@apply inline-flex items-center justify-center gap-2 px-4 py-2
text-gray-500 text-sm font-medium rounded-lg
hover:bg-gray-100 hover:text-gray-700 active:scale-95
transition-all duration-150;
}
/* ================================================================
响应式工具类
================================================================ */
/* 仅移动端/小屏显示(< lg 时可见,>= lg 隐藏) */
.mobile-only {
@apply block;
}
@media (min-width: 1024px) {
.mobile-only { display: none !important; }
}
/* 仅桌面端显示(>= lg 时可见,< lg 隐藏) */
.desktop-only {
display: none !important;
}
@media (min-width: 1024px) {
.desktop-only { display: block !important; }
}
/* 触控友好区域:最小 44x44px(满足 WCAG 触控目标) */
.touch-target {
min-width: 44px;
min-height: 44px;
display: inline-flex;
align-items: center;
justify-content: center;
touch-action: manipulation;
}
/* ===== 页面标题 ===== */
.page-title {
@apply text-lg md:text-xl font-bold text-gray-900 flex items-center gap-3 mb-5;
}
.page-title::before {
content: '';
@apply w-1 h-6 rounded-full shrink-0;
background: linear-gradient(180deg, #3b82f6, #2563eb);
}
/* ===== 卡片 ===== */
.card {
@apply bg-white rounded-xl border border-gray-200 transition-all duration-200;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04);
}
.card-hover:hover {
box-shadow: 0 4px 12px rgba(0,0,0,0.08), 0 2px 4px rgba(0,0,0,0.04);
@apply border-gray-300;
}
/* ===== 玻璃态卡片 ===== */
.glass-card {
@apply bg-white/70 backdrop-blur-xl border border-white/40 rounded-xl;
box-shadow: 0 4px 24px rgba(0,0,0,0.06), 0 1px 4px rgba(0,0,0,0.04);
}
.glass-card:hover {
@apply bg-white/85 border-white/60;
box-shadow: 0 8px 32px rgba(0,0,0,0.1), 0 2px 8px rgba(0,0,0,0.06);
}
/* ===== 增强玻璃态卡片(更明显的磨砂 + 彩色边框光晕)===== */
.glass-card-enhanced {
@apply bg-white/60 backdrop-blur-2xl border border-white/50 rounded-2xl;
box-shadow:
0 4px 24px rgba(0,0,0,0.08),
0 1px 4px rgba(0,0,0,0.04),
inset 0 1px 0 rgba(255,255,255,0.6);
transition: all 0.4s cubic-bezier(.34,1.56,.64,1);
}
.glass-card-enhanced:hover {
@apply bg-white/80 border-white/70;
box-shadow:
0 12px 40px rgba(0,0,0,0.12),
0 4px 16px rgba(0,0,0,0.08),
inset 0 1px 0 rgba(255,255,255,0.8);
transform: translateY(-2px);
}
/* ===== 多层阴影卡片(更深的深度感)===== */
.elevated-card {
@apply bg-white rounded-xl border border-gray-100 transition-all duration-300;
box-shadow:
0 1px 2px rgba(0,0,0,0.04),
0 2px 4px rgba(0,0,0,0.04),
0 4px 8px rgba(0,0,0,0.04),
0 8px 16px rgba(0,0,0,0.04);
}
.elevated-card:hover {
box-shadow:
0 2px 4px rgba(0,0,0,0.06),
0 4px 8px rgba(0,0,0,0.06),
0 8px 16px rgba(0,0,0,0.06),
0 16px 32px rgba(0,0,0,0.08);
transform: translateY(-3px);
@apply border-gray-200;
}
.elevated-card:active {
transform: translateY(-1px);
box-shadow:
0 1px 2px rgba(0,0,0,0.04),
0 2px 4px rgba(0,0,0,0.04),
0 4px 8px rgba(0,0,0,0.04);
}
/* ===== 渐变文字工具类 ===== */
.gradient-text {
@apply bg-clip-text text-transparent;
background-image: linear-gradient(135deg, var(--tw-gradient-from, #3b82f6), var(--tw-gradient-to, #8b5cf6));
}
[data-theme="emerald"] .gradient-text { background-image: linear-gradient(135deg, #10b981, #059669, #047857); }
[data-theme="cyan"] .gradient-text { background-image: linear-gradient(135deg, #06b6d4, #0891b2, #0e7490); }
/* ===== 语义色彩标记(信息面板)===== */
.info-panel-info { @apply border-l-4 border-medical-500 bg-medical-50; }
.info-panel-success { @apply border-l-4 border-green-500 bg-green-50; }
.info-panel-warning { @apply border-l-4 border-amber-500 bg-amber-50; }
.info-panel-error { @apply border-l-4 border-red-500 bg-red-50; }
/* ===== 渐变分隔线 ===== */
.section-divider {
@apply relative h-px my-8;
background: linear-gradient(90deg, transparent, #d1d5db 20%, #d1d5db 80%, transparent);
}
.section-divider-accent {
@apply section-divider;
background: linear-gradient(90deg, transparent, var(--tw-gradient-from, #3b82f6) 20%, var(--tw-gradient-from, #3b82f6) 80%, transparent);
}
[data-theme="emerald"] .section-divider-accent { background: linear-gradient(90deg, transparent, #10b981 20%, #10b981 80%, transparent); }
[data-theme="cyan"] .section-divider-accent { background: linear-gradient(90deg, transparent, #06b6d4 20%, #06b6d4 80%, transparent); }
/* ===== 输入框 ===== */
.input-field {
@apply w-full px-3 py-2 text-sm border border-gray-300 rounded-lg
focus:outline-none focus:ring-2 focus:ring-medical-500 focus:border-transparent
placeholder:text-gray-400 transition-all duration-200 bg-white;
}
.input-field:focus {
box-shadow: 0 0 0 3px rgba(59,130,246,0.12), 0 0 8px rgba(59,130,246,0.06);
}
[data-theme="emerald"] .input-field:focus { box-shadow: 0 0 0 3px rgba(16,185,129,0.12), 0 0 8px rgba(16,185,129,0.06); }
[data-theme="cyan"] .input-field:focus { box-shadow: 0 0 0 3px rgba(6,182,212,0.12), 0 0 8px rgba(6,182,212,0.06); }
.label-text { @apply block text-sm font-medium text-gray-700 mb-1; }
/* ===== Floating Label(浮动标签)===== */
.float-label-group {
@apply relative mb-4;
}
.float-label-input {
@apply w-full px-3 pt-5 pb-2 text-sm border border-gray-300 rounded-lg
bg-white
focus:outline-none focus:ring-2 focus:ring-medical-500 focus:border-transparent
placeholder-transparent transition-all duration-200;
}
.float-label-input.error {
@apply border-red-400 focus:ring-red-400;
}
.float-label-text {
@apply absolute left-3 text-gray-400 text-sm transition-all duration-200 pointer-events-none;
top: 50%;
transform: translateY(-50%);
}
.float-label-input:focus ~ .float-label-text,
.float-label-input:not(:placeholder-shown) ~ .float-label-text,
.float-label-text.active {
top: 0.5rem;
transform: translateY(0);
font-size: 0.65rem;
@apply text-medical-600;
}
.float-label-input.error ~ .float-label-text {
@apply text-red-500;
}
.float-label-error {
@apply mt-1 text-xs text-red-500 flex items-center gap-1;
animation: fade-in-up 0.2s ease-out;
}
/* ===== Password Toggle(密码可见切换)===== */
.password-toggle-btn {
@apply absolute right-2 top-1/2 -translate-y-1/2 p-1.5
text-gray-400 hover:text-gray-600
rounded-md transition-colors duration-150
min-h-[32px] min-w-[32px] flex items-center justify-center;
}
/* ===== Validation Icon(验证状态图标)===== */
.validation-icon {
@apply absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none;
}
.validation-icon-error {
@apply text-red-400;
animation: validation-pop-in 0.25s cubic-bezier(.34,1.56,.64,1);
}
.validation-icon-success {
@apply text-green-500;
animation: validation-pop-in 0.25s cubic-bezier(.34,1.56,.64,1);
}
/* ===== Badge ===== */
.badge { @apply inline-flex items-center text-xs px-2 py-0.5 rounded-full font-medium; }
.badge-success { @apply badge bg-green-100 text-green-700; }
.badge-warning { @apply badge bg-amber-100 text-amber-700; }
.badge-danger { @apply badge bg-red-100 text-red-700; }
.badge-info { @apply badge bg-medical-100 text-medical-700; }
.badge-neutral { @apply badge bg-gray-100 text-gray-600; }
[data-theme="emerald"] .badge-info { @apply bg-green-100 text-green-700; }
[data-theme="cyan"] .badge-info { @apply bg-cyan-100 text-cyan-700; }
/* ===== Trend Arrow(趋势箭头)===== */
.trend-arrow {
@apply inline-flex items-center gap-0.5 text-xs font-medium;
animation: trend-bounce-in 0.5s cubic-bezier(.34,1.56,.64,1) 0.3s both;
}
.trend-arrow-up { @apply text-green-600; }
.trend-arrow-down { @apply text-red-500; }
.trend-arrow-neutral { @apply text-gray-400; }
.trend-arrow svg {
animation: trend-arrow-pop 0.4s ease-out 0.5s both;
}
/* ===== Tag ===== */
.tag {
@apply inline-flex items-center gap-1 text-xs px-2.5 py-1 rounded-md font-medium
bg-gray-100 text-gray-600 border border-gray-200 transition-all duration-200;
}
.tag-active { @apply tag bg-medical-100 text-medical-700 border-medical-300; }
.tag-removable {
@apply tag cursor-pointer hover:bg-red-50 hover:text-red-600 hover:border-red-200;
}
/* ===== 空状态 ===== */
.empty-state { @apply flex flex-col items-center justify-center py-16 px-4 text-center; }
.empty-state-icon {
@apply w-16 h-16 mx-auto mb-4 text-gray-300;
animation: float 3s ease-in-out infinite;
}
.empty-state-title { @apply text-sm font-medium text-gray-500 mb-1; }
.empty-state-desc { @apply text-xs text-gray-400 mb-4 max-w-xs; }
/* ===== Spinner ===== */
.spinner {
@apply border-4 border-gray-200 rounded-full animate-spin;
border-top-color: #3b82f6;
}
[data-theme="emerald"] .spinner { border-top-color: #059669; }
[data-theme="cyan"] .spinner { border-top-color: #0891b2; }
/* ===== Loading Dots ===== */
.loading-dots { @apply inline-flex items-center gap-1; }
.loading-dots span {
@apply w-2 h-2 rounded-full bg-gray-400;
animation: dot-bounce 1.4s ease-in-out infinite both;
}
.loading-dots span:nth-child(1) { animation-delay: -0.32s; }
.loading-dots span:nth-child(2) { animation-delay: -0.16s; }
.loading-dots span:nth-child(3) { animation-delay: 0s; }
/* ===== 进度条 ===== */
.progress-bar {
@apply w-full h-2 bg-gray-100 rounded-full overflow-hidden;
}
.progress-bar-fill {
@apply h-full rounded-full transition-all duration-500 ease-out;
background: linear-gradient(90deg, #3b82f6, #2563eb);
animation: progress-grow 0.8s ease-out;
}
[data-theme="emerald"] .progress-bar-fill { background: linear-gradient(90deg, #10b981, #059669); }
[data-theme="cyan"] .progress-bar-fill { background: linear-gradient(90deg, #06b6d4, #0891b2); }
/* ===== Tooltip ===== */
.tooltip-wrapper { @apply relative inline-flex; }
.tooltip-content {
@apply absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2.5 py-1.5
bg-gray-900 text-white text-xs rounded-lg whitespace-nowrap
opacity-0 pointer-events-none transition-opacity duration-200 z-50;
}
.tooltip-wrapper:hover .tooltip-content { @apply opacity-100; }
.tooltip-content::after {
content: '';
@apply absolute top-full left-1/2 -translate-x-1/2
border-4 border-transparent border-t-gray-900;
}
/* ===== Toggle Switch ===== */
.toggle-switch {
@apply relative inline-flex w-11 h-6 rounded-full transition-colors duration-200
bg-gray-300 cursor-pointer;
}
.toggle-switch.active { @apply bg-medical-600; }
[data-theme="emerald"] .toggle-switch.active { @apply bg-emerald-600; }
[data-theme="cyan"] .toggle-switch.active { @apply bg-cyan-600; }
.toggle-switch::after {
content: '';
@apply absolute top-0.5 left-0.5 w-5 h-5 rounded-full bg-white shadow-sm
transition-transform duration-200;
}
.toggle-switch.active::after { transform: translateX(20px); }
/* ===== 成功勾选动画 ===== */
.checkmark-animated {
stroke-dasharray: 50;
stroke-dashoffset: 50;
animation: checkmark-draw 0.5s ease-out forwards;
}
/* ===== 分隔线 ===== */
.divider-animated {
@apply relative h-px bg-gray-200 my-6 overflow-hidden;
}
.divider-animated::after {
content: '';
@apply absolute inset-0;
background: linear-gradient(90deg, transparent, #3b82f6, transparent);
animation: divider-slide 2s ease-in-out infinite;
}
/* ===== 可拖拽手柄 ===== */
.drag-handle {
@apply cursor-grab active:cursor-grabbing text-gray-300 hover:text-gray-500
transition-colors duration-150;
}
/* ===== 心形动画 ===== */
.heart-icon {
@apply transition-all duration-300 cursor-pointer;
}
.heart-icon.liked {
@apply text-red-500 scale-110;
animation: heart-beat 0.4s ease-out;
}
}
/* ================================================================
Utilities
================================================================ */
@layer utilities {
/* ===== 移动端紧凑间距工具类 ===== */
.p-mobile { padding: 0.75rem; } /* 12px - 移动端紧凑内边距 */
.px-mobile { padding-left: 0.75rem; padding-right: 0.75rem; }
.py-mobile { padding-top: 0.75rem; padding-bottom: 0.75rem; }
.gap-mobile { gap: 0.5rem; } /* 8px - 移动端紧凑间距 */
.text-mobile { font-size: 0.8125rem; } /* 13px - 移动端略小字号 */
/* 移动端全宽(突破父容器 padding 限制) */
.mobile-full-bleed {
margin-left: -0.75rem;
margin-right: -0.75rem;
width: calc(100% + 1.5rem);
}
.skeleton {
@apply bg-gray-200 rounded;
animation: shimmer 2s ease-in-out infinite;
background-image: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.6) 50%, transparent 100%);
background-size: 200% 100%;
}
.glass {
@apply bg-white/80 backdrop-blur-md border-b border-gray-200/60;
}
/* 页面进入 */
.animate-fade-in { animation: fade-in-up 0.4s ease-out; }
.animate-fade-in-up { opacity: 0; animation: fade-in-up 0.5s ease-out forwards; }
.animate-scale-in { animation: scale-in 0.25s cubic-bezier(.16,1,.3,1); }
.animate-slide-in { animation: slide-in 0.4s ease-out; }
.animate-fade-in-left { opacity: 0; animation: fade-in-left 0.4s ease-out forwards; }
/* 卡片悬停浮升 */
.animate-lift {
transition: transform 0.3s cubic-bezier(.34,1.56,.64,1), box-shadow 0.3s ease;
}
.animate-lift:hover {
transform: translateY(-4px);
box-shadow: 0 12px 24px rgba(0,0,0,0.1), 0 4px 8px rgba(0,0,0,0.06);
}
/* 推荐替代: hover-lift(更自然的浮升 + 光晕) */
.hover-lift {
transition: transform 0.35s cubic-bezier(.34,1.56,.64,1), box-shadow 0.35s ease, border-color 0.35s ease;
will-change: transform;
}
.hover-lift:hover {
transform: translateY(-6px);
box-shadow: 0 16px 32px rgba(0,0,0,0.12), 0 6px 12px rgba(0,0,0,0.06);
}
/* 按压缩放反馈 */
.press-scale {
transition: transform 0.15s cubic-bezier(.34,1.56,.64,1);
}
.press-scale:active {
transform: scale(0.94);
}
.press-scale-sm:active {
transform: scale(0.97);
}
/* 改进波纹效果(JavaScript 驱动的点击波纹) */
.ripple-container {
position: relative;
overflow: hidden;
-webkit-mask-image: -webkit-radial-gradient(white, black);
}
.ripple-effect {
position: absolute;
border-radius: 50%;
background: rgba(255,255,255,0.35);
transform: scale(0);
animation: ripple-expand 0.6s ease-out forwards;
pointer-events: none;
}
/* 改进脉冲点(状态指示) */
.pulse-dot {
@apply relative inline-flex;
}
.pulse-dot::before {
content: '';
@apply absolute inset-0 rounded-full;
animation: pulse-dot-ring 2s ease-in-out infinite;
}
.pulse-dot-online::before { background: rgba(16,185,129,0.4); }
.pulse-dot-busy::before { background: rgba(245,158,11,0.4); }
.pulse-dot-offline::before { background: rgba(156,163,175,0.4); }
/* 改进骨架屏闪烁 */
.skeleton-shimmer {
@apply bg-gray-200 rounded;
background-image: linear-gradient(
110deg,
#e5e7eb 8%,
#f3f4f6 18%,
#e5e7eb 33%
);
background-size: 200% 100%;
animation: skeleton-shimmer-wave 1.8s ease-in-out infinite;
}
/* 文字揭示动画(逐字或整段出现) */
.text-reveal {
opacity: 0;
animation: text-reveal-fade 0.6s ease-out forwards;
}
.text-reveal-up {
opacity: 0;
transform: translateY(12px);
animation: text-reveal-up 0.6s ease-out forwards;
}
.text-reveal-stagger > * {
opacity: 0;
animation: text-reveal-fade 0.5s ease-out forwards;
}
.text-reveal-stagger > *:nth-child(1) { animation-delay: 0.05s; }
.text-reveal-stagger > *:nth-child(2) { animation-delay: 0.15s; }
.text-reveal-stagger > *:nth-child(3) { animation-delay: 0.25s; }
.text-reveal-stagger > *:nth-child(4) { animation-delay: 0.35s; }
.text-reveal-stagger > *:nth-child(5) { animation-delay: 0.45s; }
/* 打字光标闪烁增强 */
.typing-cursor::after {
content: '|';
display: inline-block;
margin-left: 1px;
animation: typing-blink 0.8s step-end infinite;
@apply text-medical-500;
font-weight: 300;
}
[data-theme="emerald"] .typing-cursor::after { color: #10b981; }
[data-theme="cyan"] .typing-cursor::after { color: #06b6d4; }
/* Stagger */
.stagger-fade > * { opacity: 0; animation: fade-in-up 0.4s ease-out forwards; }
.stagger-fade > *:nth-child(1) { animation-delay: 0.05s; }
.stagger-fade > *:nth-child(2) { animation-delay: 0.1s; }
.stagger-fade > *:nth-child(3) { animation-delay: 0.15s; }
.stagger-fade > *:nth-child(4) { animation-delay: 0.2s; }
.stagger-fade > *:nth-child(5) { animation-delay: 0.25s; }
.stagger-fade > *:nth-child(6) { animation-delay: 0.3s; }
.stagger-fade > *:nth-child(7) { animation-delay: 0.35s; }
.stagger-fade > *:nth-child(8) { animation-delay: 0.4s; }
/* 数字 */
.animate-number { animation: number-pop 0.4s cubic-bezier(.34,1.56,.64,1); }
/* 脉冲 */
.animate-glow-pulse { animation: glow-pulse 2s ease-in-out infinite; }
.animate-ring-pulse { animation: ring-pulse 1.5s ease-in-out infinite; }
/* 渐变 */
.animate-gradient-flow { background-size: 300% 300%; animation: gradient-flow 6s ease infinite; }
.text-gradient {
@apply bg-clip-text text-transparent;
background-image: linear-gradient(135deg, #3b82f6, #8b5cf6, #3b82f6);
background-size: 200% auto;
animation: gradient-flow 4s ease infinite;
}
/* 浮动 */
.animate-float { animation: float 3s ease-in-out infinite; }
/* 抖动 */
.animate-shake { animation: shake 0.5s ease-in-out; }
/* 登录表单 shake */
.animate-login-shake { animation: login-shake 0.5s ease-in-out; }
/* 弹入 */
.animate-bounce-in { animation: bounce-in 0.6s cubic-bezier(.34,1.56,.64,1); }
/* 旋转 */
.animate-spin-in { animation: spin-in 0.5s cubic-bezier(.34,1.2,.64,1); }
.animate-spin-slow { animation: spin 3s linear infinite; }
/* 波纹 */
.animate-ripple {
position: relative;
overflow: hidden;
}
.animate-ripple::after {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(circle, rgba(255,255,255,0.4) 10%, transparent 10%);
background-position: center;
background-repeat: no-repeat;
opacity: 0;
transition: opacity 0.3s, transform 0.5s;
transform: scale(10);
pointer-events: none;
}
.animate-ripple:active::after {
opacity: 1;
transform: scale(0);
transition: 0s;
}
/* 骨架 */
.skeleton-pulse { animation: skeleton-pulse 1.8s ease-in-out infinite; }
/* 打字效果 */
.typewriter {
overflow: hidden;
white-space: nowrap;
border-right: 2px solid #3b82f6;
animation: typing 3s steps(40) 1s forwards, blink-caret 0.75s step-end infinite;
}
/* 淡入淡出切换 */
.crossfade-enter { animation: crossfade-in 0.3s ease-out; }
.crossfade-exit { animation: crossfade-out 0.2s ease-in forwards; }
/* 搜索框展开 */
.search-expand {
width: 40px;
transition: width 0.4s cubic-bezier(.34,1.56,.64,1);
}
.search-expand:focus { width: 200px; }
/* 滚动揭示 */
.reveal-on-scroll {
opacity: 0;
transform: translateY(24px);
transition: opacity 0.6s ease-out, transform 0.6s ease-out;
}
.reveal-on-scroll.visible {
opacity: 1;
transform: translateY(0);
}
/* 通知红点 */
.notify-dot {
@apply absolute -top-0.5 -right-0.5 w-2.5 h-2.5 bg-red-500 rounded-full border-2 border-white;
animation: notification-bounce 0.4s ease-out, pulse-dot 2s ease-in-out 0.4s infinite;
}
.notify-dot-sm {
@apply absolute top-0 right-0 w-2 h-2 bg-red-500 rounded-full border border-white;
animation: notification-bounce 0.3s ease-out;
}
/* 高亮闪烁 */
.highlight-flash { animation: highlight-flash 1.5s ease-out; }
/* 页面阶段过渡 */
.stage-transition-enter { animation: stage-enter 0.35s ease-out; }
.stage-transition-exit { animation: stage-exit 0.25s ease-in forwards; }
/* 折叠面板 */
.collapsible-open { animation: collapse-open 0.4s ease-out forwards; }
.collapsible-close { animation: collapse-close 0.3s ease-in forwards; }
/* 计数徽章 */
.count-badge {
@apply inline-flex items-center justify-center min-w-[20px] h-5 px-1.5
bg-red-500 text-white text-[10px] font-bold rounded-full;
animation: count-badge 0.4s cubic-bezier(.34,1.56,.64,1);
}
/* 下拉刷新指示器 */
.pull-indicator {
animation: pull-indicator 0.3s ease-out;
}
/* Toast 入场 */
.toast-enter { animation: toast-enter 0.35s cubic-bezier(.34,1.2,.64,1); }
.toast-exit { animation: toast-exit 0.25s ease-in forwards; }
/* 自动保存状态点 */
.autosave-dot {
@apply inline-flex items-center gap-1.5 text-xs text-gray-400;
}
.autosave-dot::before {
content: '';
@apply w-2 h-2 rounded-full bg-green-400;
animation: auto-save-dot 1.5s ease-in-out infinite;
}
.autosave-dot.saving::before { @apply bg-amber-400; animation-duration: 0.6s; }
.autosave-dot.saved::before { @apply bg-green-500; animation: none; }
/* 菜单弹出 */
.menu-popup { animation: menu-slide-in 0.2s ease-out; }
/* 拖拽排序手柄 */
.sortable-ghost { @apply opacity-30 bg-gray-100 rounded-lg; }
.sortable-chosen { @apply shadow-lg ring-2 ring-medical-500/30; }
.sortable-drag { @apply opacity-80 rotate-2 scale-105 shadow-2xl; }
/* 骨架卡片 */
.skeleton-card {
@apply bg-white rounded-xl border border-gray-100 p-4;
animation: skeleton-pulse 1.8s ease-in-out infinite;
}
.skeleton-card-line {
@apply h-3 rounded bg-gray-200;
background-image: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.5) 50%, transparent 100%);
background-size: 200% 100%;
animation: skeleton-wave 1.5s ease-in-out infinite;
}
/* 下拉菜单项 */
.dropdown-item {
@apply w-full text-left px-3 py-2 text-sm text-gray-700
hover:bg-gray-50 hover:text-gray-900 transition-colors duration-150
flex items-center gap-2 rounded-md;
}
.dropdown-item-danger {
@apply dropdown-item text-red-600 hover:bg-red-50 hover:text-red-700;
}
/* 标签输入 */
.tag-input-wrapper {
@apply flex flex-wrap items-center gap-1.5 p-1.5 min-h-[42px]
border border-gray-300 rounded-lg bg-white
focus-within:ring-2 focus-within:ring-medical-500 focus-within:border-transparent
transition-all duration-200;
}
.tag-input-wrapper:focus-within {
box-shadow: 0 0 0 3px rgba(59,130,246,0.12);
}
/* 步骤连接线 */
.step-connector {
@apply relative flex-1 h-0.5 bg-gray-200 mx-2;
}
.step-connector.active {
background: linear-gradient(90deg, #3b82f6, #2563eb);
animation: status-flow 0.6s ease-out;
}
[data-theme="emerald"] .step-connector.active { background: linear-gradient(90deg, #10b981, #059669); }
[data-theme="cyan"] .step-connector.active { background: linear-gradient(90deg, #06b6d4, #0891b2); }
/* 时间线节点 */
.timeline-dot {
@apply relative w-3.5 h-3.5 rounded-full border-2 border-medical-500 bg-white z-10 shrink-0;
box-shadow: 0 0 0 3px rgba(59,130,246,0.12);
}
.timeline-dot.active {
@apply bg-medical-500 border-medical-600;
animation: pulse-ring 2s ease-in-out infinite;
}
.timeline-line {
@apply absolute left-[6px] top-3.5 bottom-0 w-0.5 bg-gray-200;
}
.timeline-line.active {
background: linear-gradient(to bottom, #3b82f6, #2563eb);
}
/* 数据表格行 hover */
.table-row-hover { @apply transition-all duration-150; }
.table-row-hover:hover { @apply bg-medical-50/50 -translate-y-px shadow-sm relative z-10; }
/* 空状态增强 */
.empty-state-animated .empty-state-icon {
animation: float 3s ease-in-out infinite, shimmer 3s ease-in-out infinite;
}
/* 彩色顶部条变体 */
.card-accent-blue { border-top: 3px solid #3b82f6; }
.card-accent-green { border-top: 3px solid #10b981; }
.card-accent-amber { border-top: 3px solid #f59e0b; }
.card-accent-red { border-top: 3px solid #ef4444; }
.card-accent-purple { border-top: 3px solid #8b5cf6; }
/* 呼吸脉冲按钮 */
.animate-breathing { animation: breathing 2.5s ease-in-out infinite; }
/* 统计卡片辉光悬停 */
.stat-card-glow { transition: box-shadow 0.4s ease, transform 0.3s ease; }
.stat-card-glow:hover {
transform: translateY(-3px);
animation: stat-glow 2s ease-in-out infinite;
}
/* 数字弹跳 */
.animate-count-pop { animation: count-pop 0.5s cubic-bezier(.34,1.56,.64,1); }
/* 状态脉冲 */
.animate-status-pulse { animation: status-pulse 0.5s ease-out; }
/* 字段聚焦 */
.animate-field-ripple:focus { animation: field-ripple 0.6s ease-out; }
/* 面板弹入 */
.animate-panel-up { animation: panel-slide-up 0.4s cubic-bezier(.16,1,.3,1); }
/* 缩放呼吸 */
.animate-scale-breathing { animation: scale-breathing 3s ease-in-out infinite; }
/* 筛选芯片 */
.chip-active {
@apply bg-medical-600 text-white border-medical-600;
animation: chip-pop 0.3s cubic-bezier(.34,1.2,.64,1);
}
/* 状态指示点 */
.status-dot {
@apply inline-block w-2 h-2 rounded-full;
}
.status-dot.live { @apply bg-green-500; animation: status-dot-pulse 2s ease-in-out infinite; }
.status-dot.warn { @apply bg-amber-500; animation: status-dot-pulse 3s ease-in-out infinite; }
.status-dot.idle { @apply bg-gray-300; }
/* 进度条条纹 */
.progress-striped {
background-image: linear-gradient(45deg, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent);
background-size: 1rem 1rem;
animation: progress-stripes 1s linear infinite;
}
/* 不确定进度条(条纹流动,用于无确切进度的下载场景) */
.animate-indeterminate-progress {
background-size: 200% 100% !important;
animation: indeterminate-progress 1.8s ease-in-out infinite;
}
/* 上传头像光环 */
.avatar-upload-ring {
@apply absolute inset-0 rounded-full;
border: 2px dashed theme('colors.medical.500');
animation: avatar-ring-rotate 8s linear infinite;
}
/* 数值高亮 */
.value-highlight { animation: value-highlight 0.6s ease-out; }
/* 徽章摆动 */
.animate-badge-swing { animation: badge-swing 0.6s ease-in-out; }
/* 库存告警脉冲 */
.stock-alert-pulse { animation: low-stock-pulse 2s ease-in-out infinite; }
/* ===== 触觉视觉反馈 ===== */
.haptic-feedback:active {
animation: haptic-flash 0.4s ease-out;
--haptic-color: rgba(59,130,246,0.35);
}
[data-theme="emerald"] .haptic-feedback:active { --haptic-color: rgba(16,185,129,0.35); }
[data-theme="cyan"] .haptic-feedback:active { --haptic-color: rgba(6,182,212,0.35); }
/* ===== 进度环 SVG 组件 ===== */
.progress-ring {
transform: rotate(-90deg);
}
.progress-ring-circle-bg {
fill: none;
stroke: #e5e7eb;
}
.progress-ring-circle-fill {
fill: none;
stroke: #3b82f6;
stroke-linecap: round;
transition: stroke-dashoffset 0.6s ease-out;
}
[data-theme="emerald"] .progress-ring-circle-fill { stroke: #10b981; }
[data-theme="cyan"] .progress-ring-circle-fill { stroke: #06b6d4; }
/* ===== 空状态插图增强 ===== */
.empty-state-illustration {
@apply flex flex-col items-center justify-center py-16 px-4 text-center;
}
.empty-state-illustration-icon {
@apply w-20 h-20 mx-auto mb-5;
animation: float 3s ease-in-out infinite;
opacity: 0.6;
}
.empty-state-illustration-title {
@apply text-base font-semibold text-gray-600 mb-2;
}
.empty-state-illustration-desc {
@apply text-sm text-gray-400 mb-6 max-w-sm;
}
/* ===== #7 表格行交互增强 ===== */
/* 斑马条纹 */
.table-zebra tbody tr:nth-child(even) {
@apply bg-gray-50/80;
}
.table-zebra tbody tr:nth-child(odd) {
@apply bg-white;
}
/* 行 hover 高亮 + 轻微 scale */
.table-hover-row tbody tr {
@apply transition-all duration-200;
}
.table-hover-row tbody tr:hover {
@apply bg-medical-50/60 scale-[1.005] shadow-sm relative z-10;
}
.table-hover-row tbody tr:hover td:first-child {
@apply rounded-l-lg;
}
.table-hover-row tbody tr:hover td:last-child {
@apply rounded-r-lg;
}
/* 选中行左侧蓝色边框指示 */
.table-selected-row tbody tr.selected {
@apply relative bg-medical-50/70;
}
.table-selected-row tbody tr.selected::before {
content: '';
@apply absolute left-0 top-0 bottom-0 w-1 bg-medical-500 rounded-r-full;
animation: sidebar-active-indicator 0.35s ease-out;
}
/* ===== #12 Tab 内容切换动画 ===== */
.tab-content-enter {
animation: tab-crossfade-enter 0.3s ease-out;
}
.tab-content-exit {
animation: tab-crossfade-exit 0.2s ease-in forwards;
}
/* ===== #14 下拉菜单动画 ===== */
.dropdown-enter {
animation: dropdown-scale-in 0.25s cubic-bezier(.34,1.2,.64,1);
transform-origin: top;
}
.dropdown-exit {
animation: dropdown-scale-out 0.18s ease-in forwards;
transform-origin: top;
}
}
/* ================================================================
深度动画系统 (v2) — 15项升级 · 工具类
================================================================ */
/* ----- 1. float-in:元素从下方浮动进入 ----- */
.animate-float-in {
animation: float-in 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
.animate-float-in-delay-1 { animation-delay: 0.1s; }
.animate-float-in-delay-2 { animation-delay: 0.2s; }
.animate-float-in-delay-3 { animation-delay: 0.3s; }
.animate-float-in-delay-4 { animation-delay: 0.4s; }
.animate-float-in-delay-5 { animation-delay: 0.5s; }
/* ----- 2. glow-border:边框发光动画 ----- */
.glow-border {
transition: box-shadow 0.3s ease;
}
.glow-border:focus,
.glow-border:focus-within {
animation: glow-border-pulse 2s ease-in-out infinite;
border-color: #3b82f6;
}
[data-theme="emerald"] .glow-border:focus,
[data-theme="emerald"] .glow-border:focus-within {
border-color: #059669;
animation: none;
box-shadow: 0 0 0 4px rgba(16,185,129,0.12), 0 0 12px rgba(16,185,129,0.25);
}
[data-theme="cyan"] .glow-border:focus,
[data-theme="cyan"] .glow-border:focus-within {
border-color: #0891b2;
animation: none;
box-shadow: 0 0 0 4px rgba(6,182,212,0.12), 0 0 12px rgba(6,182,212,0.25);
}
/* ----- 3. count-up:数字滚动动画(配合 JS 使用)----- */
.animate-count-up {
display: inline-block;
overflow: hidden;
}
.animate-count-up .count-digit {
display: inline-block;
animation: count-up-slide 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
/* ----- 4. typewriter-enhanced:增强打字机效果 ----- */
.typewriter-text {
overflow: hidden;
white-space: nowrap;
border-right: 2px solid transparent;
width: 0;
animation: typing 3s steps(40) 1s forwards;
}
.typewriter-cursor {
border-right: 2px solid #3b82f6;
}
[data-theme="emerald"] .typewriter-cursor { border-right-color: #10b981; }
[data-theme="cyan"] .typewriter-cursor { border-right-color: #06b6d4; }
/* 打字完成后的闪烁光标 */
.typewriter-cursor::after {
content: '|';
display: inline-block;
margin-left: 1px;
animation: typewriter-cursor-blink 0.8s step-end infinite;
color: #3b82f6;
font-weight: 300;
}
[data-theme="emerald"] .typewriter-cursor::after { color: #10b981; }
[data-theme="cyan"] .typewriter-cursor::after { color: #06b6d4; }
/* 多行打字:移除 nowrap,逐段显示 */
.typewriter-text.multiline {
white-space: normal;
width: auto;
animation: none;
}
/* ----- 5. spotlight:聚光灯扫描效果 ----- */
.spotlight-card {
position: relative;
overflow: hidden;
--mouse-x: 50%;
--mouse-y: 50%;
}
.spotlight-card::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(
600px circle at var(--mouse-x) var(--mouse-y),
rgba(59,130,246,0.12),
transparent 50%
);
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
z-index: 1;
}
[data-theme="emerald"] .spotlight-card::before {
background: radial-gradient(600px circle at var(--mouse-x) var(--mouse-y), rgba(16,185,129,0.12), transparent 50%);
}
[data-theme="cyan"] .spotlight-card::before {
background: radial-gradient(600px circle at var(--mouse-x) var(--mouse-y), rgba(6,182,212,0.12), transparent 50%);
}
.spotlight-card:hover::before {
opacity: 1;
}
/* ----- 6. morph-bg:渐变背景流动 ----- */
.morph-bg {
background: linear-gradient(270deg, #3b82f6, #8b5cf6, #ec4899, #f59e0b, #3b82f6);
background-size: 400% 400%;
animation: morph-bg-flow 12s ease infinite;
}
[data-theme="emerald"] .morph-bg {
background: linear-gradient(270deg, #10b981, #059669, #047857, #34d399, #10b981);
background-size: 400% 400%;
}
[data-theme="cyan"] .morph-bg {
background: linear-gradient(270deg, #06b6d4, #0891b2, #0e7490, #22d3ee, #06b6d4);
background-size: 400% 400%;
}
/* ----- 7. breathe-scale:呼吸缩放 ----- */
.breathe {
animation: breathe-scale 2s ease-in-out infinite;
will-change: transform;
}
.breathe-slow {
animation: breathe-scale-slow 3.5s ease-in-out infinite;
will-change: transform;
}
/* ----- 8. stagger-list:列表逐项延迟出现 ----- */
.stagger-list > .stagger-item {
opacity: 0;
animation: stagger-item-in 0.4s ease-out forwards;
}
.stagger-list > .stagger-item:nth-child(1) { animation-delay: 0.05s; }
.stagger-list > .stagger-item:nth-child(2) { animation-delay: 0.1s; }
.stagger-list > .stagger-item:nth-child(3) { animation-delay: 0.15s; }
.stagger-list > .stagger-item:nth-child(4) { animation-delay: 0.2s; }
.stagger-list > .stagger-item:nth-child(5) { animation-delay: 0.25s; }
.stagger-list > .stagger-item:nth-child(6) { animation-delay: 0.3s; }
.stagger-list > .stagger-item:nth-child(7) { animation-delay: 0.35s; }
.stagger-list > .stagger-item:nth-child(8) { animation-delay: 0.4s; }
.stagger-list > .stagger-item:nth-child(9) { animation-delay: 0.45s; }
.stagger-list > .stagger-item:nth-child(10) { animation-delay: 0.5s; }
/* ----- 9. draw-underline:下划线绘制动画 ----- */
.draw-underline {
position: relative;
text-decoration: none;
cursor: pointer;
}
.draw-underline::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
width: 100%;
height: 2px;
background: #3b82f6;
border-radius: 1px;
transform: scaleX(0);
transform-origin: left center;
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
[data-theme="emerald"] .draw-underline::after { background: #10b981; }
[data-theme="cyan"] .draw-underline::after { background: #06b6d4; }
.draw-underline:hover::after,
.draw-underline.active::after {
transform: scaleX(1);
}
/* 从中心展开的变体 */
.draw-underline-center::after {
transform-origin: center center;
}
/* ----- 10. flip-card:3D 卡片翻转 ----- */
.flip-card {
perspective: 1000px;
width: 100%;
height: 100%;
}
.flip-card-inner {
position: relative;
width: 100%;
height: 100%;
transition: transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
transform-style: preserve-3d;
}
.flip-card:hover .flip-card-inner,
.flip-card.flipped .flip-card-inner {
transform: perspective(800px) rotateY(180deg);
}
.flip-card-front,
.flip-card-back {
position: absolute;
inset: 0;
backface-visibility: hidden;
border-radius: inherit;
}
.flip-card-front {
z-index: 2;
}
.flip-card-back {
transform: rotateY(180deg);
z-index: 1;
}
/* ----- 11. wave-ripple:水波纹扩散(配合 JS 使用)----- */
.wave-ripple {
position: relative;
overflow: hidden;
-webkit-mask-image: -webkit-radial-gradient(white, black);
}
.wave-ripple .ripple-circle {
position: absolute;
border-radius: 50%;
border: 1px solid rgba(59,130,246,0.3);
background: rgba(59,130,246,0.1);
animation: wave-ripple-expand 0.8s ease-out forwards;
pointer-events: none;
}
[data-theme="emerald"] .wave-ripple .ripple-circle {
border-color: rgba(16,185,129,0.3);
background: rgba(16,185,129,0.1);
}
[data-theme="cyan"] .wave-ripple .ripple-circle {
border-color: rgba(6,182,212,0.3);
background: rgba(6,182,212,0.1);
}
/* ----- 12. elastic-bounce:弹性弹跳 ----- */
.elastic-bounce {
animation: elastic-bounce 1.2s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
/* ----- 13. dissolve-out:烟雾消散 ----- */
.dissolve-out {
animation: dissolve-out 0.6s ease-in forwards;
pointer-events: none;
}
/* 消散 + 收缩(用于卡片删除) */
.dissolve-out-collapse {
animation:
dissolve-out 0.5s ease-in forwards,
collapse-close 0.5s ease-in forwards;
pointer-events: none;
}
/* ----- 14. orbit-spin:轨道旋转加载 ----- */
.orbit-spinner {
position: relative;
width: 48px;
height: 48px;
display: inline-flex;
align-items: center;
justify-content: center;
}
.orbit-spinner .orbit-dot {
position: absolute;
width: 8px;
height: 8px;
border-radius: 50%;
background: #3b82f6;
animation: orbit-spin 1.5s linear infinite;
}
[data-theme="emerald"] .orbit-spinner .orbit-dot { background: #10b981; }
[data-theme="cyan"] .orbit-spinner .orbit-dot { background: #06b6d4; }
.orbit-spinner .orbit-dot:nth-child(2) {
animation-delay: -0.5s;
opacity: 0.7;
width: 6px;
height: 6px;
}
.orbit-spinner .orbit-dot:nth-child(3) {
animation-delay: -1s;
opacity: 0.4;
width: 5px;
height: 5px;
}
/* ----- 15. success-burst:成功撒花 ----- */
.success-burst {
position: relative;
width: 40px;
height: 40px;
display: inline-flex;
align-items: center;
justify-content: center;
}
/* 中心勾选标记 */
.success-burst::before {
content: '';
position: absolute;
width: 24px;
height: 24px;
border-radius: 50%;
background: #10b981;
z-index: 2;
animation: scale-in 0.3s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
/* 多条彩色射线(通过多个 box-shadow 模拟) */
.success-burst::after {
content: '';
position: absolute;
width: 4px;
height: 4px;
border-radius: 50%;
background: transparent;
z-index: 1;
animation: success-burst-rotate 2s linear infinite;
box-shadow:
/* 方向 0° (右侧) */
12px 0 0 0 #3b82f6,
16px 0 0 0 rgba(59,130,246,0.6),
20px 0 0 0 rgba(59,130,246,0.3),
/* 方向 45° */
8px 8px 0 0 #8b5cf6,
11px 11px 0 0 rgba(139,92,246,0.5),
/* 方向 90° (下方) */
0 12px 0 0 #10b981,
0 16px 0 0 rgba(16,185,129,0.6),
/* 方向 135° */
-8px 8px 0 0 #f59e0b,
-11px 11px 0 0 rgba(245,158,11,0.5),
/* 方向 180° (左侧) */
-12px 0 0 0 #ec4899,
-16px 0 0 0 rgba(236,72,153,0.5),
/* 方向 225° */
-8px -8px 0 0 #06b6d4,
-11px -11px 0 0 rgba(6,182,212,0.5),
/* 方向 270° (上方) */
0 -12px 0 0 #ef4444,
0 -16px 0 0 rgba(239,68,68,0.5),
/* 方向 315° */
8px -8px 0 0 #22c55e,
11px -11px 0 0 rgba(34,197,94,0.5);
}
/* ================================================================
深度动画系统 (v2) — Reduced Motion 适配
================================================================ */
@media (prefers-reduced-motion: reduce) {
.animate-float-in,
.animate-float-in-delay-1,
.animate-float-in-delay-2,
.animate-float-in-delay-3,
.animate-float-in-delay-4,
.animate-float-in-delay-5 {
animation: none;
opacity: 1;
transform: none;
}
.glow-border:focus,
.glow-border:focus-within {
animation: none;
}
.animate-count-up .count-digit {
animation: none;
opacity: 1;
transform: none;
}
.typewriter-text {
width: auto;
animation: none;
border-right: none;
overflow: visible;
white-space: normal;
}
.typewriter-cursor::after {
animation: none;
opacity: 1;
}
.spotlight-card::before {
display: none;
}
.morph-bg {
animation: none;
background-size: 100% 100%;
}
.breathe,
.breathe-slow {
animation: none;
}
.stagger-list > .stagger-item {
opacity: 1;
animation: none;
animation-delay: 0s;
}
.draw-underline::after {
transition: none;
}
.flip-card-inner {
transition: none;
}
.flip-card:hover .flip-card-inner,
.flip-card.flipped .flip-card-inner {
transform: none;
}
.wave-ripple .ripple-circle {
animation: none;
display: none;
}
.elastic-bounce {
animation: none;
opacity: 1;
transform: none;
}
.dissolve-out,
.dissolve-out-collapse {
animation: none;
opacity: 0;
}
.orbit-spinner .orbit-dot,
.orbit-spinner .orbit-dot:nth-child(2),
.orbit-spinner .orbit-dot:nth-child(3) {
animation: none;
}
.success-burst::after {
animation: none;
box-shadow: none;
}
}
/* ================================================================
Accessibility - Focus & Screen Reader
================================================================ */
/* 键盘焦点可见轮廓(Tailwind 的 focus-visible:ring 已在组件中使用,此处为全局兜底) */
:focus-visible {
outline: 2px solid #3b82f6;
outline-offset: 2px;
border-radius: 4px;
}
[data-theme="emerald"] :focus-visible { outline-color: #059669; }
[data-theme="cyan"] :focus-visible { outline-color: #0891b2; }
/* 对于已有关注样式(如 ring)的元素取消默认 outline */
:focus-visible:has([class*="ring-"]),
input:focus-visible,
textarea:focus-visible,
select:focus-visible,
button:focus-visible {
outline: none;
}
/* ===== 跳转到主内容(Skip to Content)===== */
.skip-to-content {
position: fixed;
top: -100px;
left: 50%;
transform: translateX(-50%);
z-index: 10000;
padding: 0.5rem 1rem;
background: #2563eb;
color: #fff;
font-size: 0.875rem;
font-weight: 500;
border-radius: 0 0 0.5rem 0.5rem;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
transition: top 0.2s;
outline: none;
}
.skip-to-content:focus {
top: 0;
}
/* ===== 仅屏幕阅读器可见 ===== */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
/* ===== 减少动画偏好 ===== */
@media (prefers-reduced-motion: reduce) {
.page-transition-wrapper *,
.page-transition-wrapper *::before,
.page-transition-wrapper *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* ================================================================
动画性能优化 - GPU 合成层 & 渲染隔离
使用 will-change / contain / translateZ 减少重绘和回流
================================================================ */
/* ---- GPU 合成层:将元素提升到独立图层,避免触发整体重绘 ---- */
.gpu-layer {
transform: translateZ(0);
backface-visibility: hidden;
perspective: 1000px;
}
/* ---- 渲染隔离:限制浏览器重绘范围 ---- */
.render-isolated {
contain: layout style paint;
}
/* ---- 仅动画期间启用 will-change(动画结束后应移除,此处提供基础类) ---- */
.will-animate-transform {
will-change: transform;
}
.will-animate-opacity {
will-change: opacity;
}
.will-animate-both {
will-change: transform, opacity;
}
/* ---- 为 Tailwind 动画类附加 GPU 优化 ---- */
/* 现有动画类在 hover/animation 时会自动获得 GPU 加速 */
/* 骨架屏动画:强制 GPU 合成层,避免 layout thrashing */
.skeleton,
.skeleton-shimmer,
.skeleton-card,
.skeleton-card-line,
.skeleton-avatar {
transform: translateZ(0);
backface-visibility: hidden;
contain: layout style paint;
}
/* 骨架屏动画优化:使用 GPU 友好的 transform 替代 background-position */
@keyframes skeleton-shimmer-gpu {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
/* 浮升动画:GPU 加速 */
.animate-lift,
.hover-lift,
.elevated-card {
transform: translateZ(0);
will-change: transform;
}
/* 缩放动画:GPU 加速 */
.press-scale,
.press-scale-sm,
.btn-primary,
.btn-secondary,
.btn-danger,
.btn-ghost {
transform: translateZ(0);
}
/* 渐变流动:GPU 合成层 */
.animate-gradient-flow,
.text-gradient {
transform: translateZ(0);
backface-visibility: hidden;
}
/* 通知点和状态指示器:GPU 合成(频繁更新) */
.notify-dot,
.notify-dot-sm,
.status-dot,
.pulse-dot,
.connection-dot {
transform: translateZ(0);
will-change: transform, opacity;
}
/* 页面过渡动画:GPU 合成 */
.page-enter,
.page-exit,
.crossfade-enter,
.crossfade-exit,
.page-transition-wrapper * {
transform: translateZ(0);
backface-visibility: hidden;
}
/* Spinner:GPU 合成旋转 */
.animate-spin,
.spinner,
.animate-spin-slow {
transform: translateZ(0);
}
/* ================================================================
Animations - Keyframes
================================================================ */
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
@keyframes fade-in-up {
0% { opacity: 0; transform: translateY(16px); }
100% { opacity: 1; transform: translateY(0); }
}
@keyframes scale-in {
0% { opacity: 0; transform: scale(0.9); }
100% { opacity: 1; transform: scale(1); }
}
@keyframes slide-in {
0% { opacity: 0; transform: translateX(24px); }
100% { opacity: 1; transform: translateX(0); }
}
@keyframes number-pop {
0% { transform: scale(0.6); opacity: 0; }
60% { transform: scale(1.15); opacity: 1; }
100% { transform: scale(1); opacity: 1; }
}
@keyframes glow-pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(59,130,246,0); }
50% { box-shadow: 0 0 20px 4px rgba(59,130,246,0.3); }
}
@keyframes gradient-flow {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
@keyframes float {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-8px); }
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 50%, 90% { transform: translateX(-4px); }
30%, 70% { transform: translateX(4px); }
}
@keyframes bounce-in {
0% { opacity: 0; transform: scale(0.3); }
50% { opacity: 1; transform: scale(1.05); }
70% { transform: scale(0.95); }
100% { transform: scale(1); }
}
@keyframes spin-in {
0% { opacity: 0; transform: rotate(-180deg) scale(0.3); }
100% { opacity: 1; transform: rotate(0deg) scale(1); }
}
@keyframes skeleton-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
@keyframes ripple-expand {
0% { width: 0; height: 0; opacity: 0.5; }
100% { width: 500px; height: 500px; opacity: 0; }
}
@keyframes ring-pulse {
0% { box-shadow: 0 0 0 0 rgba(59,130,246,0.4); }
70% { box-shadow: 0 0 0 20px rgba(59,130,246,0); }
100% { box-shadow: 0 0 0 0 rgba(59,130,246,0); }
}
@keyframes slide-up-in {
0% { opacity: 0; transform: translateY(100%); }
100% { opacity: 1; transform: translateY(0); }
}
@keyframes slide-out-down {
0% { opacity: 1; transform: translateY(0); }
100% { opacity: 0; transform: translateY(20px); }
}
@keyframes checkmark-draw {
0% { stroke-dashoffset: 50; }
100% { stroke-dashoffset: 0; }
}
@keyframes progress-grow {
0% { width: 0%; }
}
@keyframes tab-underline {
0% { transform: scaleX(0); }
100% { transform: scaleX(1); }
}
@keyframes card-flip-in {
0% { transform: rotateY(90deg); opacity: 0; }
100% { transform: rotateY(0deg); opacity: 1; }
}
@keyframes float-up-fade {
0% { opacity: 1; transform: translateY(0) scale(1); }
100% { opacity: 0; transform: translateY(-40px) scale(0.8); }
}
@keyframes dot-bounce {
0%, 80%, 100% { transform: scale(0); }
40% { transform: scale(1); }
}
@keyframes divider-slide {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
@keyframes heart-beat {
0% { transform: scale(1); }
25% { transform: scale(1.3); }
50% { transform: scale(0.95); }
100% { transform: scale(1); }
}
@keyframes fade-in-left {
0% { opacity: 0; transform: translateX(-20px); }
100% { opacity: 1; transform: translateX(0); }
}
@keyframes crossfade-in {
0% { opacity: 0; transform: scale(0.97); }
100% { opacity: 1; transform: scale(1); }
}
@keyframes crossfade-out {
0% { opacity: 1; transform: scale(1); }
100% { opacity: 0; transform: scale(0.97); }
}
@keyframes typing {
from { width: 0; }
to { width: 100%; }
}
@keyframes blink-caret {
from, to { border-color: transparent; }
50% { border-color: #3b82f6; }
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes wave-move {
0% { transform: translateX(0); }
100% { transform: translateX(-50%); }
}
@keyframes pulse-dot {
0%, 100% { transform: scale(1); opacity: 0.7; }
50% { transform: scale(1.8); opacity: 0.2; }
}
@keyframes slide-down-in {
0% { opacity: 0; transform: translateY(-12px); }
100% { opacity: 1; transform: translateY(0); }
}
@keyframes slide-up-out {
0% { opacity: 1; transform: translateY(0); }
100% { opacity: 0; transform: translateY(-12px); }
}
@keyframes notification-bounce {
0% { transform: scale(0); opacity: 0; }
50% { transform: scale(1.3); }
70% { transform: scale(0.9); }
100% { transform: scale(1); opacity: 1; }
}
@keyframes confetti-fall {
0% { transform: translateY(-100%) rotate(0deg); opacity: 1; }
100% { transform: translateY(100vh) rotate(720deg); opacity: 0; }
}
@keyframes status-flow {
0% { width: 0%; }
100% { width: 100%; }
}
@keyframes pull-indicator {
0% { transform: translateY(-20px); opacity: 0; }
50% { opacity: 1; }
100% { transform: translateY(0); opacity: 1; }
}
@keyframes copy-popup {
0% { opacity: 0; transform: translateY(8px) scale(0.9); }
20% { opacity: 1; transform: translateY(0) scale(1.05); }
30% { transform: scale(0.95); }
100% { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes longpress-ripple {
0% { transform: scale(0); opacity: 0.6; }
100% { transform: scale(4); opacity: 0; }
}
@keyframes pulse-ring {
0% { box-shadow: 0 0 0 0 rgba(59,130,246,0.5); }
70% { box-shadow: 0 0 0 12px rgba(59,130,246,0); }
100% { box-shadow: 0 0 0 0 rgba(59,130,246,0); }
}
@keyframes auto-save-dot {
0%, 100% { opacity: 0.4; transform: scale(0.8); }
50% { opacity: 1; transform: scale(1.2); }
}
@keyframes stage-enter {
0% { opacity: 0; transform: translateX(40px); }
100% { opacity: 1; transform: translateX(0); }
}
@keyframes stage-exit {
0% { opacity: 1; transform: translateX(0); }
100% { opacity: 0; transform: translateX(-40px); }
}
@keyframes collapse-open {
0% { max-height: 0; opacity: 0; padding-top: 0; padding-bottom: 0; }
100% { max-height: 2000px; opacity: 1; }
}
@keyframes collapse-close {
0% { max-height: 2000px; opacity: 1; }
100% { max-height: 0; opacity: 0; padding-top: 0; padding-bottom: 0; }
}
@keyframes shimmer-fast {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
@keyframes count-badge {
0% { transform: scale(0.3); opacity: 0; }
60% { transform: scale(1.1); }
100% { transform: scale(1); opacity: 1; }
}
@keyframes menu-slide-in {
0% { opacity: 0; transform: scale(0.95) translateY(-4px); }
100% { opacity: 1; transform: scale(1) translateY(0); }
}
@keyframes indicator-dash {
to { stroke-dashoffset: -100; }
}
@keyframes skeleton-wave {
0% { background-position: 200% 50%; }
100% { background-position: -200% 50%; }
}
@keyframes rotate-180 {
0% { transform: rotate(0deg); }
100% { transform: rotate(180deg); }
}
@keyframes rotate-back {
0% { transform: rotate(180deg); }
100% { transform: rotate(0deg); }
}
@keyframes highlight-flash {
0% { background-color: rgba(59,130,246,0.15); }
100% { background-color: transparent; }
}
@keyframes toast-enter {
0% { opacity: 0; transform: translateX(100%) scale(0.95); }
100% { opacity: 1; transform: translateX(0) scale(1); }
}
@keyframes toast-exit {
0% { opacity: 1; transform: translateX(0) scale(1); }
100% { opacity: 0; transform: translateX(100%) scale(0.95); }
}
/* Modal 进入动画 */
@keyframes modal-enter {
0% { opacity: 0; transform: scale(0.92) translateY(12px); }
100% { opacity: 1; transform: scale(1) translateY(0); }
}
@keyframes avatar-upload {
0% { opacity: 0; transform: scale(0.8); }
100% { opacity: 1; transform: scale(1); }
}
/* 呼吸脉冲 */
@keyframes breathing {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.06); opacity: 0.85; }
}
/* 统计卡片辉光 */
@keyframes stat-glow {
0%, 100% { box-shadow: 0 0 0 0 rgba(59,130,246,0); }
50% { box-shadow: 0 0 16px 4px rgba(59,130,246,0.15); }
}
/* 数字弹跳计数 */
@keyframes count-pop {
0% { transform: translateY(20px); opacity: 0; }
40% { transform: translateY(-4px); opacity: 1; }
60% { transform: translateY(2px); }
100% { transform: translateY(0); opacity: 1; }
}
/* 状态改变脉冲 */
@keyframes status-pulse {
0% { transform: scale(1); }
20% { transform: scale(1.12); }
100% { transform: scale(1); }
}
/* 字段聚焦波纹 */
@keyframes field-ripple {
0% { box-shadow: 0 0 0 0 rgba(59,130,246,0.3); }
100% { box-shadow: 0 0 0 8px rgba(59,130,246,0); }
}
/* 上传进度环 */
@keyframes upload-ring {
0% { stroke-dashoffset: 283; }
100% { stroke-dashoffset: 0; }
}
/* 404 浮动动画 */
@keyframes float-404 {
0%, 100% { transform: translateY(0) rotate(0deg); }
25% { transform: translateY(-12px) rotate(2deg); }
75% { transform: translateY(-6px) rotate(-2deg); }
}
/* 筛选芯片切换 */
@keyframes chip-pop {
0% { transform: scale(0.85); opacity: 0; }
60% { transform: scale(1.05); }
100% { transform: scale(1); opacity: 1; }
}
/* 状态点闪烁 */
@keyframes status-dot-pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.5; transform: scale(1.3); }
}
/* 加载进度条条纹 */
@keyframes progress-stripes {
0% { background-position: 1rem 0; }
100% { background-position: 0 0; }
}
/* 不确定进度条(条纹流动效果) */
@keyframes indeterminate-progress {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
/* 面板滑入 */
@keyframes panel-slide-up {
0% { opacity: 0; transform: translateY(20px) scale(0.97); }
100% { opacity: 1; transform: translateY(0) scale(1); }
}
/* 遮罩淡入 */
@keyframes backdrop-in {
0% { opacity: 0; backdrop-filter: blur(0); }
100% { opacity: 1; backdrop-filter: blur(4px); }
}
/* 缩放呼吸 */
@keyframes scale-breathing {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.04); }
}
/* 下拉菜单分隔线动画 */
@keyframes menu-divider-grow {
0% { transform: scaleX(0); }
100% { transform: scaleX(1); }
}
/* 头像光环旋转 */
@keyframes avatar-ring-rotate {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* 库存低库存脉冲 */
@keyframes low-stock-pulse {
0%, 100% { background-color: rgba(239,68,68,0.05); }
50% { background-color: rgba(239,68,68,0.15); }
}
/* 值高亮闪烁 */
@keyframes value-highlight {
0% { color: #2563eb; transform: scale(1.05); }
50% { color: #3b82f6; }
100% { color: inherit; transform: scale(1); }
}
/* 徽章摆动 */
@keyframes badge-swing {
0%, 100% { transform: rotate(0deg); }
25% { transform: rotate(5deg); }
75% { transform: rotate(-5deg); }
}
/* Toast 进度条收缩 */
@keyframes progress-shrink {
0% { width: 100%; }
100% { width: 0%; }
}
/* ================================================================
Additional Micro-Interaction Utilities
================================================================ */
/* 滑动入场(上) */
.animate-slide-up {
animation: slide-up-in 0.4s ease-out both;
}
/* 心脏跳动动画 */
.animate-heart-beat {
animation: heart-beat 0.4s ease-in-out;
}
/* 计数徽章弹入 */
.animate-count-badge {
animation: count-badge 0.3s ease-out both;
}
/* 滑动删除指示器 */
.animate-swipe-out {
animation: swipe-out 0.3s ease-in forwards;
}
/* 复选框选中弹跳 */
.animate-check-pop {
animation: check-pop 0.35s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
/* 输入框聚焦光晕 */
.input-glow:focus-within {
box-shadow: 0 0 0 3px rgba(5, 150, 105, 0.15);
border-color: #059669;
}
/* 列表项拖拽手柄 */
.drag-handle {
cursor: grab;
opacity: 0.3;
transition: opacity 0.2s ease;
}
.drag-handle:hover,
.drag-handle:active {
opacity: 0.6;
}
.drag-handle:active {
cursor: grabbing;
}
/* 可折叠区域指示器 */
.collapse-indicator {
transition: transform 0.3s ease;
}
.collapse-indicator.open {
transform: rotate(180deg);
}
/* 模态框缩放入场 */
.modal-scale-enter {
animation: modal-scale-in 0.25s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
/* 列表项移除动画 */
.list-item-exit {
animation: list-item-exit 0.3s ease-in forwards;
}
/* 长按涟漪效果 */
.longpress-ripple {
position: relative;
overflow: hidden;
}
.longpress-ripple::after {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(circle, rgba(5, 150, 105, 0.2) 0%, transparent 70%);
opacity: 0;
transition: opacity 0.3s ease;
}
.longpress-ripple:active::after {
opacity: 1;
}
/* 选中高亮闪烁 */
.selection-flash {
animation: highlight-flash 0.6s ease-out;
}
/* 数据刷新旋转 */
.refresh-spin {
animation: spin 0.8s linear;
}
/* 打字光标闪烁 */
.cursor-blink {
animation: blink-caret 0.8s step-end infinite;
}
/* 加载骨架 - 头像圆形 */
.skeleton-avatar {
border-radius: 50%;
background: linear-gradient(90deg, #e5e7eb 25%, #f3f4f6 50%, #e5e7eb 75%);
background-size: 200% 100%;
animation: skeleton-wave 1.5s ease-in-out infinite;
}
/* 空状态浮动图标 */
.empty-state-animated .empty-state-icon {
animation: float 3s ease-in-out infinite;
}
.empty-state-animated .empty-state-title {
animation: fade-in-up 0.5s ease-out 0.3s both;
}
.empty-state-animated .empty-state-desc {
animation: fade-in-up 0.5s ease-out 0.5s both;
}
/* 页面过渡 - 淡入 */
.page-enter {
animation: crossfade-in 0.35s ease-out both;
}
.page-exit {
animation: crossfade-out 0.2s ease-in both;
}
/* 连接状态指示器脉冲 */
.connection-dot {
width: 8px;
height: 8px;
border-radius: 50%;
display: inline-block;
}
.connection-dot.online {
background: #10b981;
animation: status-dot-pulse 2s ease-in-out infinite;
}
.connection-dot.offline {
background: #ef4444;
}
.connection-dot.reconnecting {
background: #f59e0b;
animation: status-dot-pulse 1s ease-in-out infinite;
}
/* 拖拽排序占位 */
.sortable-placeholder {
border: 2px dashed #d1d5db;
border-radius: 8px;
background: #f9fafb;
min-height: 48px;
opacity: 0.6;
}
/* 快捷操作工具栏 */
.quick-actions-bar {
backdrop-filter: blur(8px);
background: rgba(255, 255, 255, 0.85);
border-top: 1px solid rgba(0, 0, 0, 0.06);
transition: transform 0.3s ease;
}
.quick-actions-bar.hidden-bar {
transform: translateY(100%);
}
/* 键盘快捷键提示 */
.kbd {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 20px;
height: 20px;
padding: 0 4px;
font-size: 10px;
font-family: inherit;
font-weight: 500;
color: #6b7280;
background: #f3f4f6;
border: 1px solid #e5e7eb;
border-radius: 4px;
border-bottom-width: 2px;
}
/* ================================================================
Additional Keyframes
================================================================ */
@keyframes swipe-out {
0% { transform: translateX(0); opacity: 1; }
100% { transform: translateX(100%); opacity: 0; }
}
@keyframes check-pop {
0% { transform: scale(0); }
50% { transform: scale(1.3); }
100% { transform: scale(1); }
}
@keyframes modal-scale-in {
0% { transform: scale(0.9); opacity: 0; }
100% { transform: scale(1); opacity: 1; }
}
@keyframes list-item-exit {
0% { max-height: 80px; opacity: 1; margin-bottom: 8px; }
100% { max-height: 0; opacity: 0; margin-bottom: 0; padding-top: 0; padding-bottom: 0; }
}
/* ================================================================
Page Transition Keyframes(路由切换动画)
================================================================ */
/* 前进(PUSH):从右侧滑入 / 向左侧退出 */
@keyframes page-slide-from-right {
0% { opacity: 0; transform: translateX(40px); }
100% { opacity: 1; transform: translateX(0); }
}
@keyframes page-slide-exit-left {
0% { opacity: 1; transform: translateX(0); }
100% { opacity: 0; transform: translateX(-40px); }
}
/* 后退(POP):从左侧滑入 / 向右侧退出 */
@keyframes page-slide-from-left {
0% { opacity: 0; transform: translateX(-40px); }
100% { opacity: 1; transform: translateX(0); }
}
@keyframes page-slide-exit-right {
0% { opacity: 1; transform: translateX(0); }
100% { opacity: 0; transform: translateX(40px); }
}
/* 淡入淡出 */
@keyframes page-crossfade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}
@keyframes page-crossfade-out {
0% { opacity: 1; }
100% { opacity: 0; }
}
/* 缩放入场 */
@keyframes page-scale-in {
0% { opacity: 0; transform: scale(0.95); }
100% { opacity: 1; transform: scale(1); }
}
@keyframes page-scale-out {
0% { opacity: 1; transform: scale(1); }
100% { opacity: 0; transform: scale(0.95); }
}
/* PageTransition 组件使用的 CSS 类 */
.page-transition-wrapper {
--duration: var(--page-transition-duration, 300ms);
}
/* slide 进入 */
.page-slide-enter {
animation: page-slide-from-right var(--duration) cubic-bezier(.16,1,.3,1) both;
}
.page-slide-enter.page-from-left {
animation: page-slide-from-left var(--duration) cubic-bezier(.16,1,.3,1) both;
}
/* slide 退出 */
.page-slide-exit {
animation: page-slide-exit-left var(--duration) cubic-bezier(.5,0,.75,0) both;
}
.page-slide-exit.page-exit-right {
animation: page-slide-exit-right var(--duration) cubic-bezier(.5,0,.75,0) both;
}
/* crossfade */
.page-crossfade-enter {
animation: page-crossfade-in var(--duration) ease-out both;
}
.page-crossfade-exit {
animation: page-crossfade-out var(--duration) ease-in both;
}
/* scale */
.page-scale-enter {
animation: page-scale-in var(--duration) cubic-bezier(.34,1.2,.64,1) both;
}
.page-scale-exit {
animation: page-scale-out var(--duration) ease-in both;
}
/* ================================================================
Micro-Interaction Keyframes
================================================================ */
@keyframes pulse-dot-ring {
0% { transform: scale(1); opacity: 0.8; }
50% { transform: scale(2.2); opacity: 0; }
100% { transform: scale(2.2); opacity: 0; }
}
@keyframes skeleton-shimmer-wave {
0% { background-position: 200% 50%; }
100% { background-position: -200% 50%; }
}
@keyframes text-reveal-fade {
0% { opacity: 0; transform: translateY(4px); }
100% { opacity: 1; transform: translateY(0); }
}
@keyframes text-reveal-up {
0% { opacity: 0; transform: translateY(16px); }
100% { opacity: 1; transform: translateY(0); }
}
@keyframes typing-blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
/* 进度环动画 */
@keyframes progress-ring-fill {
0% { stroke-dashoffset: var(--ring-circumference, 283); }
}
/* 横幅滑入 */
@keyframes banner-slide-down {
0% { transform: translateY(-100%); opacity: 0; }
100% { transform: translateY(0); opacity: 1; }
}
/* 触觉反馈闪烁 */
@keyframes haptic-flash {
0% { box-shadow: 0 0 0 0 var(--haptic-color, rgba(59,130,246,0.4)); }
50% { box-shadow: 0 0 0 8px rgba(59,130,246,0); }
100% { box-shadow: 0 0 0 0 rgba(59,130,246,0); }
}
/* 爪印弹跳 */
@keyframes paw-bounce {
0%, 100% { transform: scale(1) rotate(0deg); }
30% { transform: scale(1.1) rotate(5deg); }
60% { transform: scale(0.95) rotate(-3deg); }
}
/* 心跳线绘制 */
@keyframes heartbeat-draw {
0% { stroke-dashoffset: 60; }
40% { stroke-dashoffset: 0; }
60% { stroke-dashoffset: 0; }
100% { stroke-dashoffset: -60; }
}
/* 心跳脉冲 */
@keyframes heartbeat-pulse {
0%, 100% { transform: scale(1); opacity: 1; }
15% { transform: scale(1.6); opacity: 0.7; }
30% { transform: scale(1); opacity: 1; }
45% { transform: scale(1.3); opacity: 0.8; }
60% { transform: scale(1); opacity: 1; }
}
/* DNA 波浪 */
@keyframes dna-wave {
0%, 100% { opacity: 0.3; }
50% { opacity: 1; }
}
/* DNA 横杠 */
@keyframes dna-bar {
0%, 100% { opacity: 0.2; }
50% { opacity: 0.8; }
}
/* 侧边栏活跃指示条滑入 */
@keyframes sidebar-active-indicator {
0% { transform: scaleY(0); opacity: 0; }
100% { transform: scaleY(1); opacity: 1; }
}
/* ================================================================
深度动画系统 (v2) — 15项升级
================================================================ */
/* ----- 1. float-in:元素从下方浮动进入(弹性曲线)----- */
@keyframes float-in {
0% { opacity: 0; transform: translateY(20px); }
100% { opacity: 1; transform: translateY(0); }
}
/* ----- 2. glow-border:边框发光脉冲(input/card 聚焦)----- */
@keyframes glow-border-pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(59,130,246,0.3), 0 0 4px rgba(59,130,246,0.15); }
50% { box-shadow: 0 0 0 4px rgba(59,130,246,0.12), 0 0 12px rgba(59,130,246,0.25); }
}
/* ----- 3. count-up:数字滚动增量的视觉准备(配合 JS)----- */
@keyframes count-up-slide {
0% { transform: translateY(1em); opacity: 0; }
60% { transform: translateY(-0.15em); opacity: 1; }
80% { transform: translateY(0.05em); }
100% { transform: translateY(0); opacity: 1; }
}
/* ----- 4. typewriter-enhanced:增强打字机逐字显示 ----- */
@keyframes typewriter-cursor-blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
/* ----- 5. spotlight:聚光灯扫描(由 JS 设置 --mouse-x/--mouse-y)----- */
/* 该效果完全通过工具类的 background 属性实现,无需 @keyframes */
/* ----- 6. morph-bg:多色渐变背景缓慢流动 ----- */
@keyframes morph-bg-flow {
0% { background-position: 0% 50%; }
25% { background-position: 50% 0%; }
50% { background-position: 100% 50%; }
75% { background-position: 50% 100%; }
100% { background-position: 0% 50%; }
}
/* ----- 7. breathe-scale:呼吸缩放(吸引注意力)----- */
@keyframes breathe-scale {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
@keyframes breathe-scale-slow {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.03); }
}
/* ----- 8. stagger-list:列表逐项延迟出现(父/子配合)----- */
@keyframes stagger-item-in {
0% { opacity: 0; transform: translateY(12px); }
100% { opacity: 1; transform: translateY(0); }
}
/* ----- 9. draw-underline:下划线从左到右绘制(::after + scaleX)----- */
/* 完全通过工具类的 ::after 伪元素 + transition 实现,无需 @keyframes */
/* ----- 10. flip-card:3D 卡片翻转 ----- */
@keyframes flip-card-in {
0% { transform: perspective(800px) rotateY(0deg); }
100% { transform: perspective(800px) rotateY(180deg); }
}
@keyframes flip-card-out {
0% { transform: perspective(800px) rotateY(180deg); }
100% { transform: perspective(800px) rotateY(0deg); }
}
/* ----- 11. wave-ripple:水波纹从点击位置扩散(配合 JS)----- */
@keyframes wave-ripple-expand {
0% { width: 0; height: 0; opacity: 0.5; transform: translate(-50%, -50%) scale(0); }
100% { width: 300px; height: 300px; opacity: 0; transform: translate(-50%, -50%) scale(1); }
}
/* ----- 12. elastic-bounce:弹性弹跳(模拟物理掉落)----- */
@keyframes elastic-bounce {
0% { transform: translateY(-60px); opacity: 0; }
15% { transform: translateY(0); opacity: 1; }
30% { transform: translateY(-20px); }
45% { transform: translateY(0); }
57% { transform: translateY(-7px); }
68% { transform: translateY(0); }
76% { transform: translateY(-2px); }
84% { transform: translateY(0); }
100% { transform: translateY(0); opacity: 1; }
}
/* ----- 13. dissolve-out:烟雾消散(删除确认/消失提示)----- */
@keyframes dissolve-out {
0% { opacity: 1; transform: scale(1); filter: blur(0); }
50% { opacity: 0.6; transform: scale(1.05); filter: blur(2px); }
100% { opacity: 0; transform: scale(0.9); filter: blur(8px); }
}
/* ----- 14. orbit-spin:轨道旋转加载动画 ----- */
@keyframes orbit-spin {
0% { transform: rotate(0deg) translateX(20px) rotate(0deg); }
100% { transform: rotate(360deg) translateX(20px) rotate(-360deg); }
}
/* ----- 15. success-burst:成功撒花(多条彩色线条爆开)----- */
@keyframes success-burst-particle {
0% { opacity: 1; transform: translate(0, 0) scale(1); }
100% { opacity: 0; transform: translate(var(--burst-x), var(--burst-y)) scale(0); }
}
@keyframes success-burst-rotate {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* ===== #12 Tab 内容切换 ===== */
@keyframes tab-crossfade-enter {
0% { opacity: 0; transform: translateY(8px); }
100% { opacity: 1; transform: translateY(0); }
}
@keyframes tab-crossfade-exit {
0% { opacity: 1; transform: translateY(0); }
100% { opacity: 0; transform: translateY(8px); }
}
/* ===== #14 下拉菜单弹性动画 ===== */
@keyframes dropdown-scale-in {
0% { opacity: 0; transform: scaleY(0.85); }
100% { opacity: 1; transform: scaleY(1); }
}
@keyframes dropdown-scale-out {
0% { opacity: 1; transform: scaleY(1); }
100% { opacity: 0; transform: scaleY(0.85); }
}
/* ===== 新增组件 keyframes ===== */
@keyframes dissolve-in {
0% { opacity: 0; transform: scale(0.85); }
100% { opacity: 1; transform: scale(1); }
}
@keyframes dissolve-out {
0% { opacity: 1; transform: scale(1); }
100% { opacity: 0; transform: scale(0.85); }
}
@keyframes tooltip-fade-in {
0% { opacity: 0; transform: scale(0.95); }
100% { opacity: 1; transform: scale(1); }
}
@keyframes modal-scale-out {
0% { transform: scale(1); opacity: 1; }
100% { transform: scale(0.9); opacity: 0; }
}
@keyframes backdrop-out {
0% { opacity: 1; }
100% { opacity: 0; }
}
/* 面包屑滑入 */
@keyframes breadcrumb-slide-in {
0% { opacity: 0; transform: translateX(-8px); }
100% { opacity: 1; transform: translateX(0); }
}
/* 登录表单 shake */
@keyframes login-shake {
0%, 100% { transform: translateX(0); }
10%, 50%, 90% { transform: translateX(-5px); }
30%, 70% { transform: translateX(5px); }
}
/* 趋势箭头弹入 */
@keyframes trend-bounce-in {
0% { opacity: 0; transform: translateY(8px) scale(0.8); }
100% { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes trend-arrow-pop {
0% { transform: scale(0); opacity: 0; }
60% { transform: scale(1.3); }
100% { transform: scale(1); opacity: 1; }
}
/* 验证图标弹入 */
@keyframes validation-pop-in {
0% { transform: translateY(-50%) scale(0); }
60% { transform: translateY(-50%) scale(1.2); }
100% { transform: translateY(-50%) scale(1); }
}
/* ================================================================
Print Styles - Medical(增强版)
打印病历时隐藏 UI 组件,优化 SOAP 内容展示,添加页眉页脚
================================================================ */
@media print {
/* ---- 隐藏不必要元素 ---- */
.no-print,
nav,
button,
header,
aside,
.skip-to-content,
[class*="Sidebar"],
[class*="Toast"],
[class*="toast"],
.btn-primary,
.btn-secondary,
.btn-ghost,
.btn-danger,
.install-banner,
.scroll-to-top {
display: none !important;
}
/* 打印专用元素可见 */
.print-only { display: block !important; }
/* ---- 页面基础样式 ---- */
body {
font-size: 12px;
line-height: 1.6;
color: #000 !important;
background: #fff !important;
}
/* 将所有灰色文字转为黑色,确保打印清晰 */
.text-gray-700,
.text-gray-800,
.text-gray-900,
.text-gray-600,
.text-gray-500,
.text-gray-400 {
color: #000 !important;
}
/* ---- 卡片打印样式 ---- */
.card {
box-shadow: none !important;
border: 1px solid #ccc !important;
break-inside: avoid;
}
/* ---- 表格样式 ---- */
table {
border-collapse: collapse;
width: 100%;
}
table th {
background: #e5e7eb !important;
color: #000 !important;
font-weight: 600;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
table td, table th {
border: 1px solid #d1d5db;
padding: 6px 8px;
text-align: left;
}
/* ---- 标题分页控制:避免标题后立即分页 ---- */
h1, h2, h3, h4, h5, h6 {
page-break-after: avoid;
color: #000 !important;
}
p, ul, ol, dl, blockquote {
orphans: 3; /* 段落底部至少保留 3 行 */
widows: 3; /* 段落顶部至少保留 3 行 */
}
/* ---- SOAP 内容:取消 max-height/overflow 限制,完整展示 ---- */
[class*="soap"],
[class*="SOAP"],
.soap-content,
.record-content,
.medical-record-content {
max-height: none !important;
overflow: visible !important;
height: auto !important;
}
/* ---- 页面设置 ---- */
@page {
margin: 1.5cm 1.8cm;
size: A4;
/* 打印专用页眉:医院名称 + 日期 */
@top-center {
content: "VetCopilot - " attr(data-hospital-name);
font-size: 10px;
color: #666;
font-family: "Microsoft YaHei", "Noto Sans SC", sans-serif;
}
@top-right {
content: attr(data-print-date);
font-size: 9px;
color: #999;
}
/* 打印专用页脚:页码 + 生成时间 */
@bottom-center {
content: "第 " counter(page) " 页,共 " counter(pages) " 页";
font-size: 9px;
color: #999;
}
@bottom-right {
content: "生成时间: " attr(data-print-time);
font-size: 8px;
color: #aaa;
}
}
/* ---- 病历编号水印(每页底部斜体浅灰)---- */
body::after {
content: attr(data-record-serial);
position: fixed;
bottom: 0.8cm;
left: 1.8cm;
font-size: 8px;
color: rgba(0, 0, 0, 0.12);
font-style: italic;
font-family: monospace;
pointer-events: none;
z-index: -1;
}
/* ---- 链接显示原始 URL ---- */
a[href]::after {
content: " (" attr(href) ")";
font-size: 10px;
color: #666;
word-break: break-all;
}
/* ---- 页面内部分页控制 ---- */
.page-break-before { page-break-before: always; }
.page-break-after { page-break-after: always; }
.avoid-break { break-inside: avoid; }
/* ---- 背景色和边框保留 ---- */
* {
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
}
}
/* ================================================================
Mobile (Capacitor) - Safe Area & Touch
================================================================ */
/* iOS notch / status bar 安全区域 */
.safe-top { padding-top: env(safe-area-inset-top, 0px); }
.safe-bottom { padding-bottom: env(safe-area-inset-bottom, 0px); }
.safe-left { padding-left: env(safe-area-inset-left, 0px); }
.safe-right { padding-right: env(safe-area-inset-right, 0px); }
/* 移动端触摸优化 - 消除 300ms 延迟 */
@media (max-width: 768px) {
/* 按钮和可交互元素使用 touch-action: manipulation 消除双击缩放延迟 */
button, a, [role="button"], input[type="submit"], input[type="button"] {
cursor: pointer;
-webkit-tap-highlight-color: transparent;
touch-action: manipulation;
}
/* 去掉 iOS 默认样式 */
input, textarea, select {
-webkit-appearance: none;
border-radius: 0;
}
/* 长按菜单禁用(可编辑区域除外) */
body {
-webkit-touch-callout: none;
-webkit-user-select: none;
user-select: none;
}
input, textarea, [contenteditable] {
-webkit-user-select: text;
user-select: text;
}
}
/* ================================================================
触摸设备专属优化(无 hover 能力 + 粗指针设备)
================================================================ */
/* 无 hover 能力的设备(触摸屏):用 active 替代 hover */
@media (hover: none) {
/* 移动端 hover 会粘滞,改为 active 反馈 */
.card-hover:hover {
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04) !important;
border-color: #e5e7eb !important;
}
.card-hover:active {
box-shadow: 0 4px 12px rgba(0,0,0,0.08), 0 2px 4px rgba(0,0,0,0.04) !important;
border-color: #d1d5db !important;
}
/* 按钮 hover → active 映射 */
.btn-primary:hover { background-color: inherit; }
.btn-secondary:hover { background-color: #ffffff; }
.btn-ghost:hover { background-color: transparent; }
.btn-primary:active { filter: brightness(0.9); }
.btn-secondary:active { background-color: #f3f4f6; }
.btn-ghost:active { background-color: #f3f4f6; }
/* 表格行 hover → active */
table tbody tr:hover { background-color: transparent; }
table tbody tr:active { background-color: #f9fafb; }
/* 链接 hover → active */
a:hover { opacity: 1; }
a:active { opacity: 0.7; }
}
/* 粗指针设备(手指触摸)额外优化 */
@media (pointer: coarse) {
/* 可滚动区域启用 iOS 惯性滚动 */
.scrollable-area,
[class*="overflow-auto"],
[class*="overflow-y-auto"],
[class*="overflow-scroll"] {
-webkit-overflow-scrolling: touch;
}
/* 确保所有可交互元素有足够大的触控面积 */
button, a, [role="button"], input[type="submit"], input[type="button"],
input[type="checkbox"], input[type="radio"], select {
min-height: 44px;
min-width: 44px;
}
}
/* ================================================================
移动端表单输入优化
================================================================ */
/* 防止 iOS 在聚焦输入框时自动缩放(font-size >= 16px) */
@media (max-width: 768px) {
input[type="text"],
input[type="tel"],
input[type="email"],
input[type="number"],
input[type="date"],
input[type="password"],
textarea,
select,
.input-field {
font-size: 16px !important;
}
}
/* ================================================================
点击高亮控制工具类
================================================================ */
/* 移除点击高亮(用于卡片、容器等非交互区域或自定义样式的交互元素) */
.tap-highlight-none {
-webkit-tap-highlight-color: transparent;
}
/* 自定义点击高亮颜色(用于需要反馈的列表项、菜单项) */
.tap-highlight-subtle {
-webkit-tap-highlight-color: rgba(0, 0, 0, 0.06);
}
.tap-highlight-primary {
-webkit-tap-highlight-color: rgba(59, 130, 246, 0.15);
}
/* Electron 桌面端专用 */
@media (display-mode: standalone) or (env(electron)) {
/* 桌面端更大的字体和间距可通过此处覆盖 */
}