diff --git a/src/__tests__/native/color-mix.test.tsx b/src/__tests__/native/color-mix.test.tsx
new file mode 100644
index 0000000..bed3cea
--- /dev/null
+++ b/src/__tests__/native/color-mix.test.tsx
@@ -0,0 +1,47 @@
+import { render, screen } from "@testing-library/react-native";
+import { View } from "react-native-css/components/View";
+import { registerCSS, testID } from "react-native-css/jest";
+
+test("color-mix() - keyword", () => {
+ registerCSS(
+ `.test {
+ --bg: red;
+ @supports (color: color-mix(in lab, red, red)) {
+ background-color: color-mix(in oklab, var(--bg) 50%, transparent);
+ }
+ }
+ `,
+ {
+ inlineVariables: false,
+ },
+ );
+
+ render();
+ const component = screen.getByTestId(testID);
+
+ expect(component.props.style).toStrictEqual({
+ backgroundColor: "rgba(255, 0, 0, 0.5)",
+ });
+});
+
+test("color-mix() - oklch", () => {
+ registerCSS(
+ `.test {
+ --bg: oklch(0.577 0.245 27.325);
+ @supports (color: color-mix(in lab, red, red)) {
+ background-color: color-mix(in oklab, var(--bg) 50%, transparent);
+ }
+ }
+ `,
+ {
+ inlineVariables: false,
+ },
+ );
+
+ render();
+ const component = screen.getByTestId(testID);
+
+ expect(component.props.style).toStrictEqual({
+ backgroundColor: "rgba(231, 0, 11, 0.5)",
+ });
+});
diff --git a/src/compiler/declarations.ts b/src/compiler/declarations.ts
index ed6137c..df9e6d1 100644
--- a/src/compiler/declarations.ts
+++ b/src/compiler/declarations.ts
@@ -1228,6 +1228,8 @@ export function parseUnparsed(
builder,
property,
);
+ case "color-mix":
+ return parseColorMix(tokenOrValue.value.arguments, builder, property);
default: {
builder.addWarning("value", `${tokenOrValue.value.name}()`);
return;
@@ -2633,6 +2635,100 @@ export function parseCalcFn(
return;
}
+export function parseColorMix(
+ tokens: TokenOrValue[],
+ builder: StylesheetBuilder,
+ property: string,
+): StyleDescriptor {
+ const [inToken, whitespace, colorSpace, comma, ...rest] = tokens;
+ if (
+ typeof inToken !== "object" ||
+ inToken.type !== "token" ||
+ inToken.value.type !== "ident" ||
+ inToken.value.value !== "in"
+ ) {
+ return;
+ }
+
+ if (
+ typeof whitespace !== "object" ||
+ whitespace.type !== "token" ||
+ whitespace.value.type !== "white-space"
+ ) {
+ return;
+ }
+
+ if (
+ typeof comma !== "object" ||
+ comma.type !== "token" ||
+ comma.value.type !== "comma"
+ ) {
+ return;
+ }
+
+ const colorSpaceArg = parseUnparsed(colorSpace, builder, property);
+ if (typeof colorSpaceArg !== "string") {
+ return;
+ }
+
+ let nextToken = rest.shift();
+
+ const leftColorArg = parseUnparsed(nextToken, builder, property);
+
+ if (!leftColorArg) {
+ return;
+ }
+
+ nextToken = rest.shift();
+
+ let leftColorPercentage: StyleDescriptor | undefined;
+ if (nextToken?.type !== "token" || nextToken.value.type !== "comma") {
+ leftColorPercentage = parseUnparsed(nextToken, builder, property);
+ nextToken = rest.shift();
+ }
+
+ if (
+ typeof nextToken !== "object" ||
+ nextToken.type !== "token" ||
+ nextToken.value.type !== "comma"
+ ) {
+ return;
+ }
+
+ nextToken = rest.shift();
+
+ const rightColorArg = parseUnparsed(nextToken, builder, property);
+
+ if (rightColorArg === "transparent") {
+ // Ignore the rest, treat as single color with alpha
+ return [{}, "colorMix", [colorSpaceArg, leftColorArg, leftColorPercentage]];
+ }
+
+ nextToken = rest.shift();
+ let rightColorPercentage: StyleDescriptor | undefined;
+ if (nextToken?.type !== "token" || nextToken.value.type !== "comma") {
+ rightColorPercentage = parseUnparsed(nextToken, builder, property);
+ nextToken = rest.shift();
+ }
+
+ // We should have expired all tokens now
+ if (nextToken) {
+ return;
+ }
+
+ return [
+ {},
+ "colorMix",
+ [
+ colorSpaceArg,
+ leftColorArg,
+ leftColorPercentage,
+ rightColorArg,
+ rightColorPercentage,
+ ],
+ ];
+}
+
export function parseCalcArguments(
[...args]: TokenOrValue[],
builder: StylesheetBuilder,
diff --git a/src/compiler/supports.ts b/src/compiler/supports.ts
index 1558d61..c3370dc 100644
--- a/src/compiler/supports.ts
+++ b/src/compiler/supports.ts
@@ -23,4 +23,6 @@ export function supportsConditionValid(condition: SupportsCondition): boolean {
const declarations: Record = {
// We don't actually support this, but its needed for Tailwind CSS
"-moz-orient": ["inline"],
+ // Special text used by TailwindCSS. We should probably change this to all color-mix
+ "color": ["color-mix(in lab, red, red)"],
};
diff --git a/src/native/styles/functions/color-mix.ts b/src/native/styles/functions/color-mix.ts
new file mode 100644
index 0000000..27a95e0
--- /dev/null
+++ b/src/native/styles/functions/color-mix.ts
@@ -0,0 +1,68 @@
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+import type { PlainColorObject } from "colorjs.io";
+import {
+ ColorSpace,
+ to as convert,
+ mix,
+ OKLab,
+ P3,
+ parse,
+ sRGB,
+ type ColorConstructor,
+} from "colorjs.io/fn";
+
+import type { StyleFunctionResolver } from "../resolve";
+
+ColorSpace.register(sRGB);
+ColorSpace.register(P3);
+ColorSpace.register(OKLab);
+
+export const colorMix: StyleFunctionResolver = (resolveValue, value) => {
+ const args = resolveValue(value[2]);
+
+ if (!Array.isArray(args) || args.length < 3) {
+ return;
+ }
+
+ try {
+ const space = args.shift();
+
+ let left: ColorConstructor | PlainColorObject = parse(
+ args.shift() as string,
+ );
+
+ let next = args.shift();
+
+ if (typeof next === "string" && next.endsWith("%")) {
+ left.alpha = parseFloat(next) / 100;
+ next = args.shift();
+ }
+
+ if (next === undefined) {
+ if (left.spaceId !== "srgb") {
+ left = convert(left, "srgb");
+ }
+
+ return `rgba(${(left.coords[0] ?? 0) * 255}, ${(left.coords[1] ?? 0) * 255}, ${(left.coords[2] ?? 0) * 255}, ${left.alpha})`;
+ }
+
+ if (typeof next !== "string") {
+ return;
+ }
+ const right = parse(next);
+
+ next = args.shift();
+ if (next && typeof next === "string" && next.endsWith("%")) {
+ right.alpha = parseFloat(next) / 100;
+ }
+
+ const result = mix(left, right, {
+ space,
+ outputSpace: "srgb",
+ });
+
+ return `rgba(${(result.coords[0] ?? 0) * 255}, ${(result.coords[1] ?? 0) * 255}, ${(result.coords[2] ?? 0) * 255}, ${result.alpha})`;
+ } catch {
+ return;
+ }
+};
diff --git a/src/native/styles/functions/index.ts b/src/native/styles/functions/index.ts
index 3e41961..02f9308 100644
--- a/src/native/styles/functions/index.ts
+++ b/src/native/styles/functions/index.ts
@@ -5,3 +5,4 @@ export * from "./numeric-functions";
export * from "./platform-functions";
export * from "./string-functions";
export * from "./transform-functions";
+export * from "./color-mix";