Accessibility
Blinc provides keyboard navigation through its event router and a semantic snapshot for platform accessibility adapters. These are separate capabilities: Tab navigation alone does not expose controls to a screen reader.
Names and roles
Standard buttons use their visible text as their accessible name. A checkbox
combines its box and label into one accessible element. Text fields expose their
current value separately from their name; cn::input(&data).label("Name")
supplies the accessible name as well as the visible label.
cn::slider(value).label("Volume") exposes one adjustable control. Its value
follows the bound number, and assistive increment/decrement uses the configured
step, bounds and change callback without requiring keyboard focus.
For a core text input, bind a name explicitly. It accepts a string, Signal or Computed:
use blinc::layout::widgets::text_input::{text_input, text_input_data};
let data = text_input_data();
let field = text_input(&data).accessibility_label("Search projects");
Custom elements can declare role, aria-label, aria-labelledby,
aria-description, aria-disabled, aria-selected, aria-checked and
aria-expanded through .attr(...). An aria-labelledby value contains the IDs
of elements whose text supplies the name. Give icon-only controls a name:
use blinc::layout::prelude::*;
let close = div()
.attr("role", "button")
.attr("tabindex", "0")
.attr("aria-label", "Close panel")
.child(text("×"));
The role describes behavior; it does not add a click handler or styling. Keep
the usual event handler and CSS for a custom control. aria-hidden="true"
removes a decorative subtree from the semantic snapshot. Disabled controls
remain discoverable, with their disabled state exposed, and reject actions.
Platform integration
The iOS host's BlincAccessibility.swift creates native accessibility elements
for GPU-rendered content. It retains their objects by Blinc stable node ID,
updates their names, values, states and bounds after UI changes, and dispatches
activation through the widget's existing handlers. Native text-field proxies
provide editing to UIKit while Blinc continues to draw the field. Return uses
the existing key handler, including cn::input(...).on_submit(|shift| ...).
Editing uses the widget's document, constraints and change callback, including
native marked-text and candidate replacement. Accessibility snapshots contain
masked password values; the keyboard connection receives the private document
separately. See mobile input methods
for the editing and commit behavior.
New CLI-generated iOS projects include this adapter. Existing hosts must add
BlincAccessibility.swift from extensions/blinc_platform_ios/swift/, include
the accessibility and IME declarations from its bridging header, and retain a
BlincAccessibility(host: view, context: ctx) instance. Call synchronize() after
blinc_build_frame(ctx) and disconnect() before destroying the context.
Update the native keyboard bridge alongside the host so it reuses an accessible
text field when that field owns the keyboard.
The iOS hosts scale their logical viewport using the user's Dynamic Type preference. Text, containing controls, accessibility frames, touch coordinates and keyboard insets use the same scale. The native editing proxy uses scaled font metrics too. Keep layouts responsive and provide scrolling when larger content no longer fits; fixed-size content can still overflow.
The automated iOS fixture covers buttons, a labelled single-line input, checkbox state, adjustable slider values, disabled actions, stable native objects and larger-text geometry. The snapshot currently exposes visible content. Assistive scrolling through offscreen content, providers for other editable widgets, advanced composite-widget semantics and screen-reader focus restoration across overlays still need platform validation and integration; this adapter is not a claim of complete library coverage.
The browser canvas, Android and desktop runners still require platform adapters for the semantic snapshot. A role stored in a Blinc tree is not automatically an HTML ARIA element or an operating-system accessibility node.
Validation
Use python3 scripts/test_ios_accessibility.py in the repository to build the
current-source fixture and run its semantic/action assertions, larger-text
geometry checks, native assistive-action tests and Apple's audit in one dedicated
iOS Simulator. The result bundle includes rendered screenshots and the native
accessibility hierarchy. The runner shuts down its Simulator on completion and
does not start an Android emulator.
Keep explicit assertions for names, roles, values and actions alongside general audits: a general audit can pass when no custom controls are exposed. Physical iOS VoiceOver validation remains necessary; Simulator results do not establish real-device screen-reader behavior.