Add ARIA tree grid semantics Wunderbaum renders no ARIA roles or states, so screen readers see a flat list of generic elements with no tree structure, no expansion state and no focus position (DOM focus stays on the container). - container: role=treegrid, aria-rowcount (rows are virtualized, so the count of all visible rows is announced, plus the header row if shown), aria-multiselectable for (non-radio) checkbox trees with selectMode multi/hier - header: role=rowgroup/row/columnheader with aria-colindex, and aria-sort for sortable columns - node list: role=rowgroup; rows: role=row with a stable id, aria-level, aria-setsize, aria-posinset (status nodes excluded; computed once per parent and render pass), aria-expanded, aria-selected (only if nodes can be selected), aria-busy and aria-rowindex; cells: role=gridcell with aria-colindex (aria-colspan for colspan nodes) - decorative icons (indent, expander, checkbox, column buttons, resizers) are aria-hidden - the container's aria-activedescendant follows the focus node, and is removed while the focused row is not rendered Adds unit tests and a short Accessibility section to the keyboard tutorial.
Recent Commits to wunderbaum:main (4)
-
Add ARIA tree grid semantics
-
Add paging nodes ("More...") for long child lists
Add paging nodes ("More...") for long child lists NodeStatusType.paging and isPagingNode() exist, but there is no way to add a paging node and the keyboard handler for it is commented out. Long (lazy) child lists, for example thousands of children under one node, therefore need a custom node type and click/keyboard handling in every application. - node.addPagingNode(titleOrData, options) appends a colspan paging status node after the children, replacing an existing one - click, Enter and Space (row and cell mode) on a paging node fire the new clickPaging event instead of activating it; the handler loads the next page and calls addChildren() / addPagingNode(). If the handler returns a promise, the node shows its loading state and ignores further clicks until it settles - strings.moreItems (default "More...") and a link-like default style - sort() keeps paging nodes at the end - node.remove() resets tree.activeNode / tree.focusNode if they are removed (as removeChildren() already does for the focus node), so removing a clicked paging node does not leave a dangling focus node for the next key press Adds unit tests and documents the clickPaging event. -
Theme: derive colors from the base variables at runtime (color-mix)
Theme: derive colors from the base variables at runtime (color-mix) The derived colors (hover, active, active cell, drop, dim, grayscale, ...) are computed by Sass at build time and declared on :root. Setting --wb-background-color or --wb-bg-highlight-color on a tree (for example for a dark mode) therefore leaves every derived color at its light default, and a theme has to restate about 20 variables. Derived variables are no longer declared on :root. Rules use var(--wb-<name>, color-mix(...)) based on the base variables that apply to the tree element (Sass helper derived-color()), so a theme sets the base colors only. The mix ratios approximate the previous defaults (within a few RGB units). Pages that set a derived variable themselves keep working, because an explicit value still wins over the fallback. Browsers without color-mix() support (before 2023) get the previous static values on :root via @supports not (...).
-
Avoid a forced layout for every rendered row
Avoid a forced layout for every rendered row _render_data() reads tree.element.scrollTop before it writes the title, for every row it renders. After the first DOM write each read forces a synchronous layout, so re-rendering the viewport (after expand, scroll or filter) costs one layout per rendered row. The value is only needed when preventScroll is set, so read it only then.