# 前端样式模块化计划(CSS) > 目标:在完全还原 `static/old_version_backup/前端备份` 视觉与交互的前提下,于 Vite + Vue 3 体系中重构 `static/style.css`,建立可维护、可组合的样式层。本文对应 `doc/frontend/componentization_plan.md` 的 CSS 版本,为后续拆分提供路径、里程碑与验证清单。 ## 当前样式现状 1. **单文件真值**:`static/style.css` 仍是唯一的设计源,`static/src/main.ts` 通过 `import '../style.css';` 全局注入。内部含重置、变量、布局、组件、动画等所有样式,尚未按模块切分。 2. **变量体系**:顶层 `:root` 暴露 `--claude-*`(背景、阴影、按钮态等)并在所有组件复用,局部还混用硬编码色值,需要梳理。 3. **全局依赖**: - DOM 结构来自 `static/src/App.vue` 及其子组件(`ConversationSidebar.vue`、`LeftPanel.vue`、`ChatArea.vue`、`InputComposer.vue`、`RightFocusPanel.vue` 等)。 - `AppShell` 提供 Toast/Confirm/EasterEgg/ContextMenu,需要共享阴影与层级变量。 - 彩蛋 CSS(`static/easter-eggs/*.css`)仍单独引入,迁移不可破坏其优先级。 ## 当前状态(2025‑11) - `static/src/styles/` 已完成 base/layout/components/utilities 分层;`main.ts` 仅导入 `./styles/index.scss`。 - `static/style.css` 不再存放样式,而是 `@import '/static/dist/assets/main.css'`,用于兼容历史入口(终端、登录等仍引用该路径)。 - 旧版 CSS 原样保存于 `static/old_version_backup/前端备份/style.css`,便于对比 legacy 视觉。 ## 拆分层级设计 ``` static/src/styles/ ├── base/ # 重置、字体、变量、icon 工具 ├── layout/ # AppShell、主容器、面板栅格 ├── components/ # 聊天/输入/侧栏/抽屉等模块 ├── utilities/ # 动画、遮罩、z-index、辅助类 └── index.scss # 汇总入口,由 main.ts 引入 ``` - **Base Layer**:`_reset.scss`、`_tokens.scss`、`_global.scss`。负责 `* { box-sizing }`、`:root` 变量、`body` 字体/背景、`.icon` 工具类等;对应当前 `style.css` 前 ~200 行。 - **Layout Layer**:`_app-shell.scss`、`_panels.scss`。控制 `.main-container`、`.conversation-sidebar`、`.sidebar-panel-card`、`.chat-container`、`.right-sidebar` 的尺寸、flex 行为与响应式断点。 - **Component Layer**:按照业务域划分子目录,例如: - `components/sidebar/`:历史列表、折叠按钮、搜索框、个人空间入口。 - `components/panels/`:文件树/TODO/子智能体、TokenDrawer、ResourceStats。 - `components/chat/`:`ChatArea` + 各 `ActionBlock`、`Thinking` 动画、`copy-code-btn`。 - `components/input/`:`InputComposer`、`QuickMenu`、工具禁用菜单、设置子菜单。 - `components/overlays/`:Toast、Confirm、Quota Toast、ContextMenu、Personalization Drawer。 - **Utilities Layer**:抽象 `@keyframes`、`transition` 变量、`.is-hidden`、`.scrollbar`、`.sr-only` 等工具类,避免每个组件重复指定。 ## 模块映射(示例) | 模块文件 | 覆盖区域 | 对应组件/Store | 说明 | | --- | --- | --- | --- | | `base/_tokens.scss` | `:root` 变量、阴影、字体 | 全局 | 拆出颜色/阴影/spacing map,供 `@use` 复用,可额外导出 SCSS map 方便组件使用。 | | `layout/_app-shell.scss` | `.main-container`、`AppShell` slot | `App.vue`, `AppShell.vue` | 负责 viewport 高度、自适应 inset、安全区 padding,保留 `--app-viewport`。 | | `layout/_sidebar.scss` | `.conversation-sidebar` ~ `.conversation-list` | `ConversationSidebar.vue`, `useConversationStore` | 保留展开/折叠两套宽度,抽象 `@mixin sidebar-button($size)` 简化按钮样式。 | | `components/chat/_message-list.scss` | `.chat-area`、`.message-item` | `ChatArea.vue` 子组件、`useChatStore` | 细分 Thinking/Text/Tool/Summary 的背景、边框、动画。 | | `components/input/_composer.scss` | `.compact-input-area` | `InputComposer.vue`, `useInputStore` | 控制多行自动高度、`.action-buttons`、`.quick-menu`。 | | `components/panels/_token-drawer.scss` | `.token-drawer`, `.usage-stat` | `TokenDrawer.vue`, `useResourceStore` | 与 `legacy_ui_spec` 的折叠动画、进度条变量保持一致。 | | `components/overlays/_toast.scss` | `.toast-stack`, `.quota-toast` | `ToastStack.vue`, `QuotaToast.vue`, `useUiStore` | 拆分常规 Toast 与 quota 警告两种配色。 | | `components/overlays/_personalization.scss` | `.personal-page-overlay` | `PersonalizationDrawer.vue`, `usePersonalizationStore` | Drawer 背景、拖拽 handle、Preset 卡片。 | > 拆分时以 `legacy_ui_spec.md` + `static/style.css` 为真值,逐块复制到对应模块后,再删除旧片段,确保 diff 清晰。 ## 迁移阶段 ### 阶段 0:建立骨架(已完成) 1. 新建 `static/src/styles/index.scss`,依次 `@use`/`@import` 四层目录。 2. 在 `static/src/main.ts` 将 `import '../style.css'` 替换为 `import './styles/index.scss';`,暂留旧 `style.css` 以供对照。 3. 引入 PostCSS/Sass 配置(若尚未启用 SCSS,可先 `npm install -D sass` 并在 Vite 中开启预处理)。 ### 阶段 1:迁移 Base + Layout(已完成) 1. 复制 `style.css` 中 reset/变量/body/header/main-container 相关片段到 base/layout 模块;`style.css` 里保留注释提示“已迁移”。 2. 验证 viewport 高度、侧栏折叠、主容器滚动与之前一致。 3. 为 CSS 变量补充 SCSS map(例如 `$colors: ( 'bg': var(--claude-bg), ... )`),方便组件层调用。 ### 阶段 2:侧栏/面板组件(已完成) 1. 以对话侧栏为起点,迁移 `.conversation-sidebar`、`.conversation-header`、`.conversation-list` 等块。 2. 按 `LeftPanel` 面板切换结构拆出 `.sidebar-panel-card`、`.panel-switcher`、`.file-tree`、`.todo-panel`、`.subagent-panel`。 3. 将 hover/active/disabled 态封装混入,消除硬编码颜色。 ### 阶段 3:聊天 + 输入域(已完成) 1. 拆分 `.chat-container`、`.chat-scroll`、`.message-item` 及各 action(thinking/text/tool/append/modify/system)的背景、边框、状态类(`.is-streaming`, `.is-collapsed`)。 2. 将 `.compact-input-area`、`.input-actions`、`.quick-menu`、`.tool-settings-menu` 拆到 `input/` 目录,保留动画 `@keyframes submenu-slide`。 3. Chat/Input 共用的 `code` 块、`.copy-code-btn`、`.token-counter` 等交叉样式放在 utilities 或共享 partial。 ### 阶段 4:Overlay/Drawer & 清理(已完成) 1. 迁移 ToastStack、QuotaToast、ConfirmDialog、ContextMenu、Personalization Drawer、EasterEgg overlay 的样式,集中管理 z-index、backdrop-filter。 2. 为 `FocusPanel`、`TokenDrawer`、`UsageDashboard` 等右栏模块建立专属文件。 3. 将 `static/style.css` 改为 `@import '/static/dist/assets/main.css'`,对外暴露单一入口,同时在 `static/old_version_backup/前端备份/style.css` 保留 legacy 真值以便比对。 ## 验证清单 1. `npm run build`、`npm run dev` 均可正常注入 SCSS,无额外 CSS 顺序冲突。 2. 浏览器端逐项对照 `doc/frontend/legacy_ui_spec.md`: - 侧栏展开/折叠宽度 & hover 状态。 - 三合一 Panel 切换 & 文件树/右键菜单层级。 - 聊天消息块的动效(思考流式、压缩动画、复制按钮)。 - Quick Menu 子菜单、工具禁用对话框、设置二级菜单。 - Toast/Quota 警告淡入淡出、个人空间 Drawer 背景模糊。 3. 对彩蛋 CSS(`static/easter-eggs/*.css`)做回归,确保 `AppShell` overlay 的 stacking context 不影响其覆盖。 ## 后续维护建议 1. **新模块接入**:新增组件请优先在 `static/src/styles/components/` 下创建局部 SCSS,再通过 `styles/index.scss` 聚合,避免回落到全局样式。 2. **主题扩展**:如需暗色/高对比主题,可在 `base/_tokens.scss` 内维护多套 map,并通过 `@mixin` 输出 CSS variables。 3. **旧入口兼容**:登录/终端等仍引用 `/static/style.css`,更新后务必同步 `npm run build` 以生成最新 `dist/assets/main.css`,否则旧入口将缺少样式。 4. **快照对比**:大规模改动前可先复制 `static/old_version_backup/前端备份/style.css` 为参考,再利用浏览器 devtools 对比差异,降低视觉回归风险。 ## 注意事项 - **优先级控制**:使用 `@layer base|layout|components|utilities`(Vite 支持原生 CSS Layers)保证与旧版 specificity 相匹配,避免引入 `!important`。 - **变量延续**:保留 `--app-viewport`、`--app-bottom-inset` 等运行时变量,store/composable 仍通过 `document.documentElement.style` 更新这些值。 - **资源路径**:`mask-image`、背景图标继续指向 `/static/icons/*`,拆分后请集中在 `base/_icon.scss` 管理。 - **主题扩展**:如需暗色或高对比方案,可在 `_tokens.scss` 中提供多套 map,使用 `data-theme` 切换;现阶段仅实现 legacy 主题。 - **发布节奏**:每完成一个层级都应在 PR 中附对比截图(侧栏、聊天、输入、Overlay),并同步更新本文件进度记录。 > 完成上述阶段后,CSS 将具备与组件结构一致的模块边界,便于日后按需维护、替换或添加主题,同时保持 `legacy` 品牌体验不变。