Hello World
@@ -275,16 +275,16 @@ exports[`react-test-renderer complex 1`] = `
exports[`react-test-renderer composite 1`] = `
Hello World
diff --git a/packages/react-native-web/src/exports/AppRegistry/__tests__/__snapshots__/index-test.js.snap b/packages/react-native-web/src/exports/AppRegistry/__tests__/__snapshots__/index-test.js.snap
index 7d1441c7..9064d7b4 100644
--- a/packages/react-native-web/src/exports/AppRegistry/__tests__/__snapshots__/index-test.js.snap
+++ b/packages/react-native-web/src/exports/AppRegistry/__tests__/__snapshots__/index-test.js.snap
@@ -2,7 +2,7 @@
exports[`AppRegistry getApplication "getStyleElement" produces styles that are a function of rendering "element": Additional CSS for styled app 1`] = `
"
-.rn-backgroundColor-1e4kli0{background-color:purple}
+.rn-backgroundColor-aot4c7{background-color:rgba(128,0,128,1.00)}
.rn-borderTopWidth-10pzpfo{border-top-width:1234px}
.rn-borderRightWidth-1y24uml{border-right-width:1234px}
.rn-borderBottomWidth-98wxn4{border-bottom-width:1234px}
diff --git a/packages/react-native-web/src/exports/ColorPropType/index.js b/packages/react-native-web/src/exports/ColorPropType/index.js
index a04493b0..e6ca0a33 100644
--- a/packages/react-native-web/src/exports/ColorPropType/index.js
+++ b/packages/react-native-web/src/exports/ColorPropType/index.js
@@ -7,10 +7,7 @@
* @noflow
*/
-import normalizeColor from 'normalize-css-color';
-
-const isWebColor = (color: string) =>
- color === 'currentcolor' || color === 'inherit' || color.indexOf('var(') === 0;
+import normalizeColor from '../../modules/normalizeColor';
const colorPropType = function(isRequired, props, propName, componentName, location, propFullName) {
const color = props[propName];
@@ -36,11 +33,6 @@ const colorPropType = function(isRequired, props, propName, componentName, locat
return;
}
- if (typeof color === 'string' && isWebColor(color)) {
- // Web supports additional color keywords and custom property values. Ignore them.
- return;
- }
-
if (normalizeColor(color) === null) {
return new Error(
'Invalid ' +
diff --git a/packages/react-native-web/src/exports/StyleSheet/__tests__/__snapshots__/ReactNativeStyleResolver-test.js.snap b/packages/react-native-web/src/exports/StyleSheet/__tests__/__snapshots__/ReactNativeStyleResolver-test.js.snap
index e0b0d0ba..fe72af72 100644
--- a/packages/react-native-web/src/exports/StyleSheet/__tests__/__snapshots__/ReactNativeStyleResolver-test.js.snap
+++ b/packages/react-native-web/src/exports/StyleSheet/__tests__/__snapshots__/ReactNativeStyleResolver-test.js.snap
@@ -34,10 +34,10 @@ Object {
exports[`StyleSheet/ReactNativeStyleResolver resolve with register, resolves to className 1`] = `
Object {
"classList": Array [
- "rn-borderTopColor-1gxhl28",
- "rn-borderRightColor-knoah9",
- "rn-borderBottomColor-1ani3fp",
- "rn-borderLeftColor-ribj9x",
+ "rn-borderTopColor-3vzq9n",
+ "rn-borderRightColor-ycnuvz",
+ "rn-borderBottomColor-wqks8h",
+ "rn-borderLeftColor-3se2kx",
"rn-borderTopWidth-13yce4e",
"rn-borderRightWidth-fnigne",
"rn-borderBottomWidth-ndvcnb",
@@ -47,17 +47,17 @@ Object {
"rn-position-u8s1d",
"rn-width-b8lwoo",
],
- "className": "rn-borderTopColor-1gxhl28 rn-borderRightColor-knoah9 rn-borderBottomColor-1ani3fp rn-borderLeftColor-ribj9x rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-left-1tsx3h3 rn-opacity-icoktb rn-position-u8s1d rn-width-b8lwoo",
+ "className": "rn-borderTopColor-3vzq9n rn-borderRightColor-ycnuvz rn-borderBottomColor-wqks8h rn-borderLeftColor-3se2kx rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-left-1tsx3h3 rn-opacity-icoktb rn-position-u8s1d rn-width-b8lwoo",
}
`;
exports[`StyleSheet/ReactNativeStyleResolver resolve with register, resolves to className 2`] = `
Object {
"classList": Array [
- "rn-borderTopColor-1gxhl28",
- "rn-borderRightColor-knoah9",
- "rn-borderBottomColor-1ani3fp",
- "rn-borderLeftColor-ribj9x",
+ "rn-borderTopColor-3vzq9n",
+ "rn-borderRightColor-ycnuvz",
+ "rn-borderBottomColor-wqks8h",
+ "rn-borderLeftColor-3se2kx",
"rn-borderTopWidth-13yce4e",
"rn-borderRightWidth-fnigne",
"rn-borderBottomWidth-ndvcnb",
@@ -67,17 +67,17 @@ Object {
"rn-position-u8s1d",
"rn-width-l0gwng",
],
- "className": "rn-borderTopColor-1gxhl28 rn-borderRightColor-knoah9 rn-borderBottomColor-1ani3fp rn-borderLeftColor-ribj9x rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-left-1tsx3h3 rn-opacity-icoktb rn-position-u8s1d rn-width-l0gwng",
+ "className": "rn-borderTopColor-3vzq9n rn-borderRightColor-ycnuvz rn-borderBottomColor-wqks8h rn-borderLeftColor-3se2kx rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-left-1tsx3h3 rn-opacity-icoktb rn-position-u8s1d rn-width-l0gwng",
}
`;
exports[`StyleSheet/ReactNativeStyleResolver resolve with register, resolves to className 3`] = `
Object {
"classList": Array [
- "rn-borderTopColor-1gxhl28",
- "rn-borderRightColor-knoah9",
- "rn-borderBottomColor-1ani3fp",
- "rn-borderLeftColor-ribj9x",
+ "rn-borderTopColor-3vzq9n",
+ "rn-borderRightColor-ycnuvz",
+ "rn-borderBottomColor-wqks8h",
+ "rn-borderLeftColor-3se2kx",
"rn-borderTopWidth-13yce4e",
"rn-borderRightWidth-fnigne",
"rn-borderBottomWidth-ndvcnb",
@@ -87,7 +87,7 @@ Object {
"rn-position-u8s1d",
"rn-width-b8lwoo",
],
- "className": "rn-borderTopColor-1gxhl28 rn-borderRightColor-knoah9 rn-borderBottomColor-1ani3fp rn-borderLeftColor-ribj9x rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-left-1tsx3h3 rn-opacity-icoktb rn-position-u8s1d rn-width-b8lwoo",
+ "className": "rn-borderTopColor-3vzq9n rn-borderRightColor-ycnuvz rn-borderBottomColor-wqks8h rn-borderLeftColor-3se2kx rn-borderTopWidth-13yce4e rn-borderRightWidth-fnigne rn-borderBottomWidth-ndvcnb rn-borderLeftWidth-gxnn5r rn-left-1tsx3h3 rn-opacity-icoktb rn-position-u8s1d rn-width-b8lwoo",
}
`;
@@ -100,13 +100,13 @@ Object {
],
"className": "rn-left-1tsx3h3 rn-opacity-icoktb rn-position-u8s1d",
"style": Object {
- "borderBottomColor": "red",
+ "borderBottomColor": "rgba(255,0,0,1.00)",
"borderBottomWidth": "0px",
- "borderLeftColor": "red",
+ "borderLeftColor": "rgba(255,0,0,1.00)",
"borderLeftWidth": "0px",
- "borderRightColor": "red",
+ "borderRightColor": "rgba(255,0,0,1.00)",
"borderRightWidth": "0px",
- "borderTopColor": "red",
+ "borderTopColor": "rgba(255,0,0,1.00)",
"borderTopWidth": "0px",
"width": "100px",
},
@@ -123,13 +123,13 @@ Object {
],
"className": "rn-left-1tsx3h3 rn-opacity-icoktb rn-position-u8s1d rn-width-l0gwng",
"style": Object {
- "borderBottomColor": "red",
+ "borderBottomColor": "rgba(255,0,0,1.00)",
"borderBottomWidth": "0px",
- "borderLeftColor": "red",
+ "borderLeftColor": "rgba(255,0,0,1.00)",
"borderLeftWidth": "0px",
- "borderRightColor": "red",
+ "borderRightColor": "rgba(255,0,0,1.00)",
"borderRightWidth": "0px",
- "borderTopColor": "red",
+ "borderTopColor": "rgba(255,0,0,1.00)",
"borderTopWidth": "0px",
},
}
@@ -144,13 +144,13 @@ Object {
],
"className": "rn-left-1tsx3h3 rn-opacity-icoktb rn-position-u8s1d",
"style": Object {
- "borderBottomColor": "red",
+ "borderBottomColor": "rgba(255,0,0,1.00)",
"borderBottomWidth": "0px",
- "borderLeftColor": "red",
+ "borderLeftColor": "rgba(255,0,0,1.00)",
"borderLeftWidth": "0px",
- "borderRightColor": "red",
+ "borderRightColor": "rgba(255,0,0,1.00)",
"borderRightWidth": "0px",
- "borderTopColor": "red",
+ "borderTopColor": "rgba(255,0,0,1.00)",
"borderTopWidth": "0px",
"width": "100px",
},
@@ -162,13 +162,13 @@ Object {
"classList": Array [],
"className": "",
"style": Object {
- "borderBottomColor": "red",
+ "borderBottomColor": "rgba(255,0,0,1.00)",
"borderBottomWidth": "0px",
- "borderLeftColor": "red",
+ "borderLeftColor": "rgba(255,0,0,1.00)",
"borderLeftWidth": "0px",
- "borderRightColor": "red",
+ "borderRightColor": "rgba(255,0,0,1.00)",
"borderRightWidth": "0px",
- "borderTopColor": "red",
+ "borderTopColor": "rgba(255,0,0,1.00)",
"borderTopWidth": "0px",
"left": "50px",
"opacity": 0.5,
@@ -183,13 +183,13 @@ Object {
"classList": Array [],
"className": "",
"style": Object {
- "borderBottomColor": "red",
+ "borderBottomColor": "rgba(255,0,0,1.00)",
"borderBottomWidth": "0px",
- "borderLeftColor": "red",
+ "borderLeftColor": "rgba(255,0,0,1.00)",
"borderLeftWidth": "0px",
- "borderRightColor": "red",
+ "borderRightColor": "rgba(255,0,0,1.00)",
"borderRightWidth": "0px",
- "borderTopColor": "red",
+ "borderTopColor": "rgba(255,0,0,1.00)",
"borderTopWidth": "0px",
"left": "50px",
"opacity": 0.5,
@@ -204,13 +204,13 @@ Object {
"classList": Array [],
"className": "",
"style": Object {
- "borderBottomColor": "red",
+ "borderBottomColor": "rgba(255,0,0,1.00)",
"borderBottomWidth": "0px",
- "borderLeftColor": "red",
+ "borderLeftColor": "rgba(255,0,0,1.00)",
"borderLeftWidth": "0px",
- "borderRightColor": "red",
+ "borderRightColor": "rgba(255,0,0,1.00)",
"borderRightWidth": "0px",
- "borderTopColor": "red",
+ "borderTopColor": "rgba(255,0,0,1.00)",
"borderTopWidth": "0px",
"left": "50px",
"opacity": 0.5,
diff --git a/packages/react-native-web/src/exports/StyleSheet/__tests__/__snapshots__/createReactDOMStyle-test.js.snap b/packages/react-native-web/src/exports/StyleSheet/__tests__/__snapshots__/createReactDOMStyle-test.js.snap
index f1d356cf..0bf5e778 100644
--- a/packages/react-native-web/src/exports/StyleSheet/__tests__/__snapshots__/createReactDOMStyle-test.js.snap
+++ b/packages/react-native-web/src/exports/StyleSheet/__tests__/__snapshots__/createReactDOMStyle-test.js.snap
@@ -26,7 +26,7 @@ Object {
exports[`StyleSheet/createReactDOMStyle shortform -> longform 1`] = `
Object {
- "borderBottomColor": "white",
+ "borderBottomColor": "rgba(255,255,255,1.00)",
"borderBottomStyle": "solid",
"borderBottomWidth": "1px",
"borderLeftStyle": "solid",
diff --git a/packages/react-native-web/src/exports/StyleSheet/__tests__/createReactDOMStyle-test.js b/packages/react-native-web/src/exports/StyleSheet/__tests__/createReactDOMStyle-test.js
index 4fe177e2..72960ae7 100644
--- a/packages/react-native-web/src/exports/StyleSheet/__tests__/createReactDOMStyle-test.js
+++ b/packages/react-native-web/src/exports/StyleSheet/__tests__/createReactDOMStyle-test.js
@@ -166,13 +166,13 @@ describe('StyleSheet/createReactDOMStyle', () => {
const resolved = createReactDOMStyle(style);
expect(resolved).toEqual({
- boxShadow: '0px 0px 0px red'
+ boxShadow: '0px 0px 0px rgba(255,0,0,1.00)'
});
});
test('shadowColor and shadowOpacity only', () => {
expect(createReactDOMStyle({ shadowColor: 'red', shadowOpacity: 0.5 })).toEqual({
- boxShadow: '0px 0px 0px rgba(255,0,0,0.5)'
+ boxShadow: '0px 0px 0px rgba(255,0,0,0.50)'
});
});
@@ -216,7 +216,7 @@ describe('StyleSheet/createReactDOMStyle', () => {
textShadowRadius: 5
})
).toEqual({
- textShadow: '1px 2px 5px red'
+ textShadow: '1px 2px 5px rgba(255,0,0,1.00)'
});
});
diff --git a/packages/react-native-web/src/exports/StyleSheet/createReactDOMStyle.js b/packages/react-native-web/src/exports/StyleSheet/createReactDOMStyle.js
index ca7c1b17..3e14a0d6 100644
--- a/packages/react-native-web/src/exports/StyleSheet/createReactDOMStyle.js
+++ b/packages/react-native-web/src/exports/StyleSheet/createReactDOMStyle.js
@@ -7,8 +7,8 @@
* @noflow
*/
+import normalizeColor from '../../modules/normalizeColor';
import normalizeValue from './normalizeValue';
-import processColor from '../processColor';
/**
* The browser implements the CSS cascade, where the order of properties is a
@@ -89,7 +89,7 @@ const resolveShadow = (resolvedStyle, style) => {
const offsetX = normalizeValue(null, width);
const offsetY = normalizeValue(null, height);
const blurRadius = normalizeValue(null, style.shadowRadius || 0);
- const color = processColor(style.shadowColor, style.shadowOpacity);
+ const color = normalizeColor(style.shadowColor, style.shadowOpacity);
if (color) {
const boxShadow = `${offsetX} ${offsetY} ${blurRadius} ${color}`;
@@ -108,7 +108,7 @@ const resolveTextShadow = (resolvedStyle, style) => {
const offsetX = normalizeValue(null, width);
const offsetY = normalizeValue(null, height);
const blurRadius = normalizeValue(null, style.textShadowRadius || 0);
- const color = processColor(style.textShadowColor);
+ const color = normalizeColor(style.textShadowColor);
if (color) {
resolvedStyle.textShadow = `${offsetX} ${offsetY} ${blurRadius} ${color}`;
@@ -162,7 +162,7 @@ const createReducer = (style, styleProps) => {
// Normalize color values
if (colorProps[prop]) {
- value = processColor(value);
+ value = normalizeColor(value);
}
// Ignore everything else with a null value
diff --git a/packages/react-native-web/src/exports/processColor/__tests__/index-test.js b/packages/react-native-web/src/exports/processColor/__tests__/index-test.js
index ec0fd4a6..244a566c 100644
--- a/packages/react-native-web/src/exports/processColor/__tests__/index-test.js
+++ b/packages/react-native-web/src/exports/processColor/__tests__/index-test.js
@@ -1,89 +1,80 @@
/* eslint-env jasmine, jest */
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
import processColor from '..';
-describe('apis/StyleSheet/processColor', () => {
+const platformSpecific = x => x;
+
+describe('processColor', () => {
describe('predefined color names', () => {
- it('should not convert "red"', () => {
- const color = processColor('red');
- expect(color).toEqual('red');
+ it('should convert red', () => {
+ const colorFromString = processColor('red');
+ const expectedInt = 0xffff0000;
+ expect(colorFromString).toEqual(platformSpecific(expectedInt));
});
- it('should not convert "white"', () => {
- const color = processColor('white');
- expect(color).toEqual('white');
+ it('should convert white', () => {
+ const colorFromString = processColor('white');
+ const expectedInt = 0xffffffff;
+ expect(colorFromString).toEqual(platformSpecific(expectedInt));
});
- it('should not convert "black"', () => {
- const color = processColor('black');
- expect(color).toEqual('black');
+ it('should convert black', () => {
+ const colorFromString = processColor('black');
+ const expectedInt = 0xff000000;
+ expect(colorFromString).toEqual(platformSpecific(expectedInt));
});
- it('should not convert "currentcolor"', () => {
- const color = processColor('currentcolor');
- expect(color).toEqual('currentcolor');
- });
-
- it('should not convert "inherit"', () => {
- const color = processColor('inherit');
- expect(color).toEqual('inherit');
- });
-
- it('should not convert "transparent"', () => {
- const color = processColor('transparent');
- expect(color).toEqual('transparent');
+ it('should convert transparent', () => {
+ const colorFromString = processColor('transparent');
+ const expectedInt = 0x00000000;
+ expect(colorFromString).toEqual(platformSpecific(expectedInt));
});
});
describe('RGB strings', () => {
- it('should not convert "rgb(x,y,z)"', () => {
- const color = processColor('rgb(10,20,30)');
- expect(color).toEqual('rgb(10,20,30)');
+ it('should convert rgb(x, y, z)', () => {
+ const colorFromString = processColor('rgb(10, 20, 30)');
+ const expectedInt = 0xff0a141e;
+ expect(colorFromString).toEqual(platformSpecific(expectedInt));
});
});
describe('RGBA strings', () => {
- it('should not convert "rgba(x,y,z,a)"', () => {
- const color = processColor('rgba(10,20,30,0.4)');
- expect(color).toEqual('rgba(10,20,30,0.4)');
+ it('should convert rgba(x, y, z, a)', () => {
+ const colorFromString = processColor('rgba(10, 20, 30, 0.4)');
+ const expectedInt = 0x660a141e;
+ expect(colorFromString).toEqual(platformSpecific(expectedInt));
});
});
describe('HSL strings', () => {
- it('should not convert "hsl(x,y%,z%)"', () => {
- const color = processColor('hsl(318,69%,55%)');
- expect(color).toEqual('hsl(318,69%,55%)');
+ it('should convert hsl(x, y%, z%)', () => {
+ const colorFromString = processColor('hsl(318, 69%, 55%)');
+ const expectedInt = 0xffdb3dac;
+ expect(colorFromString).toEqual(platformSpecific(expectedInt));
});
});
describe('HSLA strings', () => {
- it('should not convert "hsla(x,y%,z%,a)"', () => {
- const color = processColor('hsla(318,69%,55%,0.25)');
- expect(color).toEqual('hsla(318,69%,55%,0.25)');
+ it('should convert hsla(x, y%, z%, a)', () => {
+ const colorFromString = processColor('hsla(318, 69%, 55%, 0.25)');
+ const expectedInt = 0x40db3dac;
+ expect(colorFromString).toEqual(platformSpecific(expectedInt));
});
});
describe('hex strings', () => {
- it('should convert "#rrggbb"', () => {
- const color = processColor('#1e83c9');
- expect(color).toEqual('rgba(30,131,201,1)');
- });
-
- it('should convert "#rgba"', () => {
- const color = processColor('#123A');
- expect(color).toEqual('rgba(17,34,51,0.7)');
- });
-
- it('should convert "#rrggbbaa"', () => {
- const color = processColor('#1e83c9AA');
- expect(color).toEqual('rgba(30,131,201,0.7)');
- });
- });
-
- describe('color int', () => {
- it('should convert 0xff0000ff', () => {
- const color = processColor(0xff0000ff);
- expect(color).toEqual('rgba(255,0,0,1)');
+ it('should convert #xxxxxx', () => {
+ const colorFromString = processColor('#1e83c9');
+ const expectedInt = 0xff1e83c9;
+ expect(colorFromString).toEqual(platformSpecific(expectedInt));
});
});
});
diff --git a/packages/react-native-web/src/exports/processColor/index.js b/packages/react-native-web/src/exports/processColor/index.js
index bb431586..9c5ca70a 100644
--- a/packages/react-native-web/src/exports/processColor/index.js
+++ b/packages/react-native-web/src/exports/processColor/index.js
@@ -10,26 +10,20 @@
import normalizeColor from 'normalize-css-color';
-const processColor = (color: ?(string | number), opacity: number = 1) => {
- if (
- color === undefined ||
- color === null ||
- (opacity === 1 && typeof color === 'string' && color.charAt(0) !== '#')
- ) {
+const processColor = (color?: string | number): ?number => {
+ if (color === undefined || color === null) {
return color;
}
// convert number and hex
- const int32Color = normalizeColor(color);
- if (int32Color === null) {
+ let int32Color = normalizeColor(color);
+ if (int32Color === undefined || int32Color === null) {
return undefined;
}
- // convert 0xrrggbbaa into rgba
- const rgba = normalizeColor.rgba(int32Color);
- rgba.a = rgba.a.toFixed(1);
- const { r, g, b, a } = rgba;
- return `rgba(${r},${g},${b},${a * opacity})`;
+ int32Color = ((int32Color << 24) | (int32Color >>> 8)) >>> 0;
+
+ return int32Color;
};
export default processColor;
diff --git a/packages/react-native-web/src/modules/createDOMProps/__tests__/__snapshots__/index-test.js.snap b/packages/react-native-web/src/modules/createDOMProps/__tests__/__snapshots__/index-test.js.snap
index 337a4976..c392263b 100644
--- a/packages/react-native-web/src/modules/createDOMProps/__tests__/__snapshots__/index-test.js.snap
+++ b/packages/react-native-web/src/modules/createDOMProps/__tests__/__snapshots__/index-test.js.snap
@@ -4,8 +4,8 @@ exports[`modules/createDOMProps includes "rel" values for "a" elements (to secur
exports[`modules/createDOMProps includes cursor style for "button" role 1`] = `"rn-cursor-1loqt21"`;
-exports[`modules/createDOMProps includes reset styles for "a" elements 1`] = `"rn-backgroundColor-wib322 rn-color-homxoj rn-textDecoration-bauka4"`;
+exports[`modules/createDOMProps includes reset styles for "a" elements 1`] = `"rn-backgroundColor-1niwhzg rn-color-homxoj rn-textDecoration-bauka4"`;
-exports[`modules/createDOMProps includes reset styles for "button" elements 1`] = `"rn-appearance-30o5oe rn-backgroundColor-wib322 rn-color-homxoj rn-fontFamily-poiln3 rn-fontSize-7cikom rn-fontStyle-o11vmf rn-fontVariant-ebii48 rn-fontWeight-gul640 rn-lineHeight-t9a87b rn-textAlign-1ttztb7"`;
+exports[`modules/createDOMProps includes reset styles for "button" elements 1`] = `"rn-appearance-30o5oe rn-backgroundColor-1niwhzg rn-color-homxoj rn-fontFamily-poiln3 rn-fontSize-7cikom rn-fontStyle-o11vmf rn-fontVariant-ebii48 rn-fontWeight-gul640 rn-lineHeight-t9a87b rn-textAlign-1ttztb7"`;
exports[`modules/createDOMProps includes reset styles for "ul" elements 1`] = `"rn-listStyle-1ebb2ja"`;
diff --git a/packages/react-native-web/src/modules/isWebColor/index.js b/packages/react-native-web/src/modules/isWebColor/index.js
new file mode 100644
index 00000000..88f2e723
--- /dev/null
+++ b/packages/react-native-web/src/modules/isWebColor/index.js
@@ -0,0 +1,11 @@
+/**
+ * @flow
+ */
+
+const isWebColor = (color: string): boolean =>
+ color === 'currentcolor' ||
+ color === 'currentColor' ||
+ color === 'inherit' ||
+ color.indexOf('var(') === 0;
+
+export default isWebColor;
diff --git a/packages/react-native-web/src/modules/normalizeColor/index.js b/packages/react-native-web/src/modules/normalizeColor/index.js
new file mode 100644
index 00000000..94563b3f
--- /dev/null
+++ b/packages/react-native-web/src/modules/normalizeColor/index.js
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2016-present, Nicolas Gallagher.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow
+ */
+
+import isWebColor from '../isWebColor';
+import processColor from '../../exports/processColor';
+
+const normalizeColor = (color?: number | string, opacity?: number = 1) => {
+ if (color == null) return;
+
+ if (typeof color === 'string' && isWebColor(color)) {
+ return color;
+ }
+
+ const colorInt = processColor(color);
+ if (colorInt != null) {
+ const r = (colorInt >> 16) & 255;
+ const g = (colorInt >> 8) & 255;
+ const b = colorInt & 255;
+ const a = ((colorInt >> 24) & 255) / 255;
+ const alpha = (a * opacity).toFixed(2);
+ return `rgba(${r},${g},${b},${alpha})`;
+ }
+};
+
+export default normalizeColor;