Skip to content

feat(in-app): document the color scheme override and inbox accessibility labels - #406

Merged
mahmoud-elmorabea merged 10 commits into
mainfrom
mbl-2366-inbox-a11y-labels
Sep 18, 2026
Merged

mahmoud-elmorabea merged 10 commits into
mainfrom
mbl-2366-inbox-a11y-labels

Conversation

@mahmoud-elmorabea

@mahmoud-elmorabea mahmoud-elmorabea commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Documents the two JavaScript-config features the React Native wrapper has added, and pins the plugin to the release that carries both. No plugin code changes — both ride the existing JavaScript config path.

  • In-app color schemeinApp.colorScheme plus the runtime CustomerIO.inAppMessaging.setColorScheme(), for apps whose own appearance setting can disagree with the operating system.
  • Visual inbox accessibility labelsinApp.notificationInboxAccessibilityLabels, the one place the inbox still needs app-supplied strings.

Fixes MBL-2366
Fixes MBL-2479

Why no plugin options

With native auto-initialization the SDK starts before JavaScript loads, so a later CustomerIO.initialize() is a no-op. Exposing either feature in app.json would bake the values in at prebuild: the labels would ship in a single locale, and a color scheme fixed at build time cannot follow the app's own appearance toggle. So the limitation is documented rather than half-supported.

The caveat is not identical for the two, and the README says so:

  • Labels — unreachable under auto-initialization. An unset label leaves that element unlabeled rather than falling back to English, so an auto-initializing app simply gets no inbox labels.
  • Color scheme — only the config option is lost. setColorScheme() reaches the already-initialized SDK and applies immediately, re-theming messages that are already on screen, so an auto-initializing app can still pin a variant by calling it once after startup.

Dependency

React Native SDK 6.12.0 carries both APIs — accessibility labels from 6.11.0 (customerio/customerio-reactnative#658) and the color scheme from 6.12.0 (customerio/customerio-reactnative#660), both released. The root peerDependencies pin and the test app both point at that published version; no unreleased commits remain.

Verification

  • npm ci --dry-run clean in both the root package and test-app; both lockfiles resolve customerio-reactnative 6.12.0 from the npm registry with matching integrity hashes.
  • The color scheme override was exercised on an Android emulator and an iOS simulator through the React Native sample app: the configured scheme reaches the native renderer at initialization, the runtime setter changes it live, and an invalid value is reported without disturbing the scheme already in effect.

🤖 Generated with Claude Code

…t path

The native SDKs stopped shipping hardcoded English labels for the visual
notification inbox, and the React Native SDK now lets apps supply their own.
No plugin change is needed: the labels ride the existing JavaScript config.

They are deliberately not exposed as plugin options. With auto-initialization
the SDK starts before JavaScript loads, and app.json values are baked in at
prebuild, so a plugin option would ship a capability that only works for one
locale. Documented rather than half-supported.

test-app is pinned to the React Native branch so this can be built before that
SDK change is released. Revert the pin to a published version before merge.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@socket-security

socket-security Bot commented Sep 11, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatednpm/​customerio-reactnative@​6.9.0 ⏵ 6.12.093 +110010096 +1100

View full report

mahmoud-elmorabea and others added 3 commits September 12, 2026 01:03
The peer dependency is an exact pin, so the branch build (6.10.0) could not
resolve against 6.9.0 and the plugin unit tests failed to install.

Both this and the test-app pin must move to the published React Native version
before merge.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
npm ci refuses to install when package.json and package-lock.json disagree,
so the peer bump alone failed the scenario suite. Regenerated with
--package-lock-only, which also corrected a pre-existing drift where the lock
file still recorded plugin version 3.7.1 against package.json's 3.9.1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The lock file pinned the commit the branch had when this PR was opened, so the
compatibility matrix was building against a React Native revision that predates
the review fixes. Still temporary — revert to a published version before merge.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mahmoud-elmorabea mahmoud-elmorabea added the ci:full Run the full PR CI lane label Sep 13, 2026
mahmoud-elmorabea and others added 3 commits September 13, 2026 16:23
The empty state is driven entirely by SDK data, so seeing the dimmed bell
previously meant finding a profile that happened to have no messages.
Identifying a fresh random user reaches it through the real SDK path; tap the
bell afterwards, since this screen renders the panel rather than the list.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Drops the debug identify button from the test app: testers reach the empty
state by logging in with a profile that has no inbox messages.

The test app pinned the React Native SDK to a branch name, so the lockfile
could resolve to an older commit than the one under review. Pinned to the exact
commit instead while the dependency is still unreleased. Both this pin and the
peer version go back to the published release before merge.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mahmoud-elmorabea
mahmoud-elmorabea marked this pull request as ready for review September 15, 2026 12:39
@mahmoud-elmorabea
mahmoud-elmorabea requested a review from a team as a code owner September 15, 2026 12:39
@mahmoud-elmorabea mahmoud-elmorabea changed the title docs(inbox): document visual inbox accessibility labels on the JS init path feat(inbox): document visual inbox accessibility labels on the JS init path Sep 15, 2026
mahmoud-elmorabea and others added 2 commits September 16, 2026 01:54
The test app was pinned to an unreleased commit and the peer dependency to
6.10.0, which predates this API. 6.11.0 ships it, so both now point at the
published release and the lockfiles resolve from the registry rather than git.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…12.0

Widens this PR to cover both wrapper releases that added a JavaScript-config
feature: the visual inbox accessibility labels in 6.11.0 and the in-app color
scheme override in 6.12.0. The pin moves to 6.12.0, which carries both.

Still no plugin options. Both features ride the existing JavaScript config
path, and values in app.json are baked in at prebuild — which would ship the
labels in a single locale and a color scheme that cannot follow the app's own
appearance setting.

The auto-initialization caveat differs between the two, so the README says so
rather than repeating the labels' wording. Native auto-initialization defeats
`inApp.colorScheme`, because the SDK starts before JavaScript loads, but not
`setColorScheme()`, which reaches the already-initialized SDK and applies
immediately — so an auto-initializing app can still pin a variant, whereas it
simply gets no inbox labels.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mahmoud-elmorabea mahmoud-elmorabea changed the title feat(inbox): document visual inbox accessibility labels on the JS init path feat(in-app): document the color scheme override and inbox accessibility labels Sep 17, 2026
Comment thread README.md Outdated
});
```

`bellWithUnreadCount` is a template: `{count}` is replaced with the number of unread messages when the bell is announced. Spell it any other way and nothing is substituted, so the screen reader reads the text verbatim, braces included, and never says the count — the SDK warns in the JavaScript console when it spots that.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we say this warning is limited to development builds? warnIf is gated by __DEV__, so a release build with a malformed template will not write anything to the console.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 87979e7. Confirmed — warnIf is if (__DEV__ && condition) in src/utils/param-validation.ts, so the warning is compiled out of release builds. The sentence now says it is a development-build diagnostic and that a malformed template ships silently otherwise.

Comment thread README.md Outdated

**These labels require JavaScript initialization.** With native auto-initialization (a `config` block in the plugin options), the SDK is initialized before JavaScript loads, so a later `CustomerIO.initialize()` call is a no-op and the labels never reach the SDK. They are deliberately not exposed as plugin options: values in `app.json` are baked in at prebuild, which would ship a capability that only works for one locale.

An unset label leaves that element unlabeled rather than falling back to English, so an auto-initializing app simply gets no inbox labels.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we describe the per-label fallbacks here? bellWithUnreadCount falls back to bell, and an unset loading label still leaves Android’s progress role for TalkBack while VoiceOver skips it. The current wording makes partial configuration sound like every element becomes unlabeled.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 87979e7 — you were right that the old wording made partial configuration sound worse than it is. Replaced it with a per-label table taken from the two SDKs' own declarations:

  • bell unset → announced as an unnamed button.
  • bellWithUnreadCount unset → falls back to bell, so only the count goes unannounced (the badge is always hidden from screen readers, so the count is only ever spoken through this label).
  • loadingIndicator unset → the spinner stops being an accessibility element; VoiceOver skips it, TalkBack still reports Android's underlying progress role.
  • emptyState unset → the icon is hidden from assistive technologies.

Kept the "no English fallback" point, which is still accurate, and dropped the implication that every element becomes unlabeled.

Comment thread README.md Outdated

**Unlike the inbox accessibility labels above, native auto-initialization only affects the config
option, not the setter.** With a `config` block in the plugin options the SDK starts before
JavaScript loads, so `inApp.colorScheme` never arrives — but `setColorScheme()` reaches the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we qualify this with config.siteId? The plugin only initializes MessagingInApp when siteId is present. Without it, both native setters log that in-app messaging is unavailable and leave the scheme unchanged, so this does not work for every auto-initialized configuration.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 87979e7 — good catch, and this was the one that actually overstated behaviour.

Confirmed in both initializer templates: iOS CustomerIOSDKInitializer.swift guards with if let siteId = siteId { and Android CustomerIOSDKInitializer.kt with if (!(siteId.isNullOrBlank())) {, so the in-app messaging module is only added when a siteId is present. Without one there is no module for either path to reach — the config option is dropped as before, and the setter logs that in-app messaging is unavailable and leaves the scheme unchanged.

The claim is now qualified with config.siteId in the bold sentence, plus a following paragraph explaining why.

@Shahroz16 Shahroz16 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dependency bump and release path look good. The remaining comments are documentation-only and can be addressed before merge.

The `{count}` placeholder warning is a development-build diagnostic: `warnIf`
is gated on `__DEV__`, so a release build with a malformed template writes
nothing to the console. Said so, rather than implying the SDK always reports
it.

Replaced "an unset label leaves that element unlabeled" with the actual
per-element behaviour, which differs by label: `bellWithUnreadCount` falls back
to `bell`, so only the count goes unannounced, while unset loading and
empty-state labels remove those elements from assistive technologies instead of
leaving them unlabeled — and TalkBack still reports Android's progress role for
the spinner. "No English fallback" was right; "everything goes unlabeled" was
not.

Qualified the color scheme setter's auto-initialization behaviour with
`config.siteId`. Both plugin initializer templates add the in-app messaging
module only when a siteId is present, so without one there is no module for
either path to reach and the setter logs that in-app messaging is unavailable.
The previous wording claimed an auto-initializing app can always pin a variant.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mahmoud-elmorabea
mahmoud-elmorabea merged commit 64a16af into main Sep 18, 2026
23 checks passed
@mahmoud-elmorabea
mahmoud-elmorabea deleted the mbl-2366-inbox-a11y-labels branch September 18, 2026 09:44
cio-mobile-release Bot pushed a commit that referenced this pull request Sep 18, 2026
## [3.10.0](3.9.1...3.10.0) (2026-09-18)

### Features

* **in-app:** document the color scheme override and inbox accessibility labels ([#406](#406)) ([64a16af](64a16af))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:full Run the full PR CI lane

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants