Repository navigation
Description
@cart has stated that his vision for BSN involves a particular design philosophy for entities and components, specifically one in which components are made up of ordinary properties which can be merged and patched. Unfortunately, the current design for bevy_a11y is rather incompatible with this vision. I believe the reason for this is that Bevy's AccessNode is a thin wrapper around AccessKit, and attempts to mimic its API, just as the Node component mimics the API structure of Taffy. There's been some discussion of refactoring the organization of Node (#10497), and there should be a similar discussion of re-organizing AccessNode.
Currently AccessNode has several aspects which make it problematic to use in a BSN-like context:
- The properties are private, and only accessible through methods.
- The methods use different calling conventions depending on the type of the property.
- All of the accessibility properties are combined together in a single ECS component.
Consider for example, the property that controls whether an entity is "disabled" (grayed out). This is set currently via the no-argument method .set_disabled() and reset by the no-argument method .clear_disabled(). A BSN template that wants to disable a widget can't simply merge in a disabled marker component, but instead must use a hook or bundle effect which calls the appropriate bevy_a11y method.
I anticipate that BSN templates are going to provide a layered approach where various patches control different aspects of a widget. For example, you might have a widget template that adds custom styling to a standard checkbox or button, using the patch mechanism. This customization process might involve accessibility properties: for example, the only difference between a checkbox and a toggle switch, from an accessibility perspective, is whether you use the "checkbox" role or the "switch" role, so the rest of the accessibility properties can be shared between the two widget types.
Because of this, I can well imagine wanting to merge together multiple BSN templates, each of which has opinions about various accessibility attributes: the template that determines the label or the role might not be the same template as the one which determines the checked or disable states. This is easy to do if these attributes are separate components, or (at least) allow overwriting of properties using patch. If setting properties requires calling of bespoke methods, however, the process becomes much more involved, as now any setting of a property becomes a read-modify-write operation.
Things get even tricker when we add interaction into the mix. Templates don't merely insert components; they also set up processes that keep those components up to date, either via a reactivity mechanism or via ECS systems that look for specific interaction components. Presumably a template that adds component X also wants to incorporate logic that keeps component X up to date. However, you have to be careful if multiple templates want to overwrite properties within same component. This argues against large, swiss-army-knife components that encompass too many concerns.
Obviously we aren't going to re-write our own version of AccessKit. Instead, I think the solution is to come up with our own idiomatic API, and then provide a transformation from that API into the AccessKit structure.
Activity
alice-i-cecile commentedon Feb 2, 2025
Fully agree. This is the right approach here, and we may need to eventually do something similar for
taffy.alice-i-cecile commentedon Apr 3, 2025
I've set out some of my thoughts on this as part of my feedback here: #16900 (comment)
viridia commentedon Apr 24, 2025
@alice-i-cecile I have done some prototyping on this issue in the last few weeks, although none of what I have done is particularly elaborate.
Essentially, what I have done is treat the "label" property as a special case.
The justification for this is that labels are most often app-specific - that is, the text of the label varies from widget to widget depending on the app. This is not true of most other accessibility properties: for example the "checked" property behaves the same way for every checkbox. So in terms of separation of concerns, the label is owned by the app developer, while the other properties are owned by the widget author.
So rather than trying to solve the problem of splitting up
AccessibilityNodein a general way, instead what I have done is define a new component,AccessibleLabel(String). This component can be added to any entity, but it has no effect unless the entity also has anAccessibilityNodecomponent. An ECS system synchronizes the two: any change toAccessibleLabelcauses an update to thelabelproperty ofAccessibilityNode.Because it's a separate component, it can be added as an element to the bundle independently from the other accessibility properties.
This idea can be extended to other properties if desired.
Introduce `AccessibleLabel` component. (bevyengine#24308)
Introduce `AccessibleLabel` component. (bevyengine#24308)
Introduce `AccessibleLabel` component. (#24308)