reis-mobile 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,14 +5,14 @@
5
5
  },
6
6
  "metadata": {
7
7
  "description": "AI agents for mobile engineering",
8
- "version": "0.5.0"
8
+ "version": "0.6.0"
9
9
  },
10
10
  "plugins": [
11
11
  {
12
12
  "name": "reis-mobile",
13
13
  "source": "./",
14
14
  "description": "AI agents, skills and workflows for Flutter, Android, iOS and React Native development.",
15
- "version": "0.5.0",
15
+ "version": "0.6.0",
16
16
  "author": {
17
17
  "name": "Warleson Reis"
18
18
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reis-mobile",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "AI agents, skills and workflows for Flutter, Android, iOS and React Native development. Run /reis-mobile:doctor, then /reis-mobile:review.",
5
5
  "author": {
6
6
  "name": "Warleson Reis"
package/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). The project follows [Semantic Versioning](https://semver.org/).
4
4
 
5
+ ## [0.6.0] - 2026-09-16
6
+
7
+ ### Added
8
+
9
+ - `mobile-accessibility-auditor` agent: audits UI on every stack for screen reader labels, roles and states, touch targets, text scaling, focus and announcements, with a severity-ranked report and fixes in each platform's API. The router sends `accessibility` to it instead of `mobile-staff-engineer`.
10
+ - `mobile-release-engineer` agent: release readiness with a go/no-go verdict, release notes and store copy from the git history, CI/CD pipelines, signing, crash symbols and staged rollout. The router sends `release` and `deployment` to it instead of `mobile-staff-engineer`.
11
+ - `mobile-accessibility` topic skill, with references for Flutter (Semantics and `meetsGuideline` checks), native Android (Compose semantics and Views, Espresso accessibility checks), native iOS (SwiftUI, UIKit, `performAccessibilityAudit`) and React Native.
12
+ - `mobile-release` topic skill: readiness checklist and report, references for Flutter, Android, iOS and React Native (versions, signing, symbols, store checks, rollout, over-the-air updates), release notes and store listing limits, and CI/CD with GitHub Actions, fastlane and EAS.
13
+ - `mobile-debug` guides for Android ANRs (thread states, locks, `ApplicationExitInfo`), Android memory leaks (LeakCanary traces, heap dumps) and deep links on every stack (App Links verification, Universal Links and the Apple CDN, cold-start routing).
14
+
15
+ ### Changed
16
+
17
+ - `mobile-staff-engineer` no longer declares the `release`, `deployment` and `accessibility` intents.
18
+ - The plugin's fixed context cost rises to about 4,300 tokens per session.
19
+
5
20
  ## [0.5.0] - 2026-09-16
6
21
 
7
22
  ### Changed
package/README.md CHANGED
@@ -6,7 +6,7 @@ A mobile app is not a generic project. A code review that does not know about `B
6
6
 
7
7
  <p align="center">
8
8
  <a href="https://github.com/wrsilva/reis-mobile/actions/workflows/ci.yml"><img src="https://github.com/wrsilva/reis-mobile/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
9
- <img src="https://img.shields.io/badge/Version-0.5.0-blue" alt="Version 0.5.0">
9
+ <img src="https://img.shields.io/badge/Version-0.6.0-blue" alt="Version 0.6.0">
10
10
  <img src="https://img.shields.io/badge/Claude_Code-plugin-blueviolet" alt="Claude Code plugin">
11
11
  <img src="https://img.shields.io/badge/Node.js-22+-339933?logo=node.js&logoColor=white" alt="Node.js 22+">
12
12
  <img src="https://img.shields.io/badge/Dependencies-0-brightgreen" alt="Zero dependencies">
@@ -23,7 +23,7 @@ A mobile app is not a generic project. A code review that does not know about `B
23
23
 
24
24
  🔎 **Detects the stack on its own.** Flutter (app or plugin), React Native (including Expo), Kotlin Multiplatform, native Android and iOS, with languages and target platforms. A Flutter app with `android/` and `ios/` is still Flutter.
25
25
 
26
- 👥 **16 agents and 11 mobile skills.** Architect, performance and test engineers for Flutter, native Android, native iOS and React Native, a staff engineer, a native plugin specialist, a code reviewer and a lead that coordinates them all, plus skills for Gradle, Xcode and CocoaPods builds, Jetpack Compose, BLoC, Riverpod, Firebase and testing on every platform, plugins and security.
26
+ 👥 **18 agents and 13 mobile skills.** Architect, performance and test engineers for Flutter, native Android, native iOS and React Native, a staff engineer, a native plugin specialist, a code reviewer, an accessibility auditor, a release engineer and a lead that coordinates them all, plus skills for Gradle, Xcode and CocoaPods builds, Jetpack Compose, BLoC, Riverpod, Firebase and testing on every platform, plugins, security, accessibility and releases.
27
27
 
28
28
  🌐 **Answers in English or Portuguese.** `reis-mobile init eng` or `reis-mobile init pt` sets the language of reports, explanations and debates.
29
29
 
@@ -52,7 +52,8 @@ A mobile app is not a generic project. A code review that does not know about `B
52
52
 
53
53
  | Version | Highlights |
54
54
  |--------|-----------|
55
- | **v0.5.0** (current) | 11 skills instead of 60: topic skills with a guide per platform (`mobile-architecture`, `mobile-code-review`, `mobile-debug`, `mobile-test`, `mobile-security`, `mobile-firebase`) and platform skills (`mobile-flutter`, `mobile-android`, and the new `mobile-ios` and `mobile-rn`). Half the fixed context cost. |
55
+ | **v0.6.0** (current) | `mobile-accessibility-auditor` and `mobile-release-engineer` agents with the new `mobile-accessibility` and `mobile-release` skills: accessibility audits on every stack, release readiness with a go/no-go verdict, release notes and store copy, CI/CD. `mobile-debug` gains guides for ANRs, Android memory leaks and deep links. |
56
+ | **v0.5.0** | 11 skills instead of 60: topic skills with a guide per platform (`mobile-architecture`, `mobile-code-review`, `mobile-debug`, `mobile-test`, `mobile-security`, `mobile-firebase`) and platform skills (`mobile-flutter`, `mobile-android`, and the new `mobile-ios` and `mobile-rn`). Half the fixed context cost. |
56
57
  | **v0.4.0** | Native Android, iOS and React Native: architect, performance and test agents for each; `/reis-mobile:debug` for Gradle, Xcode, CocoaPods and Flutter failures; `mobile-test` and `mobile-firebase` with a guide per platform; the Android team's skills; `.reis-mobile/config.yaml` for monorepos. Skills now use stack prefixes. |
57
58
  | **v0.3.6** | `reis-mobile init eng` installs the plugin in Claude Code and Codex at once, skipping whichever is not installed; `init --uninstall` removes it from both. |
58
59
  | **v0.3.5** | Installs through npm and the Claude Code and Codex plugins. |
@@ -262,11 +263,13 @@ The router never invents a specialist: if no agent serves the intent, it warns y
262
263
  | Write or audit tests | agent `flutter-`, `android-`, `ios-` or `rn-test-engineer` | ✅ |
263
264
  | Debug a Gradle, Xcode, CocoaPods or Flutter build | `/reis-mobile:debug` | ✅ |
264
265
  | Build a plugin or debug MethodChannel/EventChannel | agent `plugin-native-expert` | ✅ |
266
+ | Audit a screen for TalkBack, VoiceOver, touch targets and text scaling | agent `mobile-accessibility-auditor` | ✅ |
267
+ | Check release readiness, write release notes or set up CI/CD | agent `mobile-release-engineer` | ✅ |
265
268
  | Full audit with several specialists | agent `lead-mobile` | ✅ |
266
269
  | Decide between two architectures with a real trade-off | `/reis-mobile:debate` | ✅ |
267
- | Dedicated test command (including XCTest and Espresso) | `/reis-mobile:test` | 🔜 v0.6 |
268
- | Check whether the app is ready for the store | `/reis-mobile:release` | 🔜 v0.7 |
269
- | Consolidated decision by consensus between agents | `/reis-mobile:council` | 🔜 v0.9 |
270
+ | Dedicated test command (including XCTest and Espresso) | `/reis-mobile:test` | 🔜 v0.7 |
271
+ | Check whether the app is ready for the store | `/reis-mobile:release` | 🔜 v0.8 |
272
+ | Consolidated decision by consensus between agents | `/reis-mobile:council` | 🔜 v0.10 |
270
273
 
271
274
  <details>
272
275
  <summary><strong>How is it different from plain Claude Code?</strong></summary>
@@ -277,8 +280,8 @@ The router never invents a specialist: if no agent serves the intent, it warns y
277
280
  | **Project context** | You explain the stack | Deterministic detection of stack, platform and variant |
278
281
  | **Review** | Generic | Mobile checklists: lifecycle, `BuildContext`, MASVS, manifest, ATS |
279
282
  | **Secrets in the diff** | Sent as they are | Masked before reaching the model |
280
- | **Specialists** | None | 16 agents and 11 mobile skills |
281
- | **Context cost** | Zero | ~3,900 fixed tokens (agent and skill descriptions), no hooks |
283
+ | **Specialists** | None | 18 agents and 13 mobile skills |
284
+ | **Context cost** | Zero | ~4,300 fixed tokens (agent and skill descriptions), no hooks |
282
285
  | **Best for** | General tasks | Teams and developers working on mobile apps |
283
286
 
284
287
  **In short:** Claude Code already knows how to code. reis-mobile makes it look at what matters in a mobile app.
@@ -329,11 +332,13 @@ The Node.js core decides **what** to load. The model decides **how** to review,
329
332
  | `rn-architect` | React Native | architecture | Feature structure, server vs client state, navigation, native modules |
330
333
  | `rn-performance-engineer` | React Native | performance | Re-renders, lists, JavaScript thread, animations, startup, bundle size |
331
334
  | `rn-test-engineer` | React Native | test | Jest, React Native Testing Library, native module mocks, Detox or Maestro |
332
- | `mobile-staff-engineer` | all | debug, architecture, performance, test, security, release, migration, dependency, build, deployment, accessibility | Senior Flutter/Android/iOS specialist for whatever no specific agent covers |
335
+ | `mobile-accessibility-auditor` | all | accessibility | Screen reader labels, roles and states, touch targets, text scaling, focus and announcements, with fixes per platform |
336
+ | `mobile-release-engineer` | all | release, deployment | Go/no-go readiness, versions and signing, crash symbols, release notes and store copy, CI/CD, staged rollout |
337
+ | `mobile-staff-engineer` | all | debug, architecture, performance, test, security, migration, dependency, build | Senior Flutter/Android/iOS specialist for whatever no specific agent covers |
333
338
  | `plugin-native-expert` | Flutter | on demand | MethodChannel, EventChannel, Pigeon and Kotlin/Swift bridges, threading and lifecycle |
334
339
  | `lead-mobile` | all | on demand | Coordinates the other agents and consolidates a single action plan |
335
340
 
336
- When more than one agent serves the intent, the stack-specific one wins. In a Flutter app, `performance` goes to `flutter-performance-engineer`; in a native Android app, to `android-performance-engineer`. Intents without a stack specialist, such as `debug` or `release`, go to `mobile-staff-engineer`.
341
+ When more than one agent serves the intent, the stack-specific one wins. In a Flutter app, `performance` goes to `flutter-performance-engineer`; in a native Android app, to `android-performance-engineer`. `accessibility` goes to `mobile-accessibility-auditor` and `release` or `deployment` to `mobile-release-engineer` on every stack. Other intents without a stack specialist, such as `debug`, go to `mobile-staff-engineer`.
337
342
 
338
343
  ### Skills
339
344
 
@@ -343,9 +348,11 @@ Every skill starts with `mobile-`. **Topic** skills cover every stack with one r
343
348
  |---|---|---|---|
344
349
  | `mobile-architecture` | Layers, state management, modularization, DI, navigation | architecture | Flutter (BLoC, Riverpod, Provider, ChangeNotifier...), Android, iOS, React Native |
345
350
  | `mobile-code-review` | Review process and checklists | review, performance | Flutter (PR review, widgets, project audit, static analysis), Android, iOS, React Native |
346
- | `mobile-debug` | Failing builds and runtime errors | debug, build, dependency, migration | Flutter (build, runtime, layout, packages), Android (Gradle, crashes, ANRs), iOS (Xcode, CocoaPods, crash logs), React Native (Metro, native modules) |
351
+ | `mobile-debug` | Failing builds and runtime errors | debug, build, dependency, migration | Flutter (build, runtime, layout, packages), Android (Gradle, crashes, ANRs, memory leaks), iOS (Xcode, CocoaPods, crash logs), React Native (Metro, native modules), deep links |
347
352
  | `mobile-test` | Unit, UI, integration and end-to-end tests | test | Flutter (widget, integration, Patrol, mocks, coverage), Android, iOS, React Native |
348
353
  | `mobile-security` | OWASP MASVS security checks | review, security, release | Flutter (token storage), Android (intents), iOS, React Native |
354
+ | `mobile-accessibility` | Screen readers, touch targets, text scaling, contrast | accessibility | Flutter (Semantics), Android (Compose and Views), iOS (SwiftUI and UIKit), React Native |
355
+ | `mobile-release` | Release readiness, store copy and CI/CD | release, deployment | Flutter, Android, iOS, React Native, release notes and store listing, CI/CD |
349
356
  | `mobile-firebase` | Firebase setup and 13 products | — | Flutter, Android, iOS, React Native, each with a guide per product |
350
357
  | `mobile-flutter` | Flutter and Dart specifics | review, performance, accessibility, migration | UI and navigation, data, native code, performance, accessibility and i18n, Dart, setup |
351
358
  | `mobile-android` | Native Android specifics, from the Android team | performance, release, migration, build, dependency | Compose UI, AGP 9 and R8, profiling, Google Play, identity, camera and media, on-device AI, Wear OS, TV, XR |
@@ -369,7 +376,7 @@ About 85 guides come from [flutter/skills](https://github.com/flutter/skills), [
369
376
 
370
377
  **Exception: `/reis-mobile:debate --external`.** This flag, and only this flag, sends the debate context — including excerpts of the files mentioned in the question — to the `codex` and `gemini` CLIs, which are third-party and have their own data policies. Without the flag, nothing leaves your session. The `review` diff is masked by `redact.mjs`, but the context you cite in a debate question does not go through that layer: check what you are sending before using `--external`.
371
378
 
372
- **Context cost.** The descriptions of the 16 agents and 11 skills add up to about 3,900 fixed tokens per session, estimated from their length (v0.3 measured about 4,800 with `claude plugin details reis-mobile`). The full content of each skill is only loaded when it is used.
379
+ **Context cost.** The descriptions of the 18 agents and 13 skills add up to about 4,300 fixed tokens per session, estimated from their length (v0.3 measured about 4,800 with `claude plugin details reis-mobile`). The full content of each skill is only loaded when it is used.
373
380
 
374
381
  **No hooks.** The plugin does not attach to Claude Code events. It only acts when you call a command or when a skill is relevant.
375
382
 
@@ -393,10 +400,11 @@ About 85 guides come from [flutter/skills](https://github.com/flutter/skills), [
393
400
  | v0.3.6 | `reis-mobile init` installs in Claude Code and Codex | ✅ |
394
401
  | v0.4.0 | Native Android and iOS skills, architect, performance and test agents for Android, iOS and React Native, `.reis-mobile/config.yaml` and `/reis-mobile:debug` (Gradle, Xcode, CocoaPods, Flutter) | ✅ |
395
402
  | v0.5.0 | Topic and platform skills (`mobile-*`), with guides for Flutter, Android, iOS and React Native in each; new `mobile-ios` and `mobile-rn` | ✅ |
396
- | v0.6.0 | `/reis-mobile:test` and native tests (XCTest, Espresso) | ⏳ |
397
- | v0.7.0 | `/reis-mobile:release` with quality gates | ⏳ |
398
- | v0.8.0 | MCP server | ⏳ |
399
- | v0.9.0 | Multi-provider (OpenAI, Gemini, OpenRouter, Ollama) beyond the debate's `--external` | ⏳ |
403
+ | v0.6.0 | Accessibility auditor and release engineer agents, `mobile-accessibility` and `mobile-release` skills, ANR, memory leak and deep link debugging guides | ✅ |
404
+ | v0.7.0 | `/reis-mobile:test` and native tests (XCTest, Espresso) | ⏳ |
405
+ | v0.8.0 | `/reis-mobile:release` with quality gates | ⏳ |
406
+ | v0.9.0 | MCP server | ⏳ |
407
+ | v0.10.0 | Multi-provider (OpenAI, Gemini, OpenRouter, Ollama) beyond the debate's `--external` | ⏳ |
400
408
  | v1.0.0 | First stable version: Flutter, Android, iOS and React Native | ⏳ |
401
409
 
402
410
  ---
@@ -39,10 +39,12 @@ Agents from this plugin are addressed with the `reis-mobile:` prefix in the Agen
39
39
  | `reis-mobile:rn-performance-engineer` | React Native: re-renders, lists, JS thread, startup, bundle size |
40
40
  | `reis-mobile:rn-test-engineer` | React Native: Jest, React Native Testing Library, Detox or Maestro |
41
41
  | `reis-mobile:mobile-code-reviewer` | Reviewing a diff or pull request |
42
+ | `reis-mobile:mobile-accessibility-auditor` | Screen reader labels, roles, touch targets, text scaling, accessibility audits |
43
+ | `reis-mobile:mobile-release-engineer` | Release readiness, release notes, store copy, CI/CD, signing and rollout |
42
44
  | `reis-mobile:plugin-native-expert` | Plugins, platform channels, native Android/iOS bridges |
43
- | `reis-mobile:mobile-staff-engineer` | Debugging, native apps, migrations, CI/CD, release, cross-platform trade-offs |
45
+ | `reis-mobile:mobile-staff-engineer` | Debugging, native apps, migrations, cross-platform trade-offs |
44
46
 
45
- Detect the stack first (`node "${CLAUDE_PLUGIN_ROOT}/bin/reis-mobile.mjs" detect`). Each architect, performance and test specialist applies only to its stack: `flutter-*` to Flutter, `android-*` to native Android, `ios-*` to native iOS and `rn-*` to React Native. In a cross-platform app, a native specialist joins only for the native folders (`android/`, `ios/`) when the problem lives there. `mobile-staff-engineer` and `mobile-code-reviewer` apply to every stack.
47
+ Detect the stack first (`node "${CLAUDE_PLUGIN_ROOT}/bin/reis-mobile.mjs" detect`). Each architect, performance and test specialist applies only to its stack: `flutter-*` to Flutter, `android-*` to native Android, `ios-*` to native iOS and `rn-*` to React Native. In a cross-platform app, a native specialist joins only for the native folders (`android/`, `ios/`) when the problem lives there. `mobile-staff-engineer`, `mobile-code-reviewer`, `mobile-accessibility-auditor` and `mobile-release-engineer` apply to every stack.
46
48
 
47
49
  ## Delegation
48
50
 
@@ -60,6 +62,7 @@ Performance issue → flutter-performance-engineer
60
62
  New feature → flutter-architect → flutter-test-engineer
61
63
  Native integration → plugin-native-expert → mobile-staff-engineer
62
64
  Full project audit → flutter-architect + flutter-performance-engineer + flutter-test-engineer + mobile-code-reviewer (parallel) → synthesis
65
+ Pre-release check → mobile-release-engineer + mobile-accessibility-auditor (parallel) → synthesis
63
66
  ```
64
67
 
65
68
  ## Debate moderation
@@ -0,0 +1,63 @@
1
+ ---
2
+ name: mobile-accessibility-auditor
3
+ description: Use this agent to audit mobile UI code for accessibility on any stack (Flutter, Jetpack Compose and Android Views, SwiftUI and UIKit, React Native) — missing or misleading screen reader labels, roles and states, decorative elements read aloud, rows split into many elements, touch targets below the minimum, text that does not scale, contrast risks, focus in dialogs and unannounced dynamic changes. It returns a severity-ranked report with file:line evidence and the fix in the platform's own API. Typical triggers are "is this screen accessible?", "TalkBack reads this button as unlabeled", an accessibility audit before release and a WCAG compliance request.
4
+ model: inherit
5
+ color: purple
6
+ tools: ["Read", "Grep", "Glob", "Bash"]
7
+ intents: [accessibility]
8
+ stacks: ["*"]
9
+ ---
10
+
11
+ You are a senior mobile accessibility engineer. You know how TalkBack and VoiceOver build their view of a screen from Flutter semantics, Compose semantics, Android Views, SwiftUI modifiers, UIKit properties and React Native props, and you judge UI by whether a person using a screen reader, large text, switch access or reduced motion can complete the flow.
12
+
13
+ ## When to act
14
+
15
+ - **Screen or component audit.** The user points at files, a screen or a flow.
16
+ - **Change review.** A diff touches UI; audit only the changed UI, reading the whole widget, composable or view when the hunk is not enough.
17
+ - **Pre-release audit.** No target given: start from the main flows (onboarding, login, the primary task, checkout or payment, settings) and shared components (buttons, list rows, inputs, dialogs), since one broken shared component breaks every screen.
18
+
19
+ The `mobile-accessibility` skill holds the checks and a reference per platform (`references/flutter.md`, `android.md`, `ios.md`, `react-native.md`); read the one for the project's stack, and the native references for native screens inside cross-platform apps.
20
+
21
+ ## Process
22
+
23
+ 1. Detect the stack (`node "${CLAUDE_PLUGIN_ROOT}/bin/reis-mobile.mjs" detect`, or the router result when available) and the UI toolkit: Compose or Views, SwiftUI or UIKit.
24
+ 2. Find the shared components first (`Grep` for custom buttons, icon buttons, list items, text fields, dialogs) and audit them once.
25
+ 3. For each screen, walk the accessibility tree in reading order as the screen reader would: name, role, state, grouping, then touch targets, text scaling, focus and announcements.
26
+ 4. **Confirm every finding in the code.** A label passed through a parameter, a theme that sets minimum sizes or a wrapper component that adds semantics can make a suspicion false. Follow the call to where the semantics are actually set.
27
+ 5. Rank by who is blocked: a user who cannot complete the flow comes before a user who needs an extra swipe.
28
+ 6. Give the minimal fix in the project's language, conventions and component library.
29
+
30
+ ## Rules
31
+
32
+ - Use only APIs that exist in the versions the project uses. Check `pubspec.lock`, the Compose BOM or `build.gradle`, the iOS deployment target or `package.json` before recommending an API added recently (for example, iOS 17-only announcement APIs).
33
+ - Labels describe the action or content in the app's language, and include context in lists ("Delete *Buy milk*", not "Delete" repeated twenty times). Do not add "button" or "image" to labels: the role already says it.
34
+ - Do not ask for hints, custom actions or sort orders where the default is already correct; extra semantics are noise too.
35
+ - Contrast and real screen reader behavior cannot be proven from code alone. Mark those findings as risks and name the tool that confirms them (Accessibility Scanner, Accessibility Inspector, `performAccessibilityAudit`, Flutter's `textContrastGuideline`).
36
+ - Do not edit files unless the user asks for fixes. When they do, keep each fix scoped and suggest the automated check that prevents the regression.
37
+ - Answer in the user's language; keep code, API names and labels' original language as they are in the project.
38
+
39
+ ## Report format
40
+
41
+ ```markdown
42
+ ## Accessibility audit
43
+ Stack and UI toolkit, scope (files, screens or diff), verdict: accessible | needs work | blocks screen reader users.
44
+
45
+ ## Critical
46
+ ## High
47
+ ## Medium
48
+ ## Low
49
+
50
+ For each finding:
51
+ - **Element** — `path:line`
52
+ - **Problem** — what the user hears or cannot do
53
+ - **Guideline** — the WCAG 2.2 criterion or platform guideline (for example, 1.1.1 Non-text Content, 4.1.2 Name, Role, Value)
54
+ - **Fix** — code in the platform's API
55
+
56
+ ## Needs device verification
57
+ Contrast, reading order across complex layouts, custom controls — what to check and with which tool.
58
+
59
+ ## Prevent regressions
60
+ Automated checks to add to the test suite for this stack.
61
+ ```
62
+
63
+ If a severity has no findings, write "No findings."
@@ -0,0 +1,58 @@
1
+ ---
2
+ name: mobile-release-engineer
3
+ description: Use this agent to get a mobile app version out safely on any stack (Flutter, native Android, native iOS, React Native and Expo) — release readiness audits with a go/no-go verdict, version and build number checks, signing and crash symbol setup (R8 mappings, dSYMs, Dart debug info, source maps), store requirements, staged rollout and rollback plans, release notes and store listing text written from the git history, and CI/CD pipelines with GitHub Actions, fastlane, EAS or Codemagic. Typical triggers are "are we ready to ship 2.3?", "write the release notes since the last tag", "set up a pipeline that uploads to TestFlight and the Play internal track" and a rejected or broken store submission.
4
+ model: inherit
5
+ color: green
6
+ tools: ["Read", "Grep", "Glob", "Bash"]
7
+ intents: [release, deployment]
8
+ stacks: ["*"]
9
+ ---
10
+
11
+ You are a senior mobile release engineer. You have shipped apps to Google Play and the App Store through staged rollouts, broken builds, rejected submissions and hotfixes, and you know that a store release cannot be rolled back: what matters is catching the problem before the upload and limiting the blast radius after it.
12
+
13
+ ## When to act
14
+
15
+ - **Readiness audit.** "Can we ship?" Check the project against the release checklist and return GO, GO WITH RISKS or NO-GO.
16
+ - **Release notes and store copy.** Turn the commits since the last release into store notes, a developer changelog and QA focus areas.
17
+ - **Pipeline.** Design, write or review a CI/CD pipeline that builds, signs, uploads and archives symbols.
18
+ - **Failed release.** An upload rejected by the store, a signing or provisioning error in CI, a build number conflict, a rollout with rising crashes. Build failures in Gradle or Xcode themselves belong to the `mobile-debug` skill; use it for the compiler and toolchain side.
19
+
20
+ The `mobile-release` skill holds the process: `SKILL.md` has the readiness checklist and report, `references/flutter.md`, `android.md`, `ios.md` and `react-native.md` the platform details, `references/store-copy.md` the release notes method and store limits, `references/ci-cd.md` the pipelines. Flutter and React Native releases also need the Android and iOS references.
21
+
22
+ ## Process
23
+
24
+ 1. Detect the stack (`node "${CLAUDE_PLUGIN_ROOT}/bin/reis-mobile.mjs" detect`, or the router result when available) and which platforms ship.
25
+ 2. Establish the release range: the last release tag (`git describe --tags --abbrev=0`), the current version and build numbers in every manifest, and the branch being released.
26
+ 3. Read the files that decide the release: `build.gradle(.kts)`, signing config, `Info.plist` and build settings, entitlements, `PrivacyInfo.xcprivacy`, `pubspec.yaml`, `app.json`/`eas.json`, `Fastfile`, CI workflows, `.gitignore`.
27
+ 4. Apply the checklist and the platform references. Every item is pass, fail with evidence, or unknown with who can confirm it.
28
+ 5. Deliver the report, notes or pipeline in the formats the skill defines.
29
+
30
+ ## Rules
31
+
32
+ - **Read-only by default.** Do not bump versions, create tags, push, run uploads, change store settings or run commands that publish (`fastlane` lanes, `eas submit`, `eas update`) unless the user explicitly asks. Propose the exact commands instead.
33
+ - **Never expose secrets.** Do not print keystore passwords, API keys, service account JSON or `.p8` contents, even when you find them. Report where a secret is committed and how to rotate it.
34
+ - **Store rules change every year.** Target API level, minimum Xcode and SDK versions, tester requirements and privacy obligations must be confirmed in the Play Console and Apple's current requirements; do not state deadlines or version numbers from memory as facts.
35
+ - **Evidence for release notes.** A change goes into the notes only when a commit, pull request or issue supports it. Unclear commits go under "needs description" for the team.
36
+ - Do not invent tool flags or action inputs. Check the installed CLI (`fastlane --version`, `eas --version`, `--help`) or the action's documented inputs before writing them into a pipeline.
37
+ - Answer in the user's language. Store notes are written in the languages the app's listing uses; say which translations are missing.
38
+
39
+ ## Output
40
+
41
+ Readiness audit:
42
+
43
+ ```markdown
44
+ ## Release readiness — <app> <version> (<build>)
45
+ Verdict: GO | GO WITH RISKS | NO-GO
46
+ Platforms: Android | iOS | both
47
+
48
+ ### Blockers
49
+ ### Risks
50
+ ### Unknown
51
+ ### Rollout plan
52
+ ### Commands
53
+ The exact commands to tag, build and upload once the blockers are fixed.
54
+ ```
55
+
56
+ Release notes: the store, changelog and QA sections from `references/store-copy.md`, with character counts for the store section of each platform.
57
+
58
+ Pipeline: the workflow files, the list of secrets to create (names only), and what the user must configure in the store consoles.
@@ -4,13 +4,13 @@ description: Use this agent for senior-level guidance across Flutter/Dart, Andro
4
4
  model: inherit
5
5
  color: blue
6
6
  tools: ["Read", "Grep", "Glob", "Bash"]
7
- intents: [debug, architecture, performance, test, security, release, migration, dependency, build, deployment, accessibility]
7
+ intents: [debug, architecture, performance, test, security, migration, dependency, build]
8
8
  stacks: ["*"]
9
9
  ---
10
10
 
11
11
  You are a **Staff Mobile Engineer** with long experience shipping production mobile apps at scale. You are an expert in Flutter/Dart, Android (Kotlin) and iOS (Swift), with deep knowledge of architecture, performance, CI/CD and native SDK integration.
12
12
 
13
- The reis-mobile router sends you work that no stack-specific specialist covers: native Android and iOS projects, debugging and build problems on any stack, migrations, releases and pipelines.
13
+ The reis-mobile router sends you work that no stack-specific specialist covers: native Android and iOS projects, debugging and build problems on any stack and migrations. Release readiness and pipelines go to `mobile-release-engineer`, accessibility audits to `mobile-accessibility-auditor`.
14
14
 
15
15
  ## When to invoke
16
16
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reis-mobile",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "AI agents and skills for mobile development — Flutter, Android, iOS and React Native — as a plugin for Claude Code and Codex: code review, debugging, tests, architecture, security and Firebase.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -0,0 +1,69 @@
1
+ ---
2
+ name: mobile-accessibility
3
+ description: Audits and fixes accessibility in mobile apps on every stack — screen reader labels, roles, states and reading order for TalkBack and VoiceOver, decorative elements, grouping, touch target size, text scaling and Dynamic Type, color contrast, focus in dialogs and announcements for dynamic content — with the exact APIs for Flutter (Semantics), native Android (Jetpack Compose semantics and Views), native iOS (SwiftUI and UIKit) and React Native, plus automated checks in tests. Use when building or reviewing UI for accessibility, preparing an accessibility audit, fixing TalkBack or VoiceOver issues, or when the user mentions a11y, WCAG, screen readers, font scaling or contrast, even if they only point at one screen.
4
+ intents: [accessibility]
5
+ stacks: ["*"]
6
+ ---
7
+
8
+ # Mobile Accessibility
9
+
10
+ One accessibility process for every mobile stack. This file holds what is true everywhere; each platform's APIs, tools and pitfalls live in its reference.
11
+
12
+ ## 1. Pick the reference
13
+
14
+ | Project | Read |
15
+ |---|---|
16
+ | Flutter | [references/flutter.md](references/flutter.md) |
17
+ | Native Android: Jetpack Compose or Views | [references/android.md](references/android.md) |
18
+ | Native iOS: SwiftUI or UIKit | [references/ios.md](references/ios.md) |
19
+ | React Native or Expo | [references/react-native.md](references/react-native.md) |
20
+
21
+ In Flutter and React Native apps, the native reference applies only to screens or components written natively (platform views, native modules with UI).
22
+
23
+ ## 2. What a screen reader user needs
24
+
25
+ Every interactive element exposes four things to the accessibility tree. Check them in this order:
26
+
27
+ 1. **Name.** What it is or does, in words: "Delete task", not "trash icon" or nothing. Text children usually provide it; icon-only controls, images that carry meaning and custom-drawn controls need an explicit label.
28
+ 2. **Role.** Button, checkbox, switch, slider, link, header, image. A tappable container without a role is read as plain text, and the user does not know it can be activated.
29
+ 3. **State and value.** Checked, selected, expanded, disabled, the slider's value. When the state changes, the new state is read.
30
+ 4. **Order and grouping.** Reading order follows the visual order. Related pieces (an avatar, a name and a timestamp in a list row) are one element, not three swipes.
31
+
32
+ And the tree must not contain noise: decorative images, dividers and duplicated text are hidden from assistive technology.
33
+
34
+ ## 3. Checks (all stacks)
35
+
36
+ **Perceivable**
37
+ - [ ] Icon-only buttons, image buttons and meaningful images have a label that describes the action or content.
38
+ - [ ] Decorative images and icons next to text that already says the same are excluded from the accessibility tree.
39
+ - [ ] Text contrast is at least 4.5:1, or 3:1 for large text; icons and control borders that carry meaning reach 3:1 (WCAG 1.4.3 and 1.4.11). Contrast cannot be proven from code alone when colors come from themes or images: say so and name the tool that measures it.
40
+ - [ ] Information is not conveyed by color only (an error shown only as a red border).
41
+
42
+ **Operable**
43
+ - [ ] Touch targets are at least 48×48 dp on Android and 44×44 pt on iOS, even when the visible icon is smaller. The layout extends the tappable area instead of the icon.
44
+ - [ ] Gestures (swipe to delete, long press, drag) have an alternative: a visible button or a custom accessibility action.
45
+ - [ ] Modal dialogs and bottom sheets move focus inside when they open and return it when they close; content behind them is not reachable.
46
+ - [ ] Timeouts, auto-advancing carousels and animations can be paused or respect the system's reduce motion setting.
47
+
48
+ **Understandable**
49
+ - [ ] Form fields have a persistent label, not only a placeholder; errors are announced and say how to fix the input.
50
+ - [ ] Screen titles and section headers are marked as headers, so users can jump between them.
51
+ - [ ] Dynamic changes the user did not trigger by moving focus — a snackbar, a validation error, "3 results", loading finished — are announced.
52
+
53
+ **Robust**
54
+ - [ ] Text scales with the system font size setting up to the largest accessibility sizes without truncation, overlap or clipped containers. Fixed heights on text containers are the usual cause.
55
+ - [ ] Custom-drawn controls (canvas, custom painters, gesture detectors) expose name, role, state and actions explicitly.
56
+ - [ ] The layout works in landscape and with the display size (screen zoom) setting increased.
57
+
58
+ ## 4. Evidence and severity
59
+
60
+ Point to `file:line` for every finding and give the fix in the platform's own API, not pseudocode.
61
+
62
+ - **Critical:** a screen reader user cannot complete the flow — an unlabeled primary action, a control that cannot be activated with the screen reader, focus trapped or lost, a custom control invisible to the tree.
63
+ - **High:** the flow is possible but misleading or very hard — wrong role, missing state, targets far below the minimum, text that clips at larger sizes, unannounced errors.
64
+ - **Medium:** noise and friction — decorative elements read aloud, rows split into many swipes, missing headers.
65
+ - **Low:** polish — hints, custom action names, reading order inside a well-labeled group.
66
+
67
+ ## 5. Verify on a device
68
+
69
+ Code review finds most problems; only assistive technology confirms the experience. Recommend the automated checks listed in the reference for the test suite, then a manual pass: TalkBack on Android and VoiceOver on iOS through the flow, the largest font size, and the platform's scanner (Accessibility Scanner on Android, Accessibility Inspector in Xcode). Say which findings came from code only.
@@ -0,0 +1,54 @@
1
+ # Accessibility on native Android
2
+
3
+ TalkBack reads the accessibility tree built from Compose semantics or from View properties. Material components fill in roles and states; custom composables with `Modifier.clickable`, `pointerInput` or `Canvas`, and custom Views, need them explicitly.
4
+
5
+ ## Jetpack Compose
6
+
7
+ | Need | API |
8
+ |---|---|
9
+ | Label an icon-only control | `Icon(Icons.Default.Delete, contentDescription = "Delete task")` inside `IconButton` |
10
+ | Hide a decorative icon or image | `contentDescription = null` |
11
+ | Role for a custom clickable | `Modifier.clickable(role = Role.Button, onClickLabel = "Open details") { }` |
12
+ | Toggle with role and state | `Modifier.toggleable(value = checked, role = Role.Checkbox, onValueChange = ...)` on the whole row |
13
+ | Merge a row into one element | `Modifier.semantics(mergeDescendants = true) {}` |
14
+ | Replace children's semantics | `Modifier.clearAndSetSemantics { contentDescription = "..." }` |
15
+ | Header | `Modifier.semantics { heading() }` |
16
+ | Custom state text | `Modifier.semantics { stateDescription = "Expanded" }` |
17
+ | Live updates | `Modifier.semantics { liveRegion = LiveRegionMode.Polite }` |
18
+ | Gesture alternative | `Modifier.semantics { customActions = listOf(CustomAccessibilityAction("Archive") { archive(); true }) }` |
19
+ | Dialog or pane title | `Modifier.semantics { paneTitle = "Filters" }` |
20
+ | Reading order | `Modifier.semantics { traversalIndex = 1f }` with `isTraversalGroup = true` on the container |
21
+
22
+ Pitfalls:
23
+
24
+ - A `Checkbox` or `Switch` next to a `Text` in a `Row` produces two elements and a checkbox without a name. Make the `Row` `toggleable` and pass `onCheckedChange = null` to the inner control.
25
+ - `Modifier.size(24.dp)` on an `IconButton` shrinks the touch target. Material components apply `Modifier.minimumInteractiveComponentSize()` (48 dp); keep the icon small, not the button.
26
+ - `Modifier.pointerInput` and `detectTapGestures` add no semantics at all; prefer `clickable`, or add `onClick` in `semantics`.
27
+ - Text sizes in `dp` instead of `sp` do not scale with the font size setting.
28
+
29
+ ## Views
30
+
31
+ | Need | API |
32
+ |---|---|
33
+ | Label | `android:contentDescription`, or `android:labelFor` on the `TextView` that labels an input |
34
+ | Hide decorative | `android:importantForAccessibility="no"` (or `contentDescription="@null"` on `ImageView`) |
35
+ | Group | `android:focusable="true"` on the container with `android:screenReaderFocusable="true"` |
36
+ | Header | `android:accessibilityHeading="true"` / `ViewCompat.setAccessibilityHeading` |
37
+ | Live updates | `android:accessibilityLiveRegion="polite"` |
38
+ | Role, state, actions | `ViewCompat.setAccessibilityDelegate` with `onInitializeAccessibilityNodeInfo`, or `ViewCompat.addAccessibilityAction` |
39
+ | Text field hint and error | `TextInputLayout` with `hint` and `error` |
40
+
41
+ `View.announceForAccessibility` is discouraged in recent Android releases in favor of live regions and pane titles; check the `compileSdk` and the method's documentation before recommending it.
42
+
43
+ ## Touch targets and text
44
+
45
+ - 48×48 dp minimum. For small icons use padding or `TouchDelegate` in Views.
46
+ - Text in `sp`; containers with `wrap_content` or minimum heights, not fixed heights.
47
+ - Test at the largest font size and display size in Settings, including the nonlinear font scaling of Android 14 and later.
48
+
49
+ ## Automated checks
50
+
51
+ - Espresso: `AccessibilityChecks.enable()` from `androidx.test.espresso:espresso-accessibility` runs the Accessibility Test Framework on every view action.
52
+ - Compose UI tests can assert semantics: `onNodeWithContentDescription("Delete task").assertHasClickAction()`, `assert(hasStateDescription("Expanded"))`.
53
+ - Lint flags missing `contentDescription` on `ImageView` and `ImageButton` (`ContentDescription`) and small touch targets in some layouts.
54
+ - Manual: TalkBack, the Accessibility Scanner app, and Settings → Accessibility → font and display size.
@@ -0,0 +1,59 @@
1
+ # Accessibility in Flutter
2
+
3
+ Flutter builds its own semantics tree from widgets and hands it to TalkBack and VoiceOver. Material and Cupertino widgets already fill in most of it; custom widgets built from `GestureDetector`, `InkWell` without children text, `CustomPaint` and images do not. For the broader Flutter guide (including localization), see `mobile-flutter` → `references/accessibility-i18n.md`.
4
+
5
+ ## Labels, roles and states
6
+
7
+ | Need | API |
8
+ |---|---|
9
+ | Label an icon button | `IconButton(tooltip: 'Delete task', ...)` — the tooltip becomes the label |
10
+ | Label a meaningful image | `Image.asset(..., semanticLabel: 'Profile photo of Ana')` |
11
+ | Hide a decorative image | `Image.asset(..., excludeFromSemantics: true)`, or wrap in `ExcludeSemantics` |
12
+ | Custom tappable widget | `Semantics(button: true, label: '...', onTap: ..., child: ...)` |
13
+ | Toggle state | `Semantics(toggled: value, ...)` or `checked:` for checkbox-like controls |
14
+ | Header | `Semantics(header: true, child: Text('Settings'))` |
15
+ | Replace children's semantics | `Semantics(label: '...', excludeSemantics: true, child: ...)` |
16
+ | Merge a row into one element | `MergeSemantics(child: ListTile(...))` |
17
+ | Extra actions for gestures | `Semantics(customSemanticsActions: {CustomSemanticsAction(label: 'Archive'): _archive})` |
18
+ | Text field label | `TextField(decoration: InputDecoration(labelText: 'Email', errorText: ...))` |
19
+
20
+ Pitfalls:
21
+
22
+ - `GestureDetector` and `InkWell` around an `Icon` expose a tap action but no label. Prefer `IconButton`, or add `Semantics`.
23
+ - `Icon(semanticLabel: ...)` labels the icon, but it is still not a button. Put the label on the tappable widget.
24
+ - `Semantics` without `container: true` may merge into an ancestor; set it when a widget must be its own node.
25
+ - `CustomPaint` is invisible to screen readers unless its painter implements `semanticsBuilder` or it is wrapped in `Semantics`.
26
+
27
+ ## Announcements and focus
28
+
29
+ - Mark text that changes on its own (a counter, a status) with `Semantics(liveRegion: true, ...)`.
30
+ - For one-off messages, `SemanticsService.announce(message, textDirection)` exists in `package:flutter/semantics.dart`; check its status in the Flutter version in `pubspec.lock`'s SDK constraint before recommending it, since the announcement APIs have been evolving for multi-view support.
31
+ - `SnackBar`s shown through `ScaffoldMessenger` are announced by the framework.
32
+ - Dialogs opened with `showDialog` and routes block the semantics of what is behind them; custom overlays built with `Stack` and `Positioned` do not — use `BlockSemantics` or a route.
33
+ - Control reading order with `Semantics(sortKey: OrdinalSortKey(n))` only when the visual order and the widget order genuinely differ.
34
+
35
+ ## Touch targets and text scaling
36
+
37
+ - Material's minimum is `kMinInteractiveDimension` (48). `ThemeData.materialTapTargetSize: MaterialTapTargetSize.shrinkWrap` shrinks it — flag it on interactive components.
38
+ - Small icons: keep the visual size and give the tappable widget `constraints: BoxConstraints(minWidth: 48, minHeight: 48)` or padding.
39
+ - Text scaling comes from `MediaQuery.textScalerOf(context)`. Flag code that overrides it for the whole app (`MediaQuery(data: ...copyWith(textScaler: TextScaler.noScaling))`) and fixed-height containers around text.
40
+ - Respect reduced motion with `MediaQuery.disableAnimationsOf(context)`.
41
+
42
+ ## Automated checks
43
+
44
+ Widget tests can assert the guidelines that `flutter_test` ships:
45
+
46
+ ```dart
47
+ testWidgets('login screen meets accessibility guidelines', (tester) async {
48
+ final handle = tester.ensureSemantics();
49
+ await tester.pumpWidget(const MaterialApp(home: LoginScreen()));
50
+
51
+ await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
52
+ await expectLater(tester, meetsGuideline(iOSTapTargetGuideline));
53
+ await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
54
+ await expectLater(tester, meetsGuideline(textContrastGuideline));
55
+ handle.dispose();
56
+ });
57
+ ```
58
+
59
+ Find semantics nodes in tests with `find.bySemanticsLabel('Delete task')`. On a device, use TalkBack and VoiceOver, and the `showSemanticsDebugger: true` flag of `MaterialApp` to see the tree.
@@ -0,0 +1,50 @@
1
+ # Accessibility on native iOS
2
+
3
+ VoiceOver reads accessibility elements built from SwiftUI modifiers or UIKit properties. Standard controls come with traits and values; custom views, gesture recognizers and drawn content need them explicitly. For the broader iOS UI guide, see `mobile-ios` → `references/ui-navigation.md`.
4
+
5
+ ## SwiftUI
6
+
7
+ | Need | API |
8
+ |---|---|
9
+ | Label an icon-only button | `Button { } label: { Image(systemName: "trash") }.accessibilityLabel("Delete task")`, or `Label("Delete task", systemImage: "trash").labelStyle(.iconOnly)` |
10
+ | Hide a decorative image | `Image(decorative: "divider")` or `.accessibilityHidden(true)` |
11
+ | Trait for a custom tappable view | Use `Button` instead of `.onTapGesture`; otherwise `.accessibilityAddTraits(.isButton)` |
12
+ | Header | `.accessibilityAddTraits(.isHeader)` |
13
+ | Group a row | `.accessibilityElement(children: .combine)` |
14
+ | Replace children | `.accessibilityElement(children: .ignore).accessibilityLabel("...")` |
15
+ | Value | `.accessibilityValue("3 of 5")` |
16
+ | Hint | `.accessibilityHint("Opens the order details")` — only when the label is not enough |
17
+ | Gesture alternative | `.accessibilityAction(named: "Archive") { archive() }` |
18
+ | Adjustable control | `.accessibilityAdjustableAction { direction in ... }` |
19
+ | Reading order | `.accessibilitySortPriority(_:)` inside a container |
20
+ | Announcement | `AccessibilityNotification.Announcement("Saved").post()` (iOS 17+) or `UIAccessibility.post(notification: .announcement, argument: "Saved")` |
21
+
22
+ Pitfalls:
23
+
24
+ - `.onTapGesture` on an `HStack` does not add the button trait, so VoiceOver does not say the row can be activated; `Button` with a custom style keeps the look and the trait.
25
+ - `.font(.system(size: 17))` does not scale with Dynamic Type. Use text styles (`.font(.body)`) and `@ScaledMetric` for custom sizes and spacing.
26
+ - `.frame(height:)` on text containers clips at accessibility sizes; use `minHeight`, and switch `HStack` to `VStack` with `@Environment(\.dynamicTypeSize)` when `dynamicTypeSize.isAccessibilitySize`.
27
+ - Respect `@Environment(\.accessibilityReduceMotion)` for animations.
28
+
29
+ ## UIKit
30
+
31
+ | Need | API |
32
+ |---|---|
33
+ | Element and label | `isAccessibilityElement = true`, `accessibilityLabel` |
34
+ | Hide decorative | `isAccessibilityElement = false` or `accessibilityElementsHidden = true` on a container |
35
+ | Traits | `accessibilityTraits = [.button]`, `.header`, `.selected`, `.notEnabled` |
36
+ | Group a cell | set `isAccessibilityElement = true` and a combined label on the cell |
37
+ | Custom actions | `accessibilityCustomActions = [UIAccessibilityCustomAction(name: "Archive") { _ in ... }]` |
38
+ | Modal | `accessibilityViewIsModal = true` on the presented view |
39
+ | Screen or layout change | `UIAccessibility.post(notification: .screenChanged, argument: firstElement)` / `.layoutChanged` |
40
+ | Dynamic Type | `UIFont.preferredFont(forTextStyle:)`, `UIFontMetrics`, `adjustsFontForContentSizeCategory = true` |
41
+
42
+ ## Touch targets
43
+
44
+ Apple's Human Interface Guidelines recommend at least 44×44 points. In SwiftUI, extend the hit area with `.frame(minWidth: 44, minHeight: 44)` and `.contentShape(Rectangle())`; in UIKit, override `point(inside:with:)` or enlarge the button's frame, not the image.
45
+
46
+ ## Automated checks
47
+
48
+ - XCUITest: `try app.performAccessibilityAudit()` (Xcode 15, iOS 17 and later) checks contrast, hit regions, element descriptions, Dynamic Type clipping and traits; pass a set of audit types or a handler to ignore known issues.
49
+ - Query by label in UI tests (`app.buttons["Delete task"]`): a test that finds elements by label breaks when a label disappears.
50
+ - Manual: VoiceOver on a device, Accessibility Inspector in Xcode (audit and inspection), and Environment Overrides in the debug bar for Dynamic Type, contrast and reduce motion.