mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
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: https://github.com/facebook/react-native/pull/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
This commit is contained in:
committed by
Lorenzo Sciandra
parent
6b5b72c4c3
commit
6287c447d6
@@ -16,6 +16,8 @@ export opaque type NativeColorValue = {
|
||||
dynamic?: {
|
||||
light: ?(ColorValue | ProcessedColorValue),
|
||||
dark: ?(ColorValue | ProcessedColorValue),
|
||||
highContrastLight?: ?(ColorValue | ProcessedColorValue),
|
||||
highContrastDark?: ?(ColorValue | ProcessedColorValue),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -26,12 +28,21 @@ export const PlatformColor = (...names: Array<string>): ColorValue => {
|
||||
export type DynamicColorIOSTuplePrivate = {
|
||||
light: ColorValue,
|
||||
dark: ColorValue,
|
||||
highContrastLight?: ColorValue,
|
||||
highContrastDark?: ColorValue,
|
||||
};
|
||||
|
||||
export const DynamicColorIOSPrivate = (
|
||||
tuple: DynamicColorIOSTuplePrivate,
|
||||
): ColorValue => {
|
||||
return {dynamic: {light: tuple.light, dark: tuple.dark}};
|
||||
return {
|
||||
dynamic: {
|
||||
light: tuple.light,
|
||||
dark: tuple.dark,
|
||||
highContrastLight: tuple.highContrastLight,
|
||||
highContrastDark: tuple.highContrastDark,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const normalizeColorObject = (
|
||||
@@ -49,6 +60,8 @@ export const normalizeColorObject = (
|
||||
dynamic: {
|
||||
light: normalizeColor(dynamic.light),
|
||||
dark: normalizeColor(dynamic.dark),
|
||||
highContrastLight: normalizeColor(dynamic.highContrastLight),
|
||||
highContrastDark: normalizeColor(dynamic.highContrastDark),
|
||||
},
|
||||
};
|
||||
return dynamicColor;
|
||||
@@ -67,6 +80,8 @@ export const processColorObject = (
|
||||
dynamic: {
|
||||
light: processColor(dynamic.light),
|
||||
dark: processColor(dynamic.dark),
|
||||
highContrastLight: processColor(dynamic.highContrastLight),
|
||||
highContrastDark: processColor(dynamic.highContrastDark),
|
||||
},
|
||||
};
|
||||
return dynamicColor;
|
||||
|
||||
@@ -14,8 +14,15 @@ import {DynamicColorIOSPrivate} from './PlatformColorValueTypes';
|
||||
export type DynamicColorIOSTuple = {
|
||||
light: ColorValue,
|
||||
dark: ColorValue,
|
||||
highContrastLight?: ColorValue,
|
||||
highContrastDark?: ColorValue,
|
||||
};
|
||||
|
||||
export const DynamicColorIOS = (tuple: DynamicColorIOSTuple): ColorValue => {
|
||||
return DynamicColorIOSPrivate({light: tuple.light, dark: tuple.dark});
|
||||
return DynamicColorIOSPrivate({
|
||||
light: tuple.light,
|
||||
dark: tuple.dark,
|
||||
highContrastLight: tuple.highContrastLight,
|
||||
highContrastDark: tuple.highContrastDark,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -13,6 +13,8 @@ import type {ColorValue} from './StyleSheet';
|
||||
export type DynamicColorIOSTuple = {
|
||||
light: ColorValue,
|
||||
dark: ColorValue,
|
||||
highContrastLight?: ColorValue,
|
||||
highContrastDark?: ColorValue,
|
||||
};
|
||||
|
||||
export const DynamicColorIOS = (tuple: DynamicColorIOSTuple): ColorValue => {
|
||||
|
||||
@@ -43,6 +43,25 @@ describe('iOS', () => {
|
||||
expect(normalizedColor).toEqual(expectedColor);
|
||||
});
|
||||
|
||||
it('should normalize iOS Dynamic colors with accessible colors', () => {
|
||||
const color = DynamicColorIOS({
|
||||
light: 'black',
|
||||
dark: 'white',
|
||||
highContrastLight: 'red',
|
||||
highContrastDark: 'blue',
|
||||
});
|
||||
const normalizedColor = normalizeColor(color);
|
||||
const expectedColor = {
|
||||
dynamic: {
|
||||
light: 'black',
|
||||
dark: 'white',
|
||||
highContrastLight: 'red',
|
||||
highContrastDark: 'blue',
|
||||
},
|
||||
};
|
||||
expect(normalizedColor).toEqual(expectedColor);
|
||||
});
|
||||
|
||||
it('should normalize iOS Dynamic colors with PlatformColor colors', () => {
|
||||
const color = DynamicColorIOS({
|
||||
light: PlatformColor('systemBlackColor'),
|
||||
|
||||
+20
-4
@@ -879,13 +879,29 @@ static NSString *RCTSemanticColorNames()
|
||||
UIColor *lightColor = [RCTConvert UIColor:light];
|
||||
id dark = [appearances objectForKey:@"dark"];
|
||||
UIColor *darkColor = [RCTConvert UIColor:dark];
|
||||
id highContrastLight = [appearances objectForKey:@"highContrastLight"];
|
||||
UIColor *highContrastLightColor = [RCTConvert UIColor:highContrastLight];
|
||||
id highContrastDark = [appearances objectForKey:@"highContrastDark"];
|
||||
UIColor *highContrastDarkColor = [RCTConvert UIColor:highContrastDark];
|
||||
if (lightColor != nil && darkColor != nil) {
|
||||
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
|
||||
if (@available(iOS 13.0, *)) {
|
||||
UIColor *color =
|
||||
[UIColor colorWithDynamicProvider:^UIColor *_Nonnull(UITraitCollection *_Nonnull collection) {
|
||||
return collection.userInterfaceStyle == UIUserInterfaceStyleDark ? darkColor : lightColor;
|
||||
}];
|
||||
UIColor *color = [UIColor colorWithDynamicProvider:^UIColor *_Nonnull(
|
||||
UITraitCollection *_Nonnull collection) {
|
||||
if (collection.userInterfaceStyle == UIUserInterfaceStyleDark) {
|
||||
if (collection.accessibilityContrast == UIAccessibilityContrastHigh && highContrastDarkColor != nil) {
|
||||
return highContrastDarkColor;
|
||||
} else {
|
||||
return darkColor;
|
||||
}
|
||||
} else {
|
||||
if (collection.accessibilityContrast == UIAccessibilityContrastHigh && highContrastLightColor != nil) {
|
||||
return highContrastLightColor;
|
||||
} else {
|
||||
return lightColor;
|
||||
}
|
||||
}
|
||||
}];
|
||||
return color;
|
||||
} else {
|
||||
#endif
|
||||
|
||||
@@ -60,7 +60,6 @@ module.exports = {
|
||||
const properties = args[0].properties;
|
||||
if (
|
||||
!(
|
||||
properties.length === 2 &&
|
||||
properties[0].type === 'Property' &&
|
||||
properties[0].key.name === 'light' &&
|
||||
properties[1].type === 'Property' &&
|
||||
|
||||
Reference in New Issue
Block a user