Skip to content

Comprehensive accessibility pass across PHP, iOS, and Android#4

Merged
shanerbaner82 merged 6 commits into
mainfrom
accessibility
Jul 5, 2026
Merged

Comprehensive accessibility pass across PHP, iOS, and Android#4
shanerbaner82 merged 6 commits into
mainfrom
accessibility

Conversation

@shanerbaner82

Copy link
Copy Markdown
Contributor

Summary

A comprehensive accessibility pass across the native-ui element library and both native renderers, plus supporting test/CI scaffolding.

  • PHPHasA11y trait wired through the element library (Button, Checkbox, Radio, Slider, Toggle, Select, Chip, ListItem, TabRow, ProgressBar, and more), so every interactive element can carry accessibility labels/roles/state. HasA11y itself lives in nativephp/mobile core.
  • iOS — accessibility labels, traits, and values threaded through the SwiftUI renderers (Button, Checkbox, Chip, Icon, ListItem, TextInput, Carousel, GestureArea, Drawer, …).
  • Android — matching semantics via Compose contentDescription / Modifier.semantics across the renderer set (Button, Checkbox, Radio, Slider, Toggle, List/ListItem, Nav, Icon, …).
  • Icons — refreshed Material catalog snapshot (2234 → 2122, a pure subset).
  • Docs — README + boost guidelines updated; LazyGridRenderer unbounded-scroller fallback explained.
  • Tests/CI — new A11yTest suite, phpunit.xml, and a GitHub Actions tests workflow.

Test plan

  • A11yTest covers the PHP-side accessibility API surface.
  • CI workflow runs the suite on push/PR.

🤖 Generated with Claude Code

shanerbaner82 and others added 6 commits July 4, 2026 14:14
…roid

PHP API:
- New HasA11y trait (a11yLabel/a11yHint via Element::setProp) adopted by
  every element — kills the copy-pasted per-element methods and gives the
  9 elements that had no a11y support (Icon, ListItem, Radio, Carousel,
  ListSection, NativeList, NativeVirtualList, NativeDrawer, Tab) the full
  API. Tab gains the fluent methods it was missing.
- ListItem: trailing_a11y_label prop labels the trailing icon_button.
- Boost guidelines rewritten with real accessibility guidance; README
  Accessibility section; Pest coverage for a11y serialization.

iOS renderers:
- Dynamic Type everywhere: @ScaledMetric-backed nuiScaledFont() replaces
  every fixed .font(.system(size:)) — text now tracks the user's
  preferred content size.
- 44pt minimum touch targets (nuiMinTapTarget) on checkbox, radio, chip,
  tabs, modal close, list icon buttons, top-bar/back/hamburger buttons,
  clickable icons.
- VoiceOver: checkbox .isToggle (iOS 17) + Checked/Unchecked value, chip
  selected value, select current-option value, button loading announced,
  Back/Open menu/bottom-nav labels + .isSelected, list rows combined into
  one focus stop, skeleton rows hidden, modal .isModal trait, text-input
  label/error exposure, carousel + gesture-area label plumbing.
- Icons decorative-by-default (hidden without a11y_label; labeled and
  .isButton when clickable). Image renderer honors new alt prop.
- Reduce Motion respected in drawer animations and screen transitions.

Android renderers:
- stateDescription misuse fixed: a11y_hint no longer clobbers the state
  channel; shared Modifier.nuiA11y merges label+hint into
  contentDescription. Only remaining stateDescription use is the
  legitimate one (button loading).
- Toggle/Checkbox/Radio rows use toggleable/selectable with proper Roles:
  one merged TalkBack focus stop, label taps activate the control, radio
  double tap-target removed.
- Roles + labels on hand-rolled clickables (icons, top-bar back/actions,
  bottom-nav tabs with selected state), 48dp targets on the same.
- Icons stop announcing machine names; image renderer honors alt;
  activity indicator announces Loading politely.
- Dark-mode-hostile hardcoded grays/white replaced with theme tokens.

Theme: default secondary/destructive/accent tokens (config + Swift
fallbacks) now meet WCAG AA 4.5:1 against their white on-colors —
visible change: accent #FB923C -> #C2410C, destructive #DC2626 ->
#B91C1C, secondary #64748B -> #475569.

Wire contract additions: alt (image), trailing_a11y_label (list_item).
Pairs with mobile-air 57079e9 (Image element alt prop).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The trait now lives on the base Edge Element (mobile-air 8797c71), so
every element inherits a11yLabel()/a11yHint()/applyA11yAttributes()
without per-element adoption — the plugin-local trait and its 24 use
statements are gone. The applyAttributes() hydration calls stay for
paths that bypass the collector's central attr pass. Wire output is
unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…derer

Companion comment for 2913a0c — records WHY the non-lazy chunked grid
exists (Compose lazy grids throw under infinite max-height constraints
where SwiftUI's LazyVGrid sizes to content).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…bset)

Auto-fetched from Google's metadata endpoint; verified against the
previous font-extracted set: 112 stale names removed, ZERO added — every
remaining name still renders with the bundled ligature font. Re-added
the provenance caution to _meta: web refreshes are only safe as a
subset of the font's ligature names unless the bundled font upgrades in
the same change. Apps regenerate enums via native-ui:generate-icons;
removed names surface as compile errors at their call sites (the
intended failure mode).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds phpunit.xml and a GitHub Actions tests workflow, and ignores the
.phpunit.cache/ directory.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
nativephp/mobile pulls in endroid/qr-code ^6.1.3, which requires
PHP ^8.4, so the dependency chain could not resolve on 8.3.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@shanerbaner82 shanerbaner82 merged commit cb3f910 into main Jul 5, 2026
2 checks passed
@shanerbaner82 shanerbaner82 deleted the accessibility branch July 5, 2026 20:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant