Skip to content

Commit 4b9d9dd

Browse files
birkirfacebook-github-bot
authored andcommitted
Accessible colors for DynamicColorIOS (#31651)
Summary: Allow you to harvest the `UIAccessibilityContrastHigh` trait from iOS to show accessible colors when high contrast mode is enabled. ```jsx // usage PlatformColorIOS({ light: '#eeeeee', dark: '#333333', highContrastLight: '#ffffff', highContrastDark: '#000000', }); // { // "dynamic": { // "light": "#eeeeee", // "dark": "#333333", // "highContrastLight": "#ffffff", // "highContrastDark": "#000000", // } // } ``` This is how apple's own dynamic system colors work under the hood (https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/color/#dynamic-system-colors) --- The react native docs mention that more keys may become available in the future, which this PR is adding: > In the future, more keys might become available for different user preferences, like high contrast. https://reactnative.dev/docs/dynamiccolorios ## Changelog <!-- Help reviewers and the release process by writing your own changelog entry. For an example, see: https://github.com/facebook/react-native/wiki/Changelog --> [iOS] [Added] - High contrast dynamic color options for dark and light mode. Pull Request resolved: #31651 Test Plan: Added unit tests for `normalizeColor` to pass the high contrast colors downstream to RCTConvert Reviewed By: lunaleaps Differential Revision: D28922536 Pulled By: p-sun fbshipit-source-id: f81417f003c3adefac50e994e62b9be14ffa91a1
1 parent 4d40b53 commit 4b9d9dd

File tree

6 files changed

+65
-7
lines changed

6 files changed

+65
-7
lines changed

Libraries/StyleSheet/PlatformColorValueTypes.ios.js

+16-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ export opaque type NativeColorValue = {
1616
dynamic?: {
1717
light: ?(ColorValue | ProcessedColorValue),
1818
dark: ?(ColorValue | ProcessedColorValue),
19+
highContrastLight?: ?(ColorValue | ProcessedColorValue),
20+
highContrastDark?: ?(ColorValue | ProcessedColorValue),
1921
},
2022
};
2123

@@ -26,12 +28,21 @@ export const PlatformColor = (...names: Array<string>): ColorValue => {
2628
export type DynamicColorIOSTuplePrivate = {
2729
light: ColorValue,
2830
dark: ColorValue,
31+
highContrastLight?: ColorValue,
32+
highContrastDark?: ColorValue,
2933
};
3034

3135
export const DynamicColorIOSPrivate = (
3236
tuple: DynamicColorIOSTuplePrivate,
3337
): ColorValue => {
34-
return {dynamic: {light: tuple.light, dark: tuple.dark}};
38+
return {
39+
dynamic: {
40+
light: tuple.light,
41+
dark: tuple.dark,
42+
highContrastLight: tuple.highContrastLight,
43+
highContrastDark: tuple.highContrastDark,
44+
},
45+
};
3546
};
3647

3748
export const normalizeColorObject = (
@@ -49,6 +60,8 @@ export const normalizeColorObject = (
4960
dynamic: {
5061
light: normalizeColor(dynamic.light),
5162
dark: normalizeColor(dynamic.dark),
63+
highContrastLight: normalizeColor(dynamic.highContrastLight),
64+
highContrastDark: normalizeColor(dynamic.highContrastDark),
5265
},
5366
};
5467
return dynamicColor;
@@ -67,6 +80,8 @@ export const processColorObject = (
6780
dynamic: {
6881
light: processColor(dynamic.light),
6982
dark: processColor(dynamic.dark),
83+
highContrastLight: processColor(dynamic.highContrastLight),
84+
highContrastDark: processColor(dynamic.highContrastDark),
7085
},
7186
};
7287
return dynamicColor;

Libraries/StyleSheet/PlatformColorValueTypesIOS.ios.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,15 @@ import {DynamicColorIOSPrivate} from './PlatformColorValueTypes';
1414
export type DynamicColorIOSTuple = {
1515
light: ColorValue,
1616
dark: ColorValue,
17+
highContrastLight?: ColorValue,
18+
highContrastDark?: ColorValue,
1719
};
1820

1921
export const DynamicColorIOS = (tuple: DynamicColorIOSTuple): ColorValue => {
20-
return DynamicColorIOSPrivate({light: tuple.light, dark: tuple.dark});
22+
return DynamicColorIOSPrivate({
23+
light: tuple.light,
24+
dark: tuple.dark,
25+
highContrastLight: tuple.highContrastLight,
26+
highContrastDark: tuple.highContrastDark,
27+
});
2128
};

Libraries/StyleSheet/PlatformColorValueTypesIOS.js

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import type {ColorValue} from './StyleSheet';
1313
export type DynamicColorIOSTuple = {
1414
light: ColorValue,
1515
dark: ColorValue,
16+
highContrastLight?: ColorValue,
17+
highContrastDark?: ColorValue,
1618
};
1719

1820
export const DynamicColorIOS = (tuple: DynamicColorIOSTuple): ColorValue => {

Libraries/StyleSheet/__tests__/normalizeColor-test.js

+19
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,25 @@ describe('iOS', () => {
4343
expect(normalizedColor).toEqual(expectedColor);
4444
});
4545

46+
it('should normalize iOS Dynamic colors with accessible colors', () => {
47+
const color = DynamicColorIOS({
48+
light: 'black',
49+
dark: 'white',
50+
highContrastLight: 'red',
51+
highContrastDark: 'blue',
52+
});
53+
const normalizedColor = normalizeColor(color);
54+
const expectedColor = {
55+
dynamic: {
56+
light: 'black',
57+
dark: 'white',
58+
highContrastLight: 'red',
59+
highContrastDark: 'blue',
60+
},
61+
};
62+
expect(normalizedColor).toEqual(expectedColor);
63+
});
64+
4665
it('should normalize iOS Dynamic colors with PlatformColor colors', () => {
4766
const color = DynamicColorIOS({
4867
light: PlatformColor('systemBlackColor'),

React/Base/RCTConvert.m

+20-4
Original file line numberDiff line numberDiff line change
@@ -879,13 +879,29 @@ + (UIColor *)UIColor:(id)json
879879
UIColor *lightColor = [RCTConvert UIColor:light];
880880
id dark = [appearances objectForKey:@"dark"];
881881
UIColor *darkColor = [RCTConvert UIColor:dark];
882+
id highContrastLight = [appearances objectForKey:@"highContrastLight"];
883+
UIColor *highContrastLightColor = [RCTConvert UIColor:highContrastLight];
884+
id highContrastDark = [appearances objectForKey:@"highContrastDark"];
885+
UIColor *highContrastDarkColor = [RCTConvert UIColor:highContrastDark];
882886
if (lightColor != nil && darkColor != nil) {
883887
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
884888
if (@available(iOS 13.0, *)) {
885-
UIColor *color =
886-
[UIColor colorWithDynamicProvider:^UIColor *_Nonnull(UITraitCollection *_Nonnull collection) {
887-
return collection.userInterfaceStyle == UIUserInterfaceStyleDark ? darkColor : lightColor;
888-
}];
889+
UIColor *color = [UIColor colorWithDynamicProvider:^UIColor *_Nonnull(
890+
UITraitCollection *_Nonnull collection) {
891+
if (collection.userInterfaceStyle == UIUserInterfaceStyleDark) {
892+
if (collection.accessibilityContrast == UIAccessibilityContrastHigh && highContrastDarkColor != nil) {
893+
return highContrastDarkColor;
894+
} else {
895+
return darkColor;
896+
}
897+
} else {
898+
if (collection.accessibilityContrast == UIAccessibilityContrastHigh && highContrastLightColor != nil) {
899+
return highContrastLightColor;
900+
} else {
901+
return lightColor;
902+
}
903+
}
904+
}];
889905
return color;
890906
} else {
891907
#endif

packages/eslint-plugin-react-native-community/platform-colors.js

-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ module.exports = {
6060
const properties = args[0].properties;
6161
if (
6262
!(
63-
properties.length === 2 &&
6463
properties[0].type === 'Property' &&
6564
properties[0].key.name === 'light' &&
6665
properties[1].type === 'Property' &&

0 commit comments

Comments
 (0)