{"id":15103429,"url":"https://github.com/davidlj95/angular-accessible-tree","last_synced_at":"2025-09-27T01:30:57.129Z","repository":{"id":235044251,"uuid":"695318355","full_name":"davidlj95/angular-accessible-tree","owner":"davidlj95","description":"ARIA non-selectable tree with Angular","archived":true,"fork":false,"pushed_at":"2024-01-11T02:06:12.000Z","size":398,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-09-21T09:05:08.011Z","etag":null,"topics":["accessibility","angular","angular2","aria","treeview"],"latest_commit_sha":null,"homepage":"https://davidlj95.github.io/angular-accessible-tree/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/davidlj95.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2023-09-22T21:02:47.000Z","updated_at":"2024-01-11T04:58:11.000Z","dependencies_parsed_at":"2024-04-22T04:10:59.770Z","dependency_job_id":"7aca1e26-5330-49d1-a07e-76f6be5459fb","html_url":"https://github.com/davidlj95/angular-accessible-tree","commit_stats":null,"previous_names":["davidlj95/angular-accessible-tree"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidlj95%2Fangular-accessible-tree","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidlj95%2Fangular-accessible-tree/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidlj95%2Fangular-accessible-tree/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidlj95%2Fangular-accessible-tree/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davidlj95","download_url":"https://codeload.github.com/davidlj95/angular-accessible-tree/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219871823,"owners_count":16554457,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["accessibility","angular","angular2","aria","treeview"],"created_at":"2024-09-25T19:24:06.021Z","updated_at":"2025-09-27T01:30:51.821Z","avatar_url":"https://github.com/davidlj95.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Angular accessible tree\nImplements a [tree view pattern](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/) using\n[Accessible Rich Internet Applications (ARIA)](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA) and following the [ARIA Authoring Practices Guide (APG)][aria-apg]. \n\nNodes can't be selected. Mainly adds keyboard interactions and focus management.\n\n*Almost* conforming to the [WAI-ARIA 1.2 specifications][aria-1.2-specs] for [trees][tree-spec]. See [conformance note](#conformance-note) for more information.\n\n[aria-1.2-specs]: https://www.w3.org/TR/wai-aria-1.2\n[aria-apg]: https://www.w3.org/WAI/ARIA/apg/\n[tree-spec]: https://www.w3.org/TR/wai-aria-1.2/#tree\n\n## Demo\nCheck live demo at\nhttps://davidlj95.github.io/angular-accessible-tree/\n\n## Implemented interactions\n[Everything non-selectable except type-ahead from the authoring guides](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/#keyboardinteraction) (mostly from [first example](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/examples/treeview-1a/)):\n- Focus is:\n  - On first visible tree node by default\n  - On last tree node explored after tree has been interacted with\n- \u003ckbd\u003eHome\u003c/kbd\u003e: Moves focus to first node without opening or closing a node.\n- \u003ckbd\u003eEnd\u003c/kbd\u003e: Moves focus to the last node that can be focused without expanding any nodes that are closed.\n- \u003ckbd\u003eArrow down\u003c/kbd\u003e: Moves focus to the next node that is focusable without opening or closing a node. If focus is on the last node, does nothing.\n- \u003ckbd\u003eArrow up\u003c/kbd\u003e: Moves focus to the previous node that is focusable without opening or closing a node. If focus is on the first node, does nothing.\n- \u003ckbd\u003eRight arrow\u003c/kbd\u003e: When focus is on a closed node, opens the node; focus does not move. When focus is on a open node, moves focus to the first child node. When focus is on an end node, does nothing.\n- \u003ckbd\u003eLeft arrow\u003c/kbd\u003e: When focus is on an open node, closes the node.\n  When focus is on a child node that is also either an end node or a closed node, moves focus to its parent node. When focus is on a root node that is also either an end node or a closed node, does nothing.\n- \u003ckbd\u003eHome\u003c/kbd\u003e: Moves focus to first node without opening or closing a node.\n- \u003ckbd\u003eEnd\u003c/kbd\u003e: Moves focus to the last node that can be focused without expanding any nodes that are closed.\n- \u003ckbd\u003e*\u003c/kbd\u003e: Expands all closed sibling nodes that are at the same level as the focused node. Focus does not move.\n\nPlus a small quick win:\n- \u003ckbd\u003eShift + *\u003c/kbd\u003e: Collapses all open sibling nodes that are at the same level as the focused node. Focus does not move.\n\n## Conformance note\nARIA tree roles are designed to select an option inside a tree. [Spec says][tree-spec] a widget with a `tree` role is\n\n\u003e A widget that allows the user to select one or more items from a hierarchically organized collection.\n\n**This implementation is missing feature to select something inside the tree**. There's no clear way on how to implement a non-selectable tree (keep reading chapter below). Probably, [disclosure pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/) is the way to go in that scenario though. I switched to that for [my website](https://github.com/davidlj95/website)\n\n### ARIA tree role doesn't support non-selectable tree nodes\nThere's an issue with trees in ARIA. Seems there's no agreement between ARIA practices and specifications on how to implement a non-selectable tree node.\n\nIf you check the [ARIA Authoring Practices Guide (APG)][aria-apg], seems that \"Tree View\" is the proper role for a hierarchical list:\n\n\u003e *\"A tree view widget presents a hierarchical list\"* [(source)][aria-apg-tree-view]\n\n[aria-apg-tree-view]: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/\n\nIn the page regarding tree views, it is also added they can be expanded and collapsed:\n\n\u003e *\"Any item in the hierarchy may have child items, and items that have children may be expanded or collapsed to show or hide the children\"* [(source)][aria-apg-tree-view]\n \nLater, it also points out what to do when nodes in the tree aren't selectable:\n\n\u003e *\"If the tree contains nodes that are not selectable, neither `aria-selected` nor `aria-checked` is present on those nodes\"* [(source)](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/#:~:text=nodes%20that%20are%20not%20selectable)\n\nSeems that's it, right? Started coding, until a wild linter warning appeared: `aria-selected` must be present in a tree item (`\u003cli role=\"treeitem\"\u003e`). But I don't want to make that tree node selectable 🤔 And the authoring practices guide says it's ok. Then why is that being raised? [Going](https://github.com/angular-eslint/angular-eslint/blob/v16.2.0/packages/eslint-plugin-template/src/rules/valid-aria.ts#L52) [down](https://github.com/angular-eslint/angular-eslint/blob/v16.2.0/packages/eslint-plugin-template/src/rules/valid-aria.ts#L12C11-L12C11) [the hole](https://github.com/A11yance/aria-query), found that\n\n**ARIA 1.2 specs actually don't allow a tree item to not have the `aria-selected` attribute**. Because `aria-selected` attribute [must be present on every tree node according to specs of a tree item][tree-item-spec]. Given it inherits from the `option` role\n\n[option-spec]: https://www.w3.org/TR/wai-aria-1.2/#option\n[tree-item-spec]: https://www.w3.org/TR/wai-aria-1.2/#treeitem\n\nSo seems there's a discrepancy about that between the [ARIA Authoring Practices Guide (APG)][aria-apg] and the [ARIA-WAI 1.2 specs][aria-1.2-specs]. Indeed, [someone already reported that](https://github.com/w3c/aria-practices/issues/667) to the ARIA APG repo.\n\nIn summary, to be specs compliant, `aria-selected` should be there for every tree item (node). Despite with a `false` value as there's nothing to select. But if we do so, we tell a node can be selected, whilst we can't select anything here. \n\nSooo\n\n**TL;DR: there's no spec compliant way to implement a tree with non-selectable nodes**\n\nI eventually used [the disclosure pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/) for a non-selectable hierarchical list instead. Specifically, in my [website](https://github.com/davidlj95/website). But if you want a tree with some selectable and some non-selectable nodes, you'll have to be a bit [evil 😈](https://en.wikipedia.org/wiki/Don%27t_be_evil) and be non-spec compliant.\n\n#### More inconsistencies around\n\nIn fact, there have also other places where they use tree item role, but no `aria-selected` is there\n\n```html\n\u003cli id=\"apples\" class=\"tree-parent\" role=\"treeitem\" tabindex=\"-1\" aria-expanded=\"false\"\u003e\n```\nSource: https://www.w3.org/WAI/GL/wiki/Using_the_WAI-ARIA_aria-expanded_state_to_mark_expandable_and_collapsible_regions\n\n#### Final notes\n- [`aria-hidden` is not needed for collapsed items](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-hidden), given `display: none` hides it from accessibility tools already\n- For an example of `tabindex` usage in ARIA practices [tree view example](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/examples/treeview-1a/) check the [JS code](https://www.w3.org/WAI/content-assets/wai-aria-practices/patterns/treeview/examples/js/treeitem.js). It's not in the inline HTML.\n  \n## Usage\nRun the app development server and play with it. There's instructions in the main `app` component about what's implemented and what not.\n\n## About\n\nThis project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 16.2.3.\n\n## Development server\n\nRun `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.\n\n## Code scaffolding\n\nRun `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.\n\n## Build\n\nRun `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.\n\n## Running unit tests\n\nRun `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).\n\n## Running end-to-end tests\n\nRun `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.\n\n## Further help\n\nTo get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.\n\n## TODO\n- [ ] Add tests (got too excited with recursion and didn't TDD)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidlj95%2Fangular-accessible-tree","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavidlj95%2Fangular-accessible-tree","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidlj95%2Fangular-accessible-tree/lists"}