agentcss 0.11.2 → 0.11.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/01-tokens/README.md +2 -2
- package/01-tokens/focus-ring.css +1 -1
- package/01-tokens/tokens.css +1 -1
- package/03-patterns/nav/README.md +1 -1
- package/03-patterns/pagination/pagination.css +2 -2
- package/03-patterns/reveal/README.md +3 -3
- package/04-recipes/analysis-report/demo.html +2 -2
- package/04-recipes/longform/README.md +1 -1
- package/04-recipes/longform/longform.html +6 -10
- package/04-recipes/report/demo.html +1 -1
- package/04-recipes/table/demo.html +1 -1
- package/04-recipes/table/table.css +1 -1
- package/04-recipes/tierA-tokens.css +1 -1
- package/09-assets/README.md +1 -1
- package/09-assets/bar/README.md +1 -3
- package/09-assets/bar/bar.css +2 -2
- package/09-assets/bar/bar.js +6 -6
- package/09-assets/bar/demo.html +2 -2
- package/09-assets/echarts-adapter/echarts-adapter.js +8 -8
- package/09-assets/model-viewer/model-viewer.css +2 -2
- package/09-assets/scientific-plot/scientific-plot.js +1 -1
- package/CHANGELOG.md +92 -34
- package/CONTRIBUTING.md +1 -1
- package/README.md +160 -365
- package/README.zh-CN.md +508 -0
- package/SUPPORT.md +2 -2
- package/ai/START-HERE.md +1 -1
- package/ai/components.json +2 -2
- package/ai/components.meta.json +1 -1
- package/ai/tokens.json +1 -1
- package/dist/09-assets/bar/bar.js +1 -1
- package/dist/09-assets/echarts-adapter/echarts-adapter.js +1 -1
- package/dist/09-assets/scientific-plot/scientific-plot.js +1 -1
- package/dist/COSTS.md +9 -9
- package/docs/INVARIANT.md +1 -1
- package/index.html +77 -62
- package/package.json +3 -3
- package/ERRORS.md +0 -1658
- package/docs/BACKLOG.md +0 -127
- package/docs/BENCHMARK.md +0 -252
- package/docs/GUIDANCE.md +0 -154
- package/docs/RELEASING.md +0 -275
- package/docs/RFC-THEME-SCOPE.md +0 -141
- package/docs/ROADMAP.md +0 -182
package/docs/BACKLOG.md
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
# 待办与判定台账(BACKLOG)
|
|
2
|
-
|
|
3
|
-
> 这份文件只回答三个问题:
|
|
4
|
-
> **① 那些"好想法"哪些已经落地、由什么守着;② 哪些裁定不做、什么条件下重开;
|
|
5
|
-
> ③ 哪些是环境边界(不是待办,别再当 TODO 挂着)。**
|
|
6
|
-
>
|
|
7
|
-
> 它的存在理由:这些结论原本散在四份报告里(交付报告 / huxx 借鉴评审 /
|
|
8
|
-
> 合并评审 / 交接单),要翻完才知道还剩什么。于是同一件事被反复讨论、
|
|
9
|
-
> 已被判"不做"的又被重新提出来做一遍。
|
|
10
|
-
>
|
|
11
|
-
> ⚠️ **本文件不写会变的数字**(门禁数、自检数、组件数)。要给数字,
|
|
12
|
-
> 就跑对应的门禁看它的输出 —— 数字只有运行时一个出处(I-7)。
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
## 一、已落地(每一项都有门禁守着,不是"写过")
|
|
17
|
-
|
|
18
|
-
| 能力 | 落点 | 守着它的判据 |
|
|
19
|
-
|---|---|---|
|
|
20
|
-
| 入场动效(六方向、位移 ≤8px、默认不接管、只播一次) | `03-patterns/reveal/` | `reveal-contract.js`(10 条)+ `motion.py --reduced-audit` |
|
|
21
|
-
| 减少动效三档(位移必停 / 真实进度必留 / 颜色压到 `--dur-fast`) | `01-tokens/` 令牌层一条覆盖规则 | `motion.py`(三向核对,非文件计数) |
|
|
22
|
-
| 详情页三变体(full / partial / null 同构) | `04-recipes/detail-page/` | `detail-page-contract.py`(标签序列逐项比对 + 视觉覆盖) |
|
|
23
|
-
| 无数据是一等状态(不隐藏、不拿 0 冒充、必须给理由、版面不塌) | 同上 `null.html` | `no-data-gate.py` |
|
|
24
|
-
| 新页面必须进视觉基线(防"新配方无人看管") | `05-audit/visual-coverage-gate.py` | 自身 `--selftest` + 反向控制 |
|
|
25
|
-
| 视觉基线防"改注释也算变更" | `shot-baseline.py` 的 `css_fingerprint()` | 剥注释/去空行后再哈希 |
|
|
26
|
-
| 包内闭包 + 消费者侧解析 | `pack-closure.js` / `entry-resolve.js` | `pack-closure` / `pkg-resolve`(判"消费者真去解析") |
|
|
27
|
-
| 自动初始化写成契约(每个全局逐个写明 是 / 条件 / 否) | `API.md`「自动初始化」+ 源码 `@autoinit` 标记 | `autoinit-gate.py`(三条判据,双向) |
|
|
28
|
-
| 门禁自己也要有牙(反向控制被真的跑到) | `behavior-reverse.sh` + `selftest-all.py` | `selftest-all`(元门禁)+ `selftest-all-rev` |
|
|
29
|
-
| 文档事实与仓库一致(组件数 / 门禁数 / stable·beta) | `05-audit/doc-facts-gate.py` | 自身 `--selftest` |
|
|
30
|
-
| 真机能力矩阵(每一行都是 VERIFIED) | `docs/LAYER-BASELINE.md` | `webview-probe-gate.py`(清单与源码双向) |
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
## 二、本轮(autoinit 契约那一轮)做的事
|
|
35
|
-
|
|
36
|
-
| 事 | 性质 | 证据 |
|
|
37
|
-
|---|---|---|
|
|
38
|
-
| `selftest-all` 自备 `:8000`(canary 字节比对 + 退出时关掉) | **修真实缺陷**:fast 档无服务 ⇒ 一条自检假红,红的原因与检查内容无关 | `python 05-audit/selftest-all.py` → 全部通过 |
|
|
39
|
-
| 判定抽成 `verdict()` + 判据 ④「跑不起来 ≠ 通过」 | 新增牙:`RC=0` 但输出说跑不起来,判红 | 自带 `--selftest` 样本 |
|
|
40
|
-
| `dead-class-gate --selftest` 接进 `behavior-reverse.sh` | 补 P2 欠账:它有自检但**从没被跑过** | `behavior-reverse.sh` |
|
|
41
|
-
| 自动初始化契约 + `autoinit-gate` | 把 P3 的误判转成真闭环:**不改行为**,只补文档与判据 | 每个破坏样本都判对 |
|
|
42
|
-
| `CONTRIBUTING` 两处过时数字 | 一处被 `doc-facts` 抓到,一处(散文标题 `159 道`)判据够不着 ⇒ 改成不写数字 | `doc-facts-gate` 绿 |
|
|
43
|
-
| `ERRORS.md` E56 / E57 | 判据前提要自己造;注释不写会变的数量 | 台账 |
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## 三、裁定不做(附重开条件)
|
|
48
|
-
|
|
49
|
-
| 事项 | 为什么不做 | 什么条件下重开 |
|
|
50
|
-
|---|---|---|
|
|
51
|
-
| **T4 全局事件总线** | 没有第二个消费者(图表不订阅主题切换)。为一个假想消费者加全局单例,是把"零依赖"换成"零依赖但有隐含全局状态" | 出现**第二个真实消费者**(例如表格排序要通知图表重绘),且实现 ≤30 行 |
|
|
52
|
-
| **View Transitions** | 真机基线含 **Chromium 97**(百度浏览器),而它要 111+。收益是"更好看"不是"更能用" | 有 Chromium 97–111 区间的实测基线,且能给出降级路径 |
|
|
53
|
-
| **装饰层兄弟门禁**(`deco-sibling`) | 库里只有 **1 个**装饰层节点(`.detail-hero__deco`),且它已经做对了(`pointer-events:none` + `aria-hidden` + 空 div)。门禁判据只能靠 `deco` 这个**命名约定**触发 —— 第二个人把那层叫 `.hero__glow` 就漏了。**靠命名的守卫是弱判据**,不如不建 | 出现第 2 个装饰层,且命名不统一 ⇒ 改成**形态判据**(绝对定位 + 覆盖父容器 + 无文本/无交互子树),不再看名字 |
|
|
54
|
-
| **滚动位置还原**(S5) | 实测三组 A/B/C 全部 `1600 → 1600`:桌面 Chromium 后退走 **bfcache** 整页恢复,脚本那段根本不需要跑。按"无法实测不合并"回退,没有留死代码 | 有**不走 bfcache** 的目标(旧 WebView),或页面返回后必然重渲染 |
|
|
55
|
-
| **给 9 个 create-only 组件加自动初始化**(P3 的 A 方案) | 自动初始化拿不到 `options` ⇒ 给一个"看起来能用、行为不对"的默认实例,比不初始化更坏;且会与用户手写的 `create` **双初始化**(双重事件、泄漏) | 组件出现"零配置也能正确工作"的形态(例如全部改由 `data-*` 驱动),且能给出去重机制 |
|
|
56
|
-
| **把散文里的数字也纳入 doc-facts** | 实测全库 6 处「NNN 道」,其中 4 处是**历史事故记录**(本来就是旧数字,改了才是篡改)。扩宽正则只会误伤历史叙述 —— 判据咬错人比不咬更坏 | 文档里出现"当前值"与"历史值"可区分的写法约定(例如统一 `@fact:` 标记),再据标记判 |
|
|
57
|
-
|
|
58
|
-
---
|
|
59
|
-
|
|
60
|
-
## 四、环境边界(不是待办,别再挂 TODO)
|
|
61
|
-
|
|
62
|
-
这三件事**代码侧已经就绪或已到边界**,缺的是外部条件。写在这里是为了
|
|
63
|
-
下次不再重新讨论一遍"要不要做""是不是没做"。
|
|
64
|
-
|
|
65
|
-
### 4.1 真机覆盖:只到现有矩阵,不再扩
|
|
66
|
-
|
|
67
|
-
| 已有(全部 VERIFIED) | 缺口 |
|
|
68
|
-
|---|---|
|
|
69
|
-
| iPhone Safari / iPad Safari / iPad 微信 / iPad 企微 / Android 微信(XWEB 146)/ Android 百度浏览器(Chromium 97) | Android **系统浏览器 / 其它 App 内置内核**(抖音、小红书、支付宝…) |
|
|
70
|
-
|
|
71
|
-
**为什么不再扩**:国内环境下拿不到稳定的 Android 系统浏览器与 Google 组件,
|
|
72
|
-
云真机又要实名与费用;使用者能提供的机器已经全部采过。
|
|
73
|
-
|
|
74
|
-
**在现有条件下已经做到了什么(这些不需要新设备)**:
|
|
75
|
-
|
|
76
|
-
1. **语法下限**:`es5-gate.py` 保证库代码不会用到 Chromium 97 不认识的语法。
|
|
77
|
-
2. **特性探测而非 UA 猜测**:`@layer` 不可用时整层回退(`LAYER-BASELINE.md` 已记录
|
|
78
|
-
Chromium 97 **不支持 `@layer`** 这条阻塞项,并按"每一行都通过才无条件切"处理)。
|
|
79
|
-
3. **能力探针页可复用**:`10-review/webview/capability-probe.html` 是自包含的,
|
|
80
|
-
任何人用手机自带浏览器打开就能采一行;采集结果按 `LAYER-BASELINE.md`
|
|
81
|
-
的表头填(必须写清**从哪个入口**打开)。
|
|
82
|
-
4. **不虚构结论**:没有实测的内核一律标 `UNVERIFIED`,不写成"应该没问题"。
|
|
83
|
-
|
|
84
|
-
### 4.2 npm 发布
|
|
85
|
-
|
|
86
|
-
代码与发布脚本已就绪(`05-audit/publish.sh`,`.npmrc` 钉官方源)。
|
|
87
|
-
缺的是账号 + 2FA 的人工动作。**注意历史坑**(`ERRORS.md` E55):
|
|
88
|
-
publish 返回 0 之后 registry 可能仍显示旧占位,**不要据此删包**。
|
|
89
|
-
|
|
90
|
-
### 4.3 W13 真模型跑批
|
|
91
|
-
|
|
92
|
-
工具链封闭(记录 v2 / 八条判据 / `models/reference.json` 必须 10/10),
|
|
93
|
-
**记录数 0 ⇒ UNVERIFIED**。缺的是模型 API 访问权。现有结论("一次生成半分到
|
|
94
|
-
一毛钱,瓶颈是轮次不是 token")来自人工实测,不是跑批。
|
|
95
|
-
|
|
96
|
-
---
|
|
97
|
-
|
|
98
|
-
## 五、如果还要往前推,按性价比排序
|
|
99
|
-
|
|
100
|
-
| 顺序 | 事项 | 为什么值得 | 前置 |
|
|
101
|
-
|---|---|---|---|
|
|
102
|
-
| 1 | **把 `docs/ROADMAP.md` 的评分表按真值重写** | 它现在还写着「运行时描述符比对待做(W3)」「npm ci + WebKit 待做(W7)」「事实门禁待做(W4)」,而这三项**实测都已完成**(`api-form-gate` 消费 `global-api-probe`;CI 有 `npm ci` + nightly webkit 矩阵;`doc-facts-gate` 在跑)。过时的路线图会让下一个人重做一遍 | 无 |
|
|
103
|
-
| 2 | 真机矩阵补一行(任何人的手机自带浏览器) | 成本 5 分钟,直接减少一个 `UNVERIFIED` | 一台能扫二维码的手机 |
|
|
104
|
-
| 3 | 把 `capability-probe` 的采集流程写成一页 SOP | 现在采集格式只写在表格里,靠人猜 | 无 |
|
|
105
|
-
| 4 | 次要:`ERRORS.md` 按"触发面"重新索引 | 查"我是不是又要犯某条"很不方便 | 无 |
|
|
106
|
-
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
## 六、怎么复查这份台账
|
|
110
|
-
|
|
111
|
-
```bash
|
|
112
|
-
cd <仓库根>
|
|
113
|
-
|
|
114
|
-
# ① 已落地的东西还在不在(全量,约 10 分钟)
|
|
115
|
-
MODE=full bash 05-audit/check-all.sh
|
|
116
|
-
|
|
117
|
-
# ② 判据有没有牙(含本轮新增的 autoinit / selftest-all / dead-class)
|
|
118
|
-
python 05-audit/selftest-all.py
|
|
119
|
-
bash 05-audit/behavior-reverse.sh
|
|
120
|
-
|
|
121
|
-
# ③ 文档说的和仓库是不是一回事
|
|
122
|
-
python 05-audit/doc-facts-gate.py
|
|
123
|
-
python 05-audit/autoinit-gate.py
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
**判定纪律**:本文件里说"已落地"的条目,如果哪天门禁红了,以门禁为准 ——
|
|
127
|
-
台账只是索引,不是证据。
|
package/docs/BENCHMARK.md
DELETED
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
# 对标基准
|
|
2
|
-
|
|
3
|
-
> 我们抄了谁、故意不抄谁、以及每一条背后的前提。
|
|
4
|
-
>
|
|
5
|
-
> 这份文档存在的唯一理由:**让"这里为什么是这样"有据可查**。
|
|
6
|
-
> 没有它,同一个问题会在半年后被重新争论一遍——而争论的双方都拿不出当时的理由。
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
## 这份文档怎么用
|
|
11
|
-
|
|
12
|
-
对标一个项目时问三件事,按顺序问:
|
|
13
|
-
|
|
14
|
-
1. **它做了什么我们没做的?**(事实)
|
|
15
|
-
2. **它的做法依赖什么前提?**(前提)
|
|
16
|
-
3. **我们的前提和它一样吗?**(判定)
|
|
17
|
-
|
|
18
|
-
只有第 3 问答"一样",才谈得上"学"。
|
|
19
|
-
**前提不同却硬学,是把别人的约束当成自己的目标**——这是对标最常见的翻车方式。
|
|
20
|
-
|
|
21
|
-
下面五节的每一节都按这个顺序写,并且把"不学"的理由写得和"学"一样详细。
|
|
22
|
-
理由是:**"不做"也是决策,写下来就不算缺口;不写下来,下次就被当成遗漏。**
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## 一、Pico CSS —— 产品理念最像我们
|
|
27
|
-
|
|
28
|
-
### 它是什么
|
|
29
|
-
|
|
30
|
-
纯 CSS、零构建、零依赖。写语义化 HTML,不用加 class,直接就好看。
|
|
31
|
-
强制明暗色是**纯 CSS** 实现:`[data-theme="dark"]` 覆盖令牌,**不需要 JS**。
|
|
32
|
-
|
|
33
|
-
### 🔴 我们故意不同:标签选择器
|
|
34
|
-
|
|
35
|
-
我们**禁止**裸标签选择器(`input { }`)。理由不是偏好,是作用域事实:
|
|
36
|
-
一条 `input { }` 的作用域是**整个文档**,包括使用者的页面。
|
|
37
|
-
详见 `INVARIANT.md` I-1。
|
|
38
|
-
|
|
39
|
-
> 这条是 Pico 的立身之本,也是我们不能要的那部分。
|
|
40
|
-
> 前提不同:Pico 假设自己就是页面的全部;我们假设自己是**嵌进别人页面的组件**。
|
|
41
|
-
|
|
42
|
-
### 🟢 学到的:强制暗色不该需要 JS
|
|
43
|
-
|
|
44
|
-
我们的暗色过去靠 JS 写 27 条 inline 属性实现。代价见 `INVARIANT.md` I-4:
|
|
45
|
-
inline style 压过一切样式表,使用者只能加 `!important` 反击。
|
|
46
|
-
|
|
47
|
-
**处置**:改为纯 CSS `data-theme`,JS 不再持有任何色值。
|
|
48
|
-
|
|
49
|
-
---
|
|
50
|
-
|
|
51
|
-
## 二、Radix —— 复杂行为最值得研究
|
|
52
|
-
|
|
53
|
-
### 它是什么
|
|
54
|
-
|
|
55
|
-
无样式的行为原语库。复杂交互(组合框、弹出层、焦点陷阱)的**可访问性行为**由它负责。
|
|
56
|
-
|
|
57
|
-
### 🟢 学到的:状态是枚举,写在 DOM 上
|
|
58
|
-
|
|
59
|
-
Radix 把状态写成 `data-state="open"` / `"closed"` / `"indeterminate"`,枚举字符串。
|
|
60
|
-
|
|
61
|
-
对比另一种写法:状态写成 class(`.is-open`)。
|
|
62
|
-
差别不在于好不好看,在于**它是不是可被观察的事实**:
|
|
63
|
-
|
|
64
|
-
| | 写在 DOM 属性上 | 写在 class 上 |
|
|
65
|
-
|---|---|---|
|
|
66
|
-
| 取值集合 | 有限、可枚举、可校验 | 无限,拼错不报错 |
|
|
67
|
-
| 能否被查询 | `querySelectorAll('[data-state=open]')` 直接可得 | 只能靠约定 |
|
|
68
|
-
| 出问题时 | 看一眼元素就知道状态 | 要去找哪段 JS 加了这个类 |
|
|
69
|
-
|
|
70
|
-
**处置**:全库状态表达收敛到 `data-*` 枚举,`is-*` 类不再作为状态来源。
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
## 三、Open Props —— 令牌基础设施
|
|
75
|
-
|
|
76
|
-
### 它是什么
|
|
77
|
-
|
|
78
|
-
令牌库。一份设计变量,导出成多种格式供不同消费方使用。
|
|
79
|
-
|
|
80
|
-
### 🔴 我们故意不同:真值来源的方向反着来
|
|
81
|
-
|
|
82
|
-
| | 真值来源 | 产物 |
|
|
83
|
-
|---|---|---|
|
|
84
|
-
| Open Props | JS / JSON | CSS(生成出来的) |
|
|
85
|
-
| 我们 | **CSS** | JSON(生成出来的) |
|
|
86
|
-
|
|
87
|
-
**为什么反着来**:`01-tokens/tokens.css` 里有大量**实测对比度注释**——
|
|
88
|
-
每个色值配着"这个组合实测对比度是多少、为什么这么选"。
|
|
89
|
-
这类注释是知识,不是格式;一旦把 CSS 变成生成产物,它们就无处安放。
|
|
90
|
-
|
|
91
|
-
**代价与对策**:反着来就意味着 JSON 会漂移(`INVARIANT.md` I-7)。
|
|
92
|
-
对策不是"记得同步",而是**同步门禁**:CSS 改了不重新生成,门禁红。
|
|
93
|
-
|
|
94
|
-
### 🟢 学到的:令牌必须有机器可读导出
|
|
95
|
-
|
|
96
|
-
散文里的令牌对程序不可用。使用者(包括自动化工具)需要的是能读的结构。
|
|
97
|
-
**处置**:`ai/tokens.json` 由 CSS 生成,`--check` 门禁保证不漂移。
|
|
98
|
-
|
|
99
|
-
---
|
|
100
|
-
|
|
101
|
-
## 四、Web Awesome —— framework-agnostic 组件 API
|
|
102
|
-
|
|
103
|
-
### 它是什么
|
|
104
|
-
|
|
105
|
-
原生自定义元素,在任何框架里行为一致,不需要适配层。
|
|
106
|
-
每个组件按**六类 API 面**文档化:slots / 属性 / 方法 / 事件 / CSS 自定义属性 / CSS parts。
|
|
107
|
-
|
|
108
|
-
### 🔴 我们故意不同:不做自定义元素
|
|
109
|
-
|
|
110
|
-
我们的前提是"把组件放进宿主页面"。Shadow DOM 会**切断与宿主令牌的继承**,
|
|
111
|
-
与前提直接冲突。零依赖、零构建是本库存在的理由,不能为了形态一致而牺牲它。
|
|
112
|
-
|
|
113
|
-
### 🟢 学到的两件事
|
|
114
|
-
|
|
115
|
-
1. **契约要按面分类**:一个组件不只"有个 class 名",它有插槽、状态、属性、事件。
|
|
116
|
-
散文描述不清这些边界。→ 结构化进 `ai/components.json`。
|
|
117
|
-
2. **级联分层**——见下一节,它值得单独讲。
|
|
118
|
-
|
|
119
|
-
---
|
|
120
|
-
|
|
121
|
-
## 五、Primer —— 长期治理
|
|
122
|
-
|
|
123
|
-
### 它是什么
|
|
124
|
-
|
|
125
|
-
GitHub 的设计系统,跨 React / Rails View Component / Figma 多端。
|
|
126
|
-
|
|
127
|
-
### 🟢 学到的:成熟度是分级,而且每级有判据
|
|
128
|
-
|
|
129
|
-
组件有 Alpha → Beta → Stable 的生命周期,每级有**明确判据**。
|
|
130
|
-
|
|
131
|
-
这条为什么重要:**它让约束可以不是一刀切的。**
|
|
132
|
-
|
|
133
|
-
- Stable 组件:约束收紧,改它要过全量门禁
|
|
134
|
-
- Alpha 组件:约束放宽,允许快速迭代、允许不兼容变更
|
|
135
|
-
|
|
136
|
-
⇒ 不再需要"所有东西都按最严的来",也不再需要"也许以后会放宽"的空头承诺。
|
|
137
|
-
|
|
138
|
-
**处置**:成熟度进 `ai/components.json`,并且**由门禁算出来,不由人声明**。
|
|
139
|
-
(人声明的成熟度会撒谎——没人会主动把自己的组件标成 alpha。)
|
|
140
|
-
|
|
141
|
-
### 🟢 学到的:把决策也写下来
|
|
142
|
-
|
|
143
|
-
Primer 的原话是 "Document decisions as well as outcomes"。
|
|
144
|
-
只记结论不记理由,等于半年后重开一遍争论。这份文档就是这条的实践。
|
|
145
|
-
|
|
146
|
-
---
|
|
147
|
-
|
|
148
|
-
## 六、级联分层的决定(`@layer`)
|
|
149
|
-
|
|
150
|
-
这是五家里技术含量最高、也最容易被误用的一条,所以单独写。
|
|
151
|
-
|
|
152
|
-
### 它解决什么问题
|
|
153
|
-
|
|
154
|
-
先问:使用者(或自动化工具)为什么会写 `!important`?
|
|
155
|
-
因为它想改一个样式,**改了不生效**,于是加码。不生效的原因是库的选择器特异性更高。
|
|
156
|
-
|
|
157
|
-
`@layer` 的解法很干净:**把库样式整体放进分层里**。
|
|
158
|
-
未分层样式**永远**赢过已分层样式,**与特异性无关**。
|
|
159
|
-
⇒ 使用者写一句 `.card { padding: 20px }` 就够了,不需要比特异性、不需要 `!important`、不需要加长选择器。
|
|
160
|
-
|
|
161
|
-
这直接拆掉三类失控:
|
|
162
|
-
|
|
163
|
-
1. `!important` 军备竞赛
|
|
164
|
-
2. 选择器越写越长(`.page-3 .card .card__body h3`)
|
|
165
|
-
3. "改不动就去改 `tokens.css`"——因为那是唯一能生效的地方
|
|
166
|
-
|
|
167
|
-
### 为什么现在不做
|
|
168
|
-
|
|
169
|
-
**目标环境是移动 WebView,基线没有确认。**
|
|
170
|
-
|
|
171
|
-
`@layer` 需要 Chrome 99+ / Safari 15.4+ / Firefox 97+。
|
|
172
|
-
在桌面浏览器这不算门槛;在移动 WebView 里,**使用者手里那批机型的基线是未知数**。
|
|
173
|
-
在未知基线上切换层叠模型,等于拿使用者的线上页面去赌。
|
|
174
|
-
|
|
175
|
-
另外两个真实代价,一并写明:
|
|
176
|
-
|
|
177
|
-
- 分层后**库内部**的覆盖关系会变(同层内仍按特异性,跨层按层序)⇒ 必须全量视觉回归;
|
|
178
|
-
- 库自己若有 `!important`,分层后它的层序**会反转**——早层里的 `!important`
|
|
179
|
-
会赢过使用者的 `!important`。所以每一处都得说清"它凭什么必须赢"。
|
|
180
|
-
|
|
181
|
-
### ⚠️ 因此前提不是「清零」,而是「每一处都能归类」
|
|
182
|
-
|
|
183
|
-
第一版我们把判据写成「交付 CSS 里 `!important` 必须为 0」。**实测后推翻**:
|
|
184
|
-
|
|
185
|
-
```
|
|
186
|
-
grep 命中 11 处 ⇒ 真解析后 10 处(1 处是注释里的文本,属假命中)
|
|
187
|
-
10 处全部正当:
|
|
188
|
-
· 9 处在 @media (prefers-reduced-motion: reduce) 内 —— 无障碍
|
|
189
|
-
· 1 处是 [hidden] { display: none } —— 正确性
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
清零会逼人删掉 `prefers-reduced-motion` 兜底,那是**让门禁去破坏正确性**。
|
|
193
|
-
而且对"操作系统级偏好"来说,分层后它赢过使用者的 `!important` 恰恰是对的。
|
|
194
|
-
|
|
195
|
-
⇒ 判据改成:**每一处都要能归到一个已批准的理由**,归不了类的才红。
|
|
196
|
-
|
|
197
|
-
### 切换的前提条件(可判)
|
|
198
|
-
|
|
199
|
-
三条全绿才切,缺一条不切:
|
|
200
|
-
|
|
201
|
-
| # | 前提 | 现状 | 怎么判 |
|
|
202
|
-
|---|---|---|---|
|
|
203
|
-
| 1 | 目标 WebView 基线确认支持 `@layer` | ❌ 未确认 | 实测数据落档,不是"应该支持" |
|
|
204
|
-
| 2 | 每处 `!important` 都能归到已批准理由 | ✅ 10/10 | `05-audit/cascade-layer-gate.py` |
|
|
205
|
-
| 3 | 分层后全量视觉回归绿 | ❌ 未执行 | 现有视觉回归基线全过 |
|
|
206
|
-
|
|
207
|
-
### 现在做了什么
|
|
208
|
-
|
|
209
|
-
不是"什么都不做"。门控已经就位,它守着三件事:
|
|
210
|
-
|
|
211
|
-
1. `@layer` 计数(当前 0);
|
|
212
|
-
2. 每处 `!important` 的分类(当前 10/10 全归类,注释与字符串里的文本不算命中);
|
|
213
|
-
3. **一旦有人加了第一条 `@layer`,门禁立刻要求前提 1、3 的实测记录落档**
|
|
214
|
-
(`docs/LAYER-BASELINE.md` 不存在就红)。
|
|
215
|
-
|
|
216
|
-
⇒ 切换那天不需要重新摸清现状,也不会有人"顺手先切一点"。直接看门禁。
|
|
217
|
-
|
|
218
|
-
---
|
|
219
|
-
|
|
220
|
-
## 七、明确不做的事(写了就不算缺口)
|
|
221
|
-
|
|
222
|
-
| 不做 | 为什么 |
|
|
223
|
-
|---|---|
|
|
224
|
-
| 改成 JSON → CSS(Open Props 方向) | 实测对比度注释是知识,生成会丢。改为 CSS 是真值 + JSON 是导出物 + 同步门禁 |
|
|
225
|
-
| 变成自定义元素库 | Shadow DOM 切断宿主令牌继承,与"嵌进宿主页面"的前提冲突 |
|
|
226
|
-
| 自带运行时框架 / 构建步骤 | 零依赖、零构建是本库的存在理由 |
|
|
227
|
-
| 把幻灯片 / 演示语义做进核心 | 核心只提供组件与契约;呈现形态是适配层的事,见 `adapters/presentation/` |
|
|
228
|
-
| 现在就切级联分层 | 移动 WebView 基线未确认,切换等于拿使用者赌 |
|
|
229
|
-
|
|
230
|
-
---
|
|
231
|
-
|
|
232
|
-
## 八、怎么验证这批工作没白做
|
|
233
|
-
|
|
234
|
-
| 声称 | 验证方式 |
|
|
235
|
-
|---|---|
|
|
236
|
-
| 令牌有机器可读导出且不漂移 | 改 `tokens.css` 不重新生成 ⇒ 门禁红 |
|
|
237
|
-
| 组件契约是结构化数据 | 注入假组件名 ⇒ 校验器拒 |
|
|
238
|
-
| 成熟度不是人写上去的 | 篡改 JSON 里的成熟度 ⇒ 门禁检出与源码不符 |
|
|
239
|
-
| 档位之间有真实区别 | 同一份文档在 strict 下报错数 > creative 下报错数 |
|
|
240
|
-
| 编辑操作不会破坏文档 | 非法操作 ⇒ 文档字节级不变 |
|
|
241
|
-
| 旧版校验器能读新版文档 | 带未知 `extensions` 的文档被忽略而非判非法 |
|
|
242
|
-
| 演示语义没进核心 | 核心目录检索呈现类关键词 ⇒ 0 命中 |
|
|
243
|
-
| 不变式层没被污染 | `INVARIANT.md` 出现组件名 / 令牌名 / 版本号 / 档位名 ⇒ 门禁红 |
|
|
244
|
-
|
|
245
|
-
---
|
|
246
|
-
|
|
247
|
-
## 附:这份文档什么时候该改
|
|
248
|
-
|
|
249
|
-
- **对标了新项目** ⇒ 加一节,按"事实 / 前提 / 判定"三段写
|
|
250
|
-
- **某个"不做"被推翻** ⇒ 不要删行,改成"过去不做,X 起做,因为 Y"——
|
|
251
|
-
保留推翻的痕迹,否则同样的争论会再来一次
|
|
252
|
-
- **前提条件变了**(比如 WebView 基线确认了)⇒ 改第六节,并按表里的三条判据执行切换
|
package/docs/GUIDANCE.md
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
# 指引(Guidance)
|
|
2
|
-
|
|
3
|
-
> **这一层是可以整份推翻的。**
|
|
4
|
-
> 它记录的是「以当前模型的能力,怎么做最不容易翻车」。
|
|
5
|
-
> 模型变强 ⇒ 这一层就该变短。**它变短是好事,不是退步。**
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 这一层为什么存在,以及它为什么不该被写死
|
|
10
|
-
|
|
11
|
-
对照 `INVARIANT.md`:那里写的是**事实**(裸标签选择器的作用域是整个文档),
|
|
12
|
-
这里写的是**补丁**(模型看不全全局 ⇒ 让它改之前先跑影响面报告)。
|
|
13
|
-
|
|
14
|
-
事实不会变。补丁会失效。
|
|
15
|
-
|
|
16
|
-
所以:
|
|
17
|
-
|
|
18
|
-
- ❌ 不要把这一层的内容写进门禁——否则三个月后门禁就在拦一个已经不存在的风险,
|
|
19
|
-
而且**没人敢删**(因为"它一直在那儿,删了会不会出事?")。
|
|
20
|
-
- ✅ 这一层应该定期被拿出来问一句:**这条现在还成立吗?**
|
|
21
|
-
- ✅ 判断标准很简单:**"如果模型是无限强的,还需要这条吗?"**
|
|
22
|
-
不需要 ⇒ 它随时可以删。
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## 一、当前模型下有效的做法
|
|
27
|
-
|
|
28
|
-
### G-1 改之前先看影响面
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
python3 05-audit/impact-report.py
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
**为什么现在需要**:模型倾向于"就改这一处",看不到它改的文件被谁依赖。
|
|
35
|
-
|
|
36
|
-
**什么时候可以删**:当模型能在一次上下文里可靠地推导出跨文件依赖时。
|
|
37
|
-
(至今还没观察到,所以保留。)
|
|
38
|
-
|
|
39
|
-
### G-2 一次只改一个语义单元
|
|
40
|
-
|
|
41
|
-
改完立刻验证,再改下一个。
|
|
42
|
-
|
|
43
|
-
**为什么现在需要**:连续改 5 处再验证,出错时无法定位是哪一处。
|
|
44
|
-
PPT 事故的形态就是"14 页一起改,改完一起崩"。
|
|
45
|
-
|
|
46
|
-
**什么时候可以删**:当验证本身足够快、且失败定位足够精确时。
|
|
47
|
-
|
|
48
|
-
### G-3 发现自己在写页面级 override ⇒ 停下来
|
|
49
|
-
|
|
50
|
-
```css
|
|
51
|
-
.slide-3 h1 { ... } /* ← 这是信号,不是解法 */
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
**为什么现在需要**:模型遇到"改不动"的本能反应是加特异性,而不是改根因。
|
|
55
|
-
|
|
56
|
-
> ⚠️ 注意:这条的**根因部分**是 `INVARIANT.md` I-2(特异性只增不减),
|
|
57
|
-
> 那是事实;"停下来"这个动作是建议。**只有后者属于这一层。**
|
|
58
|
-
|
|
59
|
-
### G-4 优先改组件,而不是改页面
|
|
60
|
-
|
|
61
|
-
**适用范围**:在 frontend-lib 仓库内改动,或明确按本库范式产出页面时。
|
|
62
|
-
|
|
63
|
-
本库提供**可选**的组件与骨架,但它不是所有页面的强制实现规范。
|
|
64
|
-
在本库范式内,要表达"两栏""卡片网格",优先用组件组合而不是手写布局。
|
|
65
|
-
|
|
66
|
-
组件不适用、或宿主项目已有自己的布局体系时,**用别的实现方式** ——
|
|
67
|
-
不要为了满足本库的实现偏好而牺牲任务结果。
|
|
68
|
-
修改已有网站时,宿主原有的框架、组件和样式体系优先。
|
|
69
|
-
|
|
70
|
-
**为什么现在需要**:模型容易写出 `width: 612px; margin-left: 37px` 这类 magic number。
|
|
71
|
-
它们既不能被门禁检查,也不能在暗色模式下自适应。
|
|
72
|
-
|
|
73
|
-
**什么时候可以删**:当模型默认倾向于复用已有抽象时。
|
|
74
|
-
|
|
75
|
-
### G-5 需要"Esc 关闭 / 点外部关闭 / 焦点归位 / 方向键游走 / 首字母跳转"时,用核,别自己写
|
|
76
|
-
|
|
77
|
-
真值源在 `01-tokens/behavior/`(现有 `dismissable`、`focus-return`、`roving`、`typeahead`,
|
|
78
|
-
以及工具核 `emit`)。用法:
|
|
79
|
-
|
|
80
|
-
1. 在组件文件里留出注入标记
|
|
81
|
-
`/* ==== BEHAVIOR INJECT BEGIN: roving ==== */` … `END`
|
|
82
|
-
2. 跑 `python 05-audit/gen-behavior.py`,把核写进来
|
|
83
|
-
3. 调它(如 `flRoving({ container, items, movable, axis, onMove })`),并在 `destroy()` 里成对销毁
|
|
84
|
-
|
|
85
|
-
⚠️ 先确认**这个核管的是不是你这件事** —— "看起来像"不等于"是":
|
|
86
|
-
|
|
87
|
-
| 你要的 | 用不用核 |
|
|
88
|
-
|---|---|
|
|
89
|
-
| Esc / 点外部关闭 | `dismissable` |
|
|
90
|
-
| 关闭后焦点还给谁 | `focus-return` |
|
|
91
|
-
| 一组项里用方向键**移动 DOM 焦点** | `roving` |
|
|
92
|
-
| 打字母/汉字**跳到**匹配项(不是过滤) | `typeahead` |
|
|
93
|
-
| 组件要**对外发通知** | `emit`(`flEmit(root, 'fl-change', { value })`) |
|
|
94
|
-
| 方向键是**翻页 / 展开收起**这类命令 | **不用** —— 那不是游走,是命令键 |
|
|
95
|
-
| 焦点常驻触发器、靠 `aria-activedescendant` 报位置 | **不用** —— 核靠 `e.target` 判断当前项,这种模式下焦点根本不在项上 |
|
|
96
|
-
| APG 菜单(菜单项按规范全 `tabindex="-1"`) | 用 `roving`,但传 `tabindex: false`(只接管步进,不碰 tabindex) |
|
|
97
|
-
| 有输入框、**打字即过滤** | **不用 `typeahead`** —— 那是过滤,不是跳转 |
|
|
98
|
-
|
|
99
|
-
**为什么现在需要**:14 个组件 JS 里,Esc 关闭被手写了 10 处、点击外部 7 处、
|
|
100
|
-
roving tabindex 33 处。每多一个副本,就多一个"这处忘了修"的机会 ——
|
|
101
|
-
而且这种漏**不报错**(同一件事在不同组件里手感不一样,只有键盘用户会遇到)。
|
|
102
|
-
|
|
103
|
-
**什么时候可以删**:当这类行为只剩一处实现时(那正是本条的目的)。
|
|
104
|
-
|
|
105
|
-
### G-6 贴进 system prompt 的极简版
|
|
106
|
-
|
|
107
|
-
> 🔴 **先把适用范围说清楚(这段被误读过)**
|
|
108
|
-
>
|
|
109
|
-
> 下面这段的适用范围是:**在 frontend-lib 仓库里改代码**,
|
|
110
|
-
> 或**明确要按本库范式产出页面**这两类任务。
|
|
111
|
-
>
|
|
112
|
-
> 它**不是**所有 HTML / 网站任务的通用规则:
|
|
113
|
-
>
|
|
114
|
-
> - 改一个已有网站时,宿主项目原有的框架、组件和样式体系**优先**;
|
|
115
|
-
> - 本库组件不适用时,用别的实现方式,不要为满足本库的偏好牺牲任务结果;
|
|
116
|
-
> - PPT、文档、邮件等非网页输出形态**不适用**这一段。
|
|
117
|
-
|
|
118
|
-
```
|
|
119
|
-
维护 frontend-lib 或严格按本库范式写页面时,遵守以下编辑边界:
|
|
120
|
-
1. 不写裸标签选择器(a{} h1{} button{})——一律挂组件根类
|
|
121
|
-
2. 不新增裸颜色与裸间距——用 tokens
|
|
122
|
-
3. 不改 01-tokens/tokens.css,除非任务明确要求改令牌
|
|
123
|
-
4. 局部改动优先改组件/改令牌;确需页面级 override 时要知道它的代价
|
|
124
|
-
(特异性只增不减,见 INVARIANT I-2),不是"绝对不许写"
|
|
125
|
-
5. 改组件样式后必须重跑该组件的视觉基线
|
|
126
|
-
6. 改之前先跑 impact-report.py 看影响范围
|
|
127
|
-
7. 需要 Esc 关闭 / 点外部关闭 / 焦点归位 / 方向键游走 / 打字定位 这类行为时,用 01-tokens/behavior/ 里的核,别自己重写
|
|
128
|
-
发现自己在写页面级 override ⇒ 先问一句"根因是不是在组件里"
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
> 这段是**可替换的**。它是"当前最好用的一段提示词",不是规范。
|
|
132
|
-
> 换模型、换版本 ⇒ 改这段,不需要改任何门禁。
|
|
133
|
-
> 它的**适用范围**同样可以改 —— 但改之前先想清楚:放宽给谁,会不会又变成别人的天花板。
|
|
134
|
-
|
|
135
|
-
---
|
|
136
|
-
|
|
137
|
-
## 二、这一层**不**做的事
|
|
138
|
-
|
|
139
|
-
| 不做 | 归属 |
|
|
140
|
-
|---|---|
|
|
141
|
-
| 不定义"哪些组件存在" | `ai/components.json`(Contract) |
|
|
142
|
-
| 不定义"strict 档能用什么" | `ai/profiles.json`(Contract) |
|
|
143
|
-
| 不断言"裸选择器会污染宿主" | `INVARIANT.md` I-1(事实) |
|
|
144
|
-
| 不把上面任何一条接进 `check-all.sh` | —— 门禁只管事实和契约 |
|
|
145
|
-
|
|
146
|
-
---
|
|
147
|
-
|
|
148
|
-
## 三、维护规则
|
|
149
|
-
|
|
150
|
-
1. **每条 G-n 必须写明「什么时候可以删」。** 写不出删除条件的,说明它不是指引,
|
|
151
|
-
要么它其实是 Invariant(搬走),要么它其实什么都没说(删掉)。
|
|
152
|
-
2. **每次模型换代,过一遍这一层。** 能删的就删,删不掉的要写清楚为什么还删不掉。
|
|
153
|
-
3. **这一层的长度是健康指标。** 它越短,说明模型越强、我们的补丁越少。
|
|
154
|
-
**不要以"这一层写得很全"为荣。**
|