@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); } /* 在未来启用暗色模式时,只需在 上添加 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)) { /* 桌面端更大的字体和间距可通过此处覆盖 */ }