feat(theme): 专业主题系统重构 — Warp/Linear 设计参考,深色/浅色完整适配

参考设计系统:
- Warp: 温暖暗色调、自然层次、克制配色
- Linear: 亮度阶梯层次、半透明边框、精确排版

核心改进:
- app-theme.ts: 完整重写深色/浅色调色板 (+surface-hover/active, border-subtle/focus, feedback-bg, placeholder)
- useTheme.ts: 修复硬编码 return 'dark',支持 dark/light/system 完整切换
- index.css: 消除硬编码颜色,全部使用 CSS 变量
- tailwind.config.js: 新增 txt/content/surface/border/ph/feedback 语义 token
- 44 文件批量迁移: 292 处 text-gray/bg-gray/text-white/placeholder-gray → 主题感知语义 token

| 44 files +611/-482 | tsc零错误 | 96/96 tests passing
This commit is contained in:
OpsXShell Dev committed 2026-04-25 19:31:51 +08:00
1 parent 07c4a5b14f
commit e9a0d21758
44 files changed
+593 -464

No files matched your search

+53 -30
View File
@@ -3,55 +3,78 @@
@tailwind utilities;
/* ===================================================================
* OpsXShell v1.3.0 — Theme CSS Variables
* OpsXShell v1.4.0 — Professional Theme System
*
* The default (no data-theme attribute) keeps dark mode as before so
* that the very first paint looks correct even before React hydrates.
* useTheme.ts will immediately set data-theme + override vars on mount.
* Default (no data-theme) is dark mode for correct first paint.
* useTheme.ts immediately sets data-theme + overrides vars on mount.
*
* Design: Warp-inspired warm dark + Linear luminance stacking
* =================================================================== */
:root {
/* ---- Dark (default, matches original hard-coded values) ---- */
/* ---- Dark (default, matches warm slate-navy palette) ---- */
--color-dark-900: #0f172a;
--color-dark-800: #1e293b;
--color-dark-700: #334155;
--color-dark-600: #475569;
/* Semantic tokens (dark default) */
--color-surface-base: #0f172a;
--color-surface-raised: #1e293b;
/* Semantic surface tokens */
--color-surface-base: #0f172a;
--color-surface-raised: #1e293b;
--color-surface-overlay: #334155;
--color-surface-inset: #475569;
--color-surface-inset: #475569;
--color-surface-hover: #374151;
--color-surface-active: #4b5563;
--color-text-primary: #e2e8f0;
/* Text */
--color-text-primary: #f1f5f9;
--color-text-secondary: #cbd5e1;
--color-text-muted: #94a3b8;
--color-text-faint: #64748b;
--color-text-dim: #475569;
--color-content-primary: #e2e8f0;
/* Content */
--color-content-primary: #f1f5f9;
--color-content-secondary: #cbd5e1;
--color-content-tertiary: #94a3b8;
--color-content-inverse: #0f172a;
--color-content-tertiary: #94a3b8;
--color-content-inverse: #0f172a;
--color-border: #334155;
--color-border-strong: #475569;
/* Border */
--color-border: #1e293b;
--color-border-subtle: #1e293b;
--color-border-default: #334155;
--color-border-strong: #475569;
--color-border-focus: #3b82f6;
--color-accent-blue: #3b82f6;
--color-accent-green: #10b981;
--color-accent-purple: #8b5cf6;
--color-accent-orange: #f97316;
--color-accent-red: #ef4444;
/* Accent */
--color-accent-blue: #3b82f6;
--color-accent-blue-hover: #60a5fa;
--color-accent-green: #10b981;
--color-accent-purple: #8b5cf6;
--color-accent-orange: #f97316;
--color-accent-red: #ef4444;
--color-feedback-success: #10b981;
--color-feedback-warning: #f59e0b;
--color-feedback-error: #ef4444;
--color-feedback-info: #3b82f6;
/* Feedback */
--color-feedback-success: #10b981;
--color-feedback-success-bg: #064e3b;
--color-feedback-warning: #f59e0b;
--color-feedback-warning-bg: #78350f;
--color-feedback-error: #ef4444;
--color-feedback-error-bg: #7f1d1d;
--color-feedback-info: #3b82f6;
--color-feedback-info-bg: #1e3a5f;
--color-scrollbar-track: #1e293b;
--color-scrollbar-thumb: #475569;
--color-scrollbar-thumb-h: #64748b;
/* Placeholder */
--color-placeholder: #64748b;
--color-placeholder-disabled: #475569;
/* UI chrome */
--color-scrollbar-track: #1e293b;
--color-scrollbar-thumb: #475569;
--color-scrollbar-thumb-h: #64748b;
--color-shadow-color: rgba(0, 0, 0, 0.4);
/* Terminal */
--color-terminal-bg: #0f172a;
}
@@ -67,8 +90,8 @@ html, body {
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: #0f172a !important;
color: #e2e8f0;
background-color: var(--color-surface-base) !important;
color: var(--color-text-primary);
overflow: hidden;
}
@@ -217,7 +240,7 @@ button {
100% { background-position: 200% 0; }
}
.animate-shimmer {
background: linear-gradient(90deg, var(--color-dark-800) 25%, var(--color-dark-700) 50%, var(--color-dark-800) 75%);
background: linear-gradient(90deg, var(--color-surface-raised) 25%, var(--color-surface-overlay) 50%, var(--color-surface-raised) 75%);
background-size: 200% 100%;
animation: shimmer 1.5s ease-in-out infinite;
}