feat(in-app): document the color scheme override and inbox accessibility labels - #406
Conversation
…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>
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
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>
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>
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>
| }); | ||
| ``` | ||
|
|
||
| `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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
|
|
||
| **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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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:
bellunset → announced as an unnamed button.bellWithUnreadCountunset → falls back tobell, so only the count goes unannounced (the badge is always hidden from screen readers, so the count is only ever spoken through this label).loadingIndicatorunset → the spinner stops being an accessibility element; VoiceOver skips it, TalkBack still reports Android's underlying progress role.emptyStateunset → 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.
|
|
||
| **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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
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>
## [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))
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.
inApp.colorSchemeplus the runtimeCustomerIO.inAppMessaging.setColorScheme(), for apps whose own appearance setting can disagree with the operating system.inApp.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 inapp.jsonwould 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:
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
peerDependenciespin and the test app both point at that published version; no unreleased commits remain.Verification
npm ci --dry-runclean in both the root package andtest-app; both lockfiles resolvecustomerio-reactnative6.12.0 from the npm registry with matching integrity hashes.🤖 Generated with Claude Code