# 前端 UI 开发规范（给 Gemini）

> 你（Gemini）**只负责前端 UI**。这份文档是你动手前的唯一须知，请**先完整读一遍再改任何代码**。
> 本项目是 **Kanboard 1.2.52**（开源 PHP 看板）的二次开发，分支 `fix-newUI`。
> 代码根目录：`W:\kanban\kanboard-1.2.52\`（下文所有相对路径都以此为基准）。
> 文档里说的"屎山"指：同一样式散落多处互相打架、死代码、内联样式乱塞、写死颜色/宽度。**本规范的全部目的就是不让它再变成屎山。**

---

## 0. 三条铁律（违反任何一条 = 制造屎山）

1. **所有 CSS 只写在 `assets/css/custom_dashboard.css`，并放进正确的编号区块。** 严禁在 `.php` 模板里内联 `<style>`，严禁新建别的 css 文件。
2. **同一个东西只允许有一处样式来源。** 改样式先去对应区块找现成的改，别另起一段重复定义（重复 = 下次没人知道哪段生效）。
3. **改颜色一律用变量，不准写死色值。** 主题色变量在 `assets/css/custom_login.css` 的 `:root` 里（如 `--login-bg`、`--login-card-bg`、`--color-primary`）。

---

## 1. 怎么运行 / 怎么看到你的改动

- 应用跑在 **`http://127.0.0.1:8000`**，由本地 PHP 实时读磁盘文件提供服务（双击 `W:\kanban\start-kanban.bat` 启动；那个黑窗口要一直开着）。
- 改完文件**不需要清缓存**：框架会给 css/js 的 URL 自动加 `?文件修改时间` 戳，存盘即换新。
- ⚠️ **弹窗（modal）内容是 AJAX 加载的，不是随页面一起来的。** 所以看弹窗的改动要：
  1. 先 **F5 刷新整个页面**（让 `<head>` 里的新 CSS 重新加载）
  2. **再重新打开那个弹窗**（光 F5 不重开弹窗，看到的还是旧弹窗 DOM）

### 「改了看不见」自查清单（按顺序排查，别瞎猜缓存）
1. 改的是不是 `custom_dashboard.css` 的**正确区块**？（不是某个模板里的内联样式 / 不是 `.min.css`）
2. 有没有 **F5 + 重新打开弹窗**？
3. 服务器还在跑吗？（浏览器 `ERR_CONNECTION_REFUSED` = 8000 端口的服务进程被关了，重开 `start-kanban.bat`，**这跟你的代码无关**）
4. 是不是被**核心写死的样式用更高特异性盖住了**？见第 5 节。

---

## 2. 你能碰 / 绝对不能碰的文件

### ✅ 允许修改（前端 UI 全在这几处）
| 文件 | 作用 |
|---|---|
| `assets/css/custom_dashboard.css` | **全站自定义样式总入口**（登录页除外）。你 95% 的工作在这里 |
| `assets/css/custom_login.css` | 登录页样式 + **全站主题色变量 `:root`** |
| `assets/js/custom_dashboard.js` | UI 行为脚本（label 转 placeholder、定位"指派给我"等） |
| `app/Template/` 下的自定义模板（见第 4 节） | 弹窗/页面的 HTML 结构 |

### ⛔ 绝对不要改（改了要么白改，要么炸）
- `assets/js/app.min.js`、`assets/js/vendor.min.js` —— **压缩文件，没有源码，无法维护**。Kanboard 弹窗的结构和行为就藏在 `app.min.js` 里，**别手改它**；要调 UI 行为就写在 `custom_dashboard.js`。
- `assets/css/*.min.css`（`vendor` / `light` / `dark` / `auto` / `print`）—— 核心主题，只读。
- `vendor/`、`app/Model/`、`app/Controller/`、`app/Core/`、`data/db.sqlite` —— 后端/数据，不归你管。

---

## 3. `custom_dashboard.css` 的结构（必须维护好目录）

文件**顶部有一个编号目录（TOC）**，正文按编号分区块。**新增样式时：放进对应编号区块；如果开了新主题，更新顶部 TOC。**

```
1.  全局背景
2.  顶栏 Header（logo / 标题 / 项目切换器）
3.  侧边栏 Sidebar
4.  页面头部标签 Page header
5.  列表 / 卡片 / 提示 / 搜索框
6.  顶栏注入组件（视图切换 / 过滤）
7.  项目概览仪表盘（KPI + 标签页）
8.  看板 Kanban board
9.  任务【编辑】弹窗表单布局  (.task-form-*)
10. 任务【新建】弹窗表单布局  (.custom-task-form-*)
11. 弹窗通用外观（所有 #modal-box）
```

### 「改什么 → 改哪里」速查
| 你想改 | 去这里 |
|---|---|
| 任意弹窗的外壳（宽度/圆角/遮罩/关闭按钮/通用按钮） | `custom_dashboard.css` **§11** |
| **新建任务**弹窗的表单布局 | `custom_dashboard.css` **§10** |
| **编辑任务**弹窗的表单布局 | `custom_dashboard.css` **§9** |
| 主题色 | `custom_login.css` 的 `:root`（亮色）和 `@media (prefers-color-scheme: dark)`（暗色） |
| 弹窗内字段的行为（隐藏 label、占位符等） | `custom_dashboard.js` |
| 弹窗的 HTML 结构（加/删字段、换列） | 对应模板（第 4 节），**样式仍写回 css** |

---

## 4. 关键模板 & 一个必须知道的"两套表单"陷阱

| 模板 | 是什么 |
|---|---|
| `app/Template/layout.php` | 全站骨架，负责加载 css/js（一般别动） |
| `app/Template/header/title.php` | 顶栏标题区 |
| `app/Template/header/custom_board_selector.php` | 项目切换下拉 |
| `app/Template/project_overview/show.php` | 项目概览（KPI 卡 + 纯 CSS 标签页） |
| `app/Template/task_creation/show.php` | **新建任务**弹窗（已去内联样式，只剩 HTML） |
| `app/Template/task_modification/show.php` | **编辑任务**弹窗 |

### ⚠️ 陷阱：新建 和 编辑 是两套不同的类名，别搞混！
- **新建任务**（`task_creation/show.php`）用 `.custom-task-form-container` / `.custom-task-form-main` / `.custom-task-form-secondary` / `.custom-task-form-tertiary`，样式在 **§10**。
- **编辑任务**（`task_modification/show.php`）用 `.task-form-container` / `.task-form-main-column` / `.task-form-secondary-column` / `.task-form-bottom`，样式在 **§9**。
- 改其中一个弹窗，**只动它对应那套类名**；想两个一起改，两节都要改。**别把 `.task-form-*` 的规则套到新建弹窗上（反之亦然），那是无效的死代码。**

---

## 5. Kanboard 核心避坑大全（不知道这些 = 改半天找不到原因）

### 5.1 核心给很多控件写死了像素宽度 —— 放进网格/弹窗会撑爆或太窄
在三列网格里，这些写死宽度会把整张表撑出**横向滚动条**。已知清单：

| 选择器 | 核心写死的宽度 | 出现在 |
|---|---|---|
| `.text-editor textarea` / `.text-editor .text-editor-preview-area` | **700px** | 描述（markdown 编辑器） |
| `input[type="text"]:not(.input-addon-field)` | 300px | 普通文本框 |
| `.form-date` / `.form-datetime` | 150px | 到期/开始时间 |
| `.form-input-small` | 150px | 参考 |
| `.form-input-large` / `.tag-autocomplete` | 400px | 标签等 |
| `.color-picker` | 220px | 颜色下拉 |
| `.task-form-main-column input[type="text"]` | 700px | 编辑弹窗标题 |

**处理原则：在表单容器里把它们强制改成 `width:100% !important; max-width:100% !important`，让它随所在列自适应。** §10 已经对编辑器和这些字段做了覆盖，照着那个写法加即可，**不要去改核心 `.min.css`**。

### 5.2 弹窗的"滚动层"是 `#modal-box`，不是 `#modal-content`
核心：`#modal-box { overflow:auto }`。所以想处理弹窗的横向溢出，要针对 `#modal-box`，针对 `#modal-content` **没用**（这是上次踩了很久的坑）。
- 已有一条**精准收口**：`#modal-box:has(.custom-task-form-container)`（用 `:has()` 只作用于任务表单弹窗，不影响其它可能需要横向滚动的弹窗）。**收溢出前要先确认右侧没有真内容被藏起来**，否则就是"裁掉内容糊弄"，不允许。

### 5.3 CSS 特异性：你的规则可能被核心更高特异性的规则盖住
例：核心 `input[type="text"]:not(.input-addon-field).form-date{width:150px}` 的特异性是 (0,3,1)，会**压过**你写的 `.custom-task-form-container input[type="text"]{...}`（0,2,1）。
- 现象就是"我明明写了 width 却不生效"。
- 解决：把选择器写得更具体（带上具体类名，如 `.custom-task-form-container input.form-date`）或在该属性上加 `!important`。**`!important` 只在确实被核心盖住时用，不要全文乱撒。**

### 5.4 样式加载顺序（`layout.php`）
`vendor/theme.min.css` → `custom_login.css` → **`custom_dashboard.css`** → DB 自定义 CSS（目前为空）。
你的 `custom_dashboard.css` 在主题之后加载，同特异性下能赢核心。变量来自 `custom_login.css`（在它之前加载，所以变量可用）。

---

## 6. 标准改动流程（每次都这样做）

1. **定位**：先按第 3 节速查表找到该改的区块/文件，去看现有规则，**在原处改**。
2. **不破坏现状**：尽量不动其它区块；删任何东西前，先确认它没被别的模板/页面引用（两套表单类名见 4 节）。
3. **用变量**：颜色用 `:root` 变量，不写死。
4. **验证**：F5 + 重新打开弹窗，亲眼确认效果；顺手看一眼别的页面/弹窗没被你连累。
5. **保持整洁**：新增区块要在 TOC 登记；写一句注释说明"这段管什么"。
6. **括号配平**：CSS 大括号要成对，别留半截规则。

---

## 7. 当前已知待办（都是 JS 行为，CSS 改不动它们）
这些在新建任务弹窗里，属于 `custom_dashboard.js` / 核心 JS 的行为问题，**别试图用 CSS 硬怼**：
- 游离的"我"（指派给我链接）定位错乱 → `custom_dashboard.js` 第 58–79 行
- 标题下红色 `*` 没被隐藏 → `custom_dashboard.js` 第 46–47 行
- 部分字段 label→占位符转换不干净 → `custom_dashboard.js` 第 6–44 行
- 颜色选择器弹层定位 → 核心 `app.min.js`（最难，建议最后碰）

---

## 8. 一句话总结
**CSS 全进 `custom_dashboard.css` 的对应编号区块；颜色用变量；别碰 `.min.js`/`.min.css`；新建和编辑是两套类名别混；核心写死的宽度要在表单里覆盖成 100%；改完 F5＋重开弹窗验证。** 守住这些，就不会再变屎山，也不会再出"改了看不见"的怪事。
