# `global.css` 分模块治理方案 ## 1. 文档目的 本方案用于治理当前前端全局样式文件 `src/styles/global.css` 体积过大、页面样式归属不清、格式化容易产生大范围无关差异、跨页面覆盖难以追踪的问题。 本方案定义拆分策略、实施批次、验证方法和回退边界。2026-09-05 已按本文完成首次全量治理,实施结果和长期维护方式见 `css-modularization-result-20260905.md`;后续仍须遵守本文的所有权、门禁和验收规则。 本方案与以下规范共同生效: - 根目录 `AGENTS.md`:定义整个仓库必须遵守的 CSS 所有权、禁止事项和验收底线。 - `docs/css-development-guidelines.md`:定义日常新增和修改 CSS 时的归属决策、命名、import、级联和审查规则。 - 本文档:只负责存量 `global.css` 的渐进拆分、验证和回退。 实施顺序必须是“强制规则 → 自动门禁 → 视觉基线 → 存量拆分”。不得先大规模移动 CSS,再补规则和门禁。 ## 2. 当前事实基线 截至 2026-09-04,只读核验结果如下: - `src/styles/global.css`:约 213,062 字节、9,846 行。 - 该文件最早创建于提交 `2f3c274`(`Initial CMPP frontend prototype`),创建时已经约 6,267 行、136KB,并非由一个很小的基础样式文件自然增长而来。 - 最近 30 天约有 30 个提交修改该文件,累计约增加 975 行、删除 274 行,是前端高频冲突文件。 - `src/main.tsx` 当前按顺序全局加载: 1. `tokens.css` 2. `reset.css` 3. `shell.css` 4. `global.css` 5. `admin.css` 6. `client.css` 7. `components.css` - 2026-07-31 的提交 `ca4f591` 曾拆出 `reset.css`、`shell.css`、`admin.css` 和 `client.css`,但拆分没有持续完成。目前 `client.css` 仅 8 行,大量运营端、客户端和业务组件样式仍留在 `global.css`。 - 当前已经存在少量页面级 CSS:短信通道、企业应用、安全检测、短信记录、短信任务进度、系统监控和客户端用户页面。这说明项目已具备页面就近管理样式的实现基础。 当前问题主要影响开发、审查和协作效率,不直接等同于浏览器运行性能问题。 ## 3. 治理目标 ### 3.1 主要目标 1. 将 `global.css` 从业务样式总仓库降级为只减不增的临时兼容入口,最终清空并删除。 2. 每个页面或业务域的样式有唯一、可定位的归属文件。 3. 修改一个页面时,不再需要格式化、审查或合并近万行全局 CSS。 4. 保持现有页面视觉、交互、响应式和打印/导出效果不变。 5. 建立规则,防止新业务样式重新写回全局文件。 ### 3.2 建议量化标准 - 第一阶段完成后:`global.css` 降至 6,000 行以内。 - 第二阶段完成后:`global.css` 降至 2,000 行以内。 - 最终阶段:`global.css` 清零并删除,或仅保留不超过 300 行、经过明确评审的全局兼容规则。 - 单个页面 CSS 建议不超过 800 行。 - 单个业务域 CSS 建议不超过 1,200 行;超过后继续按页面或组件拆分。 - 不允许业务页面通过无前缀标签选择器影响其他页面。 ## 4. 不在本次治理中处理的内容 以下事项不能与 CSS 拆分同时进行: - 页面重新设计、颜色调整、字号调整或布局优化。 - React 组件大规模重构。 - 后端 API、数据库、Redis、短信链路或权限逻辑修改。 - 将全部普通 CSS 一次性改造成 CSS Modules、Tailwind 或 CSS-in-JS。 - 删除看起来“可能无用”但没有运行时证据的选择器。 - 对整份历史 CSS 执行一次性 Prettier 重排并混入拆分提交。 拆分阶段的核心原则是“只改变样式存放位置,不改变最终计算样式”。 ## 5. 目标目录结构 建议继续使用项目当前的普通 CSS 和页面前缀命名方式,不在第一轮引入新的样式技术栈。在通用门禁和所有硬编码路径完成适配前,保留 `global.css` 文件名,行为上将其视为 legacy 文件;不得为了目录整齐过早重命名。 ```text src/ ├─ styles/ │ ├─ tokens.css # 设计变量,仅允许 CSS 自定义属性 │ ├─ reset.css # 标签重置和浏览器一致性 │ ├─ shell.css # AppShell、侧栏、顶栏、导航、页面容器 │ ├─ utilities.css # 少量明确、稳定的工具类 │ ├─ components/ │ │ ├─ buttons.css # 通用 UI Button │ │ ├─ forms.css # Input、Select、Textarea、校验提示 │ │ ├─ modal.css # Modal、Drawer、Popover │ │ ├─ table.css # 通用表格、分页、空状态 │ │ ├─ cards.css # 通用卡片与统计卡 │ │ └─ feedback.css # Tag、Alert、Toast、Loading │ └─ global.css # 拆分过程中的临时剩余区,只减不增,治理后删除 ├─ apps/ │ ├─ admin/ │ │ ├─ reporting/ # 报备资料、批次、字段映射、状态记录 │ │ ├─ enterprise-signatures/ # 企业签名、引流资料、退订/质量状态 │ │ ├─ channels/ # 通道、通道组、通道详情 │ │ ├─ sms-records/ # 短信记录与详情 │ │ ├─ sms-task-progress/ # 批次任务与进度 │ │ ├─ monitoring/ # 系统监控、服务监控、日志 │ │ ├─ risk/ # 风控、黑名单、敏感词、安全检测 │ │ └─ billing/ # 充值、对账、利润与账务页面 │ └─ client/ │ ├─ dashboard/ │ ├─ send/ │ ├─ signatures/ │ ├─ templates/ │ ├─ billing/ │ └─ users/ ``` 目录名称应优先复用现有页面目录,避免仅为样式拆分同步搬动 TSX 文件。 现有 `components.css`、`admin.css` 和 `client.css` 应先完成所有权清单,再决定迁入目录、拆分或保留;不得在旧文件仍承担相同职责时直接创建第二套重复公共样式。 ## 6. 样式归属规则 日常开发以 `docs/css-development-guidelines.md` 的归属决策为准。本节用于拆分时判断存量选择器的目标位置。 | 样式类型 | 目标文件 | 规则 | |---|---|---| | 颜色、间距、字号、圆角、阴影变量 | `tokens.css` | 只定义变量,不放页面选择器 | | `html`、`body`、标题、表单字体继承等浏览器重置 | `reset.css` | 不包含业务类名 | | 侧栏、导航、页面框架、登录框架 | `shell.css` | 仅负责应用骨架,不负责页面业务卡片 | | Button、Input、Modal、Tag、Pagination 等通用组件 | `styles/components/*.css` | 选择器必须与通用组件类名一致 | | 只被一个页面使用的样式 | 页面同目录 CSS | 由该页面 TSX 直接 import | | 同一业务域多个页面共享的样式 | 业务域目录中的共享 CSS | 必须带业务域根前缀 | | 尚未完成归属确认的旧样式 | `global.css` | 行为上作为 legacy 文件,只允许迁出、删除和保持视觉等价所需的最小修正 | 禁止继续向 `global.css` 增加以下内容: - 新页面样式。 - 新业务组件样式。 - 为解决单页问题而增加的无页面根节点限定覆盖。 - 新的 `!important`,除非有单独说明和回归用例。 ## 7. 拆分前置工作 ### 7.1 建立选择器清单 为 `global.css` 中每个顶层选择器记录: - 选择器名称。 - 被哪些 TSX 文件引用。 - 是否存在重复定义。 - 是否出现在媒体查询中。 - 是否依赖前后定义顺序。 - 建议目标文件。 - 是否存在无法确认用途的情况。 不能仅凭类名猜测归属。必须通过 `rg`、构建产物和真实页面 DOM 共同确认。 ### 7.2 建立视觉基线 至少保存以下尺寸下的真实页面截图和关键元素尺寸: - 桌面:1600×1000。 - 常见笔记本:1366×768。 - 窄屏:390×844。 基线页面至少包括: - 运营看板。 - 短信通道列表和报备详情。 - 报备字段库。 - 报备资料池、批次、通道报备明细和状态记录。 - 企业签名管理及查看资料弹窗。 - 短信记录和详情弹窗。 - 系统监控、安全检测和高风险操作弹窗。 - 客户端首页、发送短信、签名、模板、账务和用户页面。 - 运营端与客户端登录页。 使用真实 API 和测试环境数据进行最终验收;隔离截图数据只能用于布局比较,不能替代真实功能验收。 ### 7.3 冻结无关格式化 拆分完成前: - 禁止对 `global.css` 执行整文件 `prettier --write`。 - 每个提交必须检查 `git diff --stat` 和 CSS diff;出现明显超出迁移范围的大量重排立即停止。 - 增加“本提交是否只移动目标选择器”的人工检查项。 - 不通过关闭全部格式检查规避问题;应对新文件执行格式检查,对历史遗留文件采用迁出即格式化策略。 ### 7.4 建立治理基线 正式拆分前必须提交一份机器可读的基线,至少记录: - `global.css` 的字节数、规则数、选择器数和声明数。 - 已知允许暂留的全局基础选择器。 - 已拆分页面的根类名和目标样式文件。 - 当前 CSS import 层级和入口顺序。 - 临时例外的原因、影响范围和清理条件。 基线比较必须使用 PostCSS AST;行数和字节数只作为辅助指标,不能把注释或换行变化误判为新增业务规则。 ## 8. 分阶段实施方案 ### 阶段 0:建立防扩散门禁 目标:在正式拆分前停止继续恶化。 1. 提交根目录 `AGENTS.md` 和 `docs/css-development-guidelines.md`,明确规则已开始生效。 2. 保留 `global.css` 文件名并增加“只减不增”文件头;先不重命名,避免破坏当前硬编码该路径的质量脚本。 3. 扩展增量格式脚本,使新增和修改的 CSS 进入 Prettier 检查。 4. 引入 Stylelint:新文件严格执行;`global.css` 初期使用兼容配置,不要求一次清理全部历史问题。 5. 新增基于 PostCSS AST 的通用静态检查,阻止向 `global.css` 增加页面/业务选择器、无说明 `!important` 和宽泛业务标签规则。 6. 建立增长基线和显式例外清单;无例外时,`global.css` 的规则、选择器、声明和字节数均不得无说明增长。 7. CI 使用目标分支 merge-base 或显式 `QUALITY_BASE_REF` 比较完整提交范围,不能只比较工作区相对 `HEAD` 的未提交差异。 8. 建立 CSS 选择器与页面引用清单,并登记已拆分页面的根类名和所有者文件。 9. 新页面必须使用同目录 CSS;共享样式必须先通过归属判断。 验收标准:规则文档可以被 Codex 和开发者直接执行;本地质量命令与 CI 都能拦截故意加入的违规样例;删除违规样例后全部通过;不改变任何计算样式,所有现有页面和构建结果保持一致。 ### 阶段 1:基础层和应用骨架去重 目标:先迁移风险较低、归属明确的全局基础规则。 1. 对比 `global.css`、`reset.css`、`shell.css`、`components.css` 中重复的基础规则。 2. 将标签级重置统一到 `reset.css`。 3. 将侧栏、顶栏、导航、页面容器和登录框架统一到 `shell.css`。 4. 同名规则只在确认计算样式一致后删除旧定义。 风险重点:加载顺序和相同 specificity 下的后定义覆盖。 ### 阶段 2:通用 UI 组件拆分 目标:将跨页面真正复用的组件从业务样式中分离。 建议依次拆分: 1. 按钮、图标按钮和操作按钮组。 2. Input、Select、Textarea、查询栏和错误提示。 3. Modal、Drawer、确认弹窗和弹窗 Footer。 4. Tag、Alert、Loading、Empty State。 5. Table、Pagination、统计卡和详情列表。 每迁出一类组件,需要搜索所有调用页面,验证 hover、focus、disabled、loading、danger 和窄屏状态。 ### 阶段 3:报备工作台业务域 优先拆分近期最活跃、冲突概率最高的报备相关样式: - `report-*` - `channel-field-*` - `channel-selected-*` - `channel-export-*` - `report-material-*` - `report-batch-*` - 报备资料查看、导入、导出格式选择弹窗 建议建立: ```text src/apps/admin/reporting/ ├─ report-workbench.css ├─ report-field-mapping.css ├─ report-materials.css ├─ report-batches.css └─ report-records.css ``` 本阶段必须特别验证: - 字段池单卡片固定高度。 - 导出字段顺序和按钮宽度。 - 图片预览和历史字段展示。 - 状态记录大屏免横向滚动布局。 - 资料池、批次和明细页面的桌面/窄屏响应式。 ### 阶段 4:企业签名和通道域 拆分: - `signature-*` - `enterprise-signature-*` - `carrier-report-*` - `channel-*` 中不属于报备字段弹窗的通道管理样式 - 通道组、通道状态、质量矩阵和签名三网状态样式 需要先区分“通道管理共享样式”和“报备工作台专用样式”,避免仅按 `channel-` 前缀批量移动。 ### 阶段 5:短信、任务、监控和风险域 按现有页面目录继续迁移: 1. 短信发送、短信记录和发送详情。 2. 批次任务、任务进度和下游恢复/重投页面。 3. 系统日志、系统监控和服务指标。 4. 黑名单、敏感词、风险规则和安全检测。 5. 充值、对账、利润与账务页面。 已存在页面 CSS 的模块先做“从 global.css 迁出遗漏规则”,不重新创建第二份页面样式文件。 ### 阶段 6:客户端域 将 `client.css` 从当前 8 行的占位文件调整为客户端共享层,并优先把页面专用规则放到具体客户端页面目录。 重点防止运营端和客户端使用相同短类名时互相覆盖。共享规则必须确认视觉语义一致,否则分别使用 `.admin-*` 和 `.client-*` 根前缀。 ### 阶段 7:清理遗留文件 1. 对剩余选择器逐项确认引用。 2. 无引用选择器必须经过构建、DOM 检查和真实页面核验后按小批次删除。 3. 检查重复媒体查询、重复属性和无效覆盖。 4. 删除空的 `global.css` 及其入口引用。 5. 更新项目开发规范和测试进度。 ## 9. 单批次迁移方法 每一批只处理一个页面或一个可独立验证的组件族: 1. 记录目标选择器及其原始行号。 2. 搜索 TSX 使用位置和其他 CSS 中的同名定义。 3. 将完整规则连同伪类、子选择器和媒体查询一起移动。 4. 在目标页面或业务域入口显式 import 新 CSS,并确保页面具有唯一根类名命名空间。 5. 保持选择器文本、属性顺序和 import 后的级联顺序不变。 6. 运行定向测试、TypeScript 和生产构建。 7. 在三个尺寸下比较迁移前后页面。 8. 检查浏览器控制台、网络请求、滚动和弹窗层级。 9. 独立提交,不夹带业务代码修改。 修改共享样式时必须列出全部已知消费者,并逐一检查关键状态;不能只验证提出本次修改的页面。 禁止使用正则一次性移动所有相同前缀,因为同一前缀可能跨多个页面或业务域。 ## 10. 级联和加载顺序策略 CSS 拆分最大的风险不是文件移动本身,而是加载顺序变化。 建议最终固定顺序: ```text tokens → reset → shell → shared components → business shared → page styles ``` 注意事项: - 相同 specificity 的规则依赖“后加载覆盖”,移动前必须记录原顺序。 - 页面级 CSS 由懒加载页面 import 时,要验证首次进入、刷新、跨页面跳转后的样式一致性。 - SPA 中已经加载的页面 CSS 通常不会在离开路由后卸载,因此页面样式必须位于唯一页面根类名下,不能把正确性建立在“当前页面最后加载”之上。 - 不依靠提高 specificity 或新增 `!important` 掩盖顺序错误。 - 公共组件样式不能反向依赖业务页面样式。 - 媒体查询应跟随其所属组件迁移,不集中到新的全局响应式文件。 ## 11. 测试与验收要求 ### 11.1 自动化检查 每批至少执行: - 与页面匹配的 Vitest/Testing Library 用例。 - 前端全量测试。 - TypeScript 检查。 - Vite 生产构建。 - Bundle 预算检查。 - CSS/代码格式检查,仅覆盖新文件和本批实际修改文件。 - Stylelint;新文件执行严格规则,遗留文件执行过渡规则。 - `global.css` AST 增长和禁止选择器检查。 - 页面根类名、样式所有者和 import 关系检查。 - `git diff --check`。 - 选择器引用检查和重复选择器报告。 门禁实现要求: - 本地增量检查可覆盖 staged、unstaged 和 untracked CSS;提交前以 staged diff 为最终范围。 - CI 必须比较目标分支 merge-base 到当前提交,不能依赖默认 `HEAD` 导致已提交变化漏检。 - `global.css` 增长判断以 PostCSS AST 为主,行数和字节数为辅。 - 例外采用仓库内显式清单,至少包含选择器或规则、原因、影响范围和清理条件;不接受只有提交信息中的“临时处理”。 - 门禁脚本自身必须包含允许样例和拒绝样例测试。 ### 11.2 浏览器验收 构建成功不能等同于拆分成功。必须检查: - 真实页面是否加载对应 CSS 资源。 - 页面首次进入和路由切换后的样式是否一致。 - loading、空数据、失败、权限不足和历史数据状态。 - Modal、Drawer、下拉框、Sticky 表头和滚动容器。 - hover、focus、disabled、selected 和 danger 状态。 - 桌面端、笔记本和窄屏。 - 浏览器控制台无新增错误和样式资源 404。 - 页面没有新增横向溢出、遮挡或层级错误。 ### 11.3 视觉差异判定 - 纯文件拆分批次的目标是关键页面像素级无变化。 - 允许字体抗锯齿等环境噪声,但布局尺寸、间距、颜色、折行、滚动和交互状态必须一致。 - 发现视觉变化时优先恢复原级联顺序,不允许顺手“优化得更好看”后继续合并。 ## 12. Git 与协作策略 - 每个提交只拆一个页面或一个组件族。 - 提交名称示例:`refactor: extract report field mapping styles`。 - 提交前检查 staged diff,只包含目标 CSS、新 import、测试和文档。 - 不在拆分提交中执行全仓库格式化。 - 不删除或覆盖其他会话的 staged、unstaged、untracked 文件。 - CSS 门禁例外必须与对应变更同批审查;不得用扩大例外范围的方式让无关规则通过。 - 主分支持续开发时,优先迁移冲突最频繁的业务域,并缩短分支存活时间。 - 每批可独立 `git revert`,不要求整体回退全部拆分阶段。 ## 13. 风险与应对 | 风险 | 表现 | 应对 | |---|---|---| | CSS 加载顺序变化 | 颜色、间距或布局悄然变化 | 固定 import 顺序并比较 computed style | | 选择器漏迁 | 某些弹窗或窄屏状态失去样式 | 伪类、子选择器、媒体查询按选择器族迁移 | | 同名选择器跨页面复用 | 拆走后其他页面异常 | TSX/CSS 双向搜索,无法确认时暂留 legacy | | 懒加载 CSS 顺序不稳定 | 跨页面跳转后样式变化 | 页面根节点命名空间隔离,验证刷新和跳转 | | 整文件格式化污染 diff | 数千行无关修改 | 禁止格式化 legacy,迁出后只格式化新文件 | | 误删历史样式 | 历史数据或低频页面显示异常 | 删除单独分批,真实历史数据页面复验 | | 拆分期间业务需求冲突 | 合并困难、重复搬运 | 小提交、短周期、按高频业务域优先实施 | ## 14. 回退方案 每个迁移批次的回退单位是对应独立提交: 1. 浏览器验收失败时停止后续拆分。 2. 保留截图、控制台和具体选择器差异证据。 3. 回退当批提交,恢复原 import 和原文件位置。 4. 不通过追加高优先级覆盖掩盖错误。 5. 修正选择器归属或加载顺序后重新提交该批次。 CSS 拆分不涉及数据库迁移、服务配置或短信链路,因此不应触发数据库回滚、队列处理或短信重投。 ## 15. 建议实施顺序与工作量 | 阶段 | 内容 | 预计工作量 | |---|---|---:| | 0 | AGENTS、日常规范、清单、视觉基线、防扩散门禁 | 2~3 人日 | | 1 | Reset、Shell、基础层去重 | 1~2 人日 | | 2 | 通用 UI 组件拆分 | 2~4 人日 | | 3 | 报备工作台 | 2~3 人日 | | 4 | 企业签名和通道 | 2~3 人日 | | 5 | 短信、任务、监控、风险、账务 | 3~5 人日 | | 6 | 客户端页面 | 2~3 人日 | | 7 | 遗留清理、全量回归和规范固化 | 2~3 人日 | 整体建议按 2~4 周的渐进式治理安排实施,而不是一次性大提交。实际工期取决于测试环境登录态、历史数据覆盖和视觉回归工具可用性。 ## 16. 完成标准 只有同时满足以下条件,才能认为治理完成: 1. `global.css` 已删除,或只保留经过批准且不超过 300 行的真正全局规则。 2. 所有业务样式均有清晰的页面或业务域归属。 3. 新业务样式写入遗留全局文件会被门禁阻止。 4. 根目录 `AGENTS.md` 和日常 CSS 规范已成为当前生效规则,自动门禁与其一致。 5. CSS 已纳入增量 Prettier、Stylelint、所有权和 AST 增长检查,本地及 CI 均能识别完整变更范围。 6. 前端全量测试、TypeScript、生产构建和包体积检查通过。 7. 核心运营端、客户端页面在三种尺寸下完成真实浏览器回归。 8. 浏览器控制台和网络请求无新增错误,CSS 资源无 404。 9. 报备状态记录的大屏布局、字段池固定高度、Sticky 表头和所有关键弹窗没有回归。 10. `docs/testing-progress.md` 和相关系统功能用例已同步更新。 ## 17. 2026-09-05 实施状态 - `src/styles/global.css` 已删除,入口替换为 `src/styles/domains/index.css`;原1749条规则、2017个选择器、5321条声明和30个既有`!important`按原顶层节点顺序迁入14个业务域文件。 - 迁移前后Vite主CSS产物名称、字节内容和SHA-256完全一致,证明本次只改变源码所有权与文件边界,没有改变最终CSS级联结果。 - `tools/quality/css-governance-baseline.json`保存原始AST指标、完整选择器顺序、模块所有者、导入顺序和模块摘要;`verify-css-governance.mjs`验证旧文件不得重建、AST总量及顺序不变、既有`!important`不得增长、新CSS不得增加宽泛标签选择器、所有CSS必须存在明确import。 - CSS已进入增量Prettier和Stylelint。迁出的历史规则使用显式兼容清单保留现状;未登记的新CSS默认执行Stylelint标准规则,因此兼容配置不会自动扩散到新页面。 - 新增GitHub CSS质量工作流,以目标分支或父提交作为`QUALITY_BASE_REF`,避免只检查未提交工作区导致已提交违规变化漏检。 - 原方案建议逐批迁移;本次为满足一次性执行要求,采用单次原序抽取。由于14个模块共同组成一条完整且经产物哈希证明等价的级联链,保持为一个可整体回退的原子变更,避免中间提交出现规则重复或缺失。