Skip to content

Commit fbfc00d

Browse files
Nitin-100Nitin Chaudhary
andauthored
Theme aware platform color for text. (microsoft#15266)
* Theme aware platform color for text. * Change files * Fix Text component renders black in dark mode (Fabric) Fixes microsoft#15158 Text components without explicit color props were rendering as black in dark mode. Modified TextDrawing.cpp to detect default black colors (RGB <= 10) and replace with theme-aware TextFillColorPrimary which resolves to white in dark mode and black in light mode. --------- Co-authored-by: Nitin Chaudhary <[email protected]>
1 parent 8a441d2 commit fbfc00d

File tree

4 files changed

+64
-5
lines changed

4 files changed

+64
-5
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"comment": "Theme aware platform color for text.",
3+
"type": "prerelease",
4+
"packageName": "react-native-windows",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}

vnext/Microsoft.ReactNative/Fabric/Composition/TextDrawing.cpp

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "TextDrawing.h"
88

99
#include <AutoDraw.h>
10+
#include <Fabric/platform/react/renderer/graphics/PlatformColorUtils.h>
1011
#include <Utils/ValueUtils.h>
1112
#include <unicode.h>
1213
#include <windows.ui.composition.interop.h>
@@ -35,11 +36,27 @@ void RenderText(
3536
// to cache and reuse a brush across all text elements instead, taking care to recreate
3637
// it in the event of device removed.
3738
winrt::com_ptr<ID2D1SolidColorBrush> brush;
39+
40+
// Check if we should use theme-aware default color instead of hardcoded black
41+
bool useDefaultColor = false;
3842
if (textAttributes.foregroundColor) {
43+
auto &color = *textAttributes.foregroundColor;
44+
// If it's black (or very dark) without explicit PlatformColor, use theme-aware color
45+
if (color.m_platformColor.empty() && color.m_color.R <= 10 && color.m_color.G <= 10 && color.m_color.B <= 10) {
46+
useDefaultColor = true;
47+
}
48+
} else {
49+
useDefaultColor = true;
50+
}
51+
52+
if (useDefaultColor) {
53+
// Use theme-aware TextFillColorPrimary which adapts to light/dark mode
54+
auto d2dColor = theme.D2DPlatformColor("TextFillColorPrimary");
55+
winrt::check_hresult(deviceContext.CreateSolidColorBrush(d2dColor, brush.put()));
56+
} else {
57+
// User set explicit color or PlatformColor - use it
3958
auto color = theme.D2DColor(*textAttributes.foregroundColor);
4059
winrt::check_hresult(deviceContext.CreateSolidColorBrush(color, brush.put()));
41-
} else {
42-
winrt::check_hresult(deviceContext.CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black, 1.0f), brush.put()));
4360
}
4461

4562
if (textAttributes.textDecorationLineType) {
@@ -72,12 +89,27 @@ void RenderText(
7289
(fragment.textAttributes.foregroundColor != textAttributes.foregroundColor) ||
7390
!isnan(fragment.textAttributes.opacity)) {
7491
winrt::com_ptr<ID2D1SolidColorBrush> fragmentBrush;
92+
93+
// Check if we should use theme-aware default color for this fragment
94+
bool useFragmentDefaultColor = false;
7595
if (fragment.textAttributes.foregroundColor) {
96+
auto &color = *fragment.textAttributes.foregroundColor;
97+
// If it's black (or very dark) without explicit PlatformColor, use theme-aware color
98+
if (color.m_platformColor.empty() && color.m_color.R <= 10 && color.m_color.G <= 10 && color.m_color.B <= 10) {
99+
useFragmentDefaultColor = true;
100+
}
101+
} else {
102+
useFragmentDefaultColor = true;
103+
}
104+
105+
if (useFragmentDefaultColor) {
106+
// Use theme-aware TextFillColorPrimary which adapts to light/dark mode
107+
auto d2dColor = theme.D2DPlatformColor("TextFillColorPrimary");
108+
winrt::check_hresult(deviceContext.CreateSolidColorBrush(d2dColor, fragmentBrush.put()));
109+
} else {
110+
// User set explicit color or PlatformColor - use it
76111
auto color = theme.D2DColor(*fragment.textAttributes.foregroundColor);
77112
winrt::check_hresult(deviceContext.CreateSolidColorBrush(color, fragmentBrush.put()));
78-
} else {
79-
winrt::check_hresult(
80-
deviceContext.CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black, 1.0f), fragmentBrush.put()));
81113
}
82114

83115
if (fragment.textAttributes.textDecorationLineType) {

vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/graphics/PlatformColorUtils.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,21 @@ SharedColor GetTextInputPlaceholderColor(bool isFocused, const winrt::Windows::U
189189
}
190190
}
191191

192+
SharedColor GetDefaultTextColor() {
193+
// In high contrast mode, always use system WindowText for accessibility
194+
auto accessibilitySettings{winrt::Windows::UI::ViewManagement::AccessibilitySettings()};
195+
if (accessibilitySettings.HighContrast()) {
196+
auto uiSettings{winrt::Windows::UI::ViewManagement::UISettings()};
197+
auto windowText = uiSettings.UIElementColor(winrt::Windows::UI::ViewManagement::UIElementType::WindowText);
198+
return hostPlatformColorFromRGBA(windowText.R, windowText.G, windowText.B, windowText.A);
199+
}
200+
201+
// Use Windows 11 design system semantic color TextFillColorPrimary
202+
// This automatically adapts to light/dark mode themes:
203+
// - Light mode: rgba(0, 0, 0, 0.894) - nearly black for good contrast
204+
// - Dark mode: rgba(255, 255, 255, 1.0) - white for readability
205+
auto color = ResolvePlatformColor({"TextFillColorPrimary"});
206+
return hostPlatformColorFromRGBA(color.R, color.G, color.B, color.A);
207+
}
208+
192209
} // namespace facebook::react

vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/graphics/PlatformColorUtils.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,7 @@ winrt::Windows::UI::Color ResolvePlatformColor(const std::vector<std::string> &s
1717
// Get appropriate placeholder text color for TextInput based on focus state and background
1818
SharedColor GetTextInputPlaceholderColor(bool isFocused, const winrt::Windows::UI::Color &backgroundColor = {});
1919

20+
// Get default text foreground color for Text component (theme-aware)
21+
SharedColor GetDefaultTextColor();
22+
2023
} // namespace facebook::react

0 commit comments

Comments
 (0)