diff --git a/CHANGELOG.md b/CHANGELOG.md
index 23ba99fb..32b0ccda 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,42 @@
 # Changelog
 
+## 0.0.44 (Aug 3, 2025)
+
+### Fixes
+
+* [Native] Optimize `useStyleProps` internals.
+* [Native] Optimize `processStyle` internals.
+* [Native] Optimize `useCustomProperties` internals.
+* [Native] Don't overwrite `getBoundingClientRect`.
+* [Native] Fix viewport scaling of numeric `fontSize`.
+
+## 0.0.43 (Jul 27, 2025)
+
+### New features
+
+* [Native] Add experimental support for setting a custom viewport width. See #344
+
+### Fixes
+
+* [Native] Improve e2e performance ~3% by avoiding JS APIs that are more costly in Hermes.
+* [Native] Remove `display:flex` warning for flex item properties.
+
+## 0.0.42 (Jul 23, 2025)
+
+### Fixes
+
+* [Native] Revert "Use built-in React Native support for logical CSS"
+
+## 0.0.41 (Jul 17, 2025)
+
+### New features
+
+* [Native] Add support for CSS `backgroundImage` gradients on native.
+
+### Fixes
+
+* [Native] Add flexbox error logging.
+
 ## 0.0.40 (Jul 11, 2025)
 
 ### Fixes
diff --git a/apps/examples/src/components/App.js b/apps/examples/src/components/App.js
index 311de906..fd085a5e 100644
--- a/apps/examples/src/components/App.js
+++ b/apps/examples/src/components/App.js
@@ -52,7 +52,9 @@ const greenPinkTheme = css.createTheme(themeColors, {
 
 const themedStyles = css.create({
   container: {
+    display: 'flex',
     flex: 1,
+    flexDirection: 'column',
     justifyContent: 'center',
     backgroundColor: '#bbb',
     padding: 8
@@ -323,6 +325,15 @@ function Shell(): React.MixedElement {
           </html.div>
         </ExampleBlock>
 
+        {/* logical styles emulation */}
+        <ExampleBlock title="CSS Logical">
+          <html.div>
+            <html.div style={styles.logicalPadding} />
+            <html.div style={styles.logicalMargin} />
+            <html.div style={styles.logicalBorder} />
+          </html.div>
+        </ExampleBlock>
+
         {/* CSS positioning (static by default) */}
         <ExampleBlock title="CSS Position">
           <html.div style={[styles.p50, styles.relative]}>
@@ -571,7 +582,7 @@ function Shell(): React.MixedElement {
           >
             <html.span style={styles.bgWhite}>{clickData.text}</html.span>
             <html.div style={styles.flex}>
-              <html.div style={styles.flexGrow}>
+              <html.div style={[styles.flex, styles.flexGrow]}>
                 <html.div>
                   <html.span>
                     {clickEventData.altKey ? '✅' : '🚫'} altKey
@@ -593,7 +604,7 @@ function Shell(): React.MixedElement {
                   </html.span>
                 </html.div>
               </html.div>
-              <html.div style={styles.flexGrow}>
+              <html.div style={[styles.flex, styles.flexGrow]}>
                 <html.div>
                   <html.span>button: {clickEventData.button}</html.span>
                 </html.div>
@@ -654,14 +665,14 @@ const animateSequence = css.keyframes({
 });
 
 const themedTokens = css.createTheme(tokens, {
-  squareColor: 'purple',
+  squareColor: css.types.color('purple'),
   textColor: 'purple',
   inputColor: 'purple',
   inputPlaceholderColor: 'mediumpurple'
 });
 
 const themedTokensAlt = css.createTheme(tokens, {
-  squareColor: 'darkorange',
+  squareColor: css.types.color('darkorange'),
   textColor: 'darkorange',
   inputColor: 'orangered',
   inputPlaceholderColor: 'orange'
@@ -846,5 +857,34 @@ const styles = css.create({
     borderWidth: 1,
     borderStyle: 'solid',
     borderColor: 'black'
+  },
+  logicalMargin: {
+    backgroundColor: 'pink',
+    width: 50,
+    height: 50,
+    marginBlock: 20,
+    marginInline: 20,
+    borderColor: 'black',
+    borderWidth: 1,
+    borderStyle: 'solid'
+  },
+  logicalPadding: {
+    backgroundColor: 'pink',
+    width: 50,
+    height: 50,
+    paddingBlock: 20,
+    paddingInline: 20,
+    borderColor: 'black',
+    borderWidth: 1,
+    borderStyle: 'solid'
+  },
+  logicalBorder: {
+    backgroundColor: 'pink',
+    width: 50,
+    height: 50,
+    borderColor: 'red',
+    borderBlockWidth: 20,
+    borderInlineWidth: 20,
+    borderStyle: 'solid'
   }
 });
diff --git a/apps/examples/src/components/tokens.stylex.js b/apps/examples/src/components/tokens.stylex.js
index 3e5d7820..dfe353f9 100644
--- a/apps/examples/src/components/tokens.stylex.js
+++ b/apps/examples/src/components/tokens.stylex.js
@@ -8,18 +8,19 @@
  */
 
 import type { StyleVars } from 'react-strict-dom';
+import type { Types } from '@stylexjs/stylex';
 
 import { css } from 'react-strict-dom';
 
 export const tokens: StyleVars<
   $ReadOnly<{
-    squareColor: string,
+    squareColor: Types.CSSType<string>,
     textColor: string,
     inputColor: string,
     inputPlaceholderColor: string
   }>
 > = css.defineVars({
-  squareColor: 'red',
+  squareColor: css.types.color('red'),
   textColor: {
     default: 'darkred',
     '@media (prefers-color-scheme: dark)': 'lightred'
diff --git a/apps/website/docs/api/02-css/index.md b/apps/website/docs/api/02-css/index.md
index 4e6def24..ed30d4c8 100644
--- a/apps/website/docs/api/02-css/index.md
+++ b/apps/website/docs/api/02-css/index.md
@@ -72,7 +72,7 @@ The following tables represent the compatibility status of the strict CSS API fo
 | backdropFilter | ❌ | ❌ | |
 | backfaceVisibility | ✅ | ✅ | |
 | backgroundColor | ✅ | ✅ | |
-| backgroundImage | ❌ | ❌ | |
+| backgroundImage | 🟡 Partial | 🟡 Partial | Support for linear and radial gradients in React Native |
 | backgroundOrigin | ❌ | ❌ | |
 | backgroundPosition | ❌ | ❌ | |
 | backgroundRepeat | ❌ | ❌ | |
@@ -130,7 +130,7 @@ The following tables represent the compatibility status of the strict CSS API fo
 | color | ✅ | ✅ | |
 | columnGap | ✅ | ✅ | |
 | cursor | ❌ | ✅ Partial | |
-| direction | ✅ | ✅ | |
+| direction | ❌ | ❌ | |
 | display: block | 🟡 Partial | 🟡 Partial | [#2](https://github.com/facebook/react-strict-dom/issues/2) |
 | display: contents | ✅ | ✅ | |
 | display: flex | ✅ | ✅ | |
@@ -156,13 +156,13 @@ The following tables represent the compatibility status of the strict CSS API fo
 | gap | ✅ | ✅ | |
 | height | ✅ | ✅ | |
 | inlineSize | 🟡 | 🟡 | |
-| inset | ✅ | ✅ | |
-| insetBlock | ✅ | ✅ | |
-| insetBlockEnd | ✅ | ✅ | |
-| insetBlockStart | ✅ | ✅ | |
-| insetInline | ✅ | ✅ | |
-| insetInlineEnd | ✅ | ✅ | |
-| insetInlineStart | ✅ | ✅ | |
+| inset | 🟡 | 🟡 | |
+| insetBlock | 🟡 | 🟡 | |
+| insetBlockEnd | 🟡 | 🟡 | |
+| insetBlockStart | 🟡 | 🟡 | |
+| insetInline | 🟡 | 🟡 | |
+| insetInlineEnd | 🟡 | 🟡 | |
+| insetInlineStart | 🟡 | 🟡 | |
 | isolation | ✅ | ✅ | |
 | justifyContent | ✅ | ✅ | |
 | justifyItems | ❌ | ❌ | |
@@ -175,13 +175,13 @@ The following tables represent the compatibility status of the strict CSS API fo
 | listStylePosition | ❌ | ❌ | |
 | listStyleType | ❌ | ❌ | |
 | margin | ✅ | ✅ | |
-| marginBlock | ✅ | ✅ | |
-| marginBlockEnd | ✅ | ✅ | |
-| marginBlockStart | ✅ | ✅ | |
+| marginBlock | 🟡 | 🟡 | |
+| marginBlockEnd | 🟡 | 🟡 | |
+| marginBlockStart | 🟡 | 🟡 | |
 | marginBottom | ✅ | ✅ | |
-| marginInline | ✅ | ✅ | |
-| marginInlineEnd | ✅ | ✅ | |
-| marginInlineStart | ✅ | ✅ | |
+| marginInline | 🟡 | 🟡 | |
+| marginInlineEnd | 🟡 | 🟡 | |
+| marginInlineStart | 🟡 | 🟡 | |
 | marginLeft | ✅ | ✅ | |
 | marginRight | ✅ | ✅ | |
 | marginTop | ✅ | ✅ | |
@@ -208,13 +208,13 @@ The following tables represent the compatibility status of the strict CSS API fo
 | overflowX | ❌ | ❌ | |
 | overflowY | ❌ | ❌ | |
 | padding | ✅ | ✅ | |
-| paddingBlock | ✅ | ✅ | |
-| paddingBlockEnd | ✅ | ✅ | |
-| paddingBlockStart | ✅ | ✅ | |
+| paddingBlock | 🟡 | 🟡 | |
+| paddingBlockEnd | 🟡 | 🟡 | |
+| paddingBlockStart | 🟡 | 🟡 | |
 | paddingBottom | ✅ | ✅ | |
-| paddingInline | ✅ | ✅ | |
-| paddingInlineEnd | ✅ | ✅ | |
-| paddingInlineStart | ✅ | ✅ | |
+| paddingInline | 🟡 | 🟡 | |
+| paddingInlineEnd | 🟡 | 🟡 | |
+| paddingInlineStart | 🟡 | 🟡 | |
 | paddingLeft | ✅ | ✅ | |
 | paddingRight | ✅ | ✅ | |
 | paddingTop | ✅ | ✅ | |
diff --git a/package-lock.json b/package-lock.json
index c3f2785e..50fe0cb6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -31218,7 +31218,7 @@
       }
     },
     "packages/react-strict-dom": {
-      "version": "0.0.40",
+      "version": "0.0.44",
       "license": "MIT",
       "dependencies": {
         "@babel/helper-module-imports": "^7.24.7",
diff --git a/packages/benchmarks/perf/tests/css-props-tests.js b/packages/benchmarks/perf/tests/css-props-tests.js
index da4a4cd8..7896b3d6 100644
--- a/packages/benchmarks/perf/tests/css-props-tests.js
+++ b/packages/benchmarks/perf/tests/css-props-tests.js
@@ -20,6 +20,7 @@ function runSuite(opts) {
     hover: true,
     inheritedFontSize: 16,
     viewportHeight: 600,
+    viewportScale: 1,
     viewportWidth: 1024
   };
 
@@ -29,6 +30,7 @@ function runSuite(opts) {
     hover: true,
     inheritedFontSize: 16,
     viewportHeight: 600,
+    viewportScale: 1,
     viewportWidth: 1024
   };
 
diff --git a/packages/react-strict-dom/package.json b/packages/react-strict-dom/package.json
index 748dfbc2..4b186255 100644
--- a/packages/react-strict-dom/package.json
+++ b/packages/react-strict-dom/package.json
@@ -1,6 +1,6 @@
 {
   "name": "react-strict-dom",
-  "version": "0.0.40",
+  "version": "0.0.44",
   "description": "React Strict DOM",
   "exports": {
     ".": {
diff --git a/packages/react-strict-dom/src/native/stylex/CSSLengthUnitValue.js b/packages/react-strict-dom/src/native/css/CSSLengthUnitValue.js
similarity index 80%
rename from packages/react-strict-dom/src/native/stylex/CSSLengthUnitValue.js
rename to packages/react-strict-dom/src/native/css/CSSLengthUnitValue.js
index 5f8a5613..ac0b2158 100644
--- a/packages/react-strict-dom/src/native/stylex/CSSLengthUnitValue.js
+++ b/packages/react-strict-dom/src/native/css/CSSLengthUnitValue.js
@@ -17,16 +17,15 @@ type ResolvePixelValueOptions = $ReadOnly<{
   fontScale: number | void,
   inheritedFontSize: ?number,
   viewportHeight: number,
+  viewportScale: number,
   viewportWidth: number
 }>;
 
-type ParsedValue = [+value: number, +unit: CSSLengthUnitType] | null;
-
-const memoizedValues = new Map<string, ParsedValue>();
+const memoizedValues = new Map<string, CSSLengthUnitValue | null>();
 
 // TODO: this only works on simple values
 export class CSSLengthUnitValue {
-  static parse(input: string): ParsedValue {
+  static parse(input: string): CSSLengthUnitValue | null {
     const memoizedValue = memoizedValues.get(input);
     if (memoizedValue !== undefined) {
       return memoizedValue;
@@ -40,9 +39,9 @@ export class CSSLengthUnitValue {
     const value = match[1];
     const unit: $FlowFixMe = match[2];
     const parsedFloat: number = parseFloat(value);
-    const parsedValue: ParsedValue = [parsedFloat, unit];
-    memoizedValues.set(input, parsedValue);
-    return parsedValue;
+    const cssLengthUnitValue = new CSSLengthUnitValue(parsedFloat, unit);
+    memoizedValues.set(input, cssLengthUnitValue);
+    return cssLengthUnitValue;
   }
 
   value: number;
@@ -55,10 +54,11 @@ export class CSSLengthUnitValue {
 
   resolvePixelValue(options: ResolvePixelValueOptions): number {
     const {
-      viewportWidth,
-      viewportHeight,
       fontScale = 1,
-      inheritedFontSize
+      inheritedFontSize,
+      viewportHeight,
+      viewportScale = 1,
+      viewportWidth
     } = options;
     const unit = this.unit;
     const value = this.value;
@@ -66,16 +66,16 @@ export class CSSLengthUnitValue {
     switch (unit) {
       case 'em': {
         if (inheritedFontSize == null) {
-          return fontScale * 16 * value;
+          return fontScale * 16 * value * viewportScale;
         } else {
           return inheritedFontSize * value;
         }
       }
       case 'px': {
-        return value;
+        return value * viewportScale;
       }
       case 'rem': {
-        return fontScale * 16 * value;
+        return fontScale * 16 * value * viewportScale;
       }
       case 'vh': {
         return viewportHeight * valuePercent;
diff --git a/packages/react-strict-dom/src/native/css/CSSTransformValue.js b/packages/react-strict-dom/src/native/css/CSSTransformValue.js
new file mode 100644
index 00000000..5edb3a51
--- /dev/null
+++ b/packages/react-strict-dom/src/native/css/CSSTransformValue.js
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow strict
+ */
+
+import type { ReactNativeTransform } from '../../types/renderer.native';
+
+export class CSSTransformValue {
+  value: $ReadOnlyArray<ReactNativeTransform>;
+  cachedScaledTransform: void | {
+    viewportScale: number,
+    value: $ReadOnlyArray<ReactNativeTransform>
+  };
+
+  constructor(value: $ReadOnlyArray<ReactNativeTransform>) {
+    this.value = value;
+  }
+
+  resolveTransformValue(
+    viewportScale?: number = 1
+  ): $ReadOnlyArray<ReactNativeTransform> {
+    if (viewportScale === 1) {
+      return this.value;
+    }
+    if (
+      this.cachedScaledTransform != null &&
+      this.cachedScaledTransform.viewportScale === viewportScale
+    ) {
+      return this.cachedScaledTransform.value;
+    }
+
+    const scaledTransform = this.value.map((transform) => {
+      if (
+        transform.translateX != null &&
+        typeof transform.translateX === 'number'
+      ) {
+        return { translateX: transform.translateX * viewportScale };
+      }
+      if (
+        transform.translateY != null &&
+        typeof transform.translateY === 'number'
+      ) {
+        return { translateY: transform.translateY * viewportScale };
+      }
+      return transform;
+    });
+
+    this.cachedScaledTransform = {
+      viewportScale,
+      value: scaledTransform
+    };
+
+    return scaledTransform;
+  }
+}
diff --git a/packages/react-strict-dom/src/native/stylex/__tests__/CSSUnparsedValue-test.js b/packages/react-strict-dom/src/native/css/__tests__/CSSUnparsedValue-test.js
similarity index 100%
rename from packages/react-strict-dom/src/native/stylex/__tests__/CSSUnparsedValue-test.js
rename to packages/react-strict-dom/src/native/css/__tests__/CSSUnparsedValue-test.js
diff --git a/packages/react-strict-dom/src/native/stylex/__tests__/__snapshots__/fixContentBox-test.js.snap b/packages/react-strict-dom/src/native/css/__tests__/__snapshots__/fixContentBox-test.js.snap
similarity index 86%
rename from packages/react-strict-dom/src/native/stylex/__tests__/__snapshots__/fixContentBox-test.js.snap
rename to packages/react-strict-dom/src/native/css/__tests__/__snapshots__/fixContentBox-test.js.snap
index 8ebb16b4..90399ff4 100644
--- a/packages/react-strict-dom/src/native/stylex/__tests__/__snapshots__/fixContentBox-test.js.snap
+++ b/packages/react-strict-dom/src/native/css/__tests__/__snapshots__/fixContentBox-test.js.snap
@@ -32,6 +32,19 @@ exports[`fixContentBox boxSizing: content-box: height 1`] = `
 }
 `;
 
+exports[`fixContentBox boxSizing: content-box: logical 1`] = `
+{
+  "borderBlockWidth": 5,
+  "borderInlineWidth": 5,
+  "height": 100,
+  "paddingBlock": 10,
+  "paddingInline": 20,
+  "paddingInlineEnd": 30,
+  "paddingLeft": 40,
+  "width": 140,
+}
+`;
+
 exports[`fixContentBox boxSizing: content-box: maxHeight 1`] = `
 {
   "borderWidth": 2,
diff --git a/packages/react-strict-dom/src/native/stylex/__tests__/fixContentBox-test.js b/packages/react-strict-dom/src/native/css/__tests__/fixContentBox-test.js
similarity index 89%
rename from packages/react-strict-dom/src/native/stylex/__tests__/fixContentBox-test.js
rename to packages/react-strict-dom/src/native/css/__tests__/fixContentBox-test.js
index eb5a4478..bc69805b 100644
--- a/packages/react-strict-dom/src/native/stylex/__tests__/fixContentBox-test.js
+++ b/packages/react-strict-dom/src/native/css/__tests__/fixContentBox-test.js
@@ -70,6 +70,17 @@ describe('fixContentBox', () => {
         width: 100,
         height: 100
       },
+      logical: {
+        boxSizing: 'content-box',
+        borderBlockWidth: 5,
+        borderInlineWidth: 5,
+        paddingBlock: 10,
+        paddingInline: 20,
+        paddingInlineEnd: 30,
+        paddingLeft: 40,
+        width: 100,
+        height: 100
+      },
       auto: {
         boxSizing: 'content-box',
         borderWidth: 2,
@@ -100,6 +111,7 @@ describe('fixContentBox', () => {
     expect(fixContentBox(styles.minWidth)).toMatchSnapshot('minWidth');
     expect(fixContentBox(styles.minHeight)).toMatchSnapshot('minHeight');
     expect(fixContentBox(styles.allDifferent)).toMatchSnapshot('allDifferent');
+    expect(fixContentBox(styles.logical)).toMatchSnapshot('logical');
     expect(fixContentBox(styles.auto)).toMatchSnapshot('auto');
     expect(fixContentBox(styles.null)).toMatchSnapshot('null');
     expect(fixContentBox(styles.string)).toMatchSnapshot('string');
diff --git a/packages/react-strict-dom/src/native/stylex/__tests__/parseTimeValue-test.js b/packages/react-strict-dom/src/native/css/__tests__/parseTimeValue-test.js
similarity index 100%
rename from packages/react-strict-dom/src/native/stylex/__tests__/parseTimeValue-test.js
rename to packages/react-strict-dom/src/native/css/__tests__/parseTimeValue-test.js
diff --git a/packages/react-strict-dom/src/native/stylex/__tests__/parseTransform-test.js b/packages/react-strict-dom/src/native/css/__tests__/parseTransform-test.js
similarity index 93%
rename from packages/react-strict-dom/src/native/stylex/__tests__/parseTransform-test.js
rename to packages/react-strict-dom/src/native/css/__tests__/parseTransform-test.js
index 49b93530..cdf1f8cb 100644
--- a/packages/react-strict-dom/src/native/stylex/__tests__/parseTransform-test.js
+++ b/packages/react-strict-dom/src/native/css/__tests__/parseTransform-test.js
@@ -5,7 +5,11 @@
  * LICENSE file in the root directory of this source tree.
  */
 
-import { parseTransform } from '../parseTransform';
+import { parseTransform as parseTransformImpl } from '../parseTransform';
+
+function parseTransform(transform) {
+  return parseTransformImpl(transform).resolveTransformValue();
+}
 
 describe('parseTransform', () => {
   test('perspective', () => {
diff --git a/packages/react-strict-dom/src/native/stylex/customProperties.js b/packages/react-strict-dom/src/native/css/customProperties.js
similarity index 100%
rename from packages/react-strict-dom/src/native/stylex/customProperties.js
rename to packages/react-strict-dom/src/native/css/customProperties.js
diff --git a/packages/react-strict-dom/src/native/stylex/fixContentBox.js b/packages/react-strict-dom/src/native/css/fixContentBox.js
similarity index 100%
rename from packages/react-strict-dom/src/native/stylex/fixContentBox.js
rename to packages/react-strict-dom/src/native/css/fixContentBox.js
diff --git a/packages/react-strict-dom/src/native/stylex/flattenStyleXStyles.js b/packages/react-strict-dom/src/native/css/flattenStyleXStyles.js
similarity index 100%
rename from packages/react-strict-dom/src/native/stylex/flattenStyleXStyles.js
rename to packages/react-strict-dom/src/native/css/flattenStyleXStyles.js
diff --git a/packages/react-strict-dom/src/native/stylex/index.js b/packages/react-strict-dom/src/native/css/index.js
similarity index 61%
rename from packages/react-strict-dom/src/native/stylex/index.js
rename to packages/react-strict-dom/src/native/css/index.js
index 4d2e5f38..3895c0e1 100644
--- a/packages/react-strict-dom/src/native/stylex/index.js
+++ b/packages/react-strict-dom/src/native/css/index.js
@@ -6,275 +6,129 @@
  *
  * @flow strict
  */
-
-import type {
-  ReactNativeProps,
-  ReactNativeStyle
-} from '../../types/renderer.native';
-
-import type {
-  CustomProperties,
-  MutableCustomProperties,
-  IStyleX
-} from '../../types/styles';
+import type { ReactNativeProps } from '../../types/renderer.native';
+import type { ReactNativeStyle } from '../../types/renderer.native';
+import type { CustomProperties } from '../../types/styles';
+import type { MutableCustomProperties } from '../../types/styles';
+import type { IStyleX } from '../../types/styles';
 
 import { CSSLengthUnitValue } from './CSSLengthUnitValue';
+import { CSSTransformValue } from './CSSTransformValue';
 import { CSSUnparsedValue } from './typed-om/CSSUnparsedValue';
-import { errorMsg, warnMsg } from '../../shared/logUtils';
+import { errorMsg } from '../../shared/logUtils';
 import { fixContentBox } from './fixContentBox';
 import { flattenStyle } from './flattenStyleXStyles';
-import { isAllowedShortFormValue } from './isAllowedShortFormValue';
-import { isAllowedStyleKey } from './isAllowedStyleKey';
+import { lengthStyleKeySet } from './isLengthStyleKey';
 import { mediaQueryMatches } from './mediaQueryMatches';
-import { parseTextShadow } from './parseTextShadow';
-import { parseTimeValue } from './parseTimeValue';
-import { parseTransform } from './parseTransform';
-import {
-  resolveVariableReferences,
-  stringContainsVariables
-} from './customProperties';
+import { processStyle } from './processStyle';
+import { resolveVariableReferences } from './customProperties';
 import { version } from '../modules/version';
 
-type ResolveStyleOptions = $ReadOnly<{
-  active?: ?boolean,
-  colorScheme: ?('light' | 'dark'),
-  customProperties: CustomProperties,
-  focus?: ?boolean,
-  fontScale: number | void,
-  hover?: ?boolean,
-  inheritedFontSize: ?number,
-  viewportHeight: number,
-  viewportWidth: number,
-  writingDirection?: ?'ltr' | 'rtl'
-}>;
-
-const validPlaceContentValues = new Set<string>([
-  'center',
-  'flex-end',
-  'flex-start',
-  // distributed alignment
-  'space-around',
-  'space-between',
-  'space-evenly'
-]);
+export const __customProperties: MutableCustomProperties = {};
 
 /**
- * Pre-process 'create'
+ * The create method shim should do initial transforms like
+ * renaming/expanding/validating properties, essentially all the steps
+ * which can be done at initialization-time (could potentially be done at
+ * compile-time in the future).
  */
-
-function processStyle(
-  style: { +[string]: mixed },
-  skipValidation?: boolean
-): { +[string]: mixed } {
+function _create<S: { +[string]: { +[string]: mixed } }>(styles: S): {
+  [string]: mixed
+} {
   const result: { [string]: mixed } = {};
-
-  for (const propName in style) {
-    const styleValue = style[propName];
-
-    if (skipValidation !== true && !isAllowedStyleKey(propName)) {
-      if (__DEV__) {
-        warnMsg(`unsupported style property "${propName}"`);
-      }
-      continue;
+  for (const styleName in styles) {
+    const val = styles[styleName];
+    if (typeof val === 'function') {
+      result[styleName] = (...args: ?(string | number)) => {
+        const style = val(...args);
+        return processStyle(style);
+      };
+    } else {
+      result[styleName] = processStyle(styles[styleName]);
     }
+  }
+  return result;
+}
+export const create: IStyleX['create'] = _create as $FlowFixMe;
 
-    // Object values
-    else if (typeof styleValue === 'object' && styleValue != null) {
-      if (propName === '::placeholder') {
-        const placeholderStyleProps = Object.keys(styleValue);
-        for (let i = 0; i < placeholderStyleProps.length; i++) {
-          const prop = placeholderStyleProps[i];
-          if (prop === 'color') {
-            result['placeholderTextColor'] = processStyle({
-              color: styleValue.color
-            }).color;
-          } else {
-            if (__DEV__) {
-              warnMsg(`unsupported "::placeholder" style property "${prop}"`);
-            }
-          }
-        }
-        continue;
-      } else if (Object.hasOwn(styleValue, 'default')) {
-        result[propName] = processStyle(styleValue);
-        continue;
-      }
-    }
+const RE_CAPTURE_VAR_NAME = /^var\(--(.*)\)$/;
+export const createTheme = (
+  baseTokens: Tokens,
+  overrides: CustomProperties
+): CustomProperties => {
+  const result: MutableCustomProperties = { $$theme: 'theme' };
+  for (const key in baseTokens) {
+    const varName: string = baseTokens[key];
+    const normalizedKey = varName.replace(RE_CAPTURE_VAR_NAME, '$1');
+    result[normalizedKey] = overrides[key];
+  }
+  return result;
+};
 
-    // String values
-    else if (typeof styleValue === 'string') {
-      // Polyfill support for string '0' on Android
-      if (styleValue === '0') {
-        result[propName] = 0;
-        continue;
-      }
-      // Polyfill support for string opacity on Android
-      if (propName === 'opacity') {
-        result[propName] = parseFloat(styleValue);
-        continue;
-      }
-      // Polyfill support for custom property references (do this first)
-      else if (stringContainsVariables(styleValue)) {
-        result[propName] = CSSUnparsedValue.parse(propName, styleValue);
-        continue;
-      } else if (
-        propName === 'caretColor' &&
-        (typeof styleValue === 'undefined' || styleValue === 'auto')
-      ) {
-        if (__DEV__) {
-          warnMsg(
-            `unsupported style value in "${propName}:${String(styleValue)}"`
-          );
-        }
-        continue;
-      }
-      // Workaround unsupported objectFit values
-      else if (propName === 'objectFit' && styleValue === 'none') {
-        result[propName] = 'scale-down';
-        continue;
-      }
-      // Polyfill placeContent
-      else if (propName === 'placeContent') {
-        // None of these values are supported in RN for both properties.
-        if (!validPlaceContentValues.has(styleValue)) {
-          if (__DEV__) {
-            warnMsg(
-              `unsupported style value in "${propName}:${String(styleValue)}"`
-            );
-          }
-          continue;
-        }
-      }
-      // Workaround unsupported position values
-      else if (
-        propName === 'position' &&
-        (styleValue === 'fixed' || styleValue === 'sticky')
-      ) {
-        const fallback = styleValue === 'fixed' ? 'absolute' : 'relative';
-        if (__DEV__) {
-          warnMsg(
-            `unsupported style value in "position:${styleValue}". Falling back to "position:${fallback}".`
-          );
-        }
-        result[propName] = fallback;
-        continue;
-      }
-      // Workaround unsupported textAlign values
-      // https://github.com/facebook/react-native/issues/45255
-      else if (propName === 'textAlign') {
-        if (styleValue === 'start') {
-          result[propName] = 'left';
-        } else if (styleValue === 'end') {
-          result[propName] = 'right';
-        } else {
-          result[propName] = styleValue;
-        }
-        continue;
-      }
-      // Polyfill textShadow
-      else if (propName === 'textShadow') {
-        result[propName] = parseTextShadow(styleValue);
-        continue;
-      }
-      // Polyfill transform as string value
-      else if (propName === 'transform') {
-        result[propName] = parseTransform(styleValue);
-        continue;
-      }
-      // Polyfill time-valued string values (e.g., '1000ms' => 1000)
-      else if (
-        propName === 'animationDelay' ||
-        propName === 'animationDuration' ||
-        propName === 'transitionDelay' ||
-        propName === 'transitionDuration'
-      ) {
-        result[propName] = parseTimeValue(styleValue);
-        continue;
-      }
+export const defineConsts = (constants: {
+  [string]: string
+}): { [string]: string } => {
+  return Object.freeze(constants);
+};
 
-      const maybeLengthUnitValue = CSSLengthUnitValue.parse(styleValue);
-      if (maybeLengthUnitValue != null) {
-        result[propName] = new CSSLengthUnitValue(...maybeLengthUnitValue);
-        continue;
-        // React Native doesn't support these keywords or functions
-      } else if (styleValue === 'inherit' || styleValue === 'unset') {
-        // direction has native support for 'inherit'
-        if (propName === 'direction') {
-          result[propName] = 'inherit';
-          continue;
-        }
-        // inherited properties polyfill 'inherit' in useStyleProps
-        else if (
-          propName !== ':active' &&
-          propName !== ':focus' &&
-          propName !== ':hover' &&
-          propName !== 'default' &&
-          propName !== 'color' &&
-          propName !== 'cursor' &&
-          propName !== 'fontFamily' &&
-          propName !== 'fontSize' &&
-          propName !== 'fontStyle' &&
-          propName !== 'fontVariant' &&
-          propName !== 'fontWeight' &&
-          propName !== 'letterSpacing' &&
-          propName !== 'lineHeight' &&
-          propName !== 'textAlign' &&
-          propName !== 'textDecorationColor' &&
-          propName !== 'textDecorationLine' &&
-          propName !== 'textDecorationStyle' &&
-          propName !== 'textAlign' &&
-          propName !== 'textIndent' &&
-          propName !== 'textTransform' &&
-          propName !== 'whiteSpace'
-        ) {
-          if (__DEV__) {
-            warnMsg(
-              `unsupported style value in "${propName}:${String(styleValue)}"`
-            );
-          }
-          continue;
-        }
-      } else if (
-        styleValue === 'currentcolor' ||
-        styleValue === 'initial' ||
-        styleValue.includes('calc(')
-      ) {
-        if (__DEV__) {
-          warnMsg(
-            `unsupported style value in "${propName}:${String(styleValue)}"`
-          );
-        }
-        continue;
-      } else if (!isAllowedShortFormValue(propName, styleValue)) {
-        if (__DEV__) {
-          errorMsg(
-            `invalid style value in "${propName}:${String(styleValue)}". Shortform properties cannot contain multiple values. Please use longform properties.`
-          );
-        }
-        continue;
-      }
-    }
+type Tokens = { [string]: string };
+let defineVarsCount = 1;
+export const defineVars = (tokens: CustomProperties): Tokens => {
+  const result: Tokens = {};
+  for (const key in tokens) {
+    const value = tokens[key];
+    const customPropName = `${key}__id__${defineVarsCount++}`;
+    result[key] = `var(--${customPropName})`;
+    // NOTE: it's generally not a good idea to mutate the default context,
+    // but defineVars is always called before any component body is evaluated,
+    // and so it's safe to do so here.
+    __customProperties[customPropName] = value;
+  }
+  return result;
+};
 
-    // Number values
-    else if (typeof styleValue === 'number') {
-      // Polyfill numeric fontWeight (for desktop)
-      if (propName === 'fontWeight') {
-        result[propName] = styleValue.toString();
-        continue;
-      }
-      // Normalize unitless lineHeight to string
-      if (propName === 'lineHeight') {
-        result[propName] = styleValue.toString();
-        continue;
-      }
-    }
+export const firstThatWorks = <T: string | number>(
+  ...values: $ReadOnlyArray<T>
+): T => {
+  return values[0];
+};
 
-    // Everything else
-    result[propName] = styleValue;
+type Keyframes = {
+  +[key: string]: { +[k: string]: string | number }
+};
+function _keyframes(k: Keyframes): Keyframes {
+  if (__DEV__) {
+    errorMsg('css.keyframes() is not supported.');
   }
+  return k;
+}
+export const keyframes: (Keyframes) => string = _keyframes as $FlowFixMe;
 
-  return result;
+type PositionTry = {
+  +[k: string]: string | number
+};
+function _positionTry(p: PositionTry): PositionTry {
+  if (__DEV__) {
+    errorMsg('css.positionTry() is not supported.');
+  }
+  return p;
 }
+export const positionTry: (PositionTry) => string = _positionTry as $FlowFixMe;
+
+type ResolveStyleOptions = $ReadOnly<{
+  active?: ?boolean,
+  colorScheme: ?('light' | 'dark'),
+  customProperties: CustomProperties,
+  focus?: ?boolean,
+  fontScale: number | void,
+  fontSize?: ?number,
+  hover?: ?boolean,
+  inheritedFontSize: ?number,
+  viewportHeight: number,
+  viewportScale: number | void,
+  viewportWidth: number,
+  writingDirection?: ?'ltr' | 'rtl'
+}>;
 
 /**
  * Resolve 'props'
@@ -289,9 +143,11 @@ function resolveStyle(
     active,
     focus,
     fontScale,
+    fontSize: _fontSize,
     hover,
     inheritedFontSize,
     viewportHeight,
+    viewportScale = 1,
     viewportWidth
   } = options;
   const colorScheme = options.colorScheme || 'light';
@@ -320,7 +176,8 @@ function resolveStyle(
 
     // Resolve length units
     if (styleValue instanceof CSSLengthUnitValue) {
-      const fontSize = flatStyle.fontSize;
+      const fontSize =
+        flatStyle.fontSize != null ? flatStyle.fontSize : _fontSize;
       // If fontSize is being resolved, or there is no fontSize for this style,
       // we use the inherited fontSize.
       if (propName === 'fontSize' || fontSize == null) {
@@ -328,6 +185,7 @@ function resolveStyle(
           fontScale,
           inheritedFontSize: inheritedFontSize,
           viewportHeight,
+          viewportScale,
           viewportWidth
         });
         continue;
@@ -340,6 +198,7 @@ function resolveStyle(
             fontScale,
             inheritedFontSize: fontSize,
             viewportHeight,
+            viewportScale,
             viewportWidth
           });
         } else {
@@ -349,7 +208,20 @@ function resolveStyle(
       }
     }
 
-    // Resolve the stylex object-value syntax
+    if (styleValue instanceof CSSTransformValue) {
+      result[propName] = styleValue.resolveTransformValue(viewportScale);
+      continue;
+    }
+    if (
+      viewportScale !== 1 &&
+      typeof styleValue === 'number' &&
+      lengthStyleKeySet.has(propName)
+    ) {
+      result[propName] = styleValue * viewportScale;
+      continue;
+    }
+
+    // Resolve the object-value syntax
     if (
       styleValue != null &&
       typeof styleValue === 'object' &&
@@ -400,110 +272,29 @@ function resolveStyle(
     const processedStyles = processStyle(stylesToReprocess, true);
     // We can end up re-processing values without a fontSize (since it might already be resolved),
     // which generates incorrect 'em'-based values. Passing the fontSize back in avoids this.
+    // However, if the fontSize is already a number it could be incorrectly scaled so it must be
+    // passed in as an option instead of a style.
     const resolvedFontSize = result.fontSize;
     const fontSizeStyle =
-      resolvedFontSize != null ? { fontSize: resolvedFontSize } : null;
-    const resolvedStyles = resolveStyle(
-      [fontSizeStyle, processedStyles],
-      options
-    );
+      resolvedFontSize != null && typeof resolvedFontSize !== 'number'
+        ? { fontSize: resolvedFontSize }
+        : null;
+    const nextStyles =
+      fontSizeStyle != null
+        ? [fontSizeStyle, processedStyles]
+        : processedStyles;
+    const nextOptions =
+      resolvedFontSize != null && typeof resolvedFontSize === 'number'
+        ? { ...options, fontSize: resolvedFontSize }
+        : options;
+
+    const resolvedStyles = resolveStyle(nextStyles, nextOptions);
     Object.assign(result, resolvedStyles);
   }
 
   return result;
 }
 
-export const __customProperties: MutableCustomProperties = {};
-
-/**
- * The create method shim should do initial transforms like
- * renaming/expanding/validating properties, essentially all the steps
- * which can be done at initialization-time (could potentially be done at
- * compile-time in the future).
- */
-function _create<S: { +[string]: { +[string]: mixed } }>(styles: S): {
-  [string]: mixed
-} {
-  const result: { [string]: mixed } = {};
-  for (const styleName in styles) {
-    const val = styles[styleName];
-    if (typeof val === 'function') {
-      result[styleName] = (...args: ?(string | number)) => {
-        const style = val(...args);
-        return processStyle(style);
-      };
-    } else {
-      result[styleName] = processStyle(styles[styleName]);
-    }
-  }
-  return result;
-}
-export const create: IStyleX['create'] = _create as $FlowFixMe;
-
-const RE_CAPTURE_VAR_NAME = /^var\(--(.*)\)$/;
-export const createTheme = (
-  baseTokens: Tokens,
-  overrides: CustomProperties
-): CustomProperties => {
-  const result: MutableCustomProperties = { $$theme: 'theme' };
-  for (const key in baseTokens) {
-    const varName: string = baseTokens[key];
-    const normalizedKey = varName.replace(RE_CAPTURE_VAR_NAME, '$1');
-    result[normalizedKey] = overrides[key];
-  }
-  return result;
-};
-
-export const defineConsts = (constants: {
-  [string]: string
-}): { [string]: string } => {
-  return Object.freeze(constants);
-};
-
-type Tokens = { [string]: string };
-let defineVarsCount = 1;
-export const defineVars = (tokens: CustomProperties): Tokens => {
-  const result: Tokens = {};
-  for (const key in tokens) {
-    const value = tokens[key];
-    const customPropName = `${key}__id__${defineVarsCount++}`;
-    result[key] = `var(--${customPropName})`;
-    // NOTE: it's generally not a good idea to mutate the default context,
-    // but defineVars is always called before any component body is evaluated,
-    // and so it's safe to do so here.
-    __customProperties[customPropName] = value;
-  }
-  return result;
-};
-
-export const firstThatWorks = <T: string | number>(
-  ...values: $ReadOnlyArray<T>
-): T => {
-  return values[0];
-};
-
-type Keyframes = {
-  +[key: string]: { +[k: string]: string | number }
-};
-function _keyframes(k: Keyframes): Keyframes {
-  if (__DEV__) {
-    errorMsg('css.keyframes() is not supported.');
-  }
-  return k;
-}
-export const keyframes: (Keyframes) => string = _keyframes as $FlowFixMe;
-
-type PositionTry = {
-  +[k: string]: string | number
-};
-function _positionTry(p: PositionTry): PositionTry {
-  if (__DEV__) {
-    errorMsg('css.positionTry() is not supported.');
-  }
-  return p;
-}
-export const positionTry: (PositionTry) => string = _positionTry as $FlowFixMe;
-
 export function props(
   this: ResolveStyleOptions,
   ...style: $ReadOnlyArray<?{ [key: string]: mixed }>
@@ -602,10 +393,63 @@ export function props(
         nativeProps.cursorColor = styleValue;
       }
     }
+    // inset
+    else if (styleProp === 'inset') {
+      nextStyle.top ??= styleValue;
+      nextStyle.start ??= styleValue;
+      nextStyle.end ??= styleValue;
+      nextStyle.bottom ??= styleValue;
+    } else if (styleProp === 'insetBlock') {
+      nextStyle.top ??= styleValue;
+      nextStyle.bottom ??= styleValue;
+    } else if (styleProp === 'insetBlockEnd') {
+      nextStyle.bottom = flatStyle.bottom ?? styleValue;
+    } else if (styleProp === 'insetBlockStart') {
+      nextStyle.top = flatStyle.top ?? styleValue;
+    } else if (styleProp === 'insetInline') {
+      nextStyle.end ??= styleValue;
+      nextStyle.start ??= styleValue;
+    } else if (styleProp === 'insetInlineEnd') {
+      nextStyle.end = flatStyle.end ?? styleValue;
+    } else if (styleProp === 'insetInlineStart') {
+      nextStyle.start = flatStyle.start ?? styleValue;
+    }
     // lineClamp polyfill
     else if (styleProp === 'lineClamp') {
       nativeProps.numberOfLines = styleValue;
     }
+    // marginBlock
+    else if (styleProp === 'marginBlock') {
+      nextStyle.marginVertical = styleValue;
+    } else if (styleProp === 'marginBlockStart') {
+      nextStyle.marginTop ??= styleValue;
+    } else if (styleProp === 'marginBlockEnd') {
+      nextStyle.marginBottom ??= styleValue;
+    }
+    // marginInline
+    else if (styleProp === 'marginInline') {
+      nextStyle.marginHorizontal = styleValue;
+    } else if (styleProp === 'marginInlineStart') {
+      nextStyle.marginStart = styleValue;
+    } else if (styleProp === 'marginInlineEnd') {
+      nextStyle.marginEnd = styleValue;
+    }
+    // paddingBlock
+    else if (styleProp === 'paddingBlock') {
+      nextStyle.paddingVertical = styleValue;
+    } else if (styleProp === 'paddingBlockStart') {
+      nextStyle.paddingTop ??= styleValue;
+    } else if (styleProp === 'paddingBlockEnd') {
+      nextStyle.paddingBottom ??= styleValue;
+    }
+    // paddingInline
+    else if (styleProp === 'paddingInline') {
+      nextStyle.paddingHorizontal = styleValue;
+    } else if (styleProp === 'paddingInlineStart') {
+      nextStyle.paddingStart = styleValue;
+    } else if (styleProp === 'paddingInlineEnd') {
+      nextStyle.paddingEnd = styleValue;
+    }
     // '::placeholder' polyfill
     else if (styleProp === 'placeholderTextColor') {
       nativeProps.placeholderTextColor = styleValue;
@@ -637,7 +481,108 @@ export function props(
   if (boxSizingValue === 'content-box' && !version.experimental) {
     nextStyle = fixContentBox(nextStyle);
   }
+
+  // Print an error message if flex properties are used without
+  // "display:flex" being set. React Native is always using "flex"
+  // layout but web uses "flow" layout by default, which can lead
+  // to layout divergence if building for native first.
+  if (__DEV__) {
+    if (
+      nextStyle.display == null ||
+      (nextStyle.display !== 'flex' && nextStyle.display !== 'none')
+    ) {
+      if (
+        nextStyle.alignContent != null ||
+        nextStyle.alignItems != null ||
+        nextStyle.columnGap != null ||
+        nextStyle.flexDirection != null ||
+        nextStyle.flexWrap != null ||
+        nextStyle.gap != null ||
+        nextStyle.justifyContent != null ||
+        nextStyle.placeContent != null ||
+        nextStyle.rowGap != null
+      ) {
+        errorMsg('"display:flex" is required to use flexbox properties');
+      }
+    }
+  }
+
   nativeProps.style = nextStyle;
 
   return nativeProps;
 }
+
+type ValueWithDefault<+T> =
+  | T
+  | $ReadOnly<{
+      default: ValueWithDefault<T>,
+      [string]: ValueWithDefault<T>
+    }>;
+
+export const types = {
+  angle: <T: string | 0 = string | 0>(
+    value: ValueWithDefault<T>
+  ): ValueWithDefault<T> => {
+    return value;
+  },
+  color: <T: string = string>(
+    value: ValueWithDefault<T>
+  ): ValueWithDefault<T> => {
+    return value;
+  },
+  image: <T: string = string>(
+    value: ValueWithDefault<T>
+  ): ValueWithDefault<T> => {
+    return value;
+  },
+  integer: <T: number | string = number | string>(
+    value: ValueWithDefault<T>
+  ): ValueWithDefault<T> => {
+    return value;
+  },
+  length: <T: number | string = number | string>(
+    value: ValueWithDefault<T>
+  ): ValueWithDefault<T> => {
+    return value;
+  },
+  lengthPercentage: <T: number | string = number | string>(
+    value: ValueWithDefault<T>
+  ): ValueWithDefault<T> => {
+    return value;
+  },
+  number: <T: number | string = number | string>(
+    value: ValueWithDefault<T>
+  ): ValueWithDefault<T> => {
+    return value;
+  },
+  percentage: <T: number | string = number | string>(
+    value: ValueWithDefault<T>
+  ): ValueWithDefault<T> => {
+    return value;
+  },
+  resolution: <T: string = string>(
+    value: ValueWithDefault<T>
+  ): ValueWithDefault<T> => {
+    return value;
+  },
+  time: <T: string | 0 = string | 0>(
+    value: ValueWithDefault<T>
+  ): ValueWithDefault<T> => {
+    return value;
+  },
+  transformFunction: <T: string = string>(
+    value: ValueWithDefault<T>
+  ): ValueWithDefault<T> => {
+    return value;
+  },
+  transformList: <T: string = string>(
+    value: ValueWithDefault<T>
+  ): ValueWithDefault<T> => {
+    return value;
+  },
+  url: <T: string = string>(
+    value: ValueWithDefault<T>
+  ): ValueWithDefault<T> => {
+    return value;
+  }
+};
diff --git a/packages/react-strict-dom/src/native/stylex/isAllowedShortFormValue.js b/packages/react-strict-dom/src/native/css/isAllowedShortFormValue.js
similarity index 100%
rename from packages/react-strict-dom/src/native/stylex/isAllowedShortFormValue.js
rename to packages/react-strict-dom/src/native/css/isAllowedShortFormValue.js
diff --git a/packages/react-strict-dom/src/native/stylex/isAllowedStyleKey.js b/packages/react-strict-dom/src/native/css/isAllowedStyleKey.js
similarity index 99%
rename from packages/react-strict-dom/src/native/stylex/isAllowedStyleKey.js
rename to packages/react-strict-dom/src/native/css/isAllowedStyleKey.js
index 35ff6fb1..7f6e0b1c 100644
--- a/packages/react-strict-dom/src/native/stylex/isAllowedStyleKey.js
+++ b/packages/react-strict-dom/src/native/css/isAllowedStyleKey.js
@@ -18,6 +18,7 @@ const allowedStyleKeySet = new Set<string>([
   'aspectRatio',
   'backfaceVisibility',
   'backgroundColor',
+  'backgroundImage',
   'blockSize',
   'borderBottomColor',
   'borderBottomLeftRadius',
diff --git a/packages/react-strict-dom/src/native/css/isLengthStyleKey.js b/packages/react-strict-dom/src/native/css/isLengthStyleKey.js
new file mode 100644
index 00000000..e630c6ed
--- /dev/null
+++ b/packages/react-strict-dom/src/native/css/isLengthStyleKey.js
@@ -0,0 +1,88 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow strict
+ */
+
+import { version } from '../modules/version';
+
+export const lengthStyleKeySet: Set<string> = new Set([
+  'blockSize',
+  'borderBottomLeftRadius',
+  'borderBottomRightRadius',
+  'borderBottomWidth',
+  'borderBlockWidth',
+  'borderBlockEndWidth',
+  'borderBlockStartWidth',
+  'borderEndEndRadius',
+  'borderEndStartRadius',
+  'borderInlineWidth',
+  'borderInlineEndWidth',
+  'borderInlineStartWidth',
+  'borderLeftWidth',
+  'borderRadius',
+  'borderRightWidth',
+  'borderStartEndRadius',
+  'borderStartStartRadius',
+  'borderTopLeftRadius',
+  'borderTopRightRadius',
+  'borderTopWidth',
+  'borderWidth',
+  'bottom',
+  'columnGap',
+  'fontSize',
+  'gap',
+  'height',
+  'inlineSize',
+  'inset',
+  'insetBlock',
+  'insetBlockEnd',
+  'insetBlockStart',
+  'insetInline',
+  'insetInlineEnd',
+  'insetInlineStart',
+  'left',
+  'lineHeight',
+  'margin',
+  'marginBlock',
+  'marginBlockEnd',
+  'marginBlockStart',
+  'marginBottom',
+  'marginInline',
+  'marginInlineEnd',
+  'marginInlineStart',
+  'marginLeft',
+  'marginRight',
+  'marginTop',
+  'maxBlockSize',
+  'maxHeight',
+  'maxInlineSize',
+  'maxWidth',
+  'minBlockSize',
+  'minHeight',
+  'minInlineSize',
+  'minWidth',
+  'padding',
+  'paddingBlock',
+  'paddingBlockEnd',
+  'paddingBlockStart',
+  'paddingBottom',
+  'paddingInline',
+  'paddingInlineEnd',
+  'paddingInlineStart',
+  'paddingLeft',
+  'paddingRight',
+  'paddingTop',
+  'right',
+  'rowGap',
+  'top',
+  'width'
+]);
+
+if (version.experimental) {
+  lengthStyleKeySet.add('outlineOffset');
+  lengthStyleKeySet.add('outlineWidth');
+}
diff --git a/packages/react-strict-dom/src/native/stylex/mediaQueryMatches.js b/packages/react-strict-dom/src/native/css/mediaQueryMatches.js
similarity index 100%
rename from packages/react-strict-dom/src/native/stylex/mediaQueryMatches.js
rename to packages/react-strict-dom/src/native/css/mediaQueryMatches.js
diff --git a/packages/react-strict-dom/src/native/stylex/parseTextShadow.js b/packages/react-strict-dom/src/native/css/parseTextShadow.js
similarity index 100%
rename from packages/react-strict-dom/src/native/stylex/parseTextShadow.js
rename to packages/react-strict-dom/src/native/css/parseTextShadow.js
diff --git a/packages/react-strict-dom/src/native/stylex/parseTimeValue.js b/packages/react-strict-dom/src/native/css/parseTimeValue.js
similarity index 100%
rename from packages/react-strict-dom/src/native/stylex/parseTimeValue.js
rename to packages/react-strict-dom/src/native/css/parseTimeValue.js
diff --git a/packages/react-strict-dom/src/native/stylex/parseTransform.js b/packages/react-strict-dom/src/native/css/parseTransform.js
similarity index 90%
rename from packages/react-strict-dom/src/native/stylex/parseTransform.js
rename to packages/react-strict-dom/src/native/css/parseTransform.js
index 8c0f13de..aca32836 100644
--- a/packages/react-strict-dom/src/native/stylex/parseTransform.js
+++ b/packages/react-strict-dom/src/native/css/parseTransform.js
@@ -9,16 +9,16 @@
 
 import type { ReactNativeTransform } from '../../types/renderer.native';
 
+import { CSSTransformValue } from './CSSTransformValue';
+
 const transformRegex1 =
   /(perspective|scale|scaleX|scaleY|scaleZ|translateX|translateY)\(([0-9.+\-eE]+)(px|%)?\)/;
 const transformRegex2 = /(rotate|rotateX|rotateY|rotateZ|skewX|skewY)\((.*)\)/;
 const transformRegex3 = /matrix\((.*)\)/;
 
-const memoizedValues = new Map<string, ReactNativeTransform[]>();
+const memoizedValues = new Map<string, CSSTransformValue>();
 
-export function parseTransform(
-  transform: string
-): $ReadOnlyArray<ReactNativeTransform> {
+export function parseTransform(transform: string): CSSTransformValue {
   const memoizedValue = memoizedValues.get(transform);
   if (memoizedValue != null) {
     return memoizedValue;
@@ -114,7 +114,7 @@ export function parseTransform(
     }
   }
 
-  memoizedValues.set(transform, parsedTransforms);
-
-  return parsedTransforms;
+  const cssTransformValue = new CSSTransformValue(parsedTransforms);
+  memoizedValues.set(transform, cssTransformValue);
+  return cssTransformValue;
 }
diff --git a/packages/react-strict-dom/src/native/css/processStyle.js b/packages/react-strict-dom/src/native/css/processStyle.js
new file mode 100644
index 00000000..be45fe2a
--- /dev/null
+++ b/packages/react-strict-dom/src/native/css/processStyle.js
@@ -0,0 +1,257 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow strict
+ */
+
+import { CSSLengthUnitValue } from './CSSLengthUnitValue';
+import { CSSUnparsedValue } from './typed-om/CSSUnparsedValue';
+
+import { errorMsg, warnMsg } from '../../shared/logUtils';
+import { isAllowedShortFormValue } from './isAllowedShortFormValue';
+import { isAllowedStyleKey } from './isAllowedStyleKey';
+import { parseTextShadow } from './parseTextShadow';
+import { parseTimeValue } from './parseTimeValue';
+import { parseTransform } from './parseTransform';
+import { stringContainsVariables } from './customProperties';
+
+// Keys that should be excluded from inherit/unset processing
+const excludedFromInherit = new Set<string>([
+  ':active',
+  ':focus',
+  ':hover',
+  'default'
+]);
+
+const inheritedProperties = new Set<string>([
+  'color',
+  'cursor',
+  'fontFamily',
+  'fontSize',
+  'fontStyle',
+  'fontVariant',
+  'fontWeight',
+  'letterSpacing',
+  'lineHeight',
+  'textAlign',
+  'textDecorationColor',
+  'textDecorationLine',
+  'textDecorationStyle',
+  'textIndent',
+  'textTransform',
+  'whiteSpace'
+]);
+
+const placeContentValidValues = new Set<string>([
+  'center',
+  'flex-end',
+  'flex-start',
+  // distributed alignment
+  'space-around',
+  'space-between',
+  'space-evenly'
+]);
+
+const timeValuedProperties = new Set<string>([
+  'animationDelay',
+  'animationDuration',
+  'transitionDelay',
+  'transitionDuration'
+]);
+
+const unsupportedValues = new Set<string>(['currentcolor', 'initial']);
+
+export function processStyle(
+  style: { +[string]: mixed },
+  skipValidation?: boolean
+): { +[string]: mixed } {
+  const result: { [string]: mixed } = {};
+
+  for (const propName in style) {
+    const styleValue = style[propName];
+
+    if (skipValidation !== true && !isAllowedStyleKey(propName)) {
+      if (__DEV__) {
+        warnMsg(`unsupported style property "${propName}"`);
+      }
+      continue;
+    }
+
+    // Object values
+    else if (typeof styleValue === 'object' && styleValue != null) {
+      if (propName === '::placeholder') {
+        const placeholderStyleProps = Object.keys(styleValue);
+        for (let i = 0; i < placeholderStyleProps.length; i++) {
+          const prop = placeholderStyleProps[i];
+          if (prop === 'color') {
+            result['placeholderTextColor'] = processStyle({
+              color: styleValue.color
+            }).color;
+          } else {
+            if (__DEV__) {
+              warnMsg(`unsupported "::placeholder" style property "${prop}"`);
+            }
+          }
+        }
+        continue;
+      } else if (Object.hasOwn(styleValue, 'default')) {
+        result[propName] = processStyle(styleValue);
+        continue;
+      }
+    }
+
+    // String values
+    else if (typeof styleValue === 'string') {
+      // Polyfill support for string '0' on Android
+      if (styleValue === '0') {
+        result[propName] = 0;
+        continue;
+      }
+      // Polyfill support for string opacity on Android
+      if (propName === 'opacity') {
+        result[propName] = parseFloat(styleValue);
+        continue;
+      }
+      // Polyfill support for custom property references (do this first)
+      else if (stringContainsVariables(styleValue)) {
+        result[propName] = CSSUnparsedValue.parse(propName, styleValue);
+        continue;
+      } else if (
+        propName === 'caretColor' &&
+        (typeof styleValue === 'undefined' || styleValue === 'auto')
+      ) {
+        if (__DEV__) {
+          warnMsg(
+            `unsupported style value in "${propName}:${String(styleValue)}"`
+          );
+        }
+        continue;
+      } else if (propName === 'backgroundImage') {
+        result.experimental_backgroundImage = styleValue;
+        continue;
+      }
+      // Workaround unsupported objectFit values
+      else if (propName === 'objectFit' && styleValue === 'none') {
+        result[propName] = 'scale-down';
+        continue;
+      }
+      // Polyfill placeContent
+      else if (propName === 'placeContent') {
+        // None of these values are supported in RN for both properties.
+        if (!placeContentValidValues.has(styleValue)) {
+          if (__DEV__) {
+            warnMsg(
+              `unsupported style value in "${propName}:${String(styleValue)}"`
+            );
+          }
+          continue;
+        }
+      }
+      // Workaround unsupported position values
+      else if (
+        propName === 'position' &&
+        (styleValue === 'fixed' || styleValue === 'sticky')
+      ) {
+        const fallback = styleValue === 'fixed' ? 'absolute' : 'relative';
+        if (__DEV__) {
+          warnMsg(
+            `unsupported style value in "position:${styleValue}". Falling back to "position:${fallback}".`
+          );
+        }
+        result[propName] = fallback;
+        continue;
+      }
+      // Workaround unsupported textAlign values
+      // https://github.com/facebook/react-native/issues/45255
+      else if (propName === 'textAlign') {
+        if (styleValue === 'start') {
+          result[propName] = 'left';
+        } else if (styleValue === 'end') {
+          result[propName] = 'right';
+        } else {
+          result[propName] = styleValue;
+        }
+        continue;
+      }
+      // Polyfill textShadow
+      else if (propName === 'textShadow') {
+        result[propName] = parseTextShadow(styleValue);
+        continue;
+      }
+      // Polyfill transform as string value
+      else if (propName === 'transform') {
+        result[propName] = parseTransform(styleValue);
+        continue;
+      }
+      // Polyfill time-valued string values (e.g., '1000ms' => 1000)
+      else if (timeValuedProperties.has(propName)) {
+        result[propName] = parseTimeValue(styleValue);
+        continue;
+      }
+
+      const maybeLengthUnitValue = CSSLengthUnitValue.parse(styleValue);
+      if (maybeLengthUnitValue != null) {
+        result[propName] = maybeLengthUnitValue;
+        continue;
+        // React Native doesn't support these keywords or functions
+      } else if (styleValue === 'inherit' || styleValue === 'unset') {
+        // direction has native support for 'inherit'
+        if (propName === 'direction') {
+          result[propName] = 'inherit';
+          continue;
+        }
+        // inherited properties polyfill 'inherit' in useStyleProps
+        else if (
+          !excludedFromInherit.has(propName) &&
+          !inheritedProperties.has(propName)
+        ) {
+          if (__DEV__) {
+            warnMsg(
+              `unsupported style value in "${propName}:${String(styleValue)}"`
+            );
+          }
+          continue;
+        }
+      } else if (
+        unsupportedValues.has(styleValue) ||
+        styleValue.includes('calc(')
+      ) {
+        if (__DEV__) {
+          warnMsg(
+            `unsupported style value in "${propName}:${String(styleValue)}"`
+          );
+        }
+        continue;
+      } else if (!isAllowedShortFormValue(propName, styleValue)) {
+        if (__DEV__) {
+          errorMsg(
+            `invalid style value in "${propName}:${String(styleValue)}". Shortform properties cannot contain multiple values. Please use longform properties.`
+          );
+        }
+        continue;
+      }
+    }
+
+    // Number values
+    else if (typeof styleValue === 'number') {
+      // Polyfill numeric fontWeight (for desktop)
+      if (propName === 'fontWeight') {
+        result[propName] = styleValue.toString();
+        continue;
+      }
+      // Normalize unitless lineHeight to string
+      if (propName === 'lineHeight') {
+        result[propName] = styleValue.toString();
+        continue;
+      }
+    }
+
+    // Everything else
+    result[propName] = styleValue;
+  }
+
+  return result;
+}
diff --git a/packages/react-strict-dom/src/native/stylex/typed-om/CSSStyleValue.js b/packages/react-strict-dom/src/native/css/typed-om/CSSStyleValue.js
similarity index 100%
rename from packages/react-strict-dom/src/native/stylex/typed-om/CSSStyleValue.js
rename to packages/react-strict-dom/src/native/css/typed-om/CSSStyleValue.js
diff --git a/packages/react-strict-dom/src/native/stylex/typed-om/CSSUnparsedValue.js b/packages/react-strict-dom/src/native/css/typed-om/CSSUnparsedValue.js
similarity index 84%
rename from packages/react-strict-dom/src/native/stylex/typed-om/CSSUnparsedValue.js
rename to packages/react-strict-dom/src/native/css/typed-om/CSSUnparsedValue.js
index 0389d3a8..58c72231 100644
--- a/packages/react-strict-dom/src/native/stylex/typed-om/CSSUnparsedValue.js
+++ b/packages/react-strict-dom/src/native/css/typed-om/CSSUnparsedValue.js
@@ -7,8 +7,9 @@
  * @flow strict
  */
 
+import type { CSSValueASTNode } from '../../../types/value-parser';
+
 import valueParser from 'postcss-value-parser';
-import { CSSStyleValue } from './CSSStyleValue';
 import { CSSVariableReferenceValue } from './CSSVariableReferenceValue';
 import { errorMsg, warnMsg } from '../../../shared/logUtils';
 
@@ -18,8 +19,8 @@ type CSSUnparsedSegment = string | CSSVariableReferenceValue;
 const MAX_RESOLVE_DEPTH = 50;
 
 function splitComponentValueListByComma(
-  input: PostCSSValueASTNode[]
-): PostCSSValueASTNode[][] {
+  input: CSSValueASTNode[]
+): CSSValueASTNode[][] {
   const output = [];
   let current = [];
   for (const value of input) {
@@ -39,8 +40,8 @@ function splitComponentValueListByComma(
 const memoizedValues = new Map<string, CSSUnparsedValue>();
 
 // https://drafts.css-houdini.org/css-typed-om-1/#cssunparsedvalue
-export class CSSUnparsedValue extends CSSStyleValue {
-  static #resolveVariableName(input: PostCSSValueASTNode[]): string | null {
+export class CSSUnparsedValue /*extends CSSStyleValue*/ {
+  static _resolveVariableName(input: CSSValueASTNode[]): string | null {
     const cleanedInput = input.filter((i) => i.type === 'word');
     if (cleanedInput.length !== 1) {
       return null;
@@ -48,8 +49,8 @@ export class CSSUnparsedValue extends CSSStyleValue {
     return valueParser.stringify(cleanedInput[0]);
   }
 
-  static #resolveUnparsedValue(
-    input: PostCSSValueASTNode[],
+  static _resolveUnparsedValue(
+    input: CSSValueASTNode[],
     depth: number = 0
   ): CSSUnparsedValue {
     if (depth > MAX_RESOLVE_DEPTH) {
@@ -78,7 +79,7 @@ export class CSSUnparsedValue extends CSSStyleValue {
       if (currentValue.type === 'function') {
         if (currentValue.value === 'var') {
           const args = splitComponentValueListByComma(currentValue.nodes);
-          const variableName = CSSUnparsedValue.#resolveVariableName(args[0]);
+          const variableName = CSSUnparsedValue._resolveVariableName(args[0]);
           if (variableName == null) {
             if (__DEV__) {
               warnMsg(
@@ -92,7 +93,7 @@ export class CSSUnparsedValue extends CSSStyleValue {
 
           const fallbackValue =
             args[1] != null
-              ? CSSUnparsedValue.#resolveUnparsedValue(args[1], depth + 1)
+              ? CSSUnparsedValue._resolveUnparsedValue(args[1], depth + 1)
               : undefined;
 
           try {
@@ -110,7 +111,7 @@ export class CSSUnparsedValue extends CSSStyleValue {
         } else {
           // stringify the function manually but still attempt to resolve the args
           appendString(`${currentValue.value}(`);
-          const functionArgs = CSSUnparsedValue.#resolveUnparsedValue(
+          const functionArgs = CSSUnparsedValue._resolveUnparsedValue(
             currentValue.nodes,
             depth + 1
           );
@@ -140,32 +141,32 @@ export class CSSUnparsedValue extends CSSStyleValue {
     }
     const componentValueList = valueParser(input).nodes;
     const parsedValue =
-      CSSUnparsedValue.#resolveUnparsedValue(componentValueList);
+      CSSUnparsedValue._resolveUnparsedValue(componentValueList);
     memoizedValues.set(input, parsedValue);
     return parsedValue;
   }
 
-  #tokens: CSSUnparsedSegment[];
+  _tokens: CSSUnparsedSegment[];
 
   constructor(members: CSSUnparsedSegment[]) {
-    super();
-    this.#tokens = members;
+    // No super() call because it's slow in Hermes
+    this._tokens = members;
   }
 
   get(index: number): CSSUnparsedSegment {
-    return this.#tokens[index];
+    return this._tokens[index];
   }
 
   get size(): number {
-    return this.#tokens.length;
+    return this._tokens.length;
   }
 
   values(): Iterator<CSSUnparsedSegment> {
-    return this.#tokens.values();
+    return this._tokens.values();
   }
 
   toString(): string {
-    return this.#tokens
+    return this._tokens
       .map((t) => t.toString())
       .join('')
       .trim();
diff --git a/packages/react-strict-dom/src/native/stylex/typed-om/CSSVariableReferenceValue.js b/packages/react-strict-dom/src/native/css/typed-om/CSSVariableReferenceValue.js
similarity index 59%
rename from packages/react-strict-dom/src/native/stylex/typed-om/CSSVariableReferenceValue.js
rename to packages/react-strict-dom/src/native/css/typed-om/CSSVariableReferenceValue.js
index 50e68c3f..e0ca3bc8 100644
--- a/packages/react-strict-dom/src/native/stylex/typed-om/CSSVariableReferenceValue.js
+++ b/packages/react-strict-dom/src/native/css/typed-om/CSSVariableReferenceValue.js
@@ -9,44 +9,42 @@
 
 import type { CSSUnparsedValue } from './CSSUnparsedValue';
 
-import { CSSStyleValue } from './CSSStyleValue';
-
 // https://drafts.css-houdini.org/css-typed-om-1/#cssvariablereferencevalue
-export class CSSVariableReferenceValue extends CSSStyleValue {
+export class CSSVariableReferenceValue /*extends CSSStyleValue*/ {
   // https://drafts.css-houdini.org/css-typed-om-1/#custom-property-name-string
-  static #validateVariableName(variable: string): void {
+  static _validateVariableName(variable: string): void {
     if (!variable.startsWith('--')) {
       throw new TypeError(`Invalid custom property name: ${variable}`);
     }
   }
 
-  #variable: string;
-  #fallback: CSSUnparsedValue | null;
+  _variable: string;
+  _fallback: CSSUnparsedValue | null;
 
   constructor(variable: string, fallback?: CSSUnparsedValue) {
     if (__DEV__) {
-      CSSVariableReferenceValue.#validateVariableName(variable);
+      CSSVariableReferenceValue._validateVariableName(variable);
     }
-    super();
-    this.#variable = variable;
-    this.#fallback = fallback ?? null;
+    // No super() call because it's slow in Hermes
+    this._variable = variable;
+    this._fallback = fallback ?? null;
   }
 
   get variable(): string {
-    return this.#variable;
+    return this._variable;
   }
 
   set variable(variable: string): void {
-    this.#variable = variable;
+    this._variable = variable;
   }
 
   get fallback(): CSSUnparsedValue | null {
-    return this.#fallback;
+    return this._fallback;
   }
 
   toString(): string {
-    return `var(${this.#variable}${
-      this.#fallback ? `, ${this.#fallback.toString()}` : ''
+    return `var(${this._variable}${
+      this._fallback ? `, ${this._fallback.toString()}` : ''
     })`;
   }
 }
diff --git a/packages/react-strict-dom/src/native/html.js b/packages/react-strict-dom/src/native/html.js
index 5030c2ad..7331566f 100644
--- a/packages/react-strict-dom/src/native/html.js
+++ b/packages/react-strict-dom/src/native/html.js
@@ -29,9 +29,9 @@ import { createStrictDOMTextComponent as createStrictText } from './modules/crea
 import { createStrictDOMTextInputComponent as createStrictTextInput } from './modules/createStrictDOMTextInputComponent';
 // $FlowFixMe[nonstrict-import]
 import * as ReactNative from './react-native';
-import * as stylex from './stylex';
+import * as css from './css';
 
-const styles = stylex.create({
+const styles = css.create({
   bold: {
     fontWeight: 'bold'
   },
diff --git a/packages/react-strict-dom/src/native/index.js b/packages/react-strict-dom/src/native/index.js
index 6eb983ac..a586ce4c 100644
--- a/packages/react-strict-dom/src/native/index.js
+++ b/packages/react-strict-dom/src/native/index.js
@@ -20,8 +20,9 @@ import typeof * as TStyleX from '@stylexjs/stylex';
 import * as React from 'react';
 import * as compat from './compat';
 import * as html from './html';
-import * as stylex from './stylex';
+import * as _css from './css';
 import { ProvideCustomProperties } from './modules/ContextCustomProperties';
+import { ProvideViewportScale } from './modules/ContextViewportScale';
 
 type StyleTheme<V, T> = Theme<V, T>;
 type StyleVars<T> = VarGroup<T>;
@@ -48,10 +49,11 @@ function ThemeProvider(props: ProviderProps): React.Node {
 }
 
 const contexts = {
-  ThemeProvider: ThemeProvider as typeof ThemeProvider
+  ThemeProvider: ThemeProvider as typeof ThemeProvider,
+  ViewportProvider: ProvideViewportScale as typeof ProvideViewportScale
 };
 
 // Export using StyleX types as the shim has divergent types internally.
-const css: TStyleX = stylex as $FlowFixMe;
+const css: TStyleX = _css as $FlowFixMe;
 
-export { contexts, compat, css, html };
+export { compat, contexts, css, html };
diff --git a/packages/react-strict-dom/src/native/modules/ContextCustomProperties.js b/packages/react-strict-dom/src/native/modules/ContextCustomProperties.js
index 7446dfcd..eaeee5a1 100644
--- a/packages/react-strict-dom/src/native/modules/ContextCustomProperties.js
+++ b/packages/react-strict-dom/src/native/modules/ContextCustomProperties.js
@@ -10,7 +10,7 @@
 import type { CustomProperties } from '../../types/styles';
 
 import * as React from 'react';
-import { __customProperties } from '../stylex';
+import { __customProperties } from '../css';
 
 const defaultContext = __customProperties;
 const ContextCustomProperties: React.Context<CustomProperties> =
@@ -26,14 +26,14 @@ export function useCustomProperties(
   customPropertiesFromThemes: ?CustomProperties
 ): CustomProperties {
   const inheritedCustomProperties = React.useContext(ContextCustomProperties);
-  if (customPropertiesFromThemes == null) {
-    return inheritedCustomProperties;
-  }
-  // TODO: optimize
-  const customProperties = Object.assign(
-    {},
-    inheritedCustomProperties,
-    customPropertiesFromThemes
-  );
-  return customProperties;
+  return React.useMemo(() => {
+    if (customPropertiesFromThemes == null) {
+      return inheritedCustomProperties;
+    }
+    return Object.assign(
+      {},
+      inheritedCustomProperties,
+      customPropertiesFromThemes
+    );
+  }, [inheritedCustomProperties, customPropertiesFromThemes]);
 }
diff --git a/packages/react-strict-dom/src/native/modules/ContextInheritedStyles.js b/packages/react-strict-dom/src/native/modules/ContextInheritedStyles.js
index 35482f58..4027d4dd 100644
--- a/packages/react-strict-dom/src/native/modules/ContextInheritedStyles.js
+++ b/packages/react-strict-dom/src/native/modules/ContextInheritedStyles.js
@@ -29,7 +29,7 @@ if (__DEV__) {
 }
 
 // We do some special-casing to compute inherited fontSize.
-// The 'em' unit polyfill in stylex assumes the inherited fontSize is always a computed number.
+// The 'em' unit polyfill assumes the inherited fontSize is always a computed number.
 export function ProvideInheritedStyles(props: ProviderProps): React.Node {
   const { children, value } = props;
   const inheritedStyles = useInheritedStyles();
diff --git a/packages/react-strict-dom/src/native/modules/ContextViewportScale.js b/packages/react-strict-dom/src/native/modules/ContextViewportScale.js
new file mode 100644
index 00000000..eab7e791
--- /dev/null
+++ b/packages/react-strict-dom/src/native/modules/ContextViewportScale.js
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow strict-local
+ */
+
+import * as React from 'react';
+import * as ReactNative from '../react-native';
+
+type Value = $ReadOnly<{
+  scale: number
+}>;
+
+type ProviderProps = $ReadOnly<{
+  children: React.Node,
+  viewportWidth: number
+}>;
+
+const defaultContext = { scale: 1 };
+const ContextViewportScale: React.Context<Value> =
+  React.createContext(defaultContext);
+
+if (__DEV__) {
+  ContextViewportScale.displayName = 'ContextViewportScale';
+}
+
+export function ProvideViewportScale({
+  viewportWidth: logicalViewportWidth,
+  children
+}: ProviderProps): React.Node {
+  const { width: viewportWidth } = ReactNative.useWindowDimensions();
+
+  const viewportScale = React.useMemo(
+    () => ({
+      scale: viewportWidth / logicalViewportWidth
+    }),
+    [logicalViewportWidth, viewportWidth]
+  );
+
+  return (
+    <ContextViewportScale.Provider value={viewportScale}>
+      {children}
+    </ContextViewportScale.Provider>
+  );
+}
+
+export function useViewportScale(): Value {
+  return React.useContext(ContextViewportScale);
+}
diff --git a/packages/react-strict-dom/src/native/modules/createStrictDOMComponent.js b/packages/react-strict-dom/src/native/modules/createStrictDOMComponent.js
index b73c48cb..42ae5499 100644
--- a/packages/react-strict-dom/src/native/modules/createStrictDOMComponent.js
+++ b/packages/react-strict-dom/src/native/modules/createStrictDOMComponent.js
@@ -18,7 +18,6 @@ import { ProvideDisplayInside, useDisplayInside } from './ContextDisplayInside';
 import { ProvideInheritedStyles } from './ContextInheritedStyles';
 import { TextString } from './TextString';
 import { warnMsg } from '../../shared/logUtils';
-import { mergeRefs } from '../../shared/mergeRefs';
 import { useNativeProps } from './useNativeProps';
 import { useStrictDOMElement } from './useStrictDOMElement';
 
@@ -42,7 +41,7 @@ export function createStrictDOMComponent<T, P: StrictProps>(
         tagName === 'button'
           ? ReactNative.Pressable
           : ReactNative.ViewNativeComponent;
-      const elementRef = useStrictDOMElement<T>({ tagName });
+      const elementRef = useStrictDOMElement<T>(forwardedRef, { tagName });
       const hasTextAncestor = React.useContext(ReactNative.TextAncestorContext);
 
       /**
@@ -85,10 +84,7 @@ export function createStrictDOMComponent<T, P: StrictProps>(
         }
       }
 
-      nativeProps.ref = React.useMemo(
-        () => mergeRefs(elementRef, forwardedRef),
-        [elementRef, forwardedRef]
-      );
+      nativeProps.ref = elementRef;
 
       // Workaround: React Native doesn't support raw text children of View
       // Sometimes we can auto-fix this
diff --git a/packages/react-strict-dom/src/native/modules/createStrictDOMImageComponent.js b/packages/react-strict-dom/src/native/modules/createStrictDOMImageComponent.js
index 24313f67..adb596ed 100644
--- a/packages/react-strict-dom/src/native/modules/createStrictDOMImageComponent.js
+++ b/packages/react-strict-dom/src/native/modules/createStrictDOMImageComponent.js
@@ -12,10 +12,9 @@ import type { StrictReactDOMImageProps } from '../../types/StrictReactDOMImagePr
 import * as React from 'react';
 import * as ReactNative from '../react-native';
 
-import { mergeRefs } from '../../shared/mergeRefs';
 import { useNativeProps } from './useNativeProps';
 import { useStrictDOMElement } from './useStrictDOMElement';
-import * as stylex from '../stylex';
+import * as css from '../css';
 
 export function createStrictDOMImageComponent<P: StrictReactDOMImageProps, T>(
   tagName: string,
@@ -24,7 +23,7 @@ export function createStrictDOMImageComponent<P: StrictReactDOMImageProps, T>(
   const component: React.AbstractComponent<P, T> = React.forwardRef(
     function (props, forwardedRef) {
       let NativeComponent = ReactNative.Image;
-      const elementRef = useStrictDOMElement<T>({ tagName });
+      const elementRef = useStrictDOMElement<T>(forwardedRef, { tagName });
 
       const {
         alt,
@@ -100,10 +99,7 @@ export function createStrictDOMImageComponent<P: StrictReactDOMImageProps, T>(
 
       // Component-specific props
 
-      nativeProps.ref = React.useMemo(
-        () => mergeRefs(elementRef, forwardedRef),
-        [elementRef, forwardedRef]
-      );
+      nativeProps.ref = elementRef;
 
       // Use Animated components if necessary
       if (nativeProps.animated === true) {
@@ -126,7 +122,7 @@ export function createStrictDOMImageComponent<P: StrictReactDOMImageProps, T>(
   return component;
 }
 
-const styles = stylex.create({
+const styles = css.create({
   aspectRatio: (width: number, height: number) => ({
     aspectRatio: width / height,
     width,
diff --git a/packages/react-strict-dom/src/native/modules/createStrictDOMTextComponent.js b/packages/react-strict-dom/src/native/modules/createStrictDOMTextComponent.js
index 2f10ce48..7835b79c 100644
--- a/packages/react-strict-dom/src/native/modules/createStrictDOMTextComponent.js
+++ b/packages/react-strict-dom/src/native/modules/createStrictDOMTextComponent.js
@@ -16,7 +16,6 @@ import * as ReactNative from '../react-native';
 import { ProvideCustomProperties } from './ContextCustomProperties';
 import { ProvideInheritedStyles } from './ContextInheritedStyles';
 import { errorMsg } from '../../shared/logUtils';
-import { mergeRefs } from '../../shared/mergeRefs';
 import { useNativeProps } from './useNativeProps';
 import { useStrictDOMElement } from './useStrictDOMElement';
 
@@ -36,7 +35,7 @@ export function createStrictDOMTextComponent<T, P: StrictProps>(
   const component: React.AbstractComponent<P, T> = React.forwardRef(
     function (props, forwardedRef) {
       let NativeComponent = ReactNative.Text;
-      const elementRef = useStrictDOMElement<T>({ tagName });
+      const elementRef = useStrictDOMElement<T>(forwardedRef, { tagName });
 
       const { href, label } = props;
 
@@ -84,10 +83,7 @@ export function createStrictDOMTextComponent<T, P: StrictProps>(
 
       // Component-specific props
 
-      nativeProps.ref = React.useMemo(
-        () => mergeRefs(elementRef, forwardedRef),
-        [elementRef, forwardedRef]
-      );
+      nativeProps.ref = elementRef;
 
       // Workaround: Android doesn't support ellipsis truncation if Text is selectable
       // See #136
diff --git a/packages/react-strict-dom/src/native/modules/createStrictDOMTextInputComponent.js b/packages/react-strict-dom/src/native/modules/createStrictDOMTextInputComponent.js
index c1fbeafa..ed26de17 100644
--- a/packages/react-strict-dom/src/native/modules/createStrictDOMTextInputComponent.js
+++ b/packages/react-strict-dom/src/native/modules/createStrictDOMTextInputComponent.js
@@ -14,7 +14,6 @@ import * as React from 'react';
 import * as ReactNative from '../react-native';
 
 import { errorMsg } from '../../shared/logUtils';
-import { mergeRefs } from '../../shared/mergeRefs';
 import { useNativeProps } from './useNativeProps';
 import { useStrictDOMElement } from './useStrictDOMElement';
 
@@ -35,7 +34,7 @@ export function createStrictDOMTextInputComponent<
       let NativeComponent:
         | typeof ReactNative.TextInput
         | typeof AnimatedTextInput = ReactNative.TextInput;
-      const elementRef = useStrictDOMElement<T>({ tagName });
+      const elementRef = useStrictDOMElement<T>(forwardedRef, { tagName });
 
       const {
         autoComplete,
@@ -180,10 +179,7 @@ export function createStrictDOMTextInputComponent<
         nativeProps.value = value;
       }
 
-      nativeProps.ref = React.useMemo(
-        () => mergeRefs(elementRef, forwardedRef),
-        [elementRef, forwardedRef]
-      );
+      nativeProps.ref = elementRef;
 
       // Use Animated components if necessary
       if (nativeProps.animated === true) {
diff --git a/packages/react-strict-dom/src/native/modules/useNativeProps.js b/packages/react-strict-dom/src/native/modules/useNativeProps.js
index 3c659d1a..3de22e35 100644
--- a/packages/react-strict-dom/src/native/modules/useNativeProps.js
+++ b/packages/react-strict-dom/src/native/modules/useNativeProps.js
@@ -12,7 +12,7 @@ import type { ReactNativeProps } from '../../types/renderer.native';
 import type { StrictProps as StrictPropsOriginal } from '../../types/StrictProps';
 import type { Style } from '../../types/styles';
 
-import * as stylex from '../stylex';
+import * as css from '../css';
 import { errorMsg, warnMsg } from '../../shared/logUtils';
 import { extractStyleThemes } from './extractStyleThemes';
 import { isPropAllowed } from '../../shared/isPropAllowed';
@@ -421,7 +421,7 @@ export function useNativeProps(
   };
 }
 
-const styles = stylex.create({
+const styles = css.create({
   contentBox: {
     boxSizing: 'content-box'
   },
diff --git a/packages/react-strict-dom/src/native/modules/useStrictDOMElement.js b/packages/react-strict-dom/src/native/modules/useStrictDOMElement.js
index 663cf76d..8df9be26 100644
--- a/packages/react-strict-dom/src/native/modules/useStrictDOMElement.js
+++ b/packages/react-strict-dom/src/native/modules/useStrictDOMElement.js
@@ -4,15 +4,23 @@
  * This source code is licensed under the MIT license found in the
  * LICENSE file in the root directory of this source tree.
  *
- * @flow strict
+ * @flow strict-local
  */
 
 import type { CallbackRef } from '../../types/react';
 
 import * as React from 'react';
 
-import { useElementCallback } from '../../shared/useElementCallback';
 import { errorMsg } from '../../shared/logUtils';
+import { useElementCallback } from '../../shared/useElementCallback';
+import { useViewportScale } from './ContextViewportScale';
+
+type Options = {
+  tagName: string
+};
+
+// $FlowFixMe[unclear-type]
+type Node = any;
 
 function errorUnimplemented(name: string) {
   if (__DEV__) {
@@ -20,144 +28,116 @@ function errorUnimplemented(name: string) {
   }
 }
 
-/*
-function errorUnimplementedEvent(name: string) {
-  warnMsg(
-    `unsupported event type "${name}"`
-  );
+function proxy(node: Node, name: string) {
+  return (...args: Array<mixed>) => {
+    if (node?.[name]) {
+      return node[name](...args);
+    }
+    errorUnimplemented(name);
+  };
 }
-*/
 
-type Options = {
-  tagName: string
-};
+const memoizedStrictRefs: WeakMap<Node, mixed> = new WeakMap();
+
+function getOrCreateStrictRef(
+  node: Node,
+  tagName: string,
+  viewportScale: number
+) {
+  const ref = memoizedStrictRefs.get(node);
+  if (ref != null) {
+    return ref;
+  } else {
+    const strictRef = {
+      get __nativeTag() {
+        return node?.__nativeTag;
+      },
+      addEventListener: proxy(node, 'addEventListener'),
+      animate: proxy(node, 'animate'),
+      blur: proxy(node, 'blur'),
+      click: proxy(node, 'click'),
+      get complete() {
+        if (tagName === 'img') {
+          if (node?.complete == null) {
+            // Assume images are never pre-loaded in React Native
+            return false;
+          } else {
+            return node.complete;
+          }
+        }
+      },
+      contains: proxy(node, 'contains'),
+      dispatchEvent: proxy(node, 'dispatchEvent'),
+      focus: proxy(node, 'focus'),
+      getAttribute: proxy(node, 'getAttribute'),
+      getBoundingClientRect() {
+        const getBoundingClientRect =
+          node?.getBoundingClientRect ?? node?.unstable_getBoundingClientRect;
+        if (getBoundingClientRect) {
+          const rect = getBoundingClientRect.call(node);
+          if (viewportScale !== 1) {
+            return new DOMRect(
+              rect.x / viewportScale,
+              rect.y / viewportScale,
+              rect.width / viewportScale,
+              rect.height / viewportScale
+            );
+          }
+          return rect;
+        }
+        return errorUnimplemented('getBoundingClientRect');
+      },
+      getRootNode: proxy(node, 'getRootNode'),
+      hasPointerCapture: proxy(node, 'hasPointerCapture'),
+      nodeName: tagName.toUpperCase(),
+      releasePointerCapture: proxy(node, 'releasePointerCapture'),
+      removeEventListener: proxy(node, 'removeEventListener'),
+      scroll: proxy(node, 'scroll'),
+      scrollBy: proxy(node, 'scrollBy'),
+      scrollIntoView: proxy(node, 'scrollIntoView'),
+      scrollTo: proxy(node, 'scrollTo'),
+      select: proxy(node, 'select'),
+      setSelectionRange: proxy(node, 'setSelectionRange'),
+      setPointerCapture: proxy(node, 'setPointerCapture'),
+      showPicker: proxy(node, 'showPicker')
+    };
+
+    memoizedStrictRefs.set(node, strictRef);
+    return strictRef;
+  }
+}
+
+export function useStrictDOMElement<T>(
+  ref: React.RefSetter<Node>,
+  { tagName }: Options
+): CallbackRef<T> {
+  const { scale: viewportScale } = useViewportScale();
 
-export function useStrictDOMElement<T>({ tagName }: Options): CallbackRef<T> {
   const elementCallback = useElementCallback(
     React.useCallback(
       // $FlowFixMe[unclear-type]
       (node: any) => {
-        Object.defineProperty(node, 'nodeName', {
-          value: tagName.toUpperCase(),
-          writable: false
-        });
-
-        const { addEventListener } = node;
-        if (addEventListener == null) {
-          node.addEventListener = () => errorUnimplemented('addEventListener');
-        }
-
-        const { animate } = node;
-        if (animate == null) {
-          node.animate = () => errorUnimplemented('animate');
-        }
-
-        const { click } = node;
-        if (click == null) {
-          node.click = () => errorUnimplemented('click');
-        }
-
-        const { contains } = node;
-        if (contains == null) {
-          node.contains = () => errorUnimplemented('contains');
-        }
-
-        const { dispatchEvent } = node;
-        if (dispatchEvent == null) {
-          node.dispatchEvent = () => errorUnimplemented('dispatchEvent');
-        }
-
-        const { getAttribute } = node;
-        if (getAttribute == null) {
-          node.getAttribute = () => errorUnimplemented('getAttribute');
-        }
-
-        const { getBoundingClientRect } = node;
-        if (getBoundingClientRect == null) {
-          node.getBoundingClientRect = function () {
-            if (node.unstable_getBoundingClientRect != null) {
-              return node.unstable_getBoundingClientRect();
-            } else {
-              errorUnimplemented('getBoundingClientRect');
-            }
-          };
-        }
-
-        const { getRootNode } = node;
-        if (getRootNode == null) {
-          node.getRootNode = () => errorUnimplemented('getRootNode');
-        }
-
-        const { hasPointerCapture } = node;
-        if (hasPointerCapture == null) {
-          node.hasPointerCapture = () =>
-            errorUnimplemented('hasPointerCapture');
-        }
-
-        const { releasePointerCapture } = node;
-        if (releasePointerCapture == null) {
-          node.releasePointerCapture = () =>
-            errorUnimplemented('releasePointerCapture');
-        }
-
-        const { removeEventListener } = node;
-        if (removeEventListener == null) {
-          node.removeEventListener = () =>
-            errorUnimplemented('removeEventListener');
-        }
-
-        const { scroll } = node;
-        if (scroll == null) {
-          node.scroll = () => errorUnimplemented('scroll');
-        }
-
-        const { scrollBy } = node;
-        if (scrollBy == null) {
-          node.scrollBy = () => errorUnimplemented('scrollBy');
-        }
-
-        const { scrollIntoView } = node;
-        if (scrollIntoView == null) {
-          node.scrollIntoView = () => errorUnimplemented('scrollIntoView');
-        }
-
-        const { scrollTo } = node;
-        if (scrollTo == null) {
-          node.scrollTo = () => errorUnimplemented('scrollTo');
-        }
-
-        const { setPointerCapture } = node;
-        if (setPointerCapture == null) {
-          node.setPointerCapture = () =>
-            errorUnimplemented('setPointerCapture');
-        }
-
-        if (tagName === 'input' || tagName === 'textarea') {
-          const { select } = node;
-          if (select == null) {
-            node.select = () => errorUnimplemented('select');
-          }
-
-          const { setSelectionRange } = node;
-          if (setSelectionRange == null) {
-            node.setSelectionRange = () =>
-              errorUnimplemented('setSelectionRange');
-          }
-        }
-        if (tagName === 'input') {
-          const { showPicker } = node;
-          if (showPicker == null) {
-            node.showPicker = () => errorUnimplemented('showPicker');
-          }
-        }
-        if (tagName === 'img') {
-          if (node.complete == null) {
-            // Assume images are never pre-loaded in React Native
-            node.complete = false;
+        if (ref == null) {
+          return undefined;
+        } else {
+          const strictRef = getOrCreateStrictRef(node, tagName, viewportScale);
+          if (typeof ref === 'function') {
+            // $FlowFixMe[incompatible-type] - Flow does not understand ref cleanup.
+            const cleanup: void | (() => void) = ref(strictRef);
+            return typeof cleanup === 'function'
+              ? cleanup
+              : () => {
+                  ref(null);
+                };
+          } else {
+            ref.current = strictRef;
+            return () => {
+              ref.current = null;
+            };
           }
         }
       },
-      [tagName]
+      [ref, tagName, viewportScale]
     )
   );
 
diff --git a/packages/react-strict-dom/src/native/modules/useStyleProps.js b/packages/react-strict-dom/src/native/modules/useStyleProps.js
index ed604680..cb64c9fd 100644
--- a/packages/react-strict-dom/src/native/modules/useStyleProps.js
+++ b/packages/react-strict-dom/src/native/modules/useStyleProps.js
@@ -8,18 +8,49 @@
  */
 
 import type { CustomProperties, Styles, Style } from '../../types/styles';
-import type {
-  ReactNativeProps,
-  ReactNativeStyle
-} from '../../types/renderer.native';
+import type { ReactNativeProps } from '../../types/renderer.native';
+import type { ReactNativeStyle } from '../../types/renderer.native';
 
-import * as stylex from '../stylex';
+import * as css from '../css';
 import * as ReactNative from '../react-native';
 
 import { flattenStyle } from './flattenStyle';
 import { useInheritedStyles } from './ContextInheritedStyles';
 import { usePseudoStates } from './usePseudoStates';
 import { useStyleTransition } from './useStyleTransition';
+import { useViewportScale } from './ContextViewportScale';
+
+const inheritedProperties = [
+  'color',
+  'cursor',
+  'fontFamily',
+  'fontSize',
+  'fontStyle',
+  'fontVariant',
+  'fontWeight',
+  'letterSpacing',
+  'lineHeight',
+  'textAlign',
+  'textDecorationColor',
+  'textDecorationLine',
+  'textDecorationStyle',
+  'textIndent',
+  'textTransform',
+  'whiteSpace',
+  'writingDirection'
+];
+
+const eventHandlerNames = [
+  'onBlur',
+  'onFocus',
+  'onMouseEnter',
+  'onMouseLeave',
+  'onPointerCancel',
+  'onPointerDown',
+  'onPointerEnter',
+  'onPointerLeave',
+  'onPointerUp'
+];
 
 type StyleOptions = {
   customProperties: ?CustomProperties,
@@ -66,6 +97,7 @@ export function useStyleProps(
 
   const { fontScale, height, width } = ReactNative.useWindowDimensions();
   const colorScheme = ReactNative.useColorScheme();
+  const { scale: viewportScale } = useViewportScale();
 
   // These values are already computed
   const {
@@ -93,7 +125,7 @@ export function useStyleProps(
   const { active, focus, hover, handlers } = usePseudoStates(flatStyle);
 
   // Get the computed style props
-  const styleProps = stylex.props.call(
+  const styleProps = css.props.call(
     {
       active,
       colorScheme: colorScheme === 'unspecified' ? 'light' : colorScheme,
@@ -104,25 +136,17 @@ export function useStyleProps(
       inheritedFontSize:
         typeof inheritedFontSize === 'number' ? inheritedFontSize : undefined,
       viewportHeight: height,
+      viewportScale,
       viewportWidth: width
     },
     flatStyle as $FlowFixMe
   );
 
   if (handlers != null) {
-    for (const handler of [
-      'onBlur',
-      'onFocus',
-      'onMouseEnter',
-      'onMouseLeave',
-      'onPointerCancel',
-      'onPointerDown',
-      'onPointerEnter',
-      'onPointerLeave',
-      'onPointerUp'
-    ]) {
-      if (handler != null) {
-        styleProps[handler] = handlers[handler];
+    for (const handler of eventHandlerNames) {
+      const handlerValue = handlers[handler];
+      if (handlerValue != null) {
+        styleProps[handler] = handlerValue;
       }
     }
   }
@@ -143,48 +167,34 @@ export function useStyleProps(
     styleProps.style = styleWithAnimations;
   }
 
-  const {
-    color,
-    cursor,
-    fontFamily,
-    fontSize,
-    fontStyle,
-    fontVariant,
-    fontWeight,
-    letterSpacing,
-    lineHeight,
-    textAlign,
-    textDecorationColor,
-    textDecorationLine,
-    textDecorationStyle,
-    textIndent,
-    textTransform,
-    whiteSpace,
-    writingDirection,
-    ...viewStyle
-  } = styleProps.style;
+  // Create inherited values lookup for performance
+  const inheritedValues = {
+    color: inheritedColor,
+    cursor: inheritedCursor,
+    fontFamily: inheritedFontFamily,
+    fontSize: inheritedFontSize,
+    fontStyle: inheritedFontStyle,
+    fontVariant: inheritedFontVariant,
+    fontWeight: inheritedFontWeight,
+    letterSpacing: inheritedLetterSpacing,
+    lineHeight: inheritedLineHeight,
+    textAlign: inheritedTextAlign,
+    textDecorationColor: inheritedTextDecorationColor,
+    textDecorationLine: inheritedTextDecorationLine,
+    textDecorationStyle: inheritedTextDecorationStyle,
+    textIndent: inheritedTextIndent,
+    textTransform: inheritedTextTransform,
+    whiteSpace: inheritedWhiteSpace,
+    writingDirection: inheritedWritingDirection
+  };
 
   const inheritableStyle = {} as $FlowFixMe;
+  const viewStyle = {} as $FlowFixMe;
+
+  for (const key of inheritedProperties) {
+    const value = styleProps.style[key];
+    const inheritedValue = inheritedValues[key];
 
-  [
-    ['color', color, inheritedColor],
-    ['cursor', cursor, inheritedCursor],
-    ['fontFamily', fontFamily, inheritedFontFamily],
-    ['fontSize', fontSize, inheritedFontSize],
-    ['fontStyle', fontStyle, inheritedFontStyle],
-    ['fontVariant', fontVariant, inheritedFontVariant],
-    ['fontWeight', fontWeight, inheritedFontWeight],
-    ['letterSpacing', letterSpacing, inheritedLetterSpacing],
-    ['lineHeight', lineHeight, inheritedLineHeight],
-    ['textAlign', textAlign, inheritedTextAlign],
-    ['textDecorationColor', textDecorationColor, inheritedTextDecorationColor],
-    ['textDecorationLine', textDecorationLine, inheritedTextDecorationLine],
-    ['textDecorationStyle', textDecorationStyle, inheritedTextDecorationStyle],
-    ['textIndent', textIndent, inheritedTextIndent],
-    ['textTransform', textTransform, inheritedTextTransform],
-    ['whiteSpace', whiteSpace, inheritedWhiteSpace],
-    ['writingDirection', writingDirection, inheritedWritingDirection]
-  ].forEach(([key, value, inheritedValue]) => {
     let val = value;
     if (
       (withInheritedStyle && value == null) ||
@@ -197,7 +207,14 @@ export function useStyleProps(
       inheritableStyle[key] = val;
       styleProps.style[key] = val;
     }
-  });
+  }
+
+  // Copy non-inherited properties to viewStyle
+  for (const key in styleProps.style) {
+    if (!inheritedValues.hasOwnProperty(key)) {
+      viewStyle[key] = styleProps.style[key];
+    }
+  }
 
   if (withTextStyle === true) {
     const textStyle =
diff --git a/packages/react-strict-dom/src/types/react.js b/packages/react-strict-dom/src/types/react.js
index fbda1e76..fdbf4aa8 100644
--- a/packages/react-strict-dom/src/types/react.js
+++ b/packages/react-strict-dom/src/types/react.js
@@ -7,6 +7,8 @@
  * @flow strict
  */
 
+import * as React from 'react';
+
 export type CallbackRef<T> = (node: T | null) => mixed;
 export type ObjectRef<T> = { -current: T, ... };
-export type Ref<T> = CallbackRef<T> | ObjectRef<T | null>;
+export type Ref<T> = CallbackRef<T> | ObjectRef<T | null> | React.RefObject<T>;
diff --git a/packages/react-strict-dom/src/types/value-parser.js b/packages/react-strict-dom/src/types/value-parser.js
new file mode 100644
index 00000000..2fb599c0
--- /dev/null
+++ b/packages/react-strict-dom/src/types/value-parser.js
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow strict
+ */
+
+export type CSSValueASTNode =
+  | {
+      type: 'word' | 'unicode-range',
+      value: string,
+      sourceIndex: number,
+      sourceEndIndex: number
+    }
+  | {
+      type: 'string' | 'comment',
+      value: string,
+      quote: '"' | "'",
+      sourceIndex: number,
+      sourceEndIndex: number,
+      unclosed?: boolean
+    }
+  | {
+      type: 'comment',
+      value: string,
+      sourceIndex: number,
+      sourceEndIndex: number,
+      unclosed?: boolean
+    }
+  | {
+      type: 'div',
+      value: ',' | '/' | ':',
+      sourceIndex: number,
+      sourceEndIndex: number,
+      before: '' | ' ' | '  ' | '   ',
+      after: '' | ' ' | '  ' | '   '
+    }
+  | {
+      type: 'space',
+      value: ' ' | '  ' | '   ',
+      sourceIndex: number,
+      sourceEndIndex: number
+    }
+  | {
+      type: 'function',
+      value: string,
+      before: '' | ' ' | '  ' | '   ',
+      after: '' | ' ' | '  ' | '   ',
+      nodes: Array<CSSValueASTNode>,
+      unclosed?: boolean,
+      sourceIndex: number,
+      sourceEndIndex: number
+    };
diff --git a/packages/react-strict-dom/tests/__snapshots__/compat-test.native.js.snap-native b/packages/react-strict-dom/tests/__snapshots__/compat-test.native.js.snap-native
index 08f72854..49a90201 100644
--- a/packages/react-strict-dom/tests/__snapshots__/compat-test.native.js.snap-native
+++ b/packages/react-strict-dom/tests/__snapshots__/compat-test.native.js.snap-native
@@ -68,7 +68,7 @@ exports[`<compat.native> "as" equals "view": as=view 1`] = `
   style={
     {
       "boxSizing": "content-box",
-      "paddingInline": 32,
+      "paddingHorizontal": 32,
       "position": "static",
     }
   }
@@ -82,7 +82,7 @@ exports[`<compat.native> default: default 1`] = `
   style={
     {
       "boxSizing": "content-box",
-      "paddingInline": 32,
+      "paddingHorizontal": 32,
       "position": "static",
     }
   }
diff --git a/packages/react-strict-dom/tests/__snapshots__/css-test.native.js.snap-native b/packages/react-strict-dom/tests/__snapshots__/css-test.native.js.snap-native
index 610def8d..e936d759 100644
--- a/packages/react-strict-dom/tests/__snapshots__/css-test.native.js.snap-native
+++ b/packages/react-strict-dom/tests/__snapshots__/css-test.native.js.snap-native
@@ -1,5 +1,77 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`css.types.* types.angle: tokens 1`] = `
+{
+  "angle": "var(--angle__id__6)",
+}
+`;
+
+exports[`css.types.* types.color: tokens 1`] = `
+{
+  "color": "var(--color__id__7)",
+}
+`;
+
+exports[`css.types.* types.image: tokens 1`] = `
+{
+  "image": "var(--image__id__8)",
+}
+`;
+
+exports[`css.types.* types.integer: tokens 1`] = `
+{
+  "integer": "var(--integer__id__9)",
+}
+`;
+
+exports[`css.types.* types.length: tokens 1`] = `
+{
+  "length": "var(--length__id__10)",
+}
+`;
+
+exports[`css.types.* types.lengthPercentage: tokens 1`] = `
+{
+  "lengthPercentage": "var(--lengthPercentage__id__11)",
+}
+`;
+
+exports[`css.types.* types.number: tokens 1`] = `
+{
+  "number": "var(--number__id__12)",
+}
+`;
+
+exports[`css.types.* types.percentage: tokens 1`] = `
+{
+  "percentage": "var(--percentage__id__13)",
+}
+`;
+
+exports[`css.types.* types.resolution: tokens 1`] = `
+{
+  "resolution": "var(--resolution__id__14)",
+}
+`;
+
+exports[`css.types.* types.time: tokens 1`] = `
+{
+  "time": "var(--time__id__15)",
+}
+`;
+
+exports[`css.types.* types.transformFunction: tokens 1`] = `
+{
+  "transformFunction": "var(--transformFunction__id__16)",
+}
+`;
+
+exports[`css.types.* types.url: tokens 1`] = `
+{
+  "url": "var(--url__id__17)",
+}
+`;
+
 exports[`properties: custom property css.createTheme: css.__customProperties 1`] = `
 {
   "rootColor__id__1": "red",
@@ -72,6 +144,14 @@ exports[`properties: general animationDuration 1`] = `
 }
 `;
 
+exports[`properties: general backgroundImage 1`] = `
+{
+  "style": {
+    "experimental_backgroundImage": "linear-gradient(to bottom right, yellow, green)",
+  },
+}
+`;
+
 exports[`properties: general borderColor 1`] = `
 {
   "style": {
@@ -153,14 +233,6 @@ exports[`properties: general caretColor: unsupported caret color 1`] = `
 }
 `;
 
-exports[`properties: general direction: inherit 1`] = `
-{
-  "style": {
-    "direction": "inherit",
-  },
-}
-`;
-
 exports[`properties: general direction: ltr 1`] = `
 {
   "style": {
@@ -1022,7 +1094,10 @@ exports[`properties: logical direction inlineSize: minInlineSize after minWidth
 exports[`properties: logical direction inset: inset 1`] = `
 {
   "style": {
-    "inset": 1,
+    "bottom": 1,
+    "end": 1,
+    "start": 1,
+    "top": 1,
   },
 }
 `;
@@ -1031,7 +1106,6 @@ exports[`properties: logical direction inset: inset vs top 1`] = `
 {
   "style": {
     "bottom": 100,
-    "insetBlockStart": 3,
     "left": 10,
     "right": 10,
     "top": 100,
@@ -1042,7 +1116,8 @@ exports[`properties: logical direction inset: inset vs top 1`] = `
 exports[`properties: logical direction inset: insetBlock 1`] = `
 {
   "style": {
-    "insetBlock": 2,
+    "bottom": 2,
+    "top": 2,
   },
 }
 `;
@@ -1051,7 +1126,6 @@ exports[`properties: logical direction inset: insetBlock vs top 1`] = `
 {
   "style": {
     "bottom": 100,
-    "insetBlockStart": 3,
     "top": 100,
   },
 }
@@ -1060,7 +1134,7 @@ exports[`properties: logical direction inset: insetBlock vs top 1`] = `
 exports[`properties: logical direction inset: insetBlockEnd 1`] = `
 {
   "style": {
-    "insetBlockEnd": 4,
+    "bottom": 4,
   },
 }
 `;
@@ -1069,7 +1143,6 @@ exports[`properties: logical direction inset: insetBlockEnd vs bottom 1`] = `
 {
   "style": {
     "bottom": 100,
-    "insetBlockEnd": 4,
   },
 }
 `;
@@ -1077,7 +1150,7 @@ exports[`properties: logical direction inset: insetBlockEnd vs bottom 1`] = `
 exports[`properties: logical direction inset: insetBlockStart 1`] = `
 {
   "style": {
-    "insetBlockStart": 3,
+    "top": 3,
   },
 }
 `;
@@ -1085,7 +1158,6 @@ exports[`properties: logical direction inset: insetBlockStart 1`] = `
 exports[`properties: logical direction inset: insetBlockStart vs top 1`] = `
 {
   "style": {
-    "insetBlockStart": 3,
     "top": 100,
   },
 }
@@ -1094,7 +1166,8 @@ exports[`properties: logical direction inset: insetBlockStart vs top 1`] = `
 exports[`properties: logical direction inset: insetInline 1`] = `
 {
   "style": {
-    "insetInline": 5,
+    "end": 5,
+    "start": 5,
   },
 }
 `;
@@ -1102,7 +1175,7 @@ exports[`properties: logical direction inset: insetInline 1`] = `
 exports[`properties: logical direction inset: insetInlineEnd 1`] = `
 {
   "style": {
-    "insetInlineEnd": 7,
+    "end": 7,
   },
 }
 `;
@@ -1110,7 +1183,7 @@ exports[`properties: logical direction inset: insetInlineEnd 1`] = `
 exports[`properties: logical direction inset: insetInlineStart 1`] = `
 {
   "style": {
-    "insetInlineStart": 6,
+    "start": 6,
   },
 }
 `;
@@ -1118,7 +1191,7 @@ exports[`properties: logical direction inset: insetInlineStart 1`] = `
 exports[`properties: logical direction margin: marginBlock 1`] = `
 {
   "style": {
-    "marginBlock": 1,
+    "marginVertical": 1,
   },
 }
 `;
@@ -1126,7 +1199,7 @@ exports[`properties: logical direction margin: marginBlock 1`] = `
 exports[`properties: logical direction margin: marginBlockEnd 1`] = `
 {
   "style": {
-    "marginBlockEnd": 3,
+    "marginBottom": 3,
   },
 }
 `;
@@ -1134,7 +1207,7 @@ exports[`properties: logical direction margin: marginBlockEnd 1`] = `
 exports[`properties: logical direction margin: marginBlockStart 1`] = `
 {
   "style": {
-    "marginBlockStart": 2,
+    "marginTop": 2,
   },
 }
 `;
@@ -1142,7 +1215,7 @@ exports[`properties: logical direction margin: marginBlockStart 1`] = `
 exports[`properties: logical direction margin: marginInline 1`] = `
 {
   "style": {
-    "marginInline": 1,
+    "marginHorizontal": 1,
   },
 }
 `;
@@ -1150,7 +1223,7 @@ exports[`properties: logical direction margin: marginInline 1`] = `
 exports[`properties: logical direction margin: marginInlineEnd 1`] = `
 {
   "style": {
-    "marginInlineEnd": 3,
+    "marginEnd": 3,
   },
 }
 `;
@@ -1158,7 +1231,7 @@ exports[`properties: logical direction margin: marginInlineEnd 1`] = `
 exports[`properties: logical direction margin: marginInlineStart 1`] = `
 {
   "style": {
-    "marginInlineStart": 2,
+    "marginStart": 2,
   },
 }
 `;
@@ -1166,7 +1239,7 @@ exports[`properties: logical direction margin: marginInlineStart 1`] = `
 exports[`properties: logical direction padding: paddingBlock 1`] = `
 {
   "style": {
-    "paddingBlock": 1,
+    "paddingVertical": 1,
   },
 }
 `;
@@ -1174,7 +1247,7 @@ exports[`properties: logical direction padding: paddingBlock 1`] = `
 exports[`properties: logical direction padding: paddingBlockEnd 1`] = `
 {
   "style": {
-    "paddingBlockEnd": 3,
+    "paddingBottom": 3,
   },
 }
 `;
@@ -1182,7 +1255,7 @@ exports[`properties: logical direction padding: paddingBlockEnd 1`] = `
 exports[`properties: logical direction padding: paddingBlockStart 1`] = `
 {
   "style": {
-    "paddingBlockStart": 2,
+    "paddingTop": 2,
   },
 }
 `;
@@ -1190,7 +1263,7 @@ exports[`properties: logical direction padding: paddingBlockStart 1`] = `
 exports[`properties: logical direction padding: paddingInline 1`] = `
 {
   "style": {
-    "paddingInline": 1,
+    "paddingHorizontal": 1,
   },
 }
 `;
@@ -1198,7 +1271,7 @@ exports[`properties: logical direction padding: paddingInline 1`] = `
 exports[`properties: logical direction padding: paddingInlineEnd 1`] = `
 {
   "style": {
-    "paddingInlineEnd": 3,
+    "paddingEnd": 3,
   },
 }
 `;
@@ -1206,7 +1279,7 @@ exports[`properties: logical direction padding: paddingInlineEnd 1`] = `
 exports[`properties: logical direction padding: paddingInlineStart 1`] = `
 {
   "style": {
-    "paddingInlineStart": 2,
+    "paddingStart": 2,
   },
 }
 `;
diff --git a/packages/react-strict-dom/tests/__snapshots__/html-test.native.js.snap-native b/packages/react-strict-dom/tests/__snapshots__/html-test.native.js.snap-native
index 8807fc0d..425321f4 100644
--- a/packages/react-strict-dom/tests/__snapshots__/html-test.native.js.snap-native
+++ b/packages/react-strict-dom/tests/__snapshots__/html-test.native.js.snap-native
@@ -2022,3 +2022,54 @@ exports[`<html.*> style polyfills lineClamp workaround for Android 1`] = `
   text
 </Text>
 `;
+
+exports[`<html.*> viewport width lengths are scaled according to viewport width: scaled lengths 1`] = `
+<ViewNativeComponent
+  ref={[Function]}
+  style={
+    {
+      "borderWidth": 0.75,
+      "boxSizing": "content-box",
+      "height": 75,
+      "margin": 15,
+      "padding": 24,
+      "position": "static",
+      "transform": [
+        {
+          "translateX": 7.5,
+        },
+        {
+          "translateY": 15,
+        },
+      ],
+      "width": 150,
+    }
+  }
+>
+  <Text
+    ref={[Function]}
+    style={
+      {
+        "boxSizing": "content-box",
+        "fontSize": 24,
+        "lineHeight": 24,
+        "position": "static",
+      }
+    }
+  >
+    Scaled content
+  </Text>
+  <Text
+    ref={[Function]}
+    style={
+      {
+        "boxSizing": "content-box",
+        "fontSize": 18,
+        "position": "static",
+      }
+    }
+  >
+    Scaled content
+  </Text>
+</ViewNativeComponent>
+`;
diff --git a/packages/react-strict-dom/tests/css-test.native.js b/packages/react-strict-dom/tests/css-test.native.js
index 21e6e654..2acbe74c 100644
--- a/packages/react-strict-dom/tests/css-test.native.js
+++ b/packages/react-strict-dom/tests/css-test.native.js
@@ -141,11 +141,10 @@ describe('properties: general', () => {
   test('backgroundImage', () => {
     const styles = css.create({
       root: {
-        backgroundImage: 'url(https://placehold.it/300/300)'
+        backgroundImage: 'linear-gradient(to bottom right, yellow, green)'
       }
     });
-    css.props.call(mockOptions, styles.root);
-    expect(console.warn).toHaveBeenCalled();
+    expect(css.props.call(mockOptions, styles.root)).toMatchSnapshot();
   });
 
   test('borderColor', () => {
@@ -242,9 +241,6 @@ describe('properties: general', () => {
 
   test('direction', () => {
     const styles = css.create({
-      inherit: {
-        direction: 'inherit'
-      },
       ltr: {
         direction: 'ltr'
       },
@@ -252,13 +248,36 @@ describe('properties: general', () => {
         direction: 'rtl'
       }
     });
-    expect(css.props.call(mockOptions, styles.inherit)).toMatchSnapshot(
-      'inherit'
-    );
     expect(css.props.call(mockOptions, styles.ltr)).toMatchSnapshot('ltr');
     expect(css.props.call(mockOptions, styles.rtl)).toMatchSnapshot('rtl');
   });
 
+  test('display', () => {
+    const styles = css.create({
+      flex: {
+        display: 'flex'
+      },
+      align: {
+        alignItems: 'center'
+      },
+      row: {
+        flexDirection: 'row'
+      }
+    });
+    css.props.call(mockOptions, [styles.flex, styles.align]);
+    expect(console.error).not.toHaveBeenCalledWith(
+      expect.stringContaining(
+        '"display:flex" is required to use flexbox properties'
+      )
+    );
+    css.props.call(mockOptions, [styles.align, styles.row]);
+    expect(console.error).toHaveBeenCalledWith(
+      expect.stringContaining(
+        '"display:flex" is required to use flexbox properties'
+      )
+    );
+  });
+
   test('filter', () => {
     const { underTest } = css.create({
       underTest: {
@@ -1822,3 +1841,92 @@ describe('queries: @media', () => {
     expect(props.style.color).toBe('white');
   });
 });
+
+describe('css.types.*', () => {
+  test('types.angle', () => {
+    const tokens = css.defineVars({
+      angle: css.types.angle('40deg')
+    });
+    expect(tokens).toMatchSnapshot('tokens');
+  });
+
+  test('types.color', () => {
+    const tokens = css.defineVars({
+      color: css.types.color({
+        default: 'blue',
+        '@media (prefers-color-scheme: dark)': 'lightblue'
+      })
+    });
+    expect(tokens).toMatchSnapshot('tokens');
+  });
+
+  test('types.image', () => {
+    const tokens = css.defineVars({
+      image: css.types.image('./jpg')
+    });
+    expect(tokens).toMatchSnapshot('tokens');
+  });
+
+  test('types.integer', () => {
+    const tokens = css.defineVars({
+      integer: css.types.integer(4)
+    });
+    expect(tokens).toMatchSnapshot('tokens');
+  });
+
+  test('types.length', () => {
+    const tokens = css.defineVars({
+      length: css.types.length('4px')
+    });
+    expect(tokens).toMatchSnapshot('tokens');
+  });
+
+  test('types.lengthPercentage', () => {
+    const tokens = css.defineVars({
+      lengthPercentage: css.types.lengthPercentage('100%')
+    });
+    expect(tokens).toMatchSnapshot('tokens');
+  });
+
+  test('types.number', () => {
+    const tokens = css.defineVars({
+      number: css.types.number(4.4)
+    });
+    expect(tokens).toMatchSnapshot('tokens');
+  });
+
+  test('types.percentage', () => {
+    const tokens = css.defineVars({
+      percentage: css.types.percentage('100%')
+    });
+    expect(tokens).toMatchSnapshot('tokens');
+  });
+
+  test('types.resolution', () => {
+    const tokens = css.defineVars({
+      resolution: css.types.resolution('96dpi')
+    });
+    expect(tokens).toMatchSnapshot('tokens');
+  });
+
+  test('types.time', () => {
+    const tokens = css.defineVars({
+      time: css.types.time('1s')
+    });
+    expect(tokens).toMatchSnapshot('tokens');
+  });
+
+  test('types.transformFunction', () => {
+    const tokens = css.defineVars({
+      transformFunction: css.types.transformFunction('translateX(10px)')
+    });
+    expect(tokens).toMatchSnapshot('tokens');
+  });
+
+  test('types.url', () => {
+    const tokens = css.defineVars({
+      url: css.types.url('https://foobar.com')
+    });
+    expect(tokens).toMatchSnapshot('tokens');
+  });
+});
diff --git a/packages/react-strict-dom/tests/html-test.native.js b/packages/react-strict-dom/tests/html-test.native.js
index 27ff635e..b2d1cad8 100644
--- a/packages/react-strict-dom/tests/html-test.native.js
+++ b/packages/react-strict-dom/tests/html-test.native.js
@@ -9,6 +9,15 @@ import React from 'react';
 import { css, html, contexts } from 'react-strict-dom';
 import { act, create } from 'react-test-renderer';
 
+global.DOMRect = class DOMRect {
+  constructor(x, y, width, height) {
+    this.x = x;
+    this.y = y;
+    this.width = width;
+    this.height = height;
+  }
+};
+
 describe('<html.*>', () => {
   beforeEach(() => {
     // avoid console messages for these tests
@@ -1554,6 +1563,20 @@ describe('<html.*>', () => {
     });
     */
 
+    // We shouldn't create a handle if there is no underlying node
+    test('node is null', () => {
+      act(() => {
+        create(
+          <html.input
+            ref={(node) => {
+              expect(() => node.getBoundingClientRect()).toThrow();
+            }}
+          />,
+          { createNodeMock: () => null }
+        );
+      });
+    });
+
     [
       'animate',
       'click',
@@ -1630,4 +1653,85 @@ describe('<html.*>', () => {
     });
     */
   });
+
+  describe('viewport width', () => {
+    const ReactNative = require('../src/native/react-native');
+
+    beforeEach(() => {
+      jest
+        .spyOn(ReactNative, 'useWindowDimensions')
+        .mockReturnValue({ width: 960 });
+    });
+
+    afterEach(() => {
+      ReactNative.useWindowDimensions.mockRestore();
+    });
+
+    test('lengths are scaled according to viewport width', () => {
+      const { ViewportProvider } = contexts;
+
+      const styles = css.create({
+        container: {
+          borderWidth: '1px',
+          height: '100px',
+          margin: '20px',
+          padding: '2em',
+          width: '200px',
+          transform: 'translateX(10px) translateY(20px)'
+        },
+        text: {
+          fontSize: '2rem',
+          lineHeight: '1em'
+        },
+        textOther: {
+          fontSize: 24
+        }
+      });
+
+      let root;
+      act(() => {
+        root = create(
+          <ViewportProvider viewportWidth={1280}>
+            <html.div style={styles.container}>
+              <html.span style={styles.text}>Scaled content</html.span>
+              <html.span style={styles.textOther}>Scaled content</html.span>
+            </html.div>
+          </ViewportProvider>
+        );
+      });
+
+      // scale factor 0.75
+      expect(root.toJSON()).toMatchSnapshot('scaled lengths');
+    });
+
+    test('getClientBoundingRect() returns scaled values', () => {
+      const { ViewportProvider } = contexts;
+
+      function createNodeMock(element) {
+        const obj = {};
+        obj.addEventListener_unstable = () => {};
+        obj.blur = () => {};
+        obj.focus = () => {};
+        obj.removeEventListener_unstable = () => {};
+        obj.getBoundingClientRect = () => new DOMRect(21, 21, 99, 99);
+        return obj;
+      }
+
+      let scaledDomRect;
+      act(() => {
+        create(
+          <ViewportProvider viewportWidth={1280}>
+            <html.input
+              ref={(node) => {
+                scaledDomRect = node.getBoundingClientRect();
+              }}
+            />
+          </ViewportProvider>,
+          { createNodeMock }
+        );
+      });
+
+      expect(scaledDomRect).toEqual(new DOMRect(28, 28, 132, 132));
+    });
+  });
 });
diff --git a/packages/react-strict-dom/tools/jest.config.js b/packages/react-strict-dom/tools/jest.config.js
index 5f2a5ab4..2add3fad 100644
--- a/packages/react-strict-dom/tools/jest.config.js
+++ b/packages/react-strict-dom/tools/jest.config.js
@@ -14,7 +14,7 @@ module.exports = {
     '<rootDir>/src/**/*.{js,jsx}',
     // exclude
     '!<rootDir>/src/**/__tests__/**',
-    '!<rootDir>/src/native/stylex/typed-om/**',
+    '!<rootDir>/src/native/css/typed-om/**',
     '!<rootDir>/src/shared/logUtils.js',
     '!<rootDir>/src/types/**'
   ],
diff --git a/tools/flow-typed/npm/postcss-value-parser_vx.x.x.js b/tools/flow-typed/npm/postcss-value-parser_vx.x.x.js
index 449e3860..9fdefebc 100644
--- a/tools/flow-typed/npm/postcss-value-parser_vx.x.x.js
+++ b/tools/flow-typed/npm/postcss-value-parser_vx.x.x.js
@@ -7,75 +7,75 @@
  * @flow strict
  */
 
-type PostCSSValueASTNode =
-  | {
-      type: 'word' | 'unicode-range',
-      value: string,
-      sourceIndex: number,
-      sourceEndIndex: number
-    }
-  | {
-      type: 'string' | 'comment',
-      value: string,
-      quote: '"' | "'",
-      sourceIndex: number,
-      sourceEndIndex: number,
-      unclosed?: boolean
-    }
-  | {
-      type: 'comment',
-      value: string,
-      sourceIndex: number,
-      sourceEndIndex: number,
-      unclosed?: boolean
-    }
-  | {
-      type: 'div',
-      value: ',' | '/' | ':',
-      sourceIndex: number,
-      sourceEndIndex: number,
-      before: '' | ' ' | '  ' | '   ',
-      after: '' | ' ' | '  ' | '   '
-    }
-  | {
-      type: 'space',
-      value: ' ' | '  ' | '   ',
-      sourceIndex: number,
-      sourceEndIndex: number
-    }
-  | {
-      type: 'function',
-      value: string,
-      before: '' | ' ' | '  ' | '   ',
-      after: '' | ' ' | '  ' | '   ',
-      nodes: Array<PostCSSValueASTNode>,
-      unclosed?: boolean,
-      sourceIndex: number,
-      sourceEndIndex: number
-    };
+declare module 'postcss-value-parser' {
+  declare export type PostCSSValueASTNode =
+    | {
+        type: 'word' | 'unicode-range',
+        value: string,
+        sourceIndex: number,
+        sourceEndIndex: number
+      }
+    | {
+        type: 'string' | 'comment',
+        value: string,
+        quote: '"' | "'",
+        sourceIndex: number,
+        sourceEndIndex: number,
+        unclosed?: boolean
+      }
+    | {
+        type: 'comment',
+        value: string,
+        sourceIndex: number,
+        sourceEndIndex: number,
+        unclosed?: boolean
+      }
+    | {
+        type: 'div',
+        value: ',' | '/' | ':',
+        sourceIndex: number,
+        sourceEndIndex: number,
+        before: '' | ' ' | '  ' | '   ',
+        after: '' | ' ' | '  ' | '   '
+      }
+    | {
+        type: 'space',
+        value: ' ' | '  ' | '   ',
+        sourceIndex: number,
+        sourceEndIndex: number
+      }
+    | {
+        type: 'function',
+        value: string,
+        before: '' | ' ' | '  ' | '   ',
+        after: '' | ' ' | '  ' | '   ',
+        nodes: Array<PostCSSValueASTNode>,
+        unclosed?: boolean,
+        sourceIndex: number,
+        sourceEndIndex: number
+      };
 
-declare interface PostCSSValueAST {
-  nodes: Array<PostCSSValueASTNode>;
-  walk(
-    callback: (PostCSSValueASTNode, number, PostCSSValueAST) => ?false,
-    bubble?: boolean
-  ): void;
-}
+  declare interface PostCSSValueAST {
+    nodes: Array<PostCSSValueASTNode>;
+    walk(
+      callback: (PostCSSValueASTNode, number, PostCSSValueAST) => ?false,
+      bubble?: boolean
+    ): void;
+  }
 
-type PostCSSValueParser = {
-  (string): PostCSSValueAST,
-  unit(string): { number: string, unit: string } | false,
-  stringify(
-    nodes: PostCSSValueAST | PostCSSValueASTNode | PostCSSValueAST['nodes'],
-    custom?: (PostCSSValueASTNode) => string
-  ): string,
-  walk(
-    ast: PostCSSValueAST,
-    callback: (PostCSSValueASTNode, number, PostCSSValueAST) => ?false,
-    bubble?: boolean
-  ): void
-};
+  declare type PostCSSValueParser = {
+    (string): PostCSSValueAST,
+    unit(string): { number: string, unit: string } | false,
+    stringify(
+      nodes: PostCSSValueAST | PostCSSValueASTNode | PostCSSValueAST['nodes'],
+      custom?: (PostCSSValueASTNode) => string
+    ): string,
+    walk(
+      ast: PostCSSValueAST,
+      callback: (PostCSSValueASTNode, number, PostCSSValueAST) => ?false,
+      bubble?: boolean
+    ): void
+  };
 
-declare module 'postcss-value-parser' {
   declare module.exports: PostCSSValueParser;
 }
