Skip to content

fix(0.81, fabric): textInput text color not adapting to appearance changes#2914

Merged
Saadnajmi merged 5 commits intomicrosoft:0.81-stablefrom
Saadnajmi:0.81/fix-textinput-dark-mode-new-arch
Apr 11, 2026
Merged

fix(0.81, fabric): textInput text color not adapting to appearance changes#2914
Saadnajmi merged 5 commits intomicrosoft:0.81-stablefrom
Saadnajmi:0.81/fix-textinput-dark-mode-new-arch

Conversation

@Saadnajmi
Copy link
Copy Markdown
Collaborator

Summary

Backport of the changes from branch fix-textinput-dark-mode-new-arch to 0.81-stable.

See #2913 for full details.

Test Plan

Same as the original PR.

…n new architecture

On the new architecture (Fabric), TextInput text color was correct on initial
mount but didn't update when the system appearance changed (light ↔ dark mode).
Two root causes:

1. RCTNSTextAttributesFromTextAttributes skipped setting NSForegroundColorAttributeName
   when no explicit color/opacity was set. On macOS, NSAttributedString defaults to
   black (unlike iOS), making text invisible in dark mode.

2. RCTTextInputComponentView had no appearance change handler on macOS
   (viewDidChangeEffectiveAppearance), so defaultTextAttributes were never refreshed.
   Additionally, the C++ color pipeline resolves dynamic colors (like labelColor)
   to static values at creation time, so simply re-calling the attribute builder
   returns stale colors. The fix detects the default foreground color and replaces
   it with a fresh dynamic NSColor.labelColor, then re-applies the attributed text
   while suppressing state reconciliation to prevent React from overwriting the
   update with cached shadow tree values.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Saadnajmi Saadnajmi requested a review from a team as a code owner April 10, 2026 22:51
@Saadnajmi Saadnajmi changed the title fix(0.81, macOS): TextInput text color not adapting to appearance changes on new arch fix(0.81, fabric): textInput text color not adapting to appearance changes Apr 10, 2026
Saadnajmi and others added 3 commits April 10, 2026 16:15
Remove unnecessary [NSColor labelColor] override - the C++ color
pipeline already preserves dynamic NSColor objects through
wrapManagedObject/unwrapManagedObject, so re-applying text attributes
from props is sufficient for appearance adaptation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…andling

On macOS, calling [NSColor colorWithAlphaComponent:] on dynamic system
colors (like NSColor.labelColor) converts them to static resolved colors,
preventing them from adapting to appearance changes (light/dark mode).

Since getEffectiveTextAttributes() always sets opacity=1 for TextInput,
the colorWithAlphaComponent: call was always triggered but was effectively
a no-op (multiplying alpha by 1.0). Skip it when opacity is exactly 1.0
to preserve the dynamic nature of system colors.

This is the root cause fix for TextInput text not adapting to appearance
changes — the previous viewDidChangeEffectiveAppearance handler was
re-applying the same pre-resolved static color on each appearance change.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Restructure the opacity != 1.0f guard in RCTAttributedTextUtils.mm to
use #if TARGET_OS_OSX / #else / #endif so the upstream if-condition is
preserved verbatim in the #else branch. Restore inline code in
traitCollectionDidChange: to match upstream, keeping macOS additions
purely additive.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
getEffectiveTextAttributes() always sets foregroundColor (from
defaultTextAttributes()) and opacity = 1, so the condition
`textAttributes.foregroundColor || !isnan(textAttributes.opacity)` is
always true. The #if TARGET_OS_OSX else branch could never execute.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Saadnajmi Saadnajmi merged commit 764c692 into microsoft:0.81-stable Apr 11, 2026
13 of 14 checks passed
@Saadnajmi Saadnajmi deleted the 0.81/fix-textinput-dark-mode-new-arch branch April 11, 2026 02:12
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.

2 participants