
Kibana 可访问性工程从 WCAG 2.2 AA 落地到 EUI 组件与 ESLint 规则体系【免费下载链接】kibanaYour window into all of your data项目地址: https://gitcode.com/GitHub_Trending/ki/kibana本文基于 Kibana 仓库中的可访问性Accessibility共享原则文档 shared_principles.md 展开系统讲解 Kibana 在编写、重构或修复 a11y 相关 lint 报错时统一遵循的判定标准从 WCAG 2.2 AA 基准、EUI 组件优先策略、可访问命名约定、i18n 协同、HTML id 生成到键盘焦点规则与人工升级escalation边界。读完本文你不仅能理解 Kibana 可访问性规范的设计逻辑还能结合仓库真实源码与配套 ESLint 规则表把这套原则应用到自己的 EUI 组件开发中。一、定位与冲突优先级可访问性原则文档明确声明这些原则适用于每一个 Kibana 可访问性决策——无论是编写新代码、重构还是修复 lint 报错。它是 accessibility skill 的第一阅读文件各组件级指南components/*.md都是对它的扩展而非替代。当规则之间发生冲突时文档给出了清晰的优先级排序任务相关的用户或系统指令Task-specific instruction本共享原则文档shared_principles.md组件指南components/*.md或 ESLint 规则表eslint.md。也就是说越靠近具体组件/规则的指南优先级越低越靠近通用原则的文档越具有裁决力。四条硬性标准满足WCAG 2.2 AA遵循 WAI-ARIA Authoring Practices GuideAPG中的控件widget模式优先使用 EUI 组件而非原生 HTML——EUI 开箱即用地处理了 aria 属性、焦点与键盘行为只有在没有合适的 EUI 组件时才使用原生 HTML优先使用语义化 HTML 而非 ARIA——只有当原生语义不足以表达时才补充 ARIA。这两条优先体现了一个核心取向能用声明式、原生可用的手段解决问题就不引入运行时或 ARIA 层面的补救。二、编写决策顺序Authoring decision order无论是写新组件还是修复既有代码文档要求自上而下逐级推进并在第一个能解决问题的层级停下语义Semantics选对元素/EUI 组件直接使用其内建 propslabel、htmlFor、aria-label、aria-labelledby、aria-describedby、roles结构接线Structural wiring通过稳定 ididaria-labelledby把可见文本关联到控件而不是把字符串复制一份塞进隐藏 label行为Behavior只有当语义已经正确时才去调整键盘/焦点行为生命周期 hack 殿后用useEffect处理焦点或屏幕阅读器播报只是最后手段——仅当不存在声明式替代时才允许。这个顺序的意义在于防止跳级打补丁很多可访问性问题本质上是选错了元素第 1 层如果一上来就用useEffect手动聚焦、手动播报第 4 层不仅代码更脆弱还会掩盖真正的语义缺陷。三、可访问命名Accessible naming这是日常编码中出现频率最高的一组规则每个可交互元素都必须有可访问名称——按钮、链接、输入框、下拉框、自定义控件无一例外优先使用可见文本label、标题、按钮文字作为名称并通过aria-labelledby 稳定 id接线而不是把文字重复写入aria-label每个控件只使用一种命名机制——不允许aria-label和aria-labelledby同时出现不得移除title、alt、aria-label、aria-labelledby除非有更强的替代方案替换它们承载语义的图片必须有alt装饰性图片使用alt或aria-hiddentrue。visible text aria-labelledby优于复制字符串到aria-label的原因在于单一数据源。可见文案更新时辅助技术读到的名称自动跟随不会出现两处文案漂移导致的屏幕阅读器用户困惑。四、与 i18n 的协同可访问性原则文档对本地化i18n有一条强约束所有可见字符串与辅助技术字符串aria-label、tableCaption、tooltip 的content、label、title、错误信息、正文文案都必须国际化——绝不允许裸字符串字面量而程序化 tokenradio 的name、内部 id保持为普通字符串即可。这一约定与 Kibana 仓库中独立的 kibana-i18n skill 完全对齐后者规定 React 代码统一使用kbn/i18n的i18n.translate非 JSX 上下文如aria-label、title、placeholder这类接受string的 props和kbn/i18n-react的FormattedMessageJSX 上下文message id 采用plugin.feature.xxx的命名约定。两份 skill 交叉引用保证了可访问命名与国际化不会出现两套互相打架的写法。文档还补充了一条实操细则当文件里已经暴露了共享对象例如i18nTexts.modalTitle新增字符串应沿用这个本地模式而不是再插入内联的i18n.translate调用。这与后文最小确定性变更原则一脉相承。五、HTML id 生成EUI 专用工具aria-labelledby、titleProps.id这类接线都依赖稳定且不冲突的 HTML id。文档规定任何需要生成 id 的场景一律使用 EUI 的 id 生成器且要在首次调用时存进一个语义化变量如modalTitleId、fieldLabelId如果已有变量指向同一元素则直接复用。函数组件——使用useGeneratedHtmlId来自elastic/eui必须在第一次return之前调用import { useGeneratedHtmlId } from elastic/eui; const labelId useGeneratedHtmlId();类组件——使用htmlIdGenerator在render()内以稳定后缀调用import { htmlIdGenerator } from elastic/eui; render() { const labelId htmlIdGenerator()(myLabel); }这两个 API 在 Kibana 源码中是被真实大规模使用的。从源码结构看side-navigation 的 popover 组件 等使用useGeneratedHtmlId而 classic header、modal service 等较老的类组件则使用htmlIdGenerator——这与文档中函数组件用 hook、类组件用 generator的双轨规范一一对应说明该原则不是纸面约定而是仓库既有的普遍代码形态。六、键盘与焦点规则每个可交互元素必须能仅靠键盘到达并操作优先使用原生可聚焦元素button、a、input而不是divonClicktabIndex的组合;不得移除或隐藏可见的焦点指示器焦点顺序遵循逻辑阅读顺序模态框Modal与弹出层Flyout要捕获焦点并在关闭时把焦点还给触发元素自定义快捷键不得与浏览器/屏幕阅读器的快捷键冲突。配套的组件级指南 focus_and_keyboard.md 把这些原则落到了具体组件上EuiButton、EuiButtonIcon、EuiLink等内建交互控件自动参与顺序焦点导航对应 WCAG 2.1.1永远不要设置tabIndex{-1}条件性禁用应使用disabled或条件渲染。tabIndex只允许出现在明确文档化 opt-in 的模式中如 roving tabindex。一个典型陷阱是EuiToolTip的锚点直接子元素是键盘锚点如果它本身不可交互如EuiIcon、EuiText就必须手动加tabIndex{0}否则键盘用户永远无法触达 tooltip。指南给出的正确写法EuiToolTip contentDetails EuiIcon typeinfo tabIndex{0} / /EuiToolTip而给按钮加tabIndex{-1}、或给非交互 tooltip 锚点不加 tab 停靠位都被列为典型错误。七、最小确定性变更与类型安全原则文档把工程纪律也写进了可访问性规范最小确定性变更应用能符合规范模式的最小变更不做无关重构不改动布局、逻辑、license header保留既有行为与意图相同代码形态 → 相同结果不允许主观化的样式微调。类型安全禁止放宽类型string→any或压制错误ts-ignore、as any新增 props 必须与组件的类型定义严格匹配。这两条约束确保了修 a11y 报错这类任务不会被顺手扩写成一次重构也让同类问题的修复结果可预期、可回归对比。八、何时升级人工评审Escalation文档明确列出四种停下、标记为人工评审的情形这是对自动化修复无论是人还是 Agent最重要的护栏Spread props 遮蔽了接线组件上有{...props}无法追踪aria-labelledby、aria-label、name等是否已在展开中提供没有可见标题且新增标题会改变 UX/布局——需要设计或 PM 介入需求互相冲突且没有清晰的取舍例如加aria-label会与title重复但删掉title又会破坏其他消费方意图不明无法从周边代码判断某个 label、caption 或 name 是否准确描述了元素用途。九、变更边界注释与测试的底线不要在修改的行上添加说明性注释narration comments除非指南明确说明也不要删除既有注释如果 DOM 变化导致某条测试断言失败只更新该断言本身——绝不允许删除或跳过测试。这条规则保护了可访问性测试通常是断言 ARIA 属性、焦点行为的测试的约束力DOM 变了、断言跟着更新是合法的演进删掉断言则是把规范打上了洞。十、与 ESLint 规则体系的衔接规则 id → 组件指南共享原则不是孤立存在的。eslint.md 把elastic/eui命名空间下的 a11y ESLint 规则逐一映射到对应的组件指南并标注每条规则无法自动解决、需要人工评审的代码形态。摘录其中几条规则 id对应组件指南需人工评审的形态节选elastic/eui/accessible-interactive-elementfocus_and_keyboard.mdtabIndex来自{...props}或 HOC不得重设计 roving tabindexelastic/eui/no-unnamed-interactive-elementinteractive_components.md直接位于EuiFormRow下行本身提供名称{...props}含未知aria-*elastic/eui/require-aria-label-for-modalsoverlays.md{...props}遮蔽接线无可见标题且不能改 UX——升级人工elastic/eui/require-table-captiondata_tables.mdtableCaption仅经{...tableProps}传入——应在源头修复避免重复冲突 captionelastic/eui/tooltip-button-icon-wraptooltip_icon.md{...props}中未显式给出title会被静默跳过按钮缺aria-label时 autofix 无法执行可以看到人工评审形态与第八节的 escalation 条件高度一致凡是{...props}展开遮蔽了 a11y 属性、或修复会改变 UX 的场景规则都要求停下来而不是硬改。这就是共享原则与规则表的分工——原则文档定义什么是对的ESLint 表负责从报错快速路由到正确的指南。组件指南全景components/index.md 将 12 份组件指南按主题组织覆盖 Kibana 前端最常触碰的可访问性面提示类callouts.md条件挂载的EuiCallOut播报表格类data_tables.mdEuiBasicTable/EuiInMemoryTable的tableCaption表单类form_layout.mdEuiFormRowisInvalid一致性交互控件命名interactive_components.mdEuiButtonIcon、EuiComboBox、EuiSelect、EuiPagination等;覆盖层overlays.mdEuiModal、EuiFlyout、EuiConfirmModal的aria-label要求单选组radio_groups.mdname分组Tooltip 家族tooltip_content.mdcontent/title 中禁止交互元素、tooltip_icon.md图标按钮包裹且不重复 SR 文本、不用原生title、copy_tooltip.mdEuiCopy的beforeMessage、禁止嵌套 tooltip图标icons_and_tooltips.md装饰性 vs 语义性图标、EuiIconTip。结语这套规范回答了什么问题shared_principles.md 的价值不在于罗列规则而在于给出了一套可判定的决策流程遇到可访问性问题时先按优先级确认依据来源再按语义 → 接线 → 行为 → 生命周期的决策顺序逐级尝试命名走可见文本 aria-labelledby EUI id 生成器的规范路径文案一律走 i18n行为改动以键盘/焦点最小集为限遇到 spread props 遮蔽、UX 变更、需求冲突、意图不明四类情形立即升级人工。配套的 eslint.md 与 components/ 目录则把流程落到了每一条 lint 报错和每一个 EUI 组件上。对于在 Kibana 仓库内写 EUI 组件的开发者而言遵循这套流程可访问性就不再是lint 报错了再补的收尾步骤而是编写组件时内建的第一步。【免费下载链接】kibanaYour window into all of your data项目地址: https://gitcode.com/GitHub_Trending/ki/kibana创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考