无障碍(Accessibility,常缩写为"a11y")意味着你的网站或应用能被所有人使用——包括依赖屏幕阅读器、纯键盘操作、语音控制或其他辅助技术(AT)的用户。Quasar 组件内置了完善的无障碍层:语义化标签、WAI-ARIA 属性、键盘交互模式和焦点管理。本页介绍框架为你做了什么、哪些部分有意留给你的应用来处理,以及如何在各组件文档中查找详细说明。
无障碍是框架和你的应用之间的一份"契约"。Quasar 能渲染出正确的 role="checkbox" 配合 aria-checked 和 Space/Enter 键处理,但它无法知道一个纯图标按钮"表达什么意思"、你的品牌配色对比度是否足够、也不知道某个抽屉代表哪种导航地标。下方的组件概览矩阵会精确告诉你每部分职责归谁。
Quasar 提供了什么
语义化标签
组件在有对应原生 HTML 元素时优先使用原生标签——真正的 <button>(QBtn)、<a href>(各处的路由链接)、<form>(QForm)、<table>(QTable)、<hr>(QSeparator),以及来自 QLayout 家族的完整地标集合:<header>、<footer>、<aside> 和 <main>。原生元素自带语义、状态和键盘行为,这始终比用 ARIA 重新实现更稳健。
当视觉设计需要自定义元素时,组件会声明对应的 ARIA role 并管理所需的状态:role="checkbox"/"radio"/"switch" 配合 aria-checked(包括 mixed)、role="slider"/"spinbutton" 配合 aria-valuemin/max/now、role="progressbar"、role="tablist"/"tab"/"tabpanel"、role="tree"/"treeitem"(虚拟滚动模式下通过 aria-level/aria-setsize/aria-posinset 补偿)、role="combobox" + "listbox"/"option"(QSelect),以及 role="separator" 配合完整的键盘调节支持(QSplitter)。
有些容器刻意不声明 role,因为它们可以容纳任意内容,错误的 role 会导致无效标记。QMenu 就是典型例子:ARIA 的 menu role 只允许子节点是 menu item,因此 QMenu 保持中性,由你在其内部的 QList 上声明 role="menu"(当内容确实是命令列表时)。QItem 则会根据上下文推导自身 role——在已声明的 menu 内是 menuitem,在 list 内是 listitem,可点击时是 button,导航时是普通链接。详见 QList/QItem 的无障碍章节。
装饰性元素对辅助技术隐藏:每个 QIcon 默认渲染 aria-hidden="true"(交互式图标如输入框的清除按钮会通过 role 和本地化标签重新暴露),背景遮罩层、虚拟滚动填充区、自定义滚动条等辅助结构也全部 aria-hidden。
键盘支持
- 激活:所有可点击的元素都支持键盘激活。QBtn 甚至在链接形态(
<a>)按钮上合成了 Space 激活——原生<a>只响应 Enter——确保两个键都能用。可点击的 QItem、chip、展开头部、可排序表头和步骤条头部都处理 Enter/Space。 - 组合控件使用漫游 tabindex(roving tabindex)——整个控件是一个 Tab stop,用方向键在内部移动,符合 WAI-ARIA APG 规范:QTabs、QOptionGroup(单选模式)、QRating、QDate 的日历面板、QCarousel 的导航、QEditor 的工具栏以及 QTree。
- 取值控件响应方向键(QSlider、QRange、QKnob、QSplitter、QTime 的微调器),大多还支持 Home/End 和 PageUp/PageDown;QSelect 实现了完整的 combobox 键盘契约,包括前缀搜索。
- Escape 关闭以链式方式工作:只有最顶层打开的弹出(对话框、菜单、提示、叠加模式的抽屉)响应,逐个关闭弹出层。持久弹出会抖动或忽略而非关闭。
- 水平方向键支持 RTL(RTL 支持)——选项卡、单选组、滑块、日期导航、编辑器工具栏和分割面板全部在 RTL 语言包下镜像方向。
注意 Escape 处理和焦点回收是桌面模式行为;在移动平台上,关闭操作通过 Quasar 的 History 插件连接到平台返回键(Capacitor/Cordova 构建)。
焦点管理
- 对话框记录打开它的元素,在渲染时将焦点移入对话框(遵循
autofocus/data-autofocus),在模态时捕获溢出的焦点,关闭时将焦点还原到打开者。 - 菜单同理,但有一个改进:在菜单最后一个可聚焦元素上按 Tab(或第一个元素上按 Shift+Tab)会关闭菜单并从锚点继续 Tab 导航——键盘焦点永远不会泄漏到 portal 背后的虚空中。
- 焦点移动会在弹出层过渡动画播放时排队,避免竞争的焦点意图发生竞态;在模态对话框内打开的菜单会渲染在对话框元素_内部_,这样
aria-modal不会将其从辅助技术中隐藏。 - QForm 在校验失败后聚焦第一个无效字段。
- 焦点环仅在键盘操作时显示:鼠标或触摸交互后,组件将焦点停放在不可见的辅助元素上,避免焦点环闪烁;键盘聚焦时则保持可见指示器。这是内置行为——无需
:focus-visiblepolyfill。(内置指示器样式针对桌面模式;同时使用触摸和键盘的混合应用可能需要自定义焦点样式。)
屏幕阅读器标签和语言包
内置控件标签——输入框的清除按钮、chip 的删除图标、展开/折叠箭头、分页器的 首页/上一页/下一页/末页、QDate 的月/年导航、轮播箭头、编辑器工具栏——来自当前激活的 Quasar 语言包,会自动以你应用的语言播报。如果你编写自定义语言包,请覆盖这些 key(其中有些如 label.expand 是函数);屏幕阅读器用户获得的就是语言包提供的内容。
ARIA 属性透传
你放在 Quasar 组件上的任何 aria-* 属性(或 role)都会到达语义正确的元素:在表单字段上它到达原生 <input>/聚焦目标,在 QDialog 上它到达 role="dialog" 元素,以此类推。你的属性会覆盖自动生成的属性(表单字段上的 aria-describedby 会_合并_而非覆盖)。这是下面"你的职责"列表中所有内容的标准机制——无需特殊 prop。
你的应用需要负责什么
无障碍名称。 没有什么能为你自动生成有意义的名称。请为以下情况提供 aria-label(或可见文本):纯图标的 QBtn 和 FAB;为 QImg 提供 alt;为 QVideo 提供 title;为 QDialog、QDrawer、QToolbar、QOptionGroup 以及任何你希望可区分的 role="navigation"/role="group" 容器提供 aria-label/aria-labelledby。多个未标记的地标或工具栏是 WCAG 违规。注意 QTooltip 不会为其锚点命名——它只起描述作用——所以纯图标控件即使带有 tooltip 也需要自己的 aria-label。
自定义触发器上的弹出语义。 QBtnDropdown 和 QSelect 完整连接了它们触发器的 ARIA(aria-expanded、条件性 aria-controls),从 v2.25 起 QMenu 也会在其锚点上维护 aria-expanded(加上声明在 QMenu 自身 role 对应的 aria-haspopup)。它只在锚点是 ARIA 允许该状态的控件时才能这么做,因此你的锚点需要是 <button>、链接或带有 widget role 的元素——普通 <div> 什么都得不到,这是设计如此。在弹出内部的 QList 上声明 role(推荐做法)仍然需要你自己提供 aria-haspopup——在 QMenu 锚点上作为普通属性,在 QBtnDropdown 上通过其 toggle-aria-haspopup prop。详见 QMenu 和 QBtnDropdown。
地标结构。 QLayout 家族为你免费提供了每种地标各一个——不要再添加自己的 <main>(QPage 已经是了),并标记多个抽屉或导航区域使它们可以被区分。
颜色对比度。 Quasar 不强制对比度检查,无论是浅色还是深色模式。请使用 WebAIM 对比度检查器 等工具检查你的品牌配色是否符合 WCAG 2.2 对比度最低要求(普通文本 4.5:1)——如果同时支持两种主题则两种都要检查。
减少动画。 只有 CSS 动画辅助类 尊重 prefers-reduced-motion。组件过渡、涟漪效果和滚动驱动效果(QParallax)不会——对动画敏感的用户需要你自行减弱这些效果(例如 $q.config.ripple = false、transition prop、禁用自动播放)。QCarousel 的 autoplay 在 hover 或聚焦时不会暂停;如果使用了它,请提供暂停控件(WCAG 2.2.2)。
播报异步状态。 加载指示器——QSpinner、QInnerLoading、QSkeleton、QAjaxBar、QUploader 的逐文件状态——都是纯视觉的。当状态变化对用户有意义时,请用 live region(role="status" 加上简短文本如"加载中…")包裹或伴随它们,并用 aria-hidden="true" 隐藏纯装饰性的占位符。
手势的键盘替代。 滑动操作(QSlideItem)、下拉刷新和触摸平移没有键盘路径。请提供平行操作方式——可见按钮、上下文菜单,或将 QPullToRefresh 的 trigger() 方法连接到按钮。
视口缩放。 WCAG 1.4.4 要求文本可放大至 200%。Quasar CLI 脚手架在 web 模式下允许捏合缩放(width=device-width, initial-scale=1);早期脚手架生成的应用带有 user-scalable=no, maximum-scale=1 的 viewport meta,这会导致所有自动化审计失败——请更新 index.html 中的 <meta name="viewport"> 使其匹配。Cordova/Capacitor 构建有意保持固定视口以获得原生应用体验;OS 级别的缩放(iOS 的辅助功能缩放、Android 放大)在那里覆盖了无障碍需求。解锁视口的一个副作用:iOS Safari 在字号小于 16px 的输入框获得焦点时会自动缩放页面。如果这影响了你的设计,请设置 Sass 变量 $input-font-size: 16px(或将 16px 字号限定到你的字段上)而不是重新禁用缩放。
测试。 没有框架能替代测试。请用纯键盘走一遍关键流程(所有功能都能到达吗?焦点可见吗?能退出来吗?);用屏幕阅读器跑一遍(VoiceOver 随 macOS/iOS 附带,NVDA 在 Windows 上免费);并在 CI 中加入 axe 或 Lighthouse 的自动化检查——它们捕获机械性问题(名称、对比度、ARIA 有效性),让你的手工测试可以专注于流程。
组件概览
每个组件名都链接到其文档页面的"无障碍"章节,其中描述了确切行为——包括限制——以及你需要在此基础上添加什么。
按钮
| 组件 | 内置行为 | 键盘 |
|---|---|---|
| QBtn | 原生 <button> 或 <a>(需要时派生 role="button");aria-disabled;带 percentage 加载时的 progressbar ARIA | Enter/Space,包括链接按钮上的 Space |
| QBtnDropdown | 展开/折叠模式:aria-expanded、aria-controls(弹出层存在时)、本地化展开/折叠标签、可选的 toggle-aria-haspopup | 继承自 QBtn + QMenu |
| QBtnGroup | 仅视觉分组——需要你自己添加 role="group" + aria-label | 每个按钮是独立的 Tab stop |
| QBtnToggle | 每个选项有 aria-pressed;通过 attrs 为每个选项设置标签 | 每个按钮 Enter/Space |
| QFab | 触发器上的 aria-expanded/aria-controls;关闭时操作项对辅助技术隐藏;焦点返回触发器 | Enter/Space 打开和激活 |
导航
| 组件 | 内置行为 | 键盘 |
|---|---|---|
| QTabs | role="tablist"/"tab" 配合 aria-selected、aria-orientation | 漫游 tabindex;方向键(RTL 感知)、Home/End;显式激活 |
| QTabPanels | role="tabpanel"、tabindex="0";tab↔panel 的 id 关联需要手动连接(已文档化) | 面板可通过 Tab 到达 |
| QBreadcrumbs | 原生链接;无地标/aria-current——需要你自己用 <nav> 包裹并标记当前页面 | 原生链接行为 |
| QPagination | 命名的 role="navigation";本地化的 首页/上一页/下一页/末页 标签;活动页面上的 aria-current | 按钮 Enter/Space;输入模式下 Enter 提交 |
| QStepper | aria-current="step";每步是一个有标签的 group;可导航的头部暴露为按钮(禁用的暴露为 aria-disabled 按钮) | 可导航头部 Enter/Space |
| QToolbar / QBar | role="toolbar"——页面有多个时通过 aria-label 命名 | 子元素是独立的 Tab stop |
表单字段
| 组件 | 内置行为 | 键盘 |
|---|---|---|
| QField / QInput | <label for> 关联;错误通过 role="alert" 配合 aria-invalid/aria-errormessage/aria-describedby 播报(仅在消息渲染时);可访问的清除按钮 | 原生编辑;清除按钮 Enter/Space |
| QSelect | 完整 combobox 模式:role="combobox" + aria-expanded/aria-controls/aria-activedescendant,role="listbox"/"option" 配合 aria-selected 和虚拟滚动感知的 aria-setsize/posinset | 方向键打开/导航、前缀搜索、Home/End、PageUp/PageDown、Enter 选择、Esc 关闭 |
| QForm | 原生 <form>;校验失败时聚焦第一个无效字段 | 原生提交 |
| QEditor | role="textbox" + aria-multiline;工具栏遵循 APG toolbar 模式并带有本地化标签 | 工具栏漫游 tabindex,方向键/Home/End;Ctrl 格式化快捷键 |
| QFile | 字段框架 + 校验 ARIA;可键盘打开选择器 | Enter/Space 打开选择器 |
| QUploader | 真正的按钮配合本地化名称、progressbar ARIA——但你仍需播报状态变化 | 原生按钮激活 |
表单控件
| 组件 | 内置行为 | 键盘 |
|---|---|---|
| QCheckbox | role="checkbox"、三态 aria-checked(含 mixed)、aria-label 来自 label | Enter/Space 切换 |
| QRadio | role="radio" + aria-checked;组语义来自 QOptionGroup | Enter/Space 选择 |
| QToggle | role="switch" + aria-checked | Enter/Space 切换 |
| QOptionGroup | role="radiogroup"/"group";APG 单选组模式 | 漫游 tabindex;方向键选择(RTL 感知),跳过禁用项 |
| QSlider / QRange | role="slider" 配合 aria-valuemin/max/now、aria-orientation、aria-readonly(QRange:每个滑块一个命名的 slider) | 方向键步进(RTL/垂直感知),PageUp/PageDown ×10 |
| QRating | 每颗星的单选组模式,带有逐星标签(icon-aria-label) | 漫游 tabindex;方向键移动,Enter/Space 选择 |
| QKnob | 可聚焦元素上的 role="slider" 配合值 ARIA | 方向键步进,PageUp/PageDown ×10 |
| QColor | Tune(调节)选项卡(原生 input + slider)是无障碍访问路径,带有本地化的标签页/字段/滑块名称;调色板/光谱仅支持指针操作 | Tune 选项卡上的原生 input + slider 键 |
| QDate | 日期按钮带有完整日期标签、aria-pressed 选中状态、今天的 aria-current="date";本地化导航 | 漫游 tabindex;方向键跨月移动、Home/End、PageUp/PageDown(+Shift 跨年) |
| QTime | 表头每个单位为 role="spinbutton",带有本地化标签和值 ARIA;表盘是纯指针可视化 | 方向键步进、Home/End、直接数字输入 |
弹出层
| 组件 | 内置行为 | 键盘 |
|---|---|---|
| QDialog | role="dialog" + aria-modal;打开时焦点移入,模态时焦点被约束,关闭时焦点还原——通过 aria-label/aria-labelledby 命名 | Esc 关闭(持久对话框会抖动) |
| QMenu | 设计上无 role 的弹出;在内部 QList 上声明 role="menu";控件锚点上的 aria-expanded;关闭时焦点还原 | Esc 关闭;Tab 超出边缘时关闭并从锚点继续 |
| QTooltip | 通过 aria-describedby 描述其锚点(显示时)——永不命名它;role="tooltip";键盘聚焦时显示 | Esc 关闭但不移动焦点(WCAG 1.4.13) |
| QPopupProxy | 根据屏幕尺寸在 QMenu 和 QDialog 之间切换——语义跟随渲染的组件 | 委托 |
| QPopupEdit | 基于 QMenu 的编辑面板;本地化的 设置/取消 按钮;关闭时不会静默提交 | Esc 取消;Enter 保存需要你自己连接 |
列表和数据
| 组件 | 内置行为 | 键盘 |
|---|---|---|
| QList / QItem | 根据上下文派生 role:list/listitem、menu→menuitem、可点击→button、链接保持链接;禁用的可操作项有 aria-disabled | 可点击项 Enter/Space |
| QExpansionItem | 头部 role="button" 配合 aria-expanded/aria-controls 和本地化展开/折叠标签;折叠内容真正隐藏 | Enter/Space 切换 |
| QTable | 原生 <table>;可排序表头带有 aria-sort 和键盘排序;本地化的选择、分页和加载名称 | Enter/Space 排序;其他部分使用标准控件 |
| QMarkupTable | 原生 <table> 包装器(可滚动区域是 Tab stop)——表头/scope/caption 语义由你负责 | 原生 |
| QTree | role="tree"/"treeitem"/"group" 配合 aria-expanded/selected/checked;虚拟模式添加 aria-level/setsize/posinset | 漫游 tabindex;方向键导航/展开/折叠、Home/End、Enter 选择、Space 展开——或在可勾选节点上勾选 |
| QVirtualScroll | 填充区对辅助技术隐藏;切片变化时焦点不会落到 <body> 上;屏幕外的项目对辅助技术不存在 | 当容器拥有滚动时,它是一个 Tab stop |
| QTimeline | 原生 <ul>/<li> 结构 | 静态内容 |
| QChatMessage | 文本内容可读;发送/接收仅是视觉区分——通过 name 传达作者身份 | 静态内容 |
| QCarousel | 导航为 tablist,使用漫游 tabindex 和逐幻灯片标签;幻灯片为 tabpanel;本地化箭头 | 方向键(方向和 RTL 感知)、Home/End;选择跟随焦点 |
反馈和媒体
| 组件 | 内置行为 | 键盘 |
|---|---|---|
| QBadge | role="status"(礼貌的 live region)、aria-label 来自 label | — |
| QBanner | role="alert"——动态插入时自动播报 | — |
| QChip | 可点击 chip 为 role="button"(选择 chip 有 aria-pressed);删除图标支持键盘操作并带有本地化标签 | Enter/Space 激活/删除 |
| QLinearProgress / QCircularProgress | role="progressbar" 配合值 ARIA(不确定状态时移除)——请添加 aria-label | — |
| QAjaxBar | 活动时有 progressbar ARIA,空闲时 aria-hidden | — |
| QSpinner | 无——搭配 live region 或作为装饰性隐藏 | — |
| QInnerLoading | 仅视觉遮罩——需要你自己播报状态并管理被覆盖的内容 | — |
| QSkeleton | 装饰性占位符——需要你自己标记加载区域 | — |
| QIcon | 始终 aria-hidden="true";通过 attrs 为语义图标重新声明 | — |
| QImg | role="img" 由 alt 命名;alt="" 将其标记为装饰性 | — |
| QAvatar / QCard | 展示性容器;tag prop 用于语义结构 | — |
| QSeparator | 原生 <hr> 配合 aria-orientation | — |
| QVideo | <iframe> 由 title prop 命名——请务必提供 | 内嵌播放器自身的 |
| QParallax | 滚动驱动的运动;使用 media 插槽放置有意义的图片(alt)并考虑减少动画偏好的用户 | — |
布局和滚动
| 组件 | 内置行为 | 键盘 |
|---|---|---|
| QLayout | 通过子组件构建地标骨架:<header>、<footer>、<aside>、<main> | — |
| QHeader / QFooter | 真正的 <header>/<footer> 地标;隐藏的边距条离开辅助技术树;reveal-hidden 模式在焦点时重新显示 | 焦点触发重新显示 |
| QDrawer | <aside> 地标;遮罩层和打开条对辅助技术隐藏;叠加模式下无焦点陷阱 | Esc 关闭模态状态的抽屉 |
| QPage | 渲染页面的 <main>——不要再添加另一个 | — |
| QPageScroller | 定位包装器——在插槽中放置真正的按钮以支持键盘访问 | 通过插槽中的按钮 |
| QScrollArea | 自定义滚动条对辅助技术隐藏;内容溢出时容器是 Tab stop | 可聚焦后原生滚动键 |
| QSplitter | 完整的 WAI-ARIA 窗口分割器:role="separator"、aria-controls、值 ARIA、本地化名称 | 方向键调节(RTL 感知)、Home/End、Enter 折叠/恢复 |
| QSlideItem | 滑动操作仅支持指针——请提供键盘替代方案 | 无内置 |
| QInfiniteScroll | 通过原生滚动加载;加载状态不播报 | 支持键盘滚动 |
| QPullToRefresh | 指针手势;通过按钮暴露 trigger() 给键盘用户 | 通过你自己的按钮 |
纯视觉或无渲染的工具组件,无无障碍表面:QSpace、QPageSticky、QIntersection、QResizeObserver、QScrollObserver、QNoSsr、QSlideTransition 和 QResponsive。
外部资源
- WCAG 2.2 — Web 内容无障碍指南快速参考
- WAI-ARIA 创作实践指南 (APG) — Quasar 组件遵循的交互模式
- MDN 无障碍 — 实用的 HTML/ARIA 参考
- WebAIM — 文章、对比度检查器和屏幕阅读器调查数据
- axe DevTools 和 Lighthouse — 浏览器和 CI 中的自动化审计
- NVDA(Windows,免费)和 VoiceOver(macOS/iOS 内置)— 用来测试的屏幕阅读器