agentcss 0.13.1 → 0.13.2

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.
@@ -317,6 +317,12 @@
317
317
  menu.hidden = false;
318
318
  btn.setAttribute('aria-expanded', 'true');
319
319
  active = -1;
320
+ /* ⭐ 聚焦首项必须放在 open() 里,不能只写在键盘分支。
321
+ 读屏用户在浏览模式下按 Enter,NVDA 会把它变成**一次点击**,
322
+ 走的是 click 分支 ⇒ 键盘分支里的 `focusAt(0)` 根本没机会执行,
323
+ 焦点留在按钮上,菜单项永远进不去(真机用 NVDA + Edge 实测过)。
324
+ APG 的菜单按钮也是这样:打开即把焦点交给第一项。 */
325
+ focusAt(0);
320
326
  flEmit(root, 'fl-open', null);
321
327
  if (opt.onOpen) opt.onOpen();
322
328
  }
@@ -391,12 +397,11 @@
391
397
  root.addEventListener('keydown', function (e) {
392
398
  var k = e.key;
393
399
 
394
- /* 按钮上:↓ / Enter / Space 打开并聚焦首项 */
400
+ /* 按钮上:↓ / Enter / Space 打开(聚焦首项已由 open() 负责) */
395
401
  if (e.target === btn) {
396
402
  if (k === 'ArrowDown' || k === 'Enter' || k === ' ' || k === 'Spacebar') {
397
403
  e.preventDefault();
398
404
  open();
399
- focusAt(0);
400
405
  }
401
406
  return;
402
407
  }
@@ -72,10 +72,21 @@
72
72
  outline-offset: var(--focus-ring-offset);
73
73
  }
74
74
 
75
+ /* ⭐ 排序指示符改用 mask 画,不再用文字字形。
76
+ 原因:Chromium 会把 ::after 的 `content` 文本算进**可访问名**,读屏于是念出
77
+ 「公司 · ↕ · 按钮」—— 这个符号对用户毫无意义,而且自动检查抓不到(名字非空即过)。
78
+ 换成图像后字形不进无障碍树;三态形状、`opacity`、暗色跟随(currentColor)全不变。
79
+ ⚠️ 不要改回 `content: "↕" / ""` 这种替代文本语法:Firefox 不支持,箭头会直接消失。 */
75
80
  .table__sort::after {
76
- content: "↕"; /* 未排序:上下双向箭头 */
81
+ content: ""; /* 空内容 ⇒ 不进可访问名 */
82
+ inline-size: .85em;
83
+ block-size: .85em;
84
+ flex: none;
85
+ background-color: currentColor; /* 跟着文字颜色走,暗色自动适配 */
77
86
  opacity: .45;
78
- font-size: .9em;
87
+ -webkit-mask: var(--sort-glyph) center / contain no-repeat;
88
+ mask: var(--sort-glyph) center / contain no-repeat;
89
+ --sort-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 1.4 5.2 4.6h1.7v2.6h2.2V4.6h1.7z'/%3E%3Cpath d='M8 14.6 10.8 11.4h-1.7V8.8H6.9v2.6H5.2z'/%3E%3C/svg%3E");
79
90
  }
80
91
 
81
92
  /* 🔴🔴 排序指示符:三态必须**同时**用形状 + 颜色表达(WCAG 1.4.1)
@@ -95,13 +106,13 @@
95
106
  ⇒ 两条选择器并存,任何一种写法都能把箭头点亮。 */
96
107
  .table th[aria-sort="ascending"] .table__sort::after,
97
108
  .table__sort[aria-sort="ascending"]::after {
98
- content: "↑";
109
+ --sort-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 1.6 4.6 6h2.1v8h2.6V6h2.1z'/%3E%3C/svg%3E");
99
110
  opacity: 1;
100
111
  }
101
112
 
102
113
  .table th[aria-sort="descending"] .table__sort::after,
103
114
  .table__sort[aria-sort="descending"]::after {
104
- content: "↓";
115
+ --sort-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 14.4 11.4 10h-2.1V2H6.7v8H4.6z'/%3E%3C/svg%3E");
105
116
  opacity: 1;
106
117
  }
107
118
 
package/CHANGELOG.md CHANGED
@@ -16,6 +16,41 @@
16
16
 
17
17
  (无)
18
18
 
19
+ ## [0.13.2] - 2026-10-30
20
+
21
+ ### 修复 · 下拉菜单打开时,焦点没有交给第一项
22
+
23
+ 读屏用户在浏览模式下按 Enter,NVDA 会把这次按键变成**一次点击**。而原先只有键盘
24
+ 分支里会聚焦首项,click 分支只负责打开 ⇒ 菜单开了、焦点还留在按钮上,菜单项
25
+ 永远进不去。真机实测(NVDA + Edge):修复前焦点元素是那个按钮,修复后落在第一项。
26
+
27
+ 修法是把聚焦收进 `open()`,两条路径共用。
28
+
29
+ ### 修复 · 表格列头把排序符号念了出来
30
+
31
+ 列头按钮的可访问名里混着 `↕`,读屏念成「公司 · ↕ · 按钮」。
32
+ 来源是 `.table__sort::after { content: "↕" }` —— Chromium 把伪元素文本算进了
33
+ 可访问名。改为 `content: ""` + `mask` 图像画箭头:三态形状不变、颜色走
34
+ `currentColor`,**标记结构不用改**(外部集成方无需动 HTML)。
35
+
36
+ ⚠️ 不要改用 `content: "↕" / ""` 那种替代文本语法 —— Firefox 不支持,箭头会消失。
37
+
38
+ ### 判据 · 两处都补了能抓住它的检查
39
+
40
+ - `dropdown-check` 新增「点击打开也聚焦首项」。原来只测 `↓` 这条键盘路径,
41
+ 所以上面那个缺陷在 111 道门禁全绿的情况下一直存在(用改前的代码做过对照:
42
+ `↓` 绿、`↑↓` 绿、点击红)。
43
+ - `table-check` 的排序判据改为读 `mask` 的自定义属性;把降序箭头改成升序形状的
44
+ 破坏实验能当场判红,说明它仍在看结果而不是结构。
45
+
46
+ ### 文档 · 无障碍人工测试协议补上实测结果
47
+
48
+ 九个页面跑了一轮 NVDA 朗读(证据是读屏交给语音合成器的原文,不是人耳旁听),
49
+ 结果表从「全部未执行」变成实测结论;`H` 键标题层级与 macOS VoiceOver 仍为空白,
50
+ 表里如实写着「未执行」。
51
+
52
+ ---
53
+
19
54
  ## [0.13.1] - 2026-10-30
20
55
 
21
56
  **补录 Linux 三套视觉基线。0.13.0 的 tag 因这处过期未通过发布门禁,包没有发出去
package/START-HERE.md CHANGED
@@ -49,7 +49,7 @@
49
49
  | 日期区间 | `02-primitives/date-range/date-range.css` | 206 | **必需 301** (`date-range.js`) | 要选起止日期就要 |
50
50
  | 标签页 | `03-patterns/tabs/tabs.css` | 172 | **必需 316** (`tabs.js`) | 同层级内容切换就要 |
51
51
  | 折叠面板(beta) | `03-patterns/accordion/accordion.css` | 453 | **必需 389** (`accordion.js`) | 长表单分段就要 |
52
- | 动作菜单(beta) | `03-patterns/dropdown/dropdown.css` | 135 | **必需 451** (`dropdown.js`) | 要一组动作(新建 / 导出 / 删除)就要 |
52
+ | 动作菜单(beta) | `03-patterns/dropdown/dropdown.css` | 135 | **必需 456** (`dropdown.js`) | 要一组动作(新建 / 导出 / 删除)就要 |
53
53
  | 文字提示 | `03-patterns/tooltip/tooltip.css` | 89 | **必需 288** (`tooltip.js`) | 图标按钮或缩写需要一句话解释就要 |
54
54
  | 分页 | `03-patterns/pagination/pagination.css` | 195 | **必需 401** (`pagination.js`) | 数据多到要翻页就要 |
55
55
  | 树形 | `03-patterns/tree/tree.css` | 122 | **必需 464** (`tree.js`) | 层级数据(文件 / 组织 / 分类)要展开就要 |
@@ -66,7 +66,7 @@
66
66
  | analysis-report | `04-recipes/analysis-report/analysis-report.css` | 187 | — | |
67
67
  | detail-page | `04-recipes/detail-page/detail-page.css` | 190 | — | |
68
68
  | report | `04-recipes/report/report.css` | 364 | — | |
69
- | table | `04-recipes/table/table.css` | 372 | — | |
69
+ | table | `04-recipes/table/table.css` | 383 | — | |
70
70
  | bar | `09-assets/bar/bar.css` | 166 | **必需 320** (`bar.js`) | |
71
71
  | model-viewer | `09-assets/model-viewer/model-viewer.css` | 143 | **必需 423** (`model-viewer.js`) | |
72
72
  | scientific-plot | `09-assets/scientific-plot/scientific-plot.css` | 141 | **必需 610** (`scientific-plot.js`) | |
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
3
  "generatedBy": "05-audit/gen-ai-contract.py",
4
- "libraryVersion": "0.13.1",
4
+ "libraryVersion": "0.13.2",
5
5
  "$comment": "机器可读的组件契约。slots = BEM 元素名(在本库里元素名就是插槽名),states.enum = 组件会在 DOM 上写出的 data-* 取值,states.legacyClass = 仍未收敛到 data 属性的历史状态类(INVARIANT I-8 未达标)。",
6
6
  "maturityCriteria": {
7
7
  "alpha": "缺 demo.html 或缺 05-audit 下的专属门禁 ⇒ 还没被机器验证过,不要用",
@@ -265,6 +265,8 @@ fr.save();
265
265
  menu.hidden = false;
266
266
  btn.setAttribute('aria-expanded', 'true');
267
267
  active = -1;
268
+
269
+ focusAt(0);
268
270
  flEmit(root, 'fl-open', null);
269
271
  if (opt.onOpen) opt.onOpen();
270
272
  }
@@ -331,7 +333,6 @@ if (e.target === btn) {
331
333
  if (k === 'ArrowDown' || k === 'Enter' || k === ' ' || k === 'Spacebar') {
332
334
  e.preventDefault();
333
335
  open();
334
- focusAt(0);
335
336
  }
336
337
  return;
337
338
  }
@@ -1 +1 @@
1
- .table {position:relative;overflow:auto;max-height:var(--table-max-h, 70vh);background:var(--surface);border:1px solid var(--border-decor);border-radius:var(--r-md);}.table table {width:100%;border-collapse:separate;border-spacing:0;font-size:var(--fs-sm);}.table thead th {position:sticky;top:0;z-index:var(--z-sticky);background:var(--surface-raised);color:var(--text-secondary);font-weight:600;text-align:start;white-space:nowrap;padding:var(--sp-2) var(--sp-3);border-bottom:1px solid var(--border-decor);}.table__sort {display:inline-flex;align-items:center;gap:var(--sp-1);min-height:32px;padding:0 var(--sp-1);margin:0 calc(-1 * var(--sp-1));background:none;border:0;border-radius:var(--r-xs, 3px);color:inherit;font:inherit;cursor:pointer;}.table__sort:focus-visible {outline:var(--focus-ring-width) solid var(--focus-ring-color);outline-offset:var(--focus-ring-offset);}.table__sort::after {content:"↕";opacity:.45;font-size:.9em;}.table th[aria-sort="ascending"] .table__sort::after, .table__sort[aria-sort="ascending"]::after {content:"↑";opacity:1;}.table th[aria-sort="descending"] .table__sort::after, .table__sort[aria-sort="descending"]::after {content:"↓";opacity:1;}.table tbody td, .table tbody th {padding:var(--sp-2) var(--sp-3);text-align:start;border-bottom:1px solid var(--border-decor);vertical-align:top;}.table tbody tr:last-child td {border-bottom:0;}@media (hover:hover) and (pointer:fine) {.table tbody tr:hover {background:var(--surface-sunken);}}.table .num {text-align:end;font-variant-numeric:tabular-nums;font-family:var(--mono);white-space:nowrap;}.table .num--pos {color:var(--success);}.table .num--neg {color:var(--danger);}.table .num--pos::before {content:"+";}.table .num--neg::before {content:"−";}.table__empty, .table__error, .table__loading {padding:var(--sp-7) var(--sp-4);text-align:center;color:var(--text-secondary);font-size:var(--fs-sm);}.table__empty {background:var(--surface-sunken);}.table__error {background:var(--surface-sunken);color:var(--danger);}.table__loading {color:var(--text-tertiary);}.table__bar {height:8px;margin:var(--sp-2) auto;max-width:220px;background:var(--surface-sunken);border-radius:999px;overflow:hidden;}@media (prefers-reduced-motion:no-preference) {.table__bar::after {content:"";display:block;height:100%;width:40%;background:var(--accent-soft);border-radius:999px;animation:table-shimmer 1.2s var(--ease-in-out, ease-in-out) infinite;}@keyframes table-shimmer {0% {transform:translateX(-100%);}100% {transform:translateX(250%);}}}.table__empty .btn, .table__error .btn {margin-top:var(--sp-3);}.table--compact {font-size:var(--fs-xs);}.table--compact thead th, .table--compact tbody td {padding:var(--sp-1) var(--sp-2);}.table__cap {caption-side:top;text-align:start;padding:var(--sp-2) var(--sp-3);font-size:var(--fs-xs);color:var(--text-tertiary);}.table__foot {margin:var(--sp-2) 0 0;padding:0 var(--sp-1);font-size:var(--fs-xs);color:var(--text-tertiary);line-height:1.6;}.table__foot ol {margin:0;padding-inline-start:var(--sp-4);}.table thead tr + tr th {top:2.4em;font-weight:500;font-size:var(--fs-xs);color:var(--text-tertiary);border-bottom:1px solid var(--border-decor);}.table thead th[colspan] {text-align:center;border-inline-start:1px solid var(--border-decor);}.table thead th[colspan]:first-child {border-inline-start:0;}.table--financial thead th {white-space:nowrap;}.table--financial tbody th {white-space:nowrap;}.table--financial thead th:not(:first-child) {text-align:end;}.table__unit {font-weight:400;font-size:inherit;color:var(--text-secondary);margin-inline-start:0.35em;white-space:nowrap;}.table .num--na, .table .cell--na {color:var(--text-tertiary);text-align:center;}.table--academic {border:0;border-radius:0;background:transparent;max-height:none;}.table--academic table {background:transparent;}.table--academic thead th {background:transparent;color:var(--text-primary);font-weight:600;border-top:2px solid var(--border-decor-str);border-bottom:1px solid var(--border-decor);position:static;}.table--academic thead tr + tr th {font-weight:400;border-top:0;border-bottom:1px solid var(--border-decor);}.table--academic tbody td, .table--academic tbody th {border-bottom:0;}.table--academic tbody tr:last-child td, .table--academic tbody tr:last-child th {border-bottom:1px solid var(--border-decor);}@media (hover:hover) and (pointer:fine) {.table--academic tbody tr:hover {background:transparent;}}.table--stats .stat__se {color:var(--text-tertiary);font-size:var(--fs-xs);}.table--stats .stat__p {font-family:var(--mono);font-variant-numeric:tabular-nums;}.table--stats .stat__p--sig {font-weight:700;color:var(--text-primary);}.table--stats .stat__ci {font-family:var(--mono);font-variant-numeric:tabular-nums;white-space:nowrap;}@media print {.table {max-height:none;overflow:visible;}.table thead th {position:static;}.table--academic thead th {border-top:2px solid var(--text-primary);}}
1
+ .table {position:relative;overflow:auto;max-height:var(--table-max-h, 70vh);background:var(--surface);border:1px solid var(--border-decor);border-radius:var(--r-md);}.table table {width:100%;border-collapse:separate;border-spacing:0;font-size:var(--fs-sm);}.table thead th {position:sticky;top:0;z-index:var(--z-sticky);background:var(--surface-raised);color:var(--text-secondary);font-weight:600;text-align:start;white-space:nowrap;padding:var(--sp-2) var(--sp-3);border-bottom:1px solid var(--border-decor);}.table__sort {display:inline-flex;align-items:center;gap:var(--sp-1);min-height:32px;padding:0 var(--sp-1);margin:0 calc(-1 * var(--sp-1));background:none;border:0;border-radius:var(--r-xs, 3px);color:inherit;font:inherit;cursor:pointer;}.table__sort:focus-visible {outline:var(--focus-ring-width) solid var(--focus-ring-color);outline-offset:var(--focus-ring-offset);}.table__sort::after {content:"";inline-size:.85em;block-size:.85em;flex:none;background-color:currentColor;opacity:.45;-webkit-mask:var(--sort-glyph) center / contain no-repeat;mask:var(--sort-glyph) center / contain no-repeat;--sort-glyph:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 1.4 5.2 4.6h1.7v2.6h2.2V4.6h1.7z'/%3E%3Cpath d='M8 14.6 10.8 11.4h-1.7V8.8H6.9v2.6H5.2z'/%3E%3C/svg%3E");}.table th[aria-sort="ascending"] .table__sort::after, .table__sort[aria-sort="ascending"]::after {--sort-glyph:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 1.6 4.6 6h2.1v8h2.6V6h2.1z'/%3E%3C/svg%3E");opacity:1;}.table th[aria-sort="descending"] .table__sort::after, .table__sort[aria-sort="descending"]::after {--sort-glyph:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 14.4 11.4 10h-2.1V2H6.7v8H4.6z'/%3E%3C/svg%3E");opacity:1;}.table tbody td, .table tbody th {padding:var(--sp-2) var(--sp-3);text-align:start;border-bottom:1px solid var(--border-decor);vertical-align:top;}.table tbody tr:last-child td {border-bottom:0;}@media (hover:hover) and (pointer:fine) {.table tbody tr:hover {background:var(--surface-sunken);}}.table .num {text-align:end;font-variant-numeric:tabular-nums;font-family:var(--mono);white-space:nowrap;}.table .num--pos {color:var(--success);}.table .num--neg {color:var(--danger);}.table .num--pos::before {content:"+";}.table .num--neg::before {content:"−";}.table__empty, .table__error, .table__loading {padding:var(--sp-7) var(--sp-4);text-align:center;color:var(--text-secondary);font-size:var(--fs-sm);}.table__empty {background:var(--surface-sunken);}.table__error {background:var(--surface-sunken);color:var(--danger);}.table__loading {color:var(--text-tertiary);}.table__bar {height:8px;margin:var(--sp-2) auto;max-width:220px;background:var(--surface-sunken);border-radius:999px;overflow:hidden;}@media (prefers-reduced-motion:no-preference) {.table__bar::after {content:"";display:block;height:100%;width:40%;background:var(--accent-soft);border-radius:999px;animation:table-shimmer 1.2s var(--ease-in-out, ease-in-out) infinite;}@keyframes table-shimmer {0% {transform:translateX(-100%);}100% {transform:translateX(250%);}}}.table__empty .btn, .table__error .btn {margin-top:var(--sp-3);}.table--compact {font-size:var(--fs-xs);}.table--compact thead th, .table--compact tbody td {padding:var(--sp-1) var(--sp-2);}.table__cap {caption-side:top;text-align:start;padding:var(--sp-2) var(--sp-3);font-size:var(--fs-xs);color:var(--text-tertiary);}.table__foot {margin:var(--sp-2) 0 0;padding:0 var(--sp-1);font-size:var(--fs-xs);color:var(--text-tertiary);line-height:1.6;}.table__foot ol {margin:0;padding-inline-start:var(--sp-4);}.table thead tr + tr th {top:2.4em;font-weight:500;font-size:var(--fs-xs);color:var(--text-tertiary);border-bottom:1px solid var(--border-decor);}.table thead th[colspan] {text-align:center;border-inline-start:1px solid var(--border-decor);}.table thead th[colspan]:first-child {border-inline-start:0;}.table--financial thead th {white-space:nowrap;}.table--financial tbody th {white-space:nowrap;}.table--financial thead th:not(:first-child) {text-align:end;}.table__unit {font-weight:400;font-size:inherit;color:var(--text-secondary);margin-inline-start:0.35em;white-space:nowrap;}.table .num--na, .table .cell--na {color:var(--text-tertiary);text-align:center;}.table--academic {border:0;border-radius:0;background:transparent;max-height:none;}.table--academic table {background:transparent;}.table--academic thead th {background:transparent;color:var(--text-primary);font-weight:600;border-top:2px solid var(--border-decor-str);border-bottom:1px solid var(--border-decor);position:static;}.table--academic thead tr + tr th {font-weight:400;border-top:0;border-bottom:1px solid var(--border-decor);}.table--academic tbody td, .table--academic tbody th {border-bottom:0;}.table--academic tbody tr:last-child td, .table--academic tbody tr:last-child th {border-bottom:1px solid var(--border-decor);}@media (hover:hover) and (pointer:fine) {.table--academic tbody tr:hover {background:transparent;}}.table--stats .stat__se {color:var(--text-tertiary);font-size:var(--fs-xs);}.table--stats .stat__p {font-family:var(--mono);font-variant-numeric:tabular-nums;}.table--stats .stat__p--sig {font-weight:700;color:var(--text-primary);}.table--stats .stat__ci {font-family:var(--mono);font-variant-numeric:tabular-nums;white-space:nowrap;}@media print {.table {max-height:none;overflow:visible;}.table thead th {position:static;}.table--academic thead th {border-top:2px solid var(--text-primary);}}
package/dist/COSTS.md CHANGED
@@ -36,7 +36,7 @@
36
36
  | pattern | `accordion` | 2.7 KB | 1.3 KB | 1.8 KB | **5.8 KB** | 34.1 KB | −28.3 KB |
37
37
  | pattern | `content` | 2.7 KB | 1.0 KB | — | **3.7 KB** | 24.2 KB | −20.5 KB |
38
38
  | pattern | `drawer` | 2.7 KB | 0.8 KB | 2.0 KB | **5.4 KB** | 25.0 KB | −19.6 KB |
39
- | pattern | `dropdown` | 2.7 KB | 0.8 KB | 2.9 KB | **6.4 KB** | 27.9 KB | −21.5 KB |
39
+ | pattern | `dropdown` | 2.7 KB | 0.8 KB | 2.9 KB | **6.4 KB** | 28.1 KB | −21.7 KB |
40
40
  | pattern | `form-validation` | 2.7 KB | 0.8 KB | — | **3.5 KB** | 24.1 KB | −20.7 KB |
41
41
  | pattern | `list` | 2.7 KB | 0.6 KB | — | **3.3 KB** | 22.0 KB | −18.7 KB |
42
42
  | pattern | `nav` | 2.7 KB | 1.2 KB | 2.6 KB | **6.5 KB** | 30.9 KB | −24.4 KB |
@@ -51,7 +51,7 @@
51
51
  ## 三、全库
52
52
 
53
53
  - 全部组件都用上(min,含 tokens 一份):**61.2 KB**
54
- - 源码版(带注释)全库 gzip:302.3 KB
54
+ - 源码版(带注释)全库 gzip:303.0 KB
55
55
 
56
56
  > ⚠️ 源码版那个数字**不是**要付的钱:它包含我们的注释(全库 CSS 有 67% 是注释,字节口径)。
57
57
  > 它的用途只有一个 —— 让人看清"dist 到底省了多少"。
@@ -4,8 +4,13 @@
4
4
  但证明不了"念出来是否**合理**"。这一页把这件事变成**可执行、可复现、可填表**的动作,
5
5
  时长约 20 分钟。
6
6
 
7
- **当前状态:未执行。** 下面那张结果表是空的 —— 这不是省略,是事实。
8
- 跑完并填上之后,[无障碍一致性声明](ACCESSIBILITY.md) 里"没有真机读屏记录"那一条才能划掉。
7
+ **当前状态:已执行一轮(2026-10-30)。** 九个页面都跑过 Tab 遍历,浮层焦点在
8
+ `overlay` / `drawer` 上验到,**标题层级(`H` 键逐级跳)本轮没做**,结果表里如实写着
9
+ "未执行"。
10
+
11
+ 证据形态要说清楚:**不是人耳旁听**,是让 NVDA 把朗读日志写到文件,逐条取它交给语音
12
+ 合成器的原始文本(`IO - speech.speech.speak`)。这比人耳更可复现、可存档,但它证明的
13
+ 是"NVDA 念了什么",不覆盖"听起来是否别扭"。
9
14
 
10
15
  ---
11
16
 
@@ -56,25 +61,43 @@ python3 -m http.server 8000 --bind 127.0.0.1
56
61
 
57
62
  | 页面 | ① 名字 | ② 标题层级 | ③ 禁用态 | ④ 错误说明 | ⑤ 浮层焦点 | 备注(听到了什么) |
58
63
  |---|---|---|---|---|---|---|
59
- | button | 未执行 | 未执行 | 未执行 | — | — | |
60
- | input | 未执行 | 未执行 | 未执行 | 未执行 | — | |
61
- | choice | 未执行 | 未执行 | 未执行 | — | — | |
62
- | tabs | 未执行 | 未执行 | — | — | — | |
63
- | overlay | 未执行 | 未执行 | — | — | 未执行 | |
64
- | table | 未执行 | 未执行 | — | — | — | |
65
- | **accordion** | 未执行 | 未执行 | — | — | — | 重点:展开/收起时 `aria-expanded` 有没有读出来,面板与标题的关联有没有读 |
66
- | **drawer** | 未执行 | 未执行 | — | — | 未执行 | 重点:抽屉打开时焦点有没有进到里面;Esc 关闭后焦点有没有回到触发按钮 |
67
- | **dropdown** | 未执行 | 未执行 | — | — | 未执行 | 重点:方向键在菜单项之间的移动能否被读出;Esc 关列表后焦点在哪 |
68
-
69
- > **为什么后三行是补的**:`accordion` / `drawer` / `dropdown` 是当前仅有的 beta 组件里的三个,
70
- > 而它们升 stable 的判据之一就是「行为零欠账」——人工读屏是这件事**唯一的用户侧证据**。
71
- > 只覆盖 `overlay` 一个浮层,等于对另外两个浮层的真实表现没有任何记录。
72
- > 这三行同样**未执行**,加行不等于做过。
64
+ | button | 通过 | 未执行 | 未执行 | — | — | 12 个按钮全部念出名字;加载态念「忙碌中」 |
65
+ | input | 通过 | 未执行 | 未执行 | 通过 | — | 只读念「只读」;错误念「错误的输入 · 请输入有效金额」,紧跟控件 |
66
+ | choice | 通过 | 未执行 | — | — | — | 「全选(下面 3 项)」念「部分选中」;单选念「时间范围 · 分组 · 已选中 · 3 之 1 项」 |
67
+ | tabs | 通过 | 未执行 | — | — | — | 念「选项卡组 · 选项卡 · 已选中 · 2 之 1 项」;错例组三个 `tabindex=0` 各停一次,正例组只停一次 —— 对照成立 |
68
+ | overlay | 通过 | 通过(对话框标题念成 2 级) | — | — | 通过 | 打开后焦点进对话框(取消 / 确认标记),Esc 后回到「普通确认」,再 Tab 落到「删除这份报告」 |
69
+ | table | 通过 | 未执行 | — | — | — | 表格念「共 7 行 7 列 · 1 行 1 列」;🔴 **列头按钮把 `↕` 念了出来**(见下) |
70
+ | **accordion** | 通过 | 通过(标题念成 3 级) | 通过 | — | — | 「公司基本信息 · 按钮 · 已展开 · 标题 3 级」→ 回车念「已折叠」→ 再回车念「已展开」;`aria-disabled` 那栏念「不可用」 |
71
+ | **drawer** | 通过 | 未执行 | — | — | 通过 | 打开念「从left进来的抽屉 · 对话框」;Tab 在「取消 → 确定 → 关闭」之间循环;Esc 后回到「左侧 · 按钮」 |
72
+ | **dropdown** | 通过 | 未执行 | — | — | 通过(修复后) | 按钮念「菜单按钮 · 已折叠 · 子菜单」,展开念「已展开 · 菜单」。**修复前**:按 Enter 打开后焦点留在按钮上(读屏在浏览模式下把 Enter 变成点击,走 click 分支),修复后真机复测焦点落在「✓ 按日期」。方向键仍无法在项间移动 —— 极简 APG 对照页表现相同,属读屏浏览模式限制 |
73
+
74
+ > **为什么后三行是补的**:`accordion` / `drawer` / `dropdown` 这三个都是 beta 组件
75
+ > (`ai/components.json` 当前记 4 个 beta),而它们升 stable 的判据之一就是「行为零欠账」
76
+ > ——人工读屏是这件事**唯一的用户侧证据**。
77
+ > 只覆盖 `overlay` 一个浮层,等于对其他浮层的真实表现没有任何记录。
78
+
79
+ **本轮跑出来并已修掉的一条真缺陷**:`table` 的列头按钮被念成「公司 · `↕` · 按钮」——
80
+ 那个 `↕` 来自 `table.css` 里 `.table__sort::after` 的 `content`,Chromium 把伪元素文本算进了
81
+ 可访问名。**自动检查抓不到它**(可访问名"非空"就算过),212 道门禁里也没有一道看这个。
82
+
83
+ 改法:把字形换成 `mask` 图像(`content` 留空),三态形状仍是 `↕ / ↑ / ↓`,
84
+ 颜色靠 `currentColor` 跟着主题走,标记结构一个字符都不用动。
85
+ ⚠️ 不走 `content: "↕" / ""` 那条路 —— 替代文本语法 Firefox 不支持,箭头会直接消失。
86
+ 改完用 NVDA 复听:表头只剩「公司 · 按钮」;`table-check` 的排序判据也跟着改成
87
+ 读 `mask` 的自定义属性(破坏实验验证过:把降序箭头改成升序形状,判据当场红)。
88
+
89
+ **`dropdown` 那行要连着对照读**:同样的操作在极简 APG 对照页上表现一致(↓ 被吃),
90
+ 所以"方向键读不出菜单项"不是本库特有。但另一件事是**真缺陷**:用 NVDA 在浏览模式下
91
+ 按 Enter,它会把这次按键变成一次点击 ⇒ 走的是 click 分支,而本库原来只在键盘分支里
92
+ 聚焦首项 ⇒ 菜单打开了、焦点还在按钮上,菜单项永远进不去。
93
+ 自动门禁当时是绿的,因为它只测了 `↓` 这条键盘路径。
94
+ **修法与判据**:把聚焦首项收进 `open()`(两条路径共用),并补一条"点击打开也聚焦首项"。
73
95
 
74
96
  状态只填三种:**通过** / **失败(附听到的内容)** / **未执行**。
75
97
  "大概没问题"不属于这三种,不要写。
76
98
 
77
- 填写人 / 日期 / 系统与读屏版本:`____ / ____ / ____`
99
+ 填写人 / 日期 / 系统与读屏版本:
100
+ `脚本驱动 NVDA(朗读取自 speech 日志原文,非人耳旁听)/ 2026-10-30 / Windows 10 22H2 19045 · NVDA 2026.3 · Edge`
78
101
 
79
102
  ---
80
103
 
@@ -5,7 +5,7 @@
5
5
 
6
6
  | 项目 | 值 |
7
7
  |---|---|
8
- | 声明适用版本 | agentcss `0.13.1` |
8
+ | 声明适用版本 | agentcss `0.13.2` |
9
9
  | 声明日期 | 2026-10-29 |
10
10
  | 复核节奏 | 每次发版前重跑(`npm run gate` 内含全部证据) |
11
11
  | 范围 | 本库交付的 CSS / JS、全部 `demo.html`(组件页 + 图表页 + 组合页)、以及文档站生成的页面 |
@@ -87,25 +87,34 @@
87
87
  | `zoom-reflow.js` | 40 页 × 3 条 AA 硬项 | 320px 不横滚 · 200% 不裁字 · 焦点不被遮挡 |
88
88
  | `focus-ring-check` / `focus-return-check` / `keyboard-nav` | 焦点可见、焦点归还、键盘路径 | 全绿 |
89
89
 
90
- ### 人工(**尚未执行** —— 这是本声明最大的空白)
90
+ ### 人工(**已执行一轮,仍缺人耳与第二平台**)
91
91
 
92
92
  自动化的天花板必须说清楚:**axe 只能覆盖约 30–40% 的 WCAG 问题**,而且它听不见。
93
- 以下三件事本库**没有**任何一条真实记录:
94
93
 
95
- 1. **真机读屏**(NVDA / VoiceOver / TalkBack)——一条录音都没有。
96
- 2. **真实键盘 + 读屏组合下的阅读顺序**是否符合预期(`a11y-ax` 只证明"顺序稳定",
97
- 不证明"顺序正确"——后者需要人判断)。
98
- 3. **9 处「待人工」结论**(例如图表的替代文本是否真的描述了数据)。
94
+ 2026-10-30 用 NVDA 2026.3 + Edge(Windows 10 22H2)跑了一轮:把 NVDA 的朗读日志写到
95
+ 文件,逐条取它交给语音合成器的原文。九页结果见
96
+ [无障碍人工测试协议](A11Y-MANUAL-PROTOCOL.md) 第三节。
99
97
 
100
- 为了把这块从"没有"变成"可执行",协议写在
101
- [无障碍人工测试协议](A11Y-MANUAL-PROTOCOL.md):两条路径、约 20 分钟、结果直接填表。
102
- **在你或志愿者跑完之前,本声明只代表"自动检查覆盖的部分是干净的"。**
98
+ **它证明的是"NVDA 念了什么",不等于"人听着顺不顺"。** 仍然缺:
99
+
100
+ 1. **人耳旁听** —— 证据是日志文本;语气、打断、重复播报这类问题不在其中。
101
+ 2. **macOS VoiceOver / Android TalkBack** —— 一条记录都没有。
102
+ 3. **标题层级(`H` 键逐级跳)** —— 本轮没做,协议表里写着"未执行"。
103
+ 4. **9 处「待人工」结论**(例如图表的替代文本是否真的描述了数据)。
104
+
105
+ 这一轮还确认并修掉了一条真缺陷:`table` 的列头按钮会把排序符号 `↕` 念出来
106
+ (来源是 `table.css` 的 `::after` 伪元素文本,Chromium 把它算进了可访问名)。
107
+ 自动检查抓不到它 —— 可访问名"非空"就算过。
108
+ 修法是把字形换成 `mask` 图像(`content` 留空),视觉与三态形状不变;
109
+ 改完用 NVDA 复听,表头只剩「公司 · 按钮」。详见协议页第三节。
103
110
 
104
111
  ---
105
112
 
106
113
  ## 三 · 已知缺口(按影响排序)
107
114
 
108
- 1. **没有真机读屏记录**(同上)。这是与 Carbon / Spectrum 那类一致性报告差距最大的一处。
115
+ 1. **没有人耳旁听与第二平台记录**:2026-10-30 有一轮 NVDA 脚本实录(见协议页),
116
+ 但 VoiceOver / TalkBack 仍为空白,语气与播报时机也没人判断过。
117
+ 这是与 Carbon / Spectrum 那类一致性报告差距最大的一处。
109
118
  2. **没有 VPAT / ACR 正式报告**。采购流程要的那种文档本库没有出;
110
119
  上面那张 SC 表可以当它的替代品,但它不是法律意义上的 ACR。
111
120
  3. **强制色板(forced-colors)只覆盖 6 个 CSS**,Windows 高对比度模式未逐组件验证。
package/index.html CHANGED
@@ -94,7 +94,7 @@ li a:active { background: var(--surface-sunken); }
94
94
  </p>
95
95
  </main>
96
96
  <footer class="wrap">
97
- <p class="note">0.13.1 · 零第三方运行时依赖</p>
97
+ <p class="note">0.13.2 · 零第三方运行时依赖</p>
98
98
  </footer>
99
99
  </body>
100
100
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentcss",
3
- "version": "0.13.1",
3
+ "version": "0.13.2",
4
4
  "description": "移动 Web 组件库:CSS + 可选行为脚本,零第三方运行时依赖。样式用 bundler 引入,交互行为用 script 标签引入。",
5
5
  "keywords": [
6
6
  "css",