Skip to content

fix(VTreeview): more aria attributes and correct keyboard navigation#22903

Open
J-Sek wants to merge 11 commits into
devfrom
fix/vtreeview-aria-and-keyboard-nav
Open

fix(VTreeview): more aria attributes and correct keyboard navigation#22903
J-Sek wants to merge 11 commits into
devfrom
fix/vtreeview-aria-and-keyboard-nav

Conversation

@J-Sek

@J-Sek J-Sek commented Jun 5, 2026

Copy link
Copy Markdown
Contributor
  • aria-level/posinset/setsize
  • aria-expanded only for expandable nodes
  • arrow left/right should collapse/expand and move focus to parent/child (RTL-aware)
  • toggle button gets tabindex=-1 so the row owns expansion

resolves #10796
resolves #21585

Markup:

<template>
  <v-app theme="dark">
    <v-container max-width="500">
      <button>before</button>
      <v-treeview
        :items="items"
        item-value="id"
        select-strategy="classic"
        open-all
        selectable
      >
        <template #append="{ item }">
          <v-select
            v-if="item.id === 2"
            v-model="selected"
            :items="['a', 'b', 'c', 'd']"
            density="compact"
            style="width: 100px"
            hide-details
          />
          <v-switch
            v-if="item.id === 3"
            v-model="switched"
            hide-details
          />
        </template>
      </v-treeview>
      <button>after</button>
    </v-container>
  </v-app>
</template>

<script setup>
  import { ref } from 'vue'

  const selected = ref('a')
  const switched = ref(false)

  const items = [
    {
      id: 1,
      title: 'Root',
      children: [
        { id: 2, title: 'Child 1' },
        { id: 3, title: 'Child 2', children: [{ id: 4, title: 'Grandchild' }] },
      ],
    },
  ]
</script>

@docpelux

docpelux commented Jun 8, 2026

Copy link
Copy Markdown

Test with Accessibility Insights for Web now has another issue, see comment below.

=========================================================================

Title: WCAG 4.1.2: Ensure buttons have discernible text (.v-btn.v-btn--icon.v-btn--density-compact)
Tags: Accessibility, WCAG 4.1.2, button-name

Issue: Ensure buttons have discernible text (button-name - https://accessibilityinsights.io/info-examples/web/button-name)

Target application: Vuetify Dev Playground - http://localhost:8090/

Element path: #v-list-group--id-1 > .v-list-item__prepend > .v-list-item-action.v-list-item-action--start > .v-btn.v-btn--icon.v-btn--density-compact

Snippet:

How to fix:
Fix any of the following:
Element does not have inner text that is visible to screen readers
aria-label attribute does not exist or is empty
aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty
Element has no title attribute
Element does not have an implicit (wrapped)
Element does not have an explicit
Element's default semantics were not overridden with role="none" or role="presentation"

Environment: Chrome version 149.0.0.0

====

This accessibility issue was found using Accessibility Insights for Web 2.47.0 (axe-core 4.11.3), a tool that helps find and fix accessibility issues. Get more information & download this tool at http://aka.ms/AccessibilityInsights.

@docpelux

docpelux commented Jun 8, 2026

Copy link
Copy Markdown

The buttons have tabindex -1, so they are not reachable, and is ok.

They need to have aria-hidden=true.

@docpelux

docpelux commented Jun 8, 2026

Copy link
Copy Markdown

If you set selectable in the treeview, when targeting checkboxes with tab, screen reader simply says "checkbox not checked" but never reads the row, i expect something like this "Grandchild not checked" or "checkbox not checked row Grandchild".

If i navigate the tree with arrows, screen reader never tells me that Child1 or Grandchild are selectable items.

Screen reader is orca from Ubuntu.

The following is partially AI generated:

Expected behavior (per WAI-ARIA Treeview pattern):

  1. The treeitem element itself should carry aria-checked="true|false" so that navigating with
    arrow keys announces both the label and the checked state (e.g. "Child 1, not checked, tree
    item").
  2. The internal should have tabindex="-1" so it is not an independent tab
    stop. Selection should be toggled via Space on the focused treeitem.
  3. If the checkbox must remain focusable independently, it needs aria-labelledby pointing to the
    associated v-list-item-title element so the screen reader announces the correct label.

Suggested fix:

  • Add aria-checked to [role="treeitem"] elements that have a checkbox, reflecting the current
    selection state.
  • Remove the checkbox from the tab order (tabindex="-1") since focus management in a tree
    should be handled at the treeitem level, and add aria-hidden="true".
  • Add aria-label="select" to the checkbox , to mute wave (false positive), screen readers never reads it.
  • Handle Space keypress on the treeitem to toggle selection.

This aligns with the WAI-ARIA Treeview pattern
(https://www.w3.org/WAI/ARIA/apg/patterns/treeview/).

@J-Sek

J-Sek commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

Is there any UI library that ships tree component with such extensive labels? I am trying to align with react-aria that does not pollute labels with selection status, but it also has selection strategy equivalent to the "independent", so it is semantically a selection list and does not really need them.

Just curious. Is it something you'd use or are we trying to satisfy the a11y requirements without consulting with actual end users?

@docpelux

docpelux commented Jun 8, 2026

Copy link
Copy Markdown

Having to comply with the EAA regulations from the EU directive.
However I don't use a screen reader directly, so perhaps there's a better solution.
I think that when I target a selectable tree element, it should say that I targeted a selectable tree element.

However the solution I have proposed was written by AI, so I don't know...

===============

Non related, addendum

The simple solution I use for now is this:
Is a little abrupt but hides accessibility errors from automatic checks, i don't know what happens if tree changes or page is reloaded, and if causes performance issues. i leave code here for anyone has problems with trees.

Note that when this pr is merged this code conflicts with its new aria attributes.
Is a composable.

edited: 11 July

import { nextTick, onMounted } from 'vue';

/**
 * Workaround to fix/hide treeview accessibility issues.
 */
export function useTreeviewAccessibilityFix() {
    const treeviewAccessibilityFix = () => {
        nextTick(() => {
            // Fix missing aria-labels for treeview checkboxes
            const checkboxes = document.querySelectorAll('.v-treeview-item input[type="checkbox"]');
            checkboxes.forEach((checkbox, index) => {
                const item = checkbox.closest('.v-treeview-item');
                const title = item?.querySelector('.v-treeview-item__title')?.textContent || `item ${index + 1}`;
                checkbox.setAttribute('aria-label', `Select ${title}`);
            });

            // Fix missing aria-labels for treeview expand/collapse buttons
            const expandButtons = document.querySelectorAll('.v-treeview-item .v-btn--icon');
            expandButtons.forEach((button) => {
                if (!button.getAttribute('aria-label')) {
                    const item = button.closest('.v-treeview-item');
                    const title = item?.querySelector('.v-treeview-item__title')?.textContent || 'item';
                    button.setAttribute('aria-label', `Expand/Collapse ${title}`);
                }
            });
        });
    };
    onMounted(treeviewAccessibilityFix);
}

@docpelux

Copy link
Copy Markdown

any new news/ideas?

@J-Sek

J-Sek commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Still cooking

  • [enter] key to expand collapse
  • [space] to toggle selected state
  • checkbox will get the same treatment as expand button - i18n labels can be reverted entirely
  • will follow up with docs overhaul

It will need to land in dev branch - too much disruption to ship in patch release.

@J-Sek J-Sek force-pushed the fix/vtreeview-aria-and-keyboard-nav branch from 28cb511 to bb38a32 Compare June 11, 2026 22:28
@J-Sek J-Sek changed the base branch from master to dev June 11, 2026 22:28
@J-Sek J-Sek marked this pull request as ready for review June 11, 2026 22:29
@docpelux

Copy link
Copy Markdown

Everything seems ok, for me it can go.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a11y Accessibility issue C: VTreeview

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request] Add Aria-labels to VTreeview Buttons [Feature Request] v-treeview: Keyboard support

2 participants