Files
lislgosms/docs/global-css-modularization-plan-20260904.md
T
hectorzhao 458ddd97df
CSS quality / css-quality (push) Has been cancelled
refactor: 完成全局CSS模块化治理
2026-09-05 00:45:05 +08:00

458 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# `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、基础层去重 | 12 人日 |
| 2 | 通用 UI 组件拆分 | 24 人日 |
| 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个模块共同组成一条完整且经产物哈希证明等价的级联链,保持为一个可整体回退的原子变更,避免中间提交出现规则重复或缺失。