Skip to content

feat(core): expose resolved accessibility attributes on ComponentNode instead of props['@accessibility'] #4166

Open

Description

@jacobsimionato

Summary

Expose resolved AccessibilityAttributes (label, description, live, hidden) as a dedicated first-class reactive property on ComponentNode (analogous to node.metadata added in #3469 / #3708), rather than storing and resolving @accessibility (and mirroring legacy accessibility) inside node.props.

Background & Problem

With the v1.0 @-prefixed component envelope cutover (#4037, #4044), @accessibility is a component envelope mixin alongside @metadata, @catalogId, and @checks, leaving the bare name accessibility free for custom catalog component properties.

Currently, unlike @metadata (which is extracted from ComponentModel.properties and exposed on ComponentNode.metadata), @accessibility remains inside ComponentModel.properties and is resolved by GenericBinder / NodeResolver into node.props['@accessibility'] (plus a conditional mirror onto node.props['accessibility'] when the component schema does not declare a custom accessibility property).

This creates several ergonomic and architectural warts across Core SDKs and framework adapters:

  1. Manual probing & unwrapping in every adapter: Framework adapters and universal helpers must probe props['@accessibility'] ?? props['accessibility'], verify it is an attributes object rather than a custom component property, and manually unwrap or subscribe to each nested ResolvedBinding (label, description, live, hidden):
    • TypeScript (typescript/core/web_core/src/universal/host_accessibility.ts): resolveAccessibilityProps(props) loops over ['@accessibility', 'accessibility'] and unwraps bindings.
    • Flutter (dart/a2ui_flutter/lib/src/node_view.dart): reads props.value('@accessibility') ?? props.value('accessibility') and manually manages _accessibilityUnsubscribes.
    • Swift (swift/core/Sources/A2UICore/Resolution/NodeResolver.swift & swift/swiftui/Sources/A2UISwiftUI/ComponentNodeView.swift): reads @accessibility / accessibility from node.properties.
  2. Envelope leakage into node.props: Component implementations inspecting node.props see @accessibility (and the conditional accessibility mirror) mixed in with their own schema-defined component properties, requiring special-case mirroring logic in GenericBinder (finalizeResolvedProps, mirrorsAccessibility) and NodeResolver.

Proposed Solution

  1. ComponentNode.accessibility across Core SDKs (@a2ui/web_core, a2ui_core in Dart/Python, A2UICore in Swift):
    • Expose a dedicated reactive accessibility property on ComponentNode (e.g., readonly accessibility: Signal<ResolvedAccessibilityAttributes | undefined> in TypeScript, ReadonlySignal<ResolvedAccessibility?> in Dart, and corresponding properties on Python ComponentNode and Swift Node) with label, description, live, and hidden already resolved/unwrapped.
    • Resolve envelope @accessibility (and legacy bare accessibility on pre-1.0 or during the Remove legacy bare-key fallback for v1.0 component envelope fields #4082 transition when undeclared as a custom property) into node.accessibility instead of polluting node.props['@accessibility'].
  2. Framework Adapter Simplification:
    • Update @a2ui/web_core/universal (A2uiLitElement), @a2ui/react, @a2ui/angular, a2ui_flutter (NodeView), and A2UISwiftUI (ComponentNodeView) to read accessibility state directly from node.accessibility.
  3. Blueprints & Conformance:

Activity

  1. jacobsimionato commented on Nov 11, 2026

    @jacobsimionato
    CollaboratorAuthor

    Coding agent prompt: Implement a dedicated reactive accessibility property on ComponentNode across all four Core SDKs and update framework adapters to read accessibility state from node.accessibility instead of probing node.props['@accessibility'] ?? node.props['accessibility'].

    Implementation Strategy

    1. TypeScript (@a2ui/web_core & Web Renderers):
      • Inspect @typescript/core/web_core/src/resolution/component-node.ts, @typescript/core/web_core/src/resolution/node-resolver.ts, @typescript/core/web_core/src/resolution/generic-binder.ts, and @typescript/core/web_core/src/universal/host_accessibility.ts.
      • Add readonly accessibility: Signal<ResolvedAccessibilityAttributes | undefined>; to ComponentNode (similar to readonly metadata: Signal<A2uiMetadata | undefined>;).
      • Resolve the component's envelope @accessibility (and legacy bare accessibility on pre-1.0 surfaces or during the Remove legacy bare-key fallback for v1.0 component envelope fields #4082 v1.0 transition when undeclared as a custom property in the component schema) into node.accessibility with label, description, live, and hidden unwrapped.
      • Update @typescript/core/web_core/src/universal/a2ui-lit-element.ts, @typescript/renderers/react/, and @typescript/renderers/angular/ to consume node.accessibility directly.
    2. Dart (a2ui_core & a2ui_flutter):
      • Inspect @dart/a2ui_core/lib/src/resolution/component_node.dart, @dart/a2ui_core/lib/src/resolution/node_resolver.dart, @dart/a2ui_core/lib/src/resolution/generic_binder.dart, and @dart/a2ui_flutter/lib/src/node_view.dart.
      • Add ReadonlySignal<ResolvedAccessibility?> get accessibility; on ComponentNode and simplify NodeView in a2ui_flutter so it subscribes to node.accessibility rather than manually probing props.value('@accessibility') ?? props.value('accessibility').
    3. Python (a2ui_core):
      • Inspect @python/a2ui_core/src/a2ui/core/resolution/component_node.py, @python/a2ui_core/src/a2ui/core/resolution/node_resolver.py, and @python/a2ui_core/src/a2ui/core/resolution/generic_binder.py.
      • Expose resolved accessibility on ComponentNode.
    4. Swift (A2UICore & A2UISwiftUI):
      • Inspect @swift/core/Sources/A2UICore/Resolution/NodeResolver.swift and @swift/swiftui/Sources/A2UISwiftUI/ComponentNodeView.swift.
      • Expose resolved accessibility on Node and read it in ComponentNodeView.
    5. Blueprints & Conformance:
      • Update @blueprints/modules/a2ui_core.blueprint.md, @blueprints/features/node_resolution.blueprint.md, and @blueprints/modules/a2ui_framework_adapter.blueprint.md.
      • Verify all unit and conformance suites (conformance/core/accessibility.yaml) pass across TypeScript, Dart, Python, and Swift.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P2The team intends to work on this in the near future, or at a lower priority.component: core sdkAffects the core SDK layer in any languagetype: feature/enhancement

Type

No type

Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions