fix(miniapp): 修复 WXSS 通配符导致编译失败,并把 WXSS 校验升级为真编译
问题
- v2 设计收敛在 app.wxss 新增 `.tap-gap > * + *`。WXSS 不支持通配符 `*`,
wcc/wcsc 编译期直接报 `error at token '*'`,整个小程序编译不过。
- 更值得警惕的是当时 19 个 UI 契约守卫全绿:静态守卫刻意只扫
`miniapp/components/`(page 的 wxss 允许标签选择器,套用会误报),
app.wxss 落在扫描范围外,且判据里根本没有通配符这一项。
修复
- 移除 `.tap-gap > * + *`。复检 v2 新增的 .tap-min / .tap-gap / .focusable / .pressable
四个工具类全仓零引用(等价能力已各自写在组件样式内,如 button/index.wxss 直接用
`min-height: var(--tap-min)`),且 `.focusable:focus` / `.pressable:active` 在 view 上
不生效(小程序须用 hover-class),属"写了等于没写",故整块移除,只保留 token。
- 修掉同块注释里的嵌套 `/* */`:内层结束标记会提前终止外层注释,其后的文字会被
当成真实样式解析。该约束已写进注释,防止后人再踩。
守卫加固
- scripts/lib/wxss-selectors.mjs
* 新增 findUniversalSelectors(通配符判据,排除 `*=` 属性匹配符与注释内星号)
* 新增 findNestedComments(注释嵌套判据)
* 修 stripComments:原实现用等长空格替换,把注释内的换行也吃掉,导致报错行号
整体偏移(实测偏差 68 行);现保留换行,并在定位时跳过选择器前的注释占位空白
- ui-tests/wxss-component-selector-contracts.mjs
* 第 1 级:miniapp/components/(原判据,保留)
* 第 2 级(新增):全量 miniapp/**/*.wxss,覆盖 app.wxss 与 pages
* 第 3 级(新增):注释嵌套
* 三级各自带正/反自检样例
新增
- scripts/miniapp-compile-check.mjs:直接调用微信开发者工具内置的 wcc/wcsc 做真编译预检
(wcsc 单独调用不解析 @import 相对路径,脚本会先内联再编译)。
CI runner 无该工具时输出 SKIPPED 并退出 0;刻意不挂进 run-ui-contracts —— 一个在 CI 里
什么都不做的守卫被计入"全绿",正是本项目反复强调的假绿。CI 拦截由上面的静态守卫承担。
验证(均实际执行)
- node scripts/miniapp-compile-check.mjs → WXSS 88 个 + WXML 86 个,0 失败
- 负向注入:把 `.tap-gap > * + *` 改回去 → 预检 FAIL(exit 1),报错与用户原始报错逐字一致
`app.wxss(364:12): error at token '*'`;已还原并复测通过
- node scripts/run-ui-contracts.mjs → 20 / 20 PASS
This commit is contained in:
+19
-32
@@ -340,36 +340,23 @@ page {
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
可访问性工具类(v2 新增)
|
||||
可访问性:只用 token,不用工具类(2026-09-23 修订)
|
||||
--------------------------------------------------------------------------
|
||||
v2 曾在此处新增 .tap-min / .tap-gap / .focusable / .pressable 四个工具类。
|
||||
复检发现全部零引用(等价能力已各自写在组件样式内,例如 button/index.wxss
|
||||
直接使用 min-height:var(--tap-min)),且存在两个硬问题:
|
||||
|
||||
1) `.tap-gap > * + *` 用了通配符与兄弟选择器 —— WXSS 不支持,
|
||||
wcc 编译期直接报 `error at token '*'`,导致整个小程序编译失败。
|
||||
(这是编译错误,不是静默失效,代价最高。)
|
||||
2) `.pressable:active` / `.focusable:focus-visible` 的伪类在 view 上
|
||||
不生效(小程序须用 hover-class),属于"写了等于没写"。
|
||||
|
||||
故整块移除,保留 token。需要热区/间距时直接引用 token:
|
||||
min-height: var(--tap-min); → 88rpx = 44px,最小可点区域
|
||||
gap: var(--tap-gap); → 16rpx = 8px,相邻目标间距(flex gap)
|
||||
【WXSS 禁用清单】星号通配符、兄弟选择器、属性选择器。
|
||||
【注释约束】WXSS 注释**不可嵌套**:内层注释的结束标记会提前终止外层注释,
|
||||
其后的文字会被当成真实样式解析,导致编译失败或样式被污染。
|
||||
(2026-09-23 实证:本块注释初版即踩此坑。)
|
||||
========================================================================== */
|
||||
|
||||
/* 保证任意可点元素达到 44×44px 热区,且视觉尺寸可独立控制 */
|
||||
.tap-min {
|
||||
min-width: var(--tap-min);
|
||||
min-height: var(--tap-min);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 相邻可点元素之间的最小间距 */
|
||||
.tap-gap > * + * {
|
||||
margin-left: var(--tap-gap);
|
||||
}
|
||||
|
||||
/* 焦点可见(小程序支持键盘/无障碍焦点) */
|
||||
.focusable:focus,
|
||||
.focusable:focus-visible {
|
||||
outline: 4rpx solid var(--color-border-focus);
|
||||
outline-offset: 2rpx;
|
||||
}
|
||||
|
||||
/* 按压反馈:所有可点元素统一使用,替代"点了没反应" */
|
||||
.pressable {
|
||||
transition: transform var(--motion-fast) var(--ease-spring),
|
||||
opacity var(--motion-fast) var(--ease-standard);
|
||||
}
|
||||
.pressable:active {
|
||||
transform: scale(var(--press-scale));
|
||||
opacity: var(--press-opacity);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user