fix(a11y): make popover focusable; document additional usability issues
This commit is contained in:
parent
492e83db04
commit
50bb8808bf
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 |
Loading…
Reference in New Issue