The long-term goal of a3s-gui is to provide the native, cross-platform
equivalent of React Aria: a
headless behavior and accessibility layer that applications can compose into a
design system without depending on a browser.
This is a behavioral compatibility target, not a DOM compatibility target. React Aria's public concepts should have recognizable native equivalents, but the implementation must use AppKit, GTK4, and WinUI controls, focus systems, accessibility APIs, and input events directly.
The project does not claim React Aria parity yet.
Rust component and semantic hook API
|
v
Headless behavior contracts
press / hover / focus / selection / overlays / i18n
|
v
Portable semantic tree and typed Native UI IR
|
v
Behavior state machines and keyed reconciliation
|
v
Versioned platform capabilities
|
v
AppKit / GTK4 / WinUI native adapters
Native input and accessibility events travel upward through the same layers.
The behavior layer owns platform-independent semantics. Native adapters own thread affinity, widget lifetime, raw event capture, accessibility projection, and translation between platform event data and portable event context.
- Input modality is normalized once as keyboard, mouse, touch, pen, virtual, or unknown. Components must not infer it independently.
- Press is a lifecycle, not a click alias. Start, release, end, cancellation, keyboard activation, touch movement, and virtual activation must have deterministic semantics.
- Focus visibility depends on modality. Programmatic and keyboard focus can display a focus ring without making pointer focus do so automatically.
- Hover is unavailable to touch input and must not be synthesized from a touch press.
- Transient interaction state survives keyed rerenders and disappears when its node disappears.
- Collection identity is based on stable keys. Selection is a key set or an
explicit
allvalue, not one optional string. - Focus containment, restoration, and autofocus are runtime behavior, not marker attributes.
- Locale and writing direction are inherited context values and can be scoped.
- Unsupported platform behavior is reported through capabilities or an error; native setters must not silently ignore a semantic contract.
- Visible content and the accessible name are independent.
aria-labelmay override what assistive technology announces without replacing native button, item, field, or container text. - Tests assert semantic roles, accessible names, state, and interaction results. Platform class names alone are not conformance evidence.
The first shared interaction milestone is available in the portable runtime:
NativeInputModalityrepresents keyboard, mouse, touch, pen, virtual, and unknown input.NativeEventContextcarries modality, key modifiers, local position, move/wheel delta, and repeat state. Its fields are optional, so older serialized native events remain valid.ActionInvocationpreserves that typed context through action routing, so reducers can distinguish keyboard, pointer, touch, pen, and virtual input.- One native event produces an ordered invocation batch. Lifecycle-specific
callbacks run before their change callbacks, then the event bubbles from the
target through its nearest ancestors.
nodeandcurrentTargetpreserve both sides of that relationship. NativeCloseremains target-scoped so a nested dialog does not invoke the containing window's close request. - Propagation-aware reducers return
ActionPropagation::ContinueorStop. Stopping completes every callback on the current target, then removes the ancestor suffix from the response and action history. Unit-returning reducers retain the original continue-all behavior. The protocol app and all three native event pumps expose the same opt-in contract. - Native events include press start, press up, press end, press cancellation, long-press start, terminal long press, long-press end, hover start, and hover end, plus a signed cross-platform wheel event.
use_moveandUiMovableemit a real cross-platform move lifecycle. Primary pointer motion is incremental, starts only on the first non-zero delta, retains the initiating pointer identity, and ends on release or cancellation. Arrow keys emit a complete one-unit keyboard lifecycle and suppress native default or collection navigation. Move callbacks receive normalized modality, modifiers, position, repeat state, andcontext.delta.InteractionStatetracks pressed, long-pressed, hovered, focused, focus-within, and focus-visible state. It keeps transient state across keyed blueprint synchronization.GuiRuntimeresolveshover,active,focus,focus-visible, andfocus-withinstyle variants, plus the corresponding React Ariadata-[...]:*states, against that interaction state. Declaration-level source order is retained, and resolvedPortableStyleupdates are committed transactionally through the native host rather than waiting for an action callback or rerender.- Keyboard and virtual focus display focus-visible state; pointer presses clear it. Touch does not create hover state.
use_press, press-capable semantic hooks, built-in RSX components, and the RSX compiler exposeonPressUpalongside the existing press lifecycle.- Event routing supports explicit hover lifecycle handlers and falls back to
onHoverChangewith canonical boolean values. - Marked NumberField wheel sources use
context.deltaon AppKit, GTK4, and WinUI. Platform sources normalize their wheel signs to the portable DOM-style contract before the runtime derives a model-space change. - Direct
onFocus,onBlur, andonFocusChangehandlers run only for the focused target.use_focus_within,UiFocusWithin, and theonFocusWithin/onBlurWithin/onFocusWithinChangehandlers independently observe subtree entry and exit. Adjacent native blur/focus events are linked throughrelatedTarget, so moving between descendants does not churn an ancestor's focus-within state. use_focusis the React Aria-compatible direct-focus API name;use_focusableremains as a compatibility alias.use_focus_ringandUiFocusRinginclude descendant focus only whenwithin=true, matching the direct-focus default.FocusManagerderives focusable and tabbable order from the mounted keyed tree, models nested focus scopes, provides first/last/next/previous navigation, resolves containment, and directs scope autofocus to a descendant instead of the scope wrapper. Restore-enabled scopes retain the focus owner that preceded their mount and unwind nested restoration targets when they unmount.MountedOverlayRegistrydiscovers open managed overlays from the reconciled native tree and retains activation order independently of document order. Only the topmost overlay handles Escape or outside-press dismissal. Escape remains available to the focused control when keyboard dismissal is disabled, and outside dismissal requires both press start and release to occur outside the same topmost overlay.- Modal overlays project
inertonto background branches, which also removes them from the portable accessibility tree. Structural ancestors stay available for event routing, and overlays opened later through a separate portal branch remain interactive foreground layers. - Overlay autofocus, contained focus, and restoration reuse
FocusManager. Dismissal emits a target-scoped nativeCloseevent, so it invokes only the topmost overlay'sonClose.UiDialogandUiModalexposeisDismissableandisKeyboardDismissDisabled;UiPopoveradditionally exposesisNonModal, closes on focus leaving its subtree, and defaults to a modal dismissable popover. use_overlay_positionaccepts React Aria's 22 placement strings together withoffset,crossOffset,shouldFlip,shouldUpdatePosition,containerPadding, arrow geometry, andmaxHeight. Open mounted popovers resolve an explicitanchor/trigger reference or their trigger context and emit a typedpositionOverlaycommand. Logical start/end placement resolves against inherited LTR/RTL direction.GuiRuntimeexposesrequest_focus,focus_first,focus_last,focus_next, andfocus_previous. These methods validate mounted focusability, apply active-scope containment, and send a typed platform command; native focus callbacks remain the source of truth for interaction state and focus actions. An incoming native focus event that escapes a contained scope is suppressed and redirected through the same host capability.KeyedCollectionrejects duplicate identities without mutating the active collection.Selectionrepresents an explicit key set orall, andSelectionManagerimplements single/multiple, toggle/replace, range, disabled-key, disallow-empty, and async collection-update behavior.use_selectionnow exposesselectedKeyswhile retaining the scalarvalue/selectedValuecompatibility path. Selection action payloads decode legacy scalar keys, key arrays, andallthrough one typed API.- Controlled
selectedKeysand uncontrolleddefaultSelectedKeysare distinct all the way through hook serialization, RSX expansion, reconciliation, and mounted state. Omitting both no longer serializes an accidental controlled empty selection. Collection components also carrydisabledKeys,selectionBehavior,disabledBehavior,disallowEmptySelection, andshouldFocusWrap. ListBox and Tree also carry React Aria'sescapeKeyBehavior, defaulting toclearSelection;noneleaves Escape unclaimed by the selection contract. - List box, grid list, tag, tabs, tree, menu, selection input, calendar picker,
and color swatch picker components reuse the same selection contract.
RadioGroupretains React Aria's scalarvalue/defaultValuecontract. MountedSelectionRegistrydiscovers collection ownership from the mounted tree, uses declarative element keys as item identity, rejects duplicate keys before host mutation, preserves uncontrolled selection through keyed reorders, and projects controlled orallselection into native item state. Native display values remain compatibility aliases rather than identity.- Mounted selection events implement toggle/replace and modifier range behavior, update every affected sibling, bubble an aggregate stable-key payload, and track the collection's focused key. Programmatic projection is applied before keyed reconciliation so it does not transiently clear native selection during rerenders.
- ListBox, GridList, Tag, and Tree collection roots expose
onAction(key)as a distinct event fromonSelectionChange. Enter invokes the item action while Space selects. Mouse replace behavior uses one click for selection and two clicks for action; touch, pen, and virtual taps prefer action. Toggle collections with an empty selection also prefer action. Native selection callbacks produced by those primary action gestures are suppressed and reverted before user callbacks run. Items disabled only for selection can still act, while fully disabled items cannot. - Touch and pen long press select the held item and enter a persistent
collection selection mode. Later taps select instead of invoking action
until the selection is cleared. The generic
use_long_pressandUiLongPressablecontracts expose start, terminal, and end callbacks, a bounded millisecond threshold (500 ms by default), and an accessibility description. AppKitNSTimer, GTK main-loop timeout, and WinUIDispatcherQueueTimerdeliver the terminal event while the pointer remains held. At recognition, the native kernel ends long-press-start and cancels active press and move lifecycles before dispatching the terminal callback. Release-time evaluation remains as a fallback if platform timer registration fails. - Native ListBox input is normalized to that same complete snapshot contract. GTK4 and WinUI enable their native single/multiple selection modes and report all selected row values; the runtime resolves those display aliases to stable keys and replaces the previous key set atomically. AppKit row activation retains Shift and Command modifiers, then the portable manager produces the complete stable-key snapshot. Host projection and mounted state roll back together if the selection action fails.
- Mounted ListBox, Menu, Tree, Tabs/TabList, and RadioGroup collections share
one keyboard-navigation contract. Arrow, Home, End, PageUp, and PageDown
navigation skips fully disabled items, follows orientation and RTL direction,
respects optional focus wrapping, and sends typed
RequestFocuscommands on AppKit, GTK4, and WinUI. Replace and toggle selection behavior, Shift range extension, Control/Command focus-only movement, and automatic versus manual tab activation are resolved before selection actions are routed. Multiple ListBox and Tree collections use Control/Command+A forall; Escape clears selection unlessescapeKeyBehavior="none"is configured, anddisallowEmptySelectionstill prevents the clear. Both shortcuts emit the same complete stable-key payload as pointer selection. An explicitonKeyDownon the target route retains ownership of the key. PageUp and PageDown use aCollectionLayoutSnapshotcontaining the visible rectangle, content size, and stable-key item rectangles. Command hosts measure that snapshot from AppKit, GTK4, or WinUI immediately before navigation; custom and headless hosts can inject it throughGuiRuntime::set_collection_layout. Variable-size rows move by one visible extent and fully disabled rows remain excluded. A host that cannot measure layout retains deterministic collection-boundary behavior. - Tree owns controlled
expandedKeysor uncontrolleddefaultExpandedKeysindependently from selection. Nested semanticTreeItemnodes lower to stable, same-level native rows with parent-key, level, position, and set-size metadata. Up, Down, Home, End, page movement, and typeahead operate on visible preorder rows only. In LTR, Right expands or enters the first child and Left collapses or returns to the parent; RTL mirrors those keys. Expansion routes the complete stable-key set through the Tree root'sonExpandedChange, and a failed action rolls both mounted and host state back. Controlled collapse also restores focus from a hidden descendant to its nearest visible ancestor. - ListBox, Menu, Tree, Select, and ComboBox collection items support buffered
type-to-select using their explicit
textValue, accessible label, or value. The 500 ms buffer survives keyed rerenders, starts at the current item and wraps, ignores Control/Command shortcuts, and skips only items disabled for all interaction. ICU4X search collation provides locale-sensitive, case-insensitive, and accent-insensitive prefix matching. ListBox and Tree follow replace-selection rules, while Menu and open Select/ComboBox lists move focus without committing a value. AppKit uses the produced character, GTK4 uses the key's Unicode value, and WinUI translates virtual keys with the active keyboard layout without mutating dead-key state. - Logical AppKit ListBox and Tree items resolve to their concrete row buttons for responder lookup and programmatic focus. Selection activation still targets the owning collection. Rebuilding a row preserves the focused item, and programmatic AppKit focus enqueues the matching blur/focus transition. Hidden AppKit tree descendants are removed when the row list is rebuilt. GTK4 and WinUI mount Tree through their native list primitives as well; the portable hierarchy layer supplies visible flattened rows consistently on all three backends.
I18nManagerprojects inherited locale and writing direction through the keyed native tree. Scoped overrides and default locale changes are preserved across rerenders, and BCP 47 language/script subtags provide deterministic RTL inference. It creates reusable, thread-safeLocaleCollator,LocaleNumberParser,LocaleNumberFormatter,LocaleDateFormatter, andLocaleMessageFormattervalues from the effective node locale. The message formatter includes React Aria-compatible NumberField labels, role descriptions, and empty-value text for 34 locales with deterministic language/script/region fallback. Collation covers search/sort sensitivity, case-first, numeric ordering, and locale-equivalent prefix, suffix, and substring filtering. Stable ICU4X decimal parsing covers localized signs and separators, partial-input validation, and automatic Latin, Arabic, Han decimal, Devanagari, Bengali, and full-width numbering system detection. Decimal and percent styles share typed formatting options for grouping, signs, and fraction digits. Percent formatting uses localized CLDR affix patterns, scales model values for display, and defaults NumberField stepping to0.01; parsing converts localized percent input back to model space. Number-shaped text fields reuse the parser and formatter for inherited locale display before canonical range/step normalization. Date/time formatting covers localized short through full styles, seconds, calendar, numbering-system, and hour-cycle locale extensions. Collection typeahead reuses the public collator filter.use_number_fieldexposes separate group, input, increment-button, and decrement-button prop contracts.UiNumberFieldprojects that anatomy as a native group instead of collapsing it into one text field. Buttons are excluded from sequential focus, carry the next model-space value, use field-aware accessible labels (or explicitincrementAriaLabelanddecrementAriaLabeloverrides), and disable at step boundaries or for disabled/read-only fields. ArrowUp, ArrowDown, PageUp, and PageDown use the same minimum-anchored step algorithm, including decimal-noise cleanup; Home/End move to the explicit bounds. Modified key combinations are left to the application or platform. Focused vertical wheel input uses the same model-space stepping, rejects horizontal-dominant trackpad gestures and control-wheel zoom, and can be disabled independently withisWheelDisabled. Mouse presses on either stepper restore focus to the input. Mouse and pen steppers fire immediately, repeat after a 400-millisecond delay, and continue every 60 milliseconds. Touch steppers defer repeating for 600 milliseconds and preserve short-tap activation on release. Leaving, cancellation, disabled/read-only updates, and step boundaries stop the current cycle; re-entry starts a new one. AppKit, GTK4, and WinUI claim handled keys and wheels before the toolkit default runs and consume terminal stepper activation, preventing native controls from applying a duplicate change. Default stepper labels and the numeric-input role description are localized from the inherited locale; explicit stepper-label overrides remain authoritative. When the focused input's normalized accessible value changes, the runtime emits an assertive typed announcement. AppKit postsNSAccessibilityAnnouncementRequestedNotification, GTK4 callsgtk_accessible_announce, and WinUI raises a UI Automation notification. Empty values use the same locale catalog, and negative announcements use the screen-reader-friendly Unicode minus sign.- General WAI-ARIA live regions run through the same typed native announcement
channel. The runtime evaluates
aria-live, implicitalert/status/logand HTMLoutputdefaults,aria-atomic,aria-relevant, and inheritedaria-busyacross keyed rerenders. Nested live boundaries do not duplicate speech, hidden/inert content is ignored, and sensitive input values are redacted before a message reaches a host. - Visible
labeland computedaccessibilityLabelvalues remain separate through native props, planning, strict protocol v1, setter diffs, recording, and accessibility-tree projection. An explicitaria-labelsupplies the accessible name; otherwise the visible label is the fallback. AppKit appliessetAccessibilityLabel, GTK4 updates theGtkAccessiblelabel property, and WinUI callsAutomationProperties.SetName. aria-description,aria-roledescription,aria-keyshortcuts, andaria-valuetextremain distinct through the same IR and setter path. GTK4 projects all four throughGtkAccessible. AppKit projects description, role description, and value text throughNSAccessibility; its missing generic shortcut property remains portable. WinUI projects description and shortcuts through UI AutomationHelpTextandAcceleratorKey; role description and generic value-text overrides remain portable. Logical AppKit collection/tab items, GTK4 menu-model items, and the WinUI window wrapper report portable coverage instead of silently dropping unsupported native properties.- Accessibility relationship ID references resolve to mounted host nodes through
one shared registry. Forward references bind when their target mounts; target
id changes and removal rebind or clear dependents; duplicate ids stay
unresolved instead of selecting an arbitrary node. GTK4 projects
aria-labelledby,aria-describedby,aria-details,aria-controls,aria-owns,aria-flowto,aria-errormessage, andaria-activedescendant. WinUI projects complete description, control, and flow-to lists plus a single label target. AppKit projects a single resolved static-text label through its title UI element. Every relationship field has an independent capability entry, including explicit portable exceptions for logical GTK4 menu items and the WinUI window wrapper. - Accessibility structure fields remain independent through native props,
setter diffs, recording, accessibility-tree projection, and capability
audits. One shared incremental registry normalizes values and clears only
fields that are removed or become invalid. GTK4 projects level, set
position/size, row and column counts/indices/spans/index text, and sort
through exact
GtkAccessibleproperties and relations. WinUI projects level, position-in-set, and size-of-set through exactAutomationPropertiessetters. AppKit applies conservative disclosure-level, row/column count, index-range, and sort-direction hints while retaining portable capability status for the complete ARIA fields. Conformance rejects non-positive levels, positions, indices, and spans; values beyond known set or grid bounds; invalid count sentinels; native-integer overflow; and unknown sort tokens. GTK4 menu-model items and the WinUI window wrapper report portable structure coverage explicitly. - Accessibility state fields remain independent through native props, setter
diffs, recording, accessibility-tree projection, and capability audits.
AppKit projects
aria-hiddenandaria-modalthroughNSAccessibility. GTK4 projects hidden, autocomplete, multiline, popup presence, pressed, busy, and modal throughGtkAccessible; thearia-haspopupsubtype remains portable. WinUI retains generic direct-state fields portably, while aContentDialogexposes modal semantics through its automation peer. Conformance rejects invalid autocomplete, current, popup, and pressed tokens before native adapters can reinterpret them. Logical AppKit collection/tab items and GTK4 menu-model items report portable state coverage explicitly. - Native IR capabilities are versioned. Every host exposes a feature manifest with unsupported, portable, or native support levels, role-specific overrides, and auditable capability issues. Protocol render responses carry both the manifest and concrete gaps.
- Accessibility conformance validates names, focus uniqueness, selection and
checked states, exclusive-container selection, relationships, and duplicate
node identity. Invalid
aria-autocomplete,aria-current,aria-haspopup,aria-pressed,aria-live,aria-relevant, structural integers, andaria-sortvalues, plus a multi-referencearia-activedescendant, are reported as errors rather than silently changing state or announcement behavior. The same semantic tree assertions run against AppKit, GTK4, and WinUI planning adapters. Mounted selection is the source of truth for accessible selected/checked state. - AppKit, GTK4, and WinUI use the same press and keyboard state machines. Their view-backed widgets emit pointer press/re-entry/cancellation, hover, focus, key, modality, modifier, repeat, and local-position data through one portable event contract. A key-up completes the press on the original key-down node even if focus moved in between.
- Native control activation is normalized with pre-dispatch context so a platform click does not duplicate the portable keyboard lifecycle. Programmatic and assistive activation emit the complete virtual lifecycle.
- Mounted native interaction profiles follow
SetAction,SetEvents, andSetPortableStyleupdates without remounting. Callback changes and style-driven hover, press, long-press, move, and focus-modality requirements therefore update native event capture immediately. - Native surfaces are split by responsibility: widget creation, updates, hierarchy mutation, interaction translation, platform delegates, types, and styling/layout no longer share monolithic backend files.
This foundation is covered by serialization, routing, state-machine, rerender, and built-in RSX component tests.
The generic interaction source is now present on all three native backends, but support is deliberately reported by role rather than inferred from the mere existence of a platform object:
| Contract | Native coverage |
|---|---|
| Complete press lifecycle | Button, disclosure summary, link, image-map area, ListBoxItem, and TreeItem on AppKit, GTK4, and WinUI; WinUI menu items also use the complete lifecycle. |
| Long press | Shared AppKit, GTK4, and WinUI press sources emit start/end and recognize terminal long press after the configured threshold. NSTimer, GTK main-loop timeout, and DispatcherQueueTimer provide threshold-time delivery, and release-time evaluation is the fallback. |
| Move | AppKit mouse/pen drag events, GTK4 GestureDrag, and WinUI mouse/touch/pen pointer capture use one incremental move state machine. All three normalize Arrow keys to a complete keyboard lifecycle and prevent the underlying native default. |
| NumberField stepping | The shared runtime maps ArrowUp/ArrowDown/PageUp/PageDown, Home/End, focused vertical wheels, and stepper presses to model-space Change events. Wheel input rejects horizontal-dominant gestures and control-wheel zoom and honors isWheelDisabled. Built-in decrement and increment buttons expose the same next values and boundary/read-only state through native Button controls, and mouse presses preserve input focus. AppKit, GTK4, and WinUI share cancellable pointer-hold stepping with immediate mouse/pen activation, delayed touch activation, and 60-millisecond repeats; handled toolkit defaults and terminal native clicks are suppressed. Default stepper labels, the role description, and empty-value text follow a 34-locale catalog. Focused value changes emit assertive native accessibility announcements on all three backends. |
| Live regions | Stable keyed updates implement WAI-ARIA polite/assertive, atomic, relevant, and ancestor busy semantics in the shared runtime. Implicit alert/status/log/output policies and nested boundaries are honored. AppKit, GTK4, and WinUI deliver the resulting typed message through their native assistive-technology APIs; headless and protocol hosts retain the same ordered command. |
| Accessible names | aria-label is independent from visible native text and is projected through AppKit accessibility labels, GTK4 accessible label properties, and WinUI UI Automation names. Headless and protocol output retain the same computed name. The capability manifest conservatively reports portable-only coverage for AppKit logical list/tree and tab items, GTK4 gio::MenuItem, and the WinUI window wrapper. |
| Accessible descriptions | Description, role-description, shortcut, and value-text metadata have independent executable capabilities. GTK4 projects all four. AppKit projects description, role description, and value text; WinUI projects description and shortcuts. Unsupported field/backend combinations and non-accessible logical wrappers remain available to headless/protocol consumers and produce explicit portable capability issues. |
| Accessible relationships | A shared registry resolves ID-reference lists across forward mounting, id changes, removal, and duplicate-id ambiguity. GTK4 projects all eight relationship fields. WinUI projects description, controls, and flow-to lists plus single-target labels; AppKit projects single static-text labels. Remaining backend/field combinations and non-accessible wrappers retain portable semantics and produce field-level capability issues. |
| Accessible structure | Level, position/set size, row/column counts, indices, spans, index text, and sort have independent executable capabilities and shared ARIA value validation. GTK4 projects all twelve fields. WinUI projects level, position-in-set, and size-of-set. AppKit applies conservative disclosure, grid count/range, and sort hints while retaining portable status for the complete fields. Unsupported WinUI fields, logical GTK4 menu items, the WinUI window wrapper, and headless output remain portable and auditable. |
| Accessible states | Hidden, autocomplete, multiline, current, popup, pressed, live-region, busy, and modal semantics have independent executable capabilities. AppKit projects hidden and modal; GTK4 projects hidden, autocomplete, multiline, popup presence, pressed, busy, and modal. Live-region policy is evaluated by the shared runtime and delivered through each backend's native announcement channel. WinUI direct-state gaps, aria-current, the GTK4 popup subtype, and logical wrapper exceptions remain portable and auditable instead of being silently dropped. |
| Native menu activation | AppKit and GTK4 menu items emit terminal press only because their menu models do not expose a mounted generic view event source. |
| Hover and typed modality | View-backed widgets; explicit exceptions are reported for AppKit non-view wrappers/items, GTK4 menu items, and the WinUI window wrapper. |
| Focus within | Portable runtime routing on AppKit, GTK4, WinUI, and headless hosts. Native blur/focus batches are linked with relatedTarget; direct focus callbacks remain target-only while focus-within callbacks run only when a subtree boundary is crossed. |
| Interaction style projection | Runtime-resolved hover, press, long-press, move, focus, focus-visible, focus-within, selected, checked, expanded, disabled, validation, read-only, direction, and matching data-*/aria-* variants use the same transactional SetPortableStyle path on all three planning adapters. |
Focus events, scopes, and autoFocus |
Native focusable control roles listed in the capability manifest. Runtime navigation, restoration, and post-mount autoFocus all emit typed requestFocus commands; contained scopes redirect escaping native focus. AppKit uses makeFirstResponder, GTK4 uses grab_focus, and WinUI calls the fixed IUIElement::Focus(Programmatic) ABI through an isolated adapter because the generated binding leaves that method unwrapped. |
| Overlay stack | Activation ordering, topmost Escape and outside-press dismissal, modal background inertness/accessibility suppression, close-on-blur, portaled child overlays, containment, autofocus, and restoration run in the shared mounted runtime. AppKit, GTK4, and WinUI planning adapters receive the same projected props and event subscriptions. |
| Anchored overlay position | Popover and Tooltip expose one typed positioning contract and versioned command. AppKit maps it to NSPopover positioning rectangles and edges, GTK4 maps it to gtk::Popover parent/pointing rectangle/position/offset, and WinUI maps it to ToolTip placement target/rectangle/signed offsets. Headless and protocol hosts retain the same anchor relationship. Native collision behavior and exact arrow geometry remain backend-specific and are reported as portable capability coverage. |
| Selection and item action | Select/combo box, list box/tree, and tabs/tab list. GTK4 and WinUI ListBox callbacks provide complete native selection snapshots; AppKit modifier-aware row activation and all stable-key aggregation remain in the portable keyed-runtime layer. ListBox/Tree item onAction(key) separation and collection keyboard navigation are shared across adapters. |
NativeCapabilities is the executable source of truth. Global entries are
conservative and role overrides opt into verified behavior. This prevents a
native wrapper, menu model, or logical collection item from being advertised as
interactive merely because another role on that backend is interactive.
NativeInputConformanceManifestV1::from_capabilities expands each role whose
press support is marked Native into a machine-readable automation matrix. A
complete press-lifecycle role requires separate mouse and pen activation,
cancellation, and disabled cases, plus keyboard, virtual assistive activation,
and keyed-rerender cancellation. GTK4 and WinUI also require touch activation,
cancellation, and disabled-touch cases. AppKit and GTK4 menu items retain an
explicit terminal-activation exception because their menu models do not expose
the generic view event source. The expected successful activation order follows
React Aria's usePress contract:
press start, press up, press end, then terminal press.
The evidence boundary is intentionally strict:
NativeInputConformanceObservationV1::captureretains only redacted semantic press events; raw key values and native event payload values are excluded.- A verifier derives expectations from the current capability manifest rather than accepting expectations supplied by an evidence file.
- Exactly one observation is required per case. Event order, target identity, modality, keyboard activation deduplication, and click count are checked.
- Only
OperatingSystemAutomationevidence with matching OS, OS version, toolkit version, and automation-driver identity is eligible. Adapter-kernel and portable-runtime traces remain useful tests but cannot prove native support. - The native CI matrix publishes the generated AppKit, GTK4, and WinUI requirement artifacts so missing platform evidence is explicit and reviewable.
Generate or verify artifacts with:
just native-input-manifest appkit
just native-input-manifest gtk4
just native-input-manifest winui
just native-input-conformance path/to/evidence.json
just winui-input-smoke path/to/winui-smoke.jsonThe WinUI smoke harness covers XAML Button-backed Button,
DisclosureSummary, Link, ImageMapArea, and MenuItem roles plus
ListBoxItem and TreeItem inside real list containers. Mouse and keyboard use
Windows SendInput, pen and touch use synthetic pointer injection, and
assistive activation uses UI Automation InvokePattern or
SelectionItemPattern according to the native control contract. It exercises
successful activation, mouse/pen/touch cancellation, keyed-rerender
cancellation, and disabled input inside the production XAML application
lifecycle. Handled routed pointer events and preview key events feed the same
portable press state machine while the asynchronous dispatcher loop leaves
WinUI layout and input dispatch unblocked. The strict verifier accepts the
complete 98-case WinUI manifest with no semantic defect. Each target's visible
text intentionally differs from the role-specific name used to locate it
through UI Automation, so the fixture also exercises
AutomationProperties.SetName. Native automation
drivers still need to submit complete passing run artifacts on real macOS and
Linux runners.
The following behavior systems are still incomplete or only represented as props:
| Priority | Area | Required outcome |
|---|---|---|
| P0 | Native input conformance | WinUI's complete 98-case V1 manifest passes real OS automation. Populate the AppKit and GTK4 manifests with platform-run mouse, pen, touch where applicable, keyboard, assistive activation, disabled, cancellation, and keyed-rerender fixtures for every role currently marked native; then close or retain evidence-backed menu/item exceptions. |
| P1 | Event propagation | Add platform-run conformance fixtures for conditional Stop/Continue across nested native controls. |
| P1 | Focus management | Add platform-run conformance fixtures for post-mount autoFocus, nested containment, and restoration. |
| P1 | Collections and selection | Complete IME/dead-key typeahead conformance and add real-platform fixtures for layout-aware page navigation. |
| P1 | NumberField interaction | Add real-platform assistive-technology fixtures for localized labels and focused announcements. Group/input/button anatomy, localized stepper labels and role descriptions, focused native value announcements, minimum-anchored button and continuous press-and-hold stepping, Arrow/Page/Home/End keyboard semantics, focused vertical-wheel stepping with horizontal/zoom rejection and isWheelDisabled, mouse input-focus preservation, decimal-noise cleanup, boundary disabling, cancellation/re-entry, and native-default suppression are implemented. |
| P1 | Internationalization | Expand message formatting beyond NumberField, and add currency/unit parsing and formatting plus date ranges/time zones. Reusable decimal/percent parsing and formatting, partial-input validation, locale-aware filtering, localized NumberField model/display conversion, and a 34-locale NumberField message catalog now build on inherited locale/direction. |
| P1 | Accessibility conformance | Complete multi-label projection on AppKit and WinUI; add AppKit description/details/control/ownership/flow/error/active-descendant mappings and WinUI details/ownership/error/active-descendant mappings where platform APIs permit; complete exact AppKit/WinUI structure projection where their role-specific APIs permit it; add exact native mappings for remaining state gaps such as aria-current and direct AppKit/WinUI autocomplete, multiline, popup, pressed, and busy semantics; close the documented logical-item/window name, description, relationship, structure, state, and announcement exceptions; fill the remaining AppKit shortcut and WinUI role-description/value-text gaps; and add real assistive-technology fixtures. Shared lifecycle-aware relationship resolution, all GTK4 relationship mappings, supported AppKit/WinUI relationship mappings, independent aria-label, descriptive metadata, field-level structure/state capabilities, exact GTK4 and supported WinUI structure projection, conservative AppKit structure hints, exact AppKit/GTK4 state projection where supported, validated ARIA tokens and structural values, the typed announcement channel, WAI-ARIA live-region diff policy, and focused NumberField value announcements are implemented. |
| P2 | Overlays | Complete measured boundary-driven collision and arrow projection, native scroll locking, configurable outside-interaction filters, multi-window layer coordination, and real-platform positioning conformance fixtures. |
| P2 | Capability enforcement | Turn reported capability gaps into adapter policy and conformance gates where portable fallback is not sufficient. |
| P2 | Environment style variants | Add native environment and ancestry evaluators for responsive/container, theme, group, peer, and structural selector variants. These remain preserved in the style IR but inactive at runtime today. |
Adding more component names before these systems exist does not improve conformance. New components should be built by composing the shared behavior contracts rather than reimplementing press, focus, hover, selection, or locale logic.
A behavior is complete only when all of the following are true:
- Its portable contract and state machine are specified and tested.
- AppKit, GTK4, and WinUI translate native input into the same observable behavior, with documented capability differences where exact parity is not possible.
- Keyboard, pointer, touch, virtual accessibility activation, disabled state, cancellation, and rerender cases are covered where applicable.
- Accessibility role, name, state, relationships, and focus behavior are asserted semantically.
- The public documentation describes the supported contract without implying broader parity.
- A real operating-system automation run satisfies the generated versioned manifest; portable or adapter-only tests do not count as native evidence.