iter(g338): toast 读屏可达与抢占播报
This commit is contained in:
@@ -3428,6 +3428,15 @@
|
|||||||
"motivation": "本轮修输入件可访问名时发现口径靠人记:13 个组件都有 ariaLabel,commandPalette 的搜索框却只有 role=combobox + aria-controls,没有名字——读屏进命令面板只念「编辑框」,用户不知道搜什么。这类缺陷肉眼看不出来(placeholder 看着像有名字),只有读屏用户会撞上,必须让门禁兜住。",
|
"motivation": "本轮修输入件可访问名时发现口径靠人记:13 个组件都有 ariaLabel,commandPalette 的搜索框却只有 role=combobox + aria-controls,没有名字——读屏进命令面板只念「编辑框」,用户不知道搜什么。这类缺陷肉眼看不出来(placeholder 看着像有名字),只有读屏用户会撞上,必须让门禁兜住。",
|
||||||
"acceptance": "① ua verify 新增 checkAriaNames:扫描皮肤包源码里含 role: 'combobox' / role: 'slider' / type: 'range' 的创建块,块内既无 aria-label / aria-labelledby、紧随其后 3 行也没有 setAttribute('aria-label') 即报错,报出文件:行号;② 窗口只取单次 h() 调用块(回退到 h('input|textarea|select'、前进到收尾行),避免把相邻元素的 aria-label 误算成本控件的名字;③ 实跑抓到 commandPalette 一处并修好(aria-label 取 placeholder,缺省「搜索命令」);④ AGENTS 的门禁说明与 verify 成功串同步;⑤ npm run check 全绿。",
|
"acceptance": "① ua verify 新增 checkAriaNames:扫描皮肤包源码里含 role: 'combobox' / role: 'slider' / type: 'range' 的创建块,块内既无 aria-label / aria-labelledby、紧随其后 3 行也没有 setAttribute('aria-label') 即报错,报出文件:行号;② 窗口只取单次 h() 调用块(回退到 h('input|textarea|select'、前进到收尾行),避免把相邻元素的 aria-label 误算成本控件的名字;③ 实跑抓到 commandPalette 一处并修好(aria-label 取 placeholder,缺省「搜索命令」);④ AGENTS 的门禁说明与 verify 成功串同步;⑤ npm run check 全绿。",
|
||||||
"notes": ""
|
"notes": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "g338",
|
||||||
|
"domain": "tune",
|
||||||
|
"title": "toast 通知读屏可达",
|
||||||
|
"motivation": "toast 目前只走视觉通道,读屏用户完全感知不到通知的出现与消失",
|
||||||
|
"acceptance": "toast 容器带 aria-live=polite,error 级别用 role=alert 抢占播报;audit 与 verify 全绿",
|
||||||
|
"status": "open",
|
||||||
|
"notes": ""
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const PAUSE_FLOOR = 400; // 恢复时的最短剩余毫秒:悬停前已近归
|
|||||||
/*** 确保堆叠容器存在:fixed 挂在 body 末尾,后续消息依次向上堆叠 ***/
|
/*** 确保堆叠容器存在:fixed 挂在 body 末尾,后续消息依次向上堆叠 ***/
|
||||||
function ensureHost(): HTMLElement {
|
function ensureHost(): HTMLElement {
|
||||||
if (host) return host;
|
if (host) return host;
|
||||||
host = h('div', { class: 'ua-toast-host' });
|
host = h('div', { class: 'ua-toast-host', 'aria-live': 'polite', 'aria-atomic': 'false' }); // live 容器兜底播报:条目自身 role 再定抢占级别
|
||||||
document.body.appendChild(host);
|
document.body.appendChild(host);
|
||||||
return host;
|
return host;
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ export function showToast(props: ToastProps): HTMLElement {
|
|||||||
let startedAt = 0; // 本段计时起点,用于折算剩余
|
let startedAt = 0; // 本段计时起点,用于折算剩余
|
||||||
const item = h(
|
const item = h(
|
||||||
'div',
|
'div',
|
||||||
{ class: `ua-toast ua-toast-${props.variant ?? 'info'}${props.closable ? ' is-closable' : ''}${props.actionLabel ? ' is-action' : ''}`, role: 'status', id: `ua-toast-${++seq}` },
|
{ class: `ua-toast ua-toast-${props.variant ?? 'info'}${props.closable ? ' is-closable' : ''}${props.actionLabel ? ' is-action' : ''}`, role: props.variant === 'danger' ? 'alert' : 'status', id: `ua-toast-${++seq}` }, // error 抢占播报,其余礼貌播报
|
||||||
h('span', { class: 'ua-toast-msg' }, props.message),
|
h('span', { class: 'ua-toast-msg' }, props.message),
|
||||||
props.actionLabel
|
props.actionLabel
|
||||||
? h('button', { type: 'button', class: 'ua-toast-action', onClick: () => { dismiss(item); props.onAction?.(); } }, props.actionLabel) // 操作按钮(如撤销):点完立即收起再回调
|
? h('button', { type: 'button', class: 'ua-toast-action', onClick: () => { dismiss(item); props.onAction?.(); } }, props.actionLabel) // 操作按钮(如撤销):点完立即收起再回调
|
||||||
|
|||||||
Reference in New Issue
Block a user