总结一下接手项目以来Accessibility的一些收获,这个产品要求支持辅助功能,虽然已经是按照最低要求,但做起来即要考虑全键盘可以操作,也要考虑所有显示的内容和需要交互的内容可以被讲述人读出来,还要考虑高对比下的样式兼容,也是有不少的维护开发成本。
css可以通过特定选择器来指定高对比度模式下的样式
js
@media screen and (forced-colors: active) {
color: ButtonText;
backgroundColor: ButtonFace;
}
// 判断系统主题
@media (prefers-color-scheme: dark) {
.brand-text {
color: #ffffff;
}
}
可用的内置变量:
1. Button
| Keyword | Meaning |
|---|---|
ButtonBorder |
Border color of native buttons/controls (e.g., the outline of a button). |
2. Text & content
| Keyword | Meaning |
|---|---|
Canvas |
Background color of the main content area (e.g., a window's background). |
CanvasText |
Text color for content on the Canvas background. |
LinkText |
Color of unvisited hyperlinks (matches system's default link color). |
VisitedText |
Color of visited hyperlinks. |
3. Highlight & selection
| Keyword | Meaning |
|---|---|
Highlight |
Background color of selected text/elements (e.g., highlighted text). |
HighlightText |
Text color of selected content (contrasts with Highlight). |
4. UI chrome (window/frame elements)
| Keyword | Meaning |
|---|---|
ActiveBorder |
Border color of active windows/dialogs. |
ActiveCaption |
Background color of the title bar of an active window. |
ActiveCaptionText |
Text color in the title bar of an active window. |
InactiveBorder |
Border color of inactive windows/dialogs. |
InactiveCaption |
Background color of the title bar of an inactive window. |
5. Status & feedback
| Keyword | Meaning |
|---|---|
GrayText |
Color of disabled text (e.g., grayed-out buttons/options). |
InfoBackground |
Background color of tooltip/info panels. |
InfoText |
Text color in tooltip/info panels. |
html inert 属性
学到了一个新的且很重要的html属性 inert,这个词表示 "使惰性",它可以让元素中的用户交互事件都失效,适合展示顶层窗口时,把交互trap到当前窗口中。 `
js
export function disableApp() {
document.getElementById('app')?.setAttribute('inert', '')
}
export function enableApp() {
document.getElementById('app')?.removeAttribute('inert')
}
焦点 管理
如果不是做 accessibility, 我都不知道聚焦是一个这个难以处理的东西。不过也是因为这个项目之前给第三方组件库提了需求,要求弹窗弹出来自动聚焦第一个可聚焦的元素,但是可能没区分好触发方式:
- 键盘导航触发 (应该自动聚焦)
- 鼠标点击触发(不应该自动聚焦)
- 编程式触发 xxx.focus() (不应该自动聚焦,但组件库聚焦了)
为了不牵扯第三方去修改,我们监听了focus事件,发现有自动聚焦但不想聚焦的时候,就去设置blur,其中监听事件有两种:
js
parentElement.addEventListener('focus', handlefocus, { capture: true, once: true })
等价于
parentElement.addEventListener('focusin', handlefocus, { once: true }) //childElement也会触发
}
几种关于聚焦元素的伪类选择器
js
*:focus
*:focus-within
*:focus-visible 如果没有意外情况,这个选择器本身就只有在键盘导航时才显示样式,已经和鼠标做了区分
判断DOM元素是否可聚焦或者已经聚焦
js
// 检查焦点相关状态
function diagnoseFocus(element) {
return {
hasFocusVisible: element.matches(':focus-visible'),
hasFocus: element.matches(':focus'),
isActive: element.matches(':active'),
tabIndex: element.tabIndex,
computedOutline: getComputedStyle(element).outline,
focusVisibleSupported: CSS.supports('selector(:focus-visible)')
};
}
// 实时监控
document.addEventListener('focusin', (e) => {
console.log('焦点诊断:', diagnoseFocus(e.target));
});
其实非预期的行为大多出现在编程式触发,让组件库在调用 focus()方法的时候加一个 flag,使用组件库的控制起来会方便很多。