diff --git a/src/compiler/stylesheet.ts b/src/compiler/stylesheet.ts index 93a86598..4f9f0a85 100644 --- a/src/compiler/stylesheet.ts +++ b/src/compiler/stylesheet.ts @@ -32,6 +32,8 @@ const staticDeclarations = new WeakMap< const extraRules = new WeakMap[]>(); +const keywords = new Set(["unset"]); + export class StylesheetBuilder { animationFrames?: AnimationKeyframes_V2[]; animationDeclarations: StyleDeclaration[] = []; @@ -418,6 +420,8 @@ export class StylesheetBuilder { ]); } else if (Array.isArray(value) && value.some(isStyleFunction)) { declarations.push([value, propPath]); + } else if (typeof value === "string" && keywords.has(value)) { + declarations.push([value, propPath]); } else { let staticDeclarationRecord = staticDeclarations.get(declarations); if (!staticDeclarationRecord) { diff --git a/src/runtime/native/__tests__/keywords.test.tsx b/src/runtime/native/__tests__/keywords.test.tsx new file mode 100644 index 00000000..3be107c1 --- /dev/null +++ b/src/runtime/native/__tests__/keywords.test.tsx @@ -0,0 +1,18 @@ +import { View } from "react-native"; + +import { renderHook } from "@testing-library/react-native"; +import { registerCSS } from "react-native-css/jest"; + +import { useNativeCss } from "../react/useNativeCss"; + +test("unset", () => { + registerCSS(`.my-class { background-color: unset; }`); + + const { result } = renderHook(() => { + return useNativeCss(View, { className: "my-class" }); + }); + + expect(result.current.props).toStrictEqual({ + style: { backgroundColor: undefined }, + }); +}); diff --git a/src/runtime/native/styles/resolve.ts b/src/runtime/native/styles/resolve.ts index 2e152353..7383dce1 100644 --- a/src/runtime/native/styles/resolve.ts +++ b/src/runtime/native/styles/resolve.ts @@ -116,10 +116,16 @@ export function resolveValue( case "number": case "boolean": return value; - case "string": - return value.endsWith("px") // Inline vars() might set a value with a px suffix - ? parseInt(value.slice(0, -2), 10) - : value; + case "string": { + if (value === "unset") { + return null; + } else if (value.endsWith("px")) { + // Inline vars() might set a value with a px suffix + return parseInt(value.slice(0, -2), 10); + } else { + return value; + } + } case "object": { if (!Array.isArray(value)) { return value; diff --git a/src/runtime/utils/objects.ts b/src/runtime/utils/objects.ts index 7109e5ca..ac7e8442 100644 --- a/src/runtime/utils/objects.ts +++ b/src/runtime/utils/objects.ts @@ -49,8 +49,13 @@ export function applyValue( prop: string, value: any, ) { + // This is confusing. + // An undefined value means "don't set anything" (something failed while parsing) + // While a null value means "remove this value", which in React Native means "set to undefined" if (value === undefined) { return; + } else if (value === null) { + value = undefined; } if (transformKeys.has(prop)) {