diff --git a/src/__tests__/native/absolute-units.test.tsx b/src/__tests__/native/absolute-units.test.tsx
new file mode 100644
index 00000000..236ae38f
--- /dev/null
+++ b/src/__tests__/native/absolute-units.test.tsx
@@ -0,0 +1,62 @@
+import { render, screen } from "@testing-library/react-native";
+import { View } from "react-native-css/components/View";
+import { registerCSS, testID } from "react-native-css/jest";
+
+// css-values-4 §6.2: 1in = 2.54cm = 25.4mm = 101.6Q = 72pt = 6pc = 96px
+const UNITS = [
+ ["1in", 96],
+ ["2.54cm", 96],
+ ["10mm", 37.7953],
+ ["4Q", 3.7795],
+ ["3pt", 4],
+ ["1pc", 16],
+] as const;
+
+function styleFor(className: string): unknown {
+ render();
+ return screen.getByTestId(testID).props.style;
+}
+
+test.each(UNITS)("width: %s is %d pixels", (length, pixels) => {
+ registerCSS(`.subject { width: ${length}; }`);
+
+ expect(styleFor("subject")).toStrictEqual({ width: pixels });
+});
+
+test.each(UNITS)(
+ "a variable holding %s resolves to %d pixels at runtime",
+ (length, pixels) => {
+ registerCSS(`
+ .theme { --width: ${length}; }
+ .subject { --width: ${length}; width: var(--width); }
+ `);
+
+ expect(styleFor("theme subject")).toStrictEqual({ width: pixels });
+ },
+);
+
+test("an absolute length converts when rem is not inlined", () => {
+ registerCSS(`.subject { width: 1in; }`, { inlineRem: false });
+
+ expect(styleFor("subject")).toStrictEqual({ width: 96 });
+});
+
+test("a media query reads an absolute length", () => {
+ registerCSS(`
+ @media (min-width: 4in) { .subject { width: 1pc; } }
+ @media (max-width: 4in) { .subject { height: 1pc; } }
+ `);
+
+ expect(styleFor("subject")).toStrictEqual({ width: 16 });
+});
+
+test("a shorthand converts each of its lengths", () => {
+ registerCSS(`.subject { margin: 1in 1pc; }`);
+
+ expect(styleFor("subject")).toStrictEqual({
+ marginTop: 96,
+ marginRight: 16,
+ marginBottom: 96,
+ marginLeft: 16,
+ });
+});
diff --git a/src/compiler/declarations.ts b/src/compiler/declarations.ts
index 9ca2aab8..f4e01f3d 100644
--- a/src/compiler/declarations.ts
+++ b/src/compiler/declarations.ts
@@ -36,7 +36,7 @@ import type {
UnresolvedColor,
} from "lightningcss";
-import { isStyleFunction } from "../utilities";
+import { ABSOLUTE_UNIT_PIXELS, isStyleFunction } from "../utilities";
import { isMappingProperty } from "./atRules";
import type {
StyleDescriptor,
@@ -1461,6 +1461,7 @@ export function parseLength(
case "q":
case "pt":
case "pc":
+ return round(length.value * ABSOLUTE_UNIT_PIXELS[length.unit]);
case "ex":
case "rex":
case "ch":
diff --git a/src/utilities/absolute-units.ts b/src/utilities/absolute-units.ts
new file mode 100644
index 00000000..42d0e27c
--- /dev/null
+++ b/src/utilities/absolute-units.ts
@@ -0,0 +1,9 @@
+/** css-values-4 §6.2 fixes each absolute length unit against the inch (1in = 96px) rather than the device. */
+export const ABSOLUTE_UNIT_PIXELS = {
+ cm: 96 / 2.54,
+ in: 96,
+ mm: 96 / 25.4,
+ pc: 16,
+ pt: 96 / 72,
+ q: 96 / 101.6,
+} as const satisfies Record;
diff --git a/src/utilities/index.ts b/src/utilities/index.ts
index 0c95da63..96b0ee30 100644
--- a/src/utilities/index.ts
+++ b/src/utilities/index.ts
@@ -1,3 +1,4 @@
export * from "./specificity";
export * from "./style-descriptor";
export * from "./dot-notation.types";
+export * from "./absolute-units";