fix(a11y): make popover focusable; document additional usability issues

This commit is contained in:
upsiflu 2024-12-20 15:58:35 +01:00
parent 492e83db04
commit 50bb8808bf
4 changed files with 89 additions and 23 deletions

View File

@ -6,6 +6,12 @@ import { isMobileView, useScreenSize } from '~/composables/screen'
import { POPOVER_INJECTION_KEY, POPOVER_CONTEXT_INJECTION_KEY } from '~/injection-keys'
import { type ColorProps, type DefaultProps, type RaisedProps, propsToColor } from '~/composables/colors';
/* TODO: Basic accessibility
-> See ui-docs
*/
const open = defineModel('open', { default: false })
const { positioning = 'vertical', ...colorProps } = defineProps<{
@ -117,6 +123,7 @@ watch(open, (isOpen) => {
<div
ref="slot"
class="funkwhale popover-container"
style="display:contents;"
>
<slot
:isOpen="open"
@ -137,7 +144,8 @@ watch(open, (isOpen) => {
:style="position"
:class="{ 'is-mobile': isMobile }"
class="funkwhale popover"
v-bind="propsToColor(colorProps)"
v-bind="propsToColor(colorProps)"
style="display:flex; flex-direction:column;"
>
<slot name="items" />
</div>

View File

@ -1,5 +1,5 @@
<script setup lang="ts">
import { computed, inject, ref } from 'vue'
import { inject, ref } from 'vue'
import { type RouterLinkProps, RouterLink } from 'vue-router'
import { POPOVER_CONTEXT_INJECTION_KEY, type PopoverContext } from '~/injection-keys'
@ -39,15 +39,15 @@ emit('internal:id', id)
<div class="after" />
<slot name="after" />
</RouterLink>
<div v-else
<button v-else
@mouseover="hoveredItem = id"
class="popover-item"
class="popover-item ghost"
>
<slot />
<div class="after" />
<slot name="after" />
</div>
</button>
</template>
<style scoped>

View File

@ -37,6 +37,7 @@ const radioMenu = ref(false)
const seperatorMenu = ref(false)
const subMenu = ref(false)
const extraItemsMenu = ref(false)
const linksMenu = ref(false)
const fullMenu= ref(false)
const isOpen = ref(false)
</script>
@ -45,6 +46,57 @@ const isOpen = ref(false)
Popovers (`Popover`) are dropdown menus activated by a button. Use popovers to create dropdown menus ranging from simple lists to complex nested menus.
::: warning A11y issues
This component has severe usability issues and cannot be used as-is.
### Add keyboard operability
> I can't operate the popup with a keyboard. Remove barrier for people not using a mouse.
- ✅ All items can be focused and activated by `Space`
(use `<button>` element instead of `<div>`)
- **Tab order:** Users can go to the next and previous items with `Tab` and `Shift+Tab`. When they have opened a sub-menu, the next focusable element is inside the sub-menu. When they have closed it, the focus jumps back to where it was.
- **Dismissal:** Users can close a menu or sub-menu with `ESC`
- **Arrow keys:** Users can move up and down a menu, open sub-menus with `->` and close them with `<-`. I have added something like this to `Tabs.vue`, for reference.
### Implement expected behavior
> Switching to submenus is error-prone. When moving cursor into freshly opened submenu, it should not close if the cursor crosses another menu item.
<img style="mix-blend-mode:multiply; width: 50%; float:right;" src="./popover/image.png" />
- **Dead triangle:** Add a triangular invisible node that covers the possible paths from the current mouse position to the menu items.
---
> Large menus disappear. I can't scroll to see all options.
- **Submenu-to-Modal:** Lists longer than 12 or so items are not recommended and should be replaced with modals.
---
> Submenus open without a delay, and they don't close unless I click somewhere outside them, which goes against established expectations.
- **Expansion delay:** Sub-menus open after 200ms
- **Auto-close:** Sub-menus close when the outside is hovered. There may be a delay of 200ms. Menus close when they lose focus.
---
Common Ui libraries in the Vue ecosystem such as vuetify or shadcn-vue all implement these features. It may be prudent to use their components.
::: tip Quick mitigation tactics:
- Place complex interfaces into nested [`Modal`](./modal)s
- Place long lists into [native `<Select>` elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select)
- Avoid sub-menus
:::
Common uses:
- "More actions" dropdown menus
@ -52,24 +104,6 @@ Common uses:
- Settings menus
- Context menus (right-click menus)
::: warning
This component has severe usability issues and cannot be used as-is:
- I can't operate the popup with a keyboard. Remove barrier for people not using a mouse (A11y)
- Switching to submenus is error-prone. When moving cursor into freshly opened submenu, it should not close if the cursor crosses another menu item
- Large menus disappear. When menus get big, they need to scroll.
- Submenus open without a delay, and they don't close unless I click somewhere outside them, which goes against established expectations.
Common Ui libraries in the Vue ecosystem such as vuetify or shadcn-vue all implement these features. It may be prudent to use their components.
**Quick mitigation tactics:**
- Place complex interfaces into nested [`Modal`](./modal)s
- Place long lists into [a native `<Select>` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select)s.
:::
| Prop | Data type | Required? | Description |
| ------ | --------- | --------- | ---------------------------------------------------------- |
| `open` | Boolean | No | Controls whether the popover is open. Defaults to `false`. |
@ -453,6 +487,30 @@ const open = ref(false)
You can use `PopoverItem`s as Links by providing a `to` prop with the route object or and external Url (`http...`). Read more on the [Link component](./link) page.
<Popover v-model:open="linksMenu">
<OptionsButton @click="linksMenu = !linksMenu" />
<template #items>
<PopoverItem to="a">
<i class="bi bi-music-note-list" />
Hello
</PopoverItem>
<PopoverSubmenu>
<i class="bi bi-music-note-list" />
Change language
<template #items>
<PopoverItem
v-for="(language, key) in SUPPORTED_LOCALES"
:key="key"
:to="key"
>
<i class="bi bi-gear-fill" />
{{ language }}
</PopoverItem>
</template>
</PopoverSubmenu>
</template>
</Popover>
## Menu
Here is an example of a completed menu containing all supported features.

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB