What happened?
When a QDialog opens in modal mode (non-seamless), its backdrop element (.q-dialog__backdrop) is rendered with both aria-hidden="true" and tabindex="-1" at the same time. During the open transition, Quasar's focus management blurs the previously focused element and then attempts to focus the dialog's inner content. However, the focus can land on the backdrop element, which is both focusable (due to tabindex="-1") and hidden from assistive technology (due to aria-hidden="true").
The browser detects this contradiction and emits the following console warning:
Blocked aria-hidden on an element because its descendant retained focus.
The focus must not be hidden from assistive technology users.
Avoid using aria-hidden on a focused element or its ancestor.
Consider using the inert attribute instead, which will also prevent focus.
For more details, see the aria-hidden section of the WAI-ARIA specification
at https://w3c.github.io/aria/#aria-hidden.
Element with focus: <div.q-dialog__backdrop fixed-full ...>
Ancestor with aria-hidden: <div.q-dialog__backdrop fixed-full ... aria-hidden="true" tabindex="-1">
The browser functionally overrides the aria-hidden (so there is no actual accessibility breakage), but the console warning is noisy and causes unnecessary concern for developers using Quasar.
What did you expect to happen?
The backdrop element should not have conflicting attributes. The expected behavior is one of:
- Option A (recommended): Remove
aria-hidden="true" from the backdrop entirely. The backdrop is an internal visual component of the dialog, not background content that should be hidden from assistive technology. Keep tabindex="-1" for programmatic focus during transitions.
- Option B: Replace
aria-hidden="true" with the [inert](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inert) attribute, which natively prevents focus while also hiding content from AT — avoiding the contradiction entirely.
- Option C: Keep
aria-hidden="true" but remove tabindex="-1" so the backdrop cannot receive programmatic focus.
No console warning should appear under normal dialog open/close cycles.
Reproduction URL
none
How to reproduce?
- Create a
QDialog with v-model bound to a boolean ref, without seamless prop (default modal mode).
- Open the dialog by setting the
v-model to true (programmatically or via user click).
- Open the browser's developer console.
- Observe the "Blocked aria-hidden on an element because its descendant retained focus" warning.
This is also reproducible with persistent dialogs: clicking the backdrop triggers the shake() animation, which re-focuses the dialog and can land focus on the backdrop element again, re-triggering the warning.
Flavour
Quasar CLI with Vite (@quasar/cli | @quasar/app-vite)
Areas
Components (quasar)
Platforms/Browsers
Electron
Quasar info output
Relevant log output
Additional context
- Root cause location: In
ui/src/components/dialog/QDialog.js, the renderPortalContent() function hardcodes both attributes on the backdrop element:
h('div', {
class: 'q-dialog__backdrop fixed-full',
'aria-hidden': 'true',
tabindex: -1,
onClick: onBackdropClick
})
- Confirmed present in latest
dev branch (commit 9dcc6fc, Apr 30, 2026) — the issue has not been fixed upstream.
- The
handleShow() method calls document.activeElement?.blur() followed by registerTick(focus), and focus() calls node.focus({ preventScroll: true }) on the dialog's innerRef. During the Vue transition, the innerRef may not yet contain focusable children, so focus falls onto the backdrop element.
- This combination is explicitly prohibited by the [WAI-ARIA 1.2 specification](https://w3c.github.io/aria/#aria-hidden):
"Authors MUST NOT set aria-hidden="true" on a focused element or any of its ancestors."
What happened?
When a
QDialogopens in modal mode (non-seamless), its backdrop element (.q-dialog__backdrop) is rendered with botharia-hidden="true"andtabindex="-1"at the same time. During the open transition, Quasar's focus management blurs the previously focused element and then attempts to focus the dialog's inner content. However, the focus can land on the backdrop element, which is both focusable (due totabindex="-1") and hidden from assistive technology (due toaria-hidden="true").The browser detects this contradiction and emits the following console warning:
The browser functionally overrides the
aria-hidden(so there is no actual accessibility breakage), but the console warning is noisy and causes unnecessary concern for developers using Quasar.What did you expect to happen?
The backdrop element should not have conflicting attributes. The expected behavior is one of:
aria-hidden="true"from the backdrop entirely. The backdrop is an internal visual component of the dialog, not background content that should be hidden from assistive technology. Keeptabindex="-1"for programmatic focus during transitions.aria-hidden="true"with the[inert](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inert)attribute, which natively prevents focus while also hiding content from AT — avoiding the contradiction entirely.aria-hidden="true"but removetabindex="-1"so the backdrop cannot receive programmatic focus.No console warning should appear under normal dialog open/close cycles.
Reproduction URL
none
How to reproduce?
QDialogwithv-modelbound to a boolean ref, withoutseamlessprop (default modal mode).v-modeltotrue(programmatically or via user click).This is also reproducible with persistent dialogs: clicking the backdrop triggers the
shake()animation, which re-focuses the dialog and can land focus on the backdrop element again, re-triggering the warning.Flavour
Quasar CLI with Vite (@quasar/cli | @quasar/app-vite)
Areas
Components (quasar)
Platforms/Browsers
Electron
Quasar info output
Relevant log output
Additional context
ui/src/components/dialog/QDialog.js, therenderPortalContent()function hardcodes both attributes on the backdrop element:devbranch (commit9dcc6fc, Apr 30, 2026) — the issue has not been fixed upstream.handleShow()method callsdocument.activeElement?.blur()followed byregisterTick(focus), andfocus()callsnode.focus({ preventScroll: true })on the dialog'sinnerRef. During the Vue transition, theinnerRefmay not yet contain focusable children, so focus falls onto the backdrop element.