An accessibility-first renderless (headless) dialog component made for Vue https://renatodeleao.github.io/a11y-vue-dialog/
MIT License
Bot releases are hidden (Show)
The [workaround introduced on release v0.4.4](https://github.com/renatodeleao/a11y-vue-dialog/releases/tag/v0.4.4)
came back to haunt me. Even though I could probably find another workaround not to produce a breaking change I decided to go the simpler way and fix it without workarounds.
The side-effect is now that this package requires a new rootRef
binding besides the already mandatory dialogRef
, and backdropRef
can no longer be used as root as present in the documentation.
So new dialogs MUST HAVE the following structure
<a11y-vue-dialog #default="{ rootRef, dialogRef, backdropRef }">
<div v-bind="rootRef.props">
<div v-bind="backdropRef.props" v-on="backdropRef.listeners" /> <!-- optional -->
<div v-bind="dialogRef.props" v-on="dialogRef.listeners">
</div>
</div>
</a11y-vue-dialog>
as usual, you're free to use any elements/components of your choice and everything else is optional (but not for long, since this is a11y dialog I'll be more strict and throw errors if, for example, the mandatory titleRef
is not binding.
Published by renatodeleao over 1 year ago
focus-trap
was bumped
to 7.3.1
with bugfixes from the underlying tabbable
package bumped to 6.1.1
.
Changes are mostly related with support for HTML inert
attribute.
Check their releases for more info:
Published by renatodeleao almost 2 years ago
Just upgrades focus-trap dependency from 6.9.4 to 7.2.0. Since it's a breaking change from the underlying package, bump this packages version as new minor pre-release.
None of my examples/demos were impacted by this change, but if yours do, please refer to focus-trap releases
this patch enables the component to work correctly when in a vue 3 environment paired @vue/compat
(aka migration build).
It does so by forcing MODE: 3
in their compatConfig option, which makes vue treat it as regular vue3 component.
Published by renatodeleao about 2 years ago
From this version on we provide universal support for vue 2 and vue 3 with the same package 🎊
Full Changelog: https://github.com/renatodeleao/a11y-vue-dialog/compare/v0.8.0-beta.1...v0.8.0-beta.2
Published by renatodeleao about 2 years ago
Fixing dialogs breaking when null
was provided as initialFocus
value. New focus-trap
does not allow it.
Full Changelog: https://github.com/renatodeleao/a11y-vue-dialog/compare/v0.8.0-beta.0...v0.8.0-beta.1
Published by renatodeleao about 2 years ago
This version has one bug: it requires to have at least on focusRef
binding. Because focus-trap
version does not accept initialFocus
config to be null
and breaks instead of finding the first tabbable element. Fixed in the next version.
portal-vue
as dependency: no longer ships any portal
as a dependency, as this component should work with any user land one. closed #11 and #16A11yVueDialogRenderless
to simply A11yDialog
. We no longer provided any styled solution, so there's no need to make the name state that fact. We're assumedly headless/renderless now.Vue.component('name', A11yDialog')
which was everything that our plugin
was doing to be honest.focus-trap
to the latest versionFull Changelog: https://github.com/renatodeleao/a11y-vue-dialog/compare/v0.7.0...v0.8.0-beta.0
Published by renatodeleao over 3 years ago
v-if
on the slot content root as was suggested in readme examples. That being said, it's not wrong to apply v-if
to the a11y-vue-dialog
itself, to a wrapper element, to the <portal>
element or the <transition>
element itself. Before this minor, if any of these cases happened, the handleClose
handler would not be called, and some leaks could occur. Now we call it correctly on beforeDestroy
, meaning that consumers can safely implement in any way.@close
Also tested usage with portal-vue-simple
and it works flawlessly, I will probably make this as the official peer-dependency for the official 1.0 release as it's more than enough for our dialog needs and only 3k
(1.3k gzip).
Remove preventBackgroundScroll
prop.
overflow: hidden
to body
but we all know that preventing backgroundScroll in a cross-browser/device world is far more complicated than that.overflow:hidden
to body
assumed that the consumer app "scroller" was body, when it might not be the case.@show
, @hide
and added example so of how to achieve previous behaviour using them in your compositions.Allow close on Esc even if role="alertdialog"
as per wai-aria guideline example
Note that backdrop
click to close is still prevented, because technically it's outside the dialog
element (and we haven't tested another way so), probably in the next release we'll have smarter prevention.
Content authors SHOULD make alert dialogs modal by ensuring that, while the alertdialog is shown, keyboard and mouse interactions only operate within the dialog.
https://www.w3.org/TR/wai-aria-1.1/#alertdialog
Published by renatodeleao about 4 years ago
Fixes focus-trap
initialFocus
option handling of autofocus
elements. Here's the default focus order:
1. focusRef` binding
2. elements with `[autofocus]` attribute — (`document.querySelector('[autofocus]'))
3. first focusable element
4. use focus-trap fallback (dialogRef)
The problem was that we weren't checking if 2 was a valid candidate for the autofocus
attribute — input, select, button and textarea to be precise — and I faced a bug where a vue
input component wrapper didn't have inheritAttrs: false
, so a magic <div autofocus>
was trying to be erroneously focused, not focusing the actual input.
Note that if you want to focus an invalid element you can still do
<div tabindex="0" v-bind="focusRef.attrs">
If you're that kind of person, don't forget the tabindex!
</div>
Published by renatodeleao about 4 years ago
Focus management like a pro!
Published by renatodeleao about 4 years ago
package.json
main
field, that would break systems without specific setup for transpilling our source code... i mean why the heck was i shipping a package point to the src in the first place?upkg
field that points to minified versionThanks to Dependabot, you're tha bot!
Published by renatodeleao over 4 years ago
It's the same as the previous release, but with actual build
or dist
code in it.
Published by renatodeleao over 4 years ago
🚨🚨🚨 This release was published without the build code, use the next patch.🚨🚨🚨
Another day, another exception.
On a project of mine i've added focus-visible
polyfill as to match a ui requirement on focus styles and suddenly the dialog was broken. focus-visible
polyfill adds attributes to elements and attaches focus and blur events to the document: those attribute changes were triggering mutations, and the events were setting document.activeElement
to body for a brief moment. Our mutationObserver
was catching that brief moment as "the focused item was removed/hidden and it's not focus anymore, do the focus trap magic to find the next focusable element" But the next focusable element to body
is always the first focusable element of the dialog, so we're stuck in a loop.
Fix was just moving our observer verifications into a setTimeout()
call, moving them to the next queue, after the focus-visible
and other focus attaching plugins/polyfills done their things
Published by renatodeleao over 4 years ago
Make the naive patch on v0.4.5
not so naive, by keeping track of mutated focus elements positions and continue the expected tab sequence even after focus is lost to body
(out of the trap) due to a mutation event (could be v-if
, v-show
, class
that hidden, setting the button disabled
— basically anything that makes an interactive/focusable element inert
).
This is the TL;DR and https://github.com/renatodeleao/a11y-vue-dialog/commit/bfade5bb292fc6a1e8e01ace815f62d33bd8667f description and comments might help with details if interested.
I've spent more time on this than i should, got a couple of grey hairs and I imagine that there's probably a better/faster/stronger/simpler way of doing it. At this point I just don't fucking care. I've reached the breaking point and managed to get out of there and that's all that matters to me.
Yes it still doesn't work as expected for all possible scenarios, but it doesn't break either. I'll bump this to a minor as soon as i've got a definitive solution, but I don't want to hear about dialogs in the next month, just want to go back to my job now. Check https://github.com/renatodeleao/a11y-vue-dialog/issues/22 for progress.
☮️ :shipit:
Published by renatodeleao over 4 years ago
This release patches a bug that happened when focused element was removed/hidden from the dom after an action on itself. #21
Note: this is not a perfect solution, but at least we can close the dialog 🤷♂️ afterwards.
I'll release a more robust solution on next patch.
Published by renatodeleao over 4 years ago
A user found this edge case while "pseudo-dragging2 text selection from within to outside dialog (backdrop). Since its dialog backdrop was allso the root (wrapper) of the dialogRef
itself, mouseup
event bubbled
and triggered click
event at backdropRef
(note: expected native behaviour) and closing the dialog.
Although it's the native behaviour, it's apparently not user-friend.
I wasn't sure if i should write code to handle this edge case or add a note to the docs, saying:
if you don't want this behaviour, make sure your
backdropRef
is not the same element as your dialog wrapper (root).
I end up including it, till some-else complains, hopefully after v1.0.0 will have more feedback for the expected usage.
Uff, this was long: now this doesn't happen by default ⬇
https://github.com/renatodeleao/a11y-vue-dialog/commit/f61af1afe4a914a37ec138e99641bf848b5ada63
Published by renatodeleao over 4 years ago
adding overflow hidden to this.$root was assuming too much of it's html markup structure and could create unwanted side effects (like empty scrollbars on non-full-width root divs).
This was added to prevent modal jump, but adding overflow-y: scroll
to html is way more common now.
Anyways, if you need that kind of control, add watcher
the open
prop of your composition, and perform your custom side-effects there.
Published by renatodeleao over 4 years ago
esc
while on focused on an <input type="search">
will not close the dialog unless the input is empty (default behaviour of this input is to reset the input value and we don't want to interfere with that)